sim-modem/api/modem/modem.go

394 lines
8.9 KiB
Go
Raw Normal View History

2024-07-18 16:34:26 +00:00
package modem
import (
2024-07-21 13:05:09 +00:00
"encoding/json"
2024-07-18 16:34:26 +00:00
"errors"
"fmt"
"log"
"math"
"os"
"os/exec"
2024-07-21 13:05:09 +00:00
"slices"
"strconv"
2024-07-18 16:34:26 +00:00
"strings"
"time"
2024-07-22 15:53:34 +00:00
"github.com/CGSG-2021-AE4/modem-test/api/modem/at"
2024-07-18 16:34:26 +00:00
)
2024-07-21 13:05:09 +00:00
type GpsInfo struct {
Latitude float64 `json:"Latitude"`
Longitude float64 `json:"Longitude"`
LatitudeIndicator string `json:"Latitude_indicator"` // North/South
LongitudeIndicator string `json:"Longitude_indicator"` // West/East
Speed float64 `json:"Speed"`
Course float64 `json:"-"`
Altitude float64 `json:"-"`
Date string `json:"-"`
Time string `json:"-"`
}
2024-07-18 16:34:26 +00:00
type modem struct {
// Serial stuff
2024-07-21 13:05:09 +00:00
baudrate int
2024-07-18 16:34:26 +00:00
deviceName string
2024-07-21 13:05:09 +00:00
port at.Port
2024-07-18 16:34:26 +00:00
isAvailable bool
// Gpio stuff
2024-07-21 13:05:09 +00:00
onOffPin gpioPin
2024-07-18 16:34:26 +00:00
// Other values
2024-07-21 13:05:09 +00:00
gpsInfo GpsInfo
2024-07-18 16:34:26 +00:00
lastUpdateTime time.Time
}
type Modem interface {
Init() error
SearchPort(isSoft bool) error
Connect() error
2024-07-21 13:05:09 +00:00
Ping() error
2024-07-18 16:34:26 +00:00
SwitchToGpsMode() error
CalculateSpeed(newLatitude, newlongitude float64)
Update() error
2024-07-21 13:05:09 +00:00
GetInfo() (string, error)
2024-07-18 16:34:26 +00:00
TestGPS() error
}
func New() Modem {
return &modem{
baudrate: 115200,
2024-07-21 13:05:09 +00:00
onOffPin: gpioPin{Pin: 6},
2024-07-18 16:34:26 +00:00
lastUpdateTime: time.Now(),
}
}
func (m *modem) Init() error {
2024-07-21 13:05:09 +00:00
// Turn module on
if err := m.onOffPin.Init(); err != nil {
return fmt.Errorf("gpio pin init: %w", err)
}
2024-07-23 14:26:24 +00:00
// onOffPin.PowerOn()
2024-07-21 13:05:09 +00:00
2024-07-23 14:26:24 +00:00
log.Println("===============================")
2024-07-21 13:05:09 +00:00
// Soft search
2024-07-18 16:34:26 +00:00
if err := m.SearchPort(true); err != nil {
return fmt.Errorf("soft port search: %w", err)
}
2024-07-21 13:05:09 +00:00
// Common search
if m.port == nil {
2024-07-18 16:34:26 +00:00
if err := m.SearchPort(false); err != nil {
return fmt.Errorf("not soft port search: %w", err)
}
}
2024-07-21 13:05:09 +00:00
if m.port == nil {
2024-07-18 16:34:26 +00:00
return errors.New("no port is detected")
}
2024-07-21 13:05:09 +00:00
2024-07-23 14:26:24 +00:00
log.Println("===============================")
2024-07-21 13:05:09 +00:00
// Connect
2024-07-18 16:34:26 +00:00
if err := m.Connect(); err != nil {
return fmt.Errorf("connect: %w", err)
}
2024-07-23 14:26:24 +00:00
log.Println("===============================")
2024-07-21 13:05:09 +00:00
// Tests
2024-07-18 16:34:26 +00:00
if err := m.TestGPS(); err != nil {
return fmt.Errorf("testGPS: %w", err)
}
return nil
}
2024-07-21 13:05:09 +00:00
func (m *modem) checkPort(portName string) error {
// Check device for existance
if _, err := os.Stat(portName); err != nil {
return fmt.Errorf("device does not exist")
}
// Check serial connection
// Connect
2024-07-22 17:37:02 +00:00
m.port = at.New(portName, m.baudrate)
if err := m.port.Connect(); err != nil {
2024-07-21 13:05:09 +00:00
return fmt.Errorf("connect: %w", err)
}
2024-07-22 17:37:02 +00:00
defer m.port.Disconnect() // Do not bother about errors...
2024-07-21 13:05:09 +00:00
// Ping
log.Println("Ping...")
if err := m.Ping(); err != nil {
return fmt.Errorf("ping error: %w", err)
}
// Check model
2024-07-23 09:22:53 +00:00
log.Println("Check model...")
resp, err := m.port.Send("AT+CGMM")
if err != nil {
return fmt.Errorf("get model: %w", err)
}
if !resp.Check() {
return fmt.Errorf("error response: %s", resp)
}
2024-07-23 14:26:24 +00:00
model := strings.Split(resp.String(), "\n")[0]
2024-07-21 13:05:09 +00:00
if err != nil {
return fmt.Errorf("get model: %w", err)
}
2024-07-23 14:26:24 +00:00
rightModel := "SIMCOM_SIM7600E-H"
// log.Printf("[% x]\n [% x]", []byte("SIMCOM_SIM7600E-H"), []byte(model))
if model[:len(rightModel)] != rightModel {
2024-07-21 13:05:09 +00:00
return fmt.Errorf("invalid modem model: %s", model)
}
2024-07-23 14:26:24 +00:00
log.Println("Model right")
2024-07-21 13:05:09 +00:00
return nil
}
2024-07-18 16:34:26 +00:00
2024-07-21 13:05:09 +00:00
func (m *modem) SearchPort(isSoft bool) error {
2024-07-18 16:34:26 +00:00
// Get ports
2024-07-21 13:05:09 +00:00
ports := []string{"ttyUSB1", "ttyUSB2", "ttyUSB3"}
if !isSoft {
ps, err := GetTtyDevices()
2024-07-18 16:34:26 +00:00
if err != nil {
2024-07-21 13:05:09 +00:00
fmt.Errorf("get serial devices: %w", err)
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
ports = ps
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
2024-07-18 16:34:26 +00:00
// Check ports
2024-07-21 13:05:09 +00:00
SearchLoop:
2024-07-18 16:34:26 +00:00
for _, p := range ports {
2024-07-21 13:05:09 +00:00
log.Printf("Checking port %s ...\n", p)
2024-07-18 16:34:26 +00:00
2024-07-21 13:05:09 +00:00
if err := m.checkPort("/dev/" + p); err != nil {
log.Printf("Check failed: %s\n", err.Error())
continue SearchLoop
2024-07-18 16:34:26 +00:00
}
log.Print("Found modem on port: ", p)
2024-07-21 13:05:09 +00:00
m.port = at.New("/dev/"+p, m.baudrate)
2024-07-18 16:34:26 +00:00
m.isAvailable = true
return nil
}
return nil
}
func (m *modem) Connect() error {
2024-07-21 13:05:09 +00:00
if m.port == nil {
return fmt.Errorf("port is not defined")
}
return m.port.Connect()
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
func (m *modem) Ping() error {
2024-07-23 09:22:53 +00:00
resp, err := m.port.Send("AT")
if err != nil {
return err
}
2024-07-23 14:26:24 +00:00
if !resp.Check() {
2024-07-23 09:22:53 +00:00
return fmt.Errorf("connection lost")
}
return nil
2024-07-18 16:34:26 +00:00
}
func (m *modem) SwitchToGpsMode() error {
2024-07-21 13:05:09 +00:00
log.Println("Enabling GPS mode...")
// Reset intput
if err := m.port.GetSerialPort().ResetInputBuffer(); err != nil {
2024-07-18 16:34:26 +00:00
return fmt.Errorf("reset input buffer: %w", err)
}
2024-07-21 13:05:09 +00:00
// Check gps mode status
2024-07-23 09:22:53 +00:00
resp, err := m.port.Send("AT+CGPS?")
2024-07-18 16:34:26 +00:00
if err != nil {
2024-07-21 13:05:09 +00:00
return fmt.Errorf("make at ask: %w", err)
2024-07-18 16:34:26 +00:00
}
2024-07-23 09:22:53 +00:00
if !resp.Check() {
return fmt.Errorf("error response")
}
2024-07-23 14:26:24 +00:00
ans := strings.Replace(strings.Split(strings.Split(resp.RmFront("+CGPS:").String(), "\n")[0], ",")[0], " ", "", -1)
2024-07-21 13:05:09 +00:00
if ans == "1" {
2024-07-18 16:34:26 +00:00
log.Println("GPS already enabled")
return nil
}
2024-07-23 14:26:24 +00:00
log.Println(ans)
2024-07-21 13:05:09 +00:00
// Modem is not in GPS mode
2024-07-23 09:22:53 +00:00
resp, err = m.port.Send("AT+CGPS=1")
2024-07-21 13:05:09 +00:00
if err != nil {
return fmt.Errorf("try to switch to gps: %w", err)
}
2024-07-23 09:22:53 +00:00
if !resp.Check() {
return fmt.Errorf("switch tp GPS failed")
}
2024-07-21 13:05:09 +00:00
log.Println("GPS mode enabled")
2024-07-18 16:34:26 +00:00
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 ?
2024-07-21 13:05:09 +00:00
dLat := deg2rad(math.Abs(newLatitude - m.gpsInfo.Latitude))
dLon := deg2rad(math.Abs(newLongitude - m.gpsInfo.Longitude))
2024-07-18 16:34:26 +00:00
a := math.Sin(dLat/2)*math.Sin(dLat/2) +
2024-07-21 13:05:09 +00:00
math.Cos(deg2rad(newLatitude))*math.Cos(deg2rad(m.gpsInfo.Latitude))*math.Sin(dLon/2)*math.Sin(dLon/2)
2024-07-18 16:34:26 +00:00
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
2024-07-21 13:05:09 +00:00
m.gpsInfo.Speed = earthRad * c / (math.Abs(float64(time.Since(m.lastUpdateTime))))
2024-07-18 16:34:26 +00:00
}
func (m *modem) Update() error {
log.Println("Update")
if !m.isAvailable {
log.Println("No connection to module")
return nil
}
2024-07-23 09:22:53 +00:00
// ans, err := m.port.Request(at.CmdQuestion, "CGPSINFO")
// if err != nil {
// return fmt.Errorf("check GPS info mode: %w", err)
// }
// ok := ans == "1"
// if !ok {
// _, err := m.port.Request(at.CmdCheck, "CGPSINFO")
// if err != nil {
// return fmt.Errorf("switch to GPS info mode: %w", err)
// }
// log.Println("switched to GPS mode")
// } else {
// log.Println("mode in right GPS mode")
// }
2024-07-21 13:05:09 +00:00
// Update
log.Println("Receiving GPS data...")
2024-07-23 14:26:24 +00:00
resp, err := m.port.Send("AT+CGPSINFO")
2024-07-21 13:05:09 +00:00
if err != nil {
return fmt.Errorf("receive GPS data: %w", err)
}
2024-07-23 09:22:53 +00:00
if !resp.Check() {
return fmt.Errorf("error response")
}
2024-07-21 13:05:09 +00:00
log.Println("Decoding data...")
2024-07-23 09:22:53 +00:00
coordinates := strings.Split(strings.Split(resp.RmFront("+CGPSINFO:").String(), "\n")[0], ",")
2024-07-21 13:05:09 +00:00
m.gpsInfo.Latitude, err = strconv.ParseFloat(coordinates[0], 64)
if err != nil {
return fmt.Errorf("parse latitude: %w", err)
}
m.gpsInfo.Longitude, err = strconv.ParseFloat(coordinates[2], 64)
if err != nil {
return fmt.Errorf("parse longitude: %w", err)
}
m.gpsInfo.LatitudeIndicator = coordinates[1]
m.gpsInfo.LatitudeIndicator = coordinates[3]
m.gpsInfo.Date = coordinates[4]
m.gpsInfo.Time = coordinates[5]
m.gpsInfo.Altitude, err = strconv.ParseFloat(coordinates[6], 64)
if err != nil {
return fmt.Errorf("parse altitude: %w", err)
}
m.gpsInfo.Speed, err = strconv.ParseFloat(coordinates[7], 64)
if err != nil {
return fmt.Errorf("parse speed: %w", err)
}
m.gpsInfo.Course, err = strconv.ParseFloat(coordinates[8], 64)
if err != nil {
return fmt.Errorf("parse course: %w", err)
}
log.Println("Decoded successfully")
return nil
}
type ModemInfo struct {
Port string `json:"Port"`
GpsInfo
}
type Info struct {
Modem ModemInfo `json:"Modem"`
2024-07-18 16:34:26 +00:00
}
2024-07-21 13:05:09 +00:00
func (m *modem) GetInfo() (string, error) {
info := Info{
Modem: ModemInfo{
Port: m.port.GetName(),
GpsInfo: m.gpsInfo,
},
}
buf, err := json.Marshal(info)
if err != nil {
fmt.Errorf("marshal info: %w", err)
}
return string(buf), nil // why you clamp
}
// Difference: I do not set \n at the end of string
func (m *modem) GetShortInfo() string {
return fmt.Sprintf("%f,%s,%f,%s", m.gpsInfo.Latitude, m.gpsInfo.LatitudeIndicator, m.gpsInfo.Longitude, m.gpsInfo.LongitudeIndicator)
}
func (m *modem) SaveGPS(path string) error {
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("open file: %w", err)
}
defer f.Close()
if _, err = f.WriteString(m.GetShortInfo()); err != nil {
return fmt.Errorf("write file: %W", err)
}
return nil
2024-07-18 16:34:26 +00:00
}
func (m *modem) TestGPS() error {
2024-07-21 13:05:09 +00:00
log.Println("Testing GPS")
if err := m.SwitchToGpsMode(); err != nil {
return fmt.Errorf("switch to GPS: %w", err)
}
if err := m.Update(); err != nil {
return fmt.Errorf("update: %w", err)
}
log.Println("Current coords:", m.GetShortInfo())
2024-07-18 16:34:26 +00:00
return nil
}
2024-07-21 13:05:09 +00:00
func GetTtyDevices() ([]string, error) {
devices := []string{}
// Get ports
/**/
log.Print("Search for ports...")
out, err := exec.Command("/bin/ls", "/dev").Output()
if err != nil {
return nil, fmt.Errorf("execute ls command: %w", err)
}
allPorts := strings.Split(string(out), "\n")
for _, p := range allPorts {
if len(p) > 3 && p[:3] == "tty" {
devices = append(devices, p)
}
}
slices.Reverse(devices) // ASK why
return devices, nil
}
2024-07-18 16:34:26 +00:00
/*
TODOs:
maybe to store read/write buf in obj
QUESTIONS:
2024-07-21 13:05:09 +00:00
JSON why you clamp
2024-07-18 16:34:26 +00:00
*/
/*
*/