package modem import ( "errors" "fmt" "log" "math" "os" "os/exec" "strings" "time" gpio "github.com/warthog618/go-gpiocdev" "go.bug.st/serial" ) type modem struct { // Serial stuff port string deviceName string baudrate int inputBuf []byte serialPort serial.Port isAvailable bool // Gpio stuff powerKey int gpioLine *gpio.Line // Other values speed float64 latitude_ns float64 latitude float64 longitude_we float64 longitude float64 lastUpdateTime time.Time } type Modem interface { Init() error SearchPort(isSoft bool) error PowerOn() error PowerOff() error Connect() error Ping() (bool, error) SwitchToGpsMode() error CalculateSpeed(newLatitude, newlongitude float64) Update() error GetInfo() string TestGPS() error } func New() Modem { return &modem{ baudrate: 115200, powerKey: 6, lastUpdateTime: time.Now(), inputBuf: make([]byte, 128), } } func (m *modem) Init() error { if err := m.SearchPort(true); err != nil { return fmt.Errorf("soft port search: %w", err) } if m.port == "" { if err := m.SearchPort(false); err != nil { return fmt.Errorf("not soft port search: %w", err) } } if m.port == "" { return errors.New("no port is detected") } if err := m.Connect(); err != nil { return fmt.Errorf("connect: %w", err) } if err := m.TestGPS(); err != nil { return fmt.Errorf("testGPS: %w", err) } return nil } func (m *modem) SearchPort(isSoft bool) error { ports := []string{} // Get ports if isSoft { ports = []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"} } else { /**/ log.Print("Search for ports...") out, err := exec.Command("/bin/ls", "/dev").Output() if err != nil { return fmt.Errorf("execute ls command: %w", err) } allPorts := strings.Split(string(out), "\n") for _, p := range allPorts { if len(p) > 2 && p[:3] == "tty" { ports = append(ports, p) } } // slices.Reverse(ports) // TODO why } // Check ports log.Println("Found ports: ", ports) log.Println("Check...") defer func() { if m.serialPort != nil { m.disconnect() // TODO maybe handle } }() for _, p := range ports { if m.serialPort != nil { if err := m.disconnect(); err != nil { return fmt.Errorf("disconnect: %w", err) } } log.Println("Checking port: ", p) if _, err := os.Stat("/dev/" + p); err != nil { continue } if err := m.connect("/dev/" + p); err != nil { log.Println("Error:", fmt.Errorf("connect: %w", err).Error()) continue } // Ping log.Println("Ping...") if ok, err := m.Ping(); err != nil || !ok { if err != nil { return fmt.Errorf("ping error: %w", err) } return errors.New("modem does not ping") } // Check model { ok, err := m.makeAtEchoReqAndCheck("AT+CGMM\r\n", "SIMCOM_SIM7600E-H") if err != nil { return fmt.Errorf("make serial request: %w", err) } if !ok { continue } } log.Print("Found modem on port: ", p) m.port = "/dev/" + p m.isAvailable = true return nil } // return errors.New("no a compatible modem port found") return nil } func (m *modem) Connect() error { return m.connect(m.port) } func (m *modem) Ping() (bool, error) { return m.makeAtEchoReqAndCheck("AT\r\n", "OK") } func (m *modem) SwitchToGpsMode() error { if err := m.serialPort.ResetInputBuffer(); err != nil { return fmt.Errorf("reset input buffer: %w", err) } ans, err := m.makeAtReq("AT+CGPS?\r\n") if err != nil { return fmt.Errorf("make serial request: %w", err) } resp := strings.Split(ans, ":") if !(len(resp) > 1 && resp[0] == "+CGPS") { return errors.New("lost connection while checking gps status") } switch resp[1][1] { case '1': log.Println("GPS already enabled") return nil case '0': ok, err := m.makeAtEchoReqAndCheck("AT+CGPS=1", "OK") if err != nil { return fmt.Errorf("try to switch to gps mode echo reqest: %w", err) } if !ok { return errors.New("lost connection while trying to switch to gps mode") } default: return errors.New("unexpected response of gps status") } return nil } func deg2rad(deg float64) float64 { return deg * (math.Pi / 180) } func (m *modem) CalculateSpeed(newLatitude, newLongitude float64) { log.Println("Calculate speed") earthRad := 6371.0 // TODO ? dLat := deg2rad(math.Abs(newLatitude - m.latitude)) dLon := deg2rad(math.Abs(newLongitude - m.longitude)) a := math.Sin(dLat/2)*math.Sin(dLat/2) + math.Cos(deg2rad(newLatitude))*math.Cos(deg2rad(m.latitude))*math.Sin(dLon/2)*math.Sin(dLon/2) c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) m.speed = earthRad * c / (math.Abs(float64(time.Since(m.lastUpdateTime)))) } func (m *modem) Update() error { log.Println("Update") if !m.isAvailable { log.Println("No connection to module") return nil } m.serialPort.ResetInputBuffer() // MAKE } func (m *modem) GetInfo() string { return "" } func (m *modem) TestGPS() error { return nil } /* TODOs: maybe to store read/write buf in obj QUESTIONS: do many threads? */ /* */