238 lines
4.9 KiB
Go
238 lines
4.9 KiB
Go
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?
|
|
*/
|
|
|
|
/*
|
|
*/
|