163 lines
4.5 KiB
Go
163 lines
4.5 KiB
Go
package gps
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
type nmeaFlags int
|
||
|
||
const (
|
||
gga nmeaFlags = 1 << iota // global positioning systemfix data
|
||
rmc // recommended minimumspecific GPS/TRANSIT data
|
||
gpgsv // GPS satellites in view
|
||
gpgsa // GPS DOP and active satellites
|
||
vtg // track made good and ground speed
|
||
xfi // Global Positioning SystemExtended FixData.)Bit 6 – GLGSV (GLONASS satellites in view GLONASSfixesonly
|
||
glgsa // 1. GPS/2. Glonass/3. GALILE DOPandActiveSatellites.
|
||
gns // fix data for GNSS receivers; output for GPS, GLONASS, GALILEO
|
||
_ // Reserved
|
||
gagsv // GALILEO satellites in view
|
||
_ // Reserved
|
||
_ // Reserved
|
||
_ // Reserved
|
||
_ // Reserved
|
||
_ // Reserved
|
||
bdpqgsa // BEIDOU/QZSS DOP and activesatellites
|
||
bdpqgsv // BEIDOUQZSS satellites in view
|
||
|
||
nmeaFlagsMask = (1 << 18) - 1
|
||
|
||
collectTimeout = 5 * time.Second
|
||
)
|
||
|
||
func secondCountDownTimer(title string, logger *log.Logger, t time.Duration) {
|
||
counter := 0
|
||
for {
|
||
if counter > int(t.Seconds()) {
|
||
break
|
||
}
|
||
logger.Printf("%s: %d/%f\n", title, counter, t.Seconds())
|
||
time.Sleep(time.Second)
|
||
counter += 1
|
||
}
|
||
}
|
||
|
||
// Go... otherwise will throw warning
|
||
var nmeaFlagsAll = gga | rmc | gpgsv | gpgsa | vtg | xfi | glgsa | gns | gagsv | bdpqgsa | bdpqgsv
|
||
|
||
func (g *gps) rawCollect(flags nmeaFlags) (string, error) {
|
||
// Need to omplement low level write/read here because collect must be atomic operation
|
||
// If other command is executed(write/read) it will read a part of nmea report and cause an error
|
||
|
||
// Setup gps
|
||
|
||
// Receive data from all contries' satelites, enable DPO(Dynamic Power Optimization) mode
|
||
// if resp, err := g.port.Send("AT+CGNSSMODE=15,1"); err != nil {
|
||
// return "", fmt.Errorf("AT+CGNSSMODE= request: %w", err)
|
||
// } else {
|
||
// if !resp.Check() {
|
||
// return "", fmt.Errorf("AT+CGNSSMODE= error response: %s", resp)
|
||
// }
|
||
// }
|
||
// Receive all types of data
|
||
// if resp, err := g.port.Send("AT+CGPSNMEA=200191"); err != nil {
|
||
// return "", fmt.Errorf("AT+CGPSNMEA= request: %w", err)
|
||
// } else {
|
||
// if !resp.Check() {
|
||
// return "", fmt.Errorf("AT+CGPSNMEA= error response: %s", resp)
|
||
// }
|
||
// }
|
||
// Set output rate to 10Hz
|
||
if resp, err := g.port.Send("AT+CGPSNMEARATE=1"); err != nil {
|
||
return "", fmt.Errorf("AT+CGPSNMEARATE= request: %w", err)
|
||
} else {
|
||
if !resp.Check() {
|
||
return "", fmt.Errorf("AT+CGPSNMEARATE= error response: %s", resp)
|
||
}
|
||
}
|
||
|
||
g.switchGpsMode(true)
|
||
g.port.Mutex().Lock()
|
||
s := g.port.SerialPort()
|
||
defer func() {
|
||
g.port.Mutex().Unlock()
|
||
g.switchGpsMode(false)
|
||
}()
|
||
|
||
// Send AT+CGPSINFOCFG=255, flags
|
||
flags &= nmeaFlagsMask
|
||
if _, err := s.Write([]byte("AT+CGPSINFOCFG=1,31\r\n")); err != nil {
|
||
return "", fmt.Errorf("serial port write 1: %w", err)
|
||
}
|
||
// Do I need to read answer
|
||
|
||
// Wait
|
||
secondCountDownTimer("Collecting NMEA data", g.logger, collectTimeout)
|
||
|
||
// Send AT+CGPSINFOCFG=0, flags
|
||
if _, err := s.Write([]byte("AT+CGPSINFOCFG=0,31\r\n")); err != nil {
|
||
return "", fmt.Errorf("serial port write 2: %w", err)
|
||
}
|
||
|
||
time.Sleep(100 * time.Millisecond) // To enshure
|
||
|
||
// Read
|
||
outBuf := make([]byte, 0)
|
||
buf := make([]byte, 128)
|
||
|
||
readLoop:
|
||
for {
|
||
n, err := s.Read(buf)
|
||
if err != nil {
|
||
return string(outBuf), fmt.Errorf("serial port read: %w", err)
|
||
}
|
||
if n == 0 {
|
||
break readLoop
|
||
}
|
||
outBuf = append(outBuf, buf[:n]...)
|
||
}
|
||
|
||
return string(outBuf), nil
|
||
}
|
||
|
||
func (g *gps) collectNmeaReports(flags nmeaFlags) ([]string, error) {
|
||
// Raw collect
|
||
resp, err := g.rawCollect(flags)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("raw collect: %w", err)
|
||
}
|
||
|
||
// DEBUG
|
||
g.logger.Println("NMEA raw collect:", resp)
|
||
|
||
// Right responce struct:
|
||
// \r\n
|
||
// OK
|
||
// \r\n
|
||
// (NMEA sentence)...
|
||
// \r\n
|
||
// OK
|
||
// \r\n
|
||
strs := strings.Split(strings.Replace(resp, "\r", "", -1), "\n")
|
||
|
||
// Check
|
||
// Now wait for:
|
||
// OK
|
||
// (NMEA sentence)...
|
||
// OK
|
||
// if len(strs) < 2 {
|
||
// return nil, fmt.Errorf("responce too few rows: %d", len(strs))
|
||
// }
|
||
// if !(strs[0] == "OK" && strs[len(strs)-1] == "OK") {
|
||
// return nil, fmt.Errorf("not OK responce: [% s]", strs)
|
||
// }
|
||
|
||
// This... response is not stable
|
||
// Every time it gives one or two OK and in ramdom order
|
||
// So I will not check gor it
|
||
return strs, nil
|
||
}
|