gps: improve error handling

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2023-01-14 11:06:25 +01:00
committed by Ron Evans
parent b1c9612158
commit 5b461ec2d1
2 changed files with 44 additions and 24 deletions
+25 -4
View File
@@ -13,8 +13,28 @@ import (
var (
errInvalidNMEASentenceLength = errors.New("invalid NMEA sentence length")
errInvalidNMEAChecksum = errors.New("invalid NMEA sentence checksum")
errEmptyNMEASentence = errors.New("cannot parse empty NMEA sentence")
errUnknownNMEASentence = errors.New("unsupported NMEA sentence type")
errInvalidGGASentence = errors.New("invalid GGA NMEA sentence")
errInvalidRMCSentence = errors.New("invalid RMC NMEA sentence")
)
type GPSError struct {
Info string
Err error
}
func newGPSError(err error, info string) GPSError {
return GPSError{
Info: info,
Err: err,
}
}
func (ge GPSError) Error() string {
return ge.Err.Error() + " " + ge.Info
}
// Device wraps a connection to a GPS device.
type Device struct {
buffer []byte
@@ -127,16 +147,17 @@ func (gps *Device) WriteBytes(bytes []byte) {
// validSentence checks if a sentence has been received uncorrupted
func validSentence(sentence string) error {
if len(sentence) < 4 || sentence[0] != '$' || sentence[len(sentence)-3] != '*' {
if len(sentence) < 7 || sentence[0] != '$' || sentence[len(sentence)-3] != '*' {
return errInvalidNMEASentenceLength
}
var cs byte = 0
for i := 1; i < len(sentence)-3; i++ {
cs ^= sentence[i]
}
checksum := hex.EncodeToString([]byte{cs})
if (checksum[0] != sentence[len(sentence)-2]) || (checksum[1] != sentence[len(sentence)-1]) {
return errInvalidNMEAChecksum
checksum := strings.ToUpper(hex.EncodeToString([]byte{cs}))
if checksum != sentence[len(sentence)-2:len(sentence)] {
return newGPSError(errInvalidNMEAChecksum, "expected "+sentence[len(sentence)-2:len(sentence)]+
" got "+checksum)
}
return nil
+19 -20
View File
@@ -1,19 +1,11 @@
package gps
import (
"errors"
"strconv"
"strings"
"time"
)
var (
errEmptyNMEASentence = errors.New("cannot parse empty NMEA sentence")
errUnknownNMEASentence = errors.New("unsupported NMEA sentence type")
errInvalidGGASentence = errors.New("invalid GGA NMEA sentence")
errInvalidRMCSentence = errors.New("invalid RMC NMEA sentence")
)
// Parser for GPS NMEA sentences.
type Parser struct {
}
@@ -51,18 +43,20 @@ func NewParser() Parser {
}
// Parse parses a NMEA sentence looking for fix info.
func (parser *Parser) Parse(sentence string) (fix Fix, err error) {
func (parser *Parser) Parse(sentence string) (Fix, error) {
var fix Fix
if sentence == "" {
err = errEmptyNMEASentence
return
return fix, errEmptyNMEASentence
}
if len(sentence) < 6 {
return fix, errInvalidNMEASentenceLength
}
typ := sentence[3:6]
switch typ {
case "GGA":
fields := strings.Split(sentence, ",")
if len(fields) != 15 {
err = errInvalidGGASentence
return
return fix, errInvalidGGASentence
}
fix.Altitude = findAltitude(fields[9])
@@ -71,11 +65,12 @@ func (parser *Parser) Parse(sentence string) (fix Fix, err error) {
fix.Latitude = findLatitude(fields[2], fields[3])
fix.Time = findTime(fields[1])
fix.Valid = (fix.Altitude != -99999) && (fix.Satellites > 0)
return fix, nil
case "RMC":
fields := strings.Split(sentence, ",")
if len(fields) != 13 {
err = errInvalidRMCSentence
return
return fix, errInvalidRMCSentence
}
fix.Longitude = findLongitude(fields[5], fields[6])
@@ -83,11 +78,12 @@ func (parser *Parser) Parse(sentence string) (fix Fix, err error) {
fix.Time = findTime(fields[1])
fix.Speed = findSpeed(fields[7])
fix.Heading = findHeading(fields[8])
fix.Valid = (len(fields[2]) > 0 && fields[2][0:1] == "A")
default:
err = errUnknownNMEASentence
fix.Valid = (len(fields[2]) > 0 && fields[2] == "A")
return fix, nil
}
return
return fix, newGPSError(errUnknownNMEASentence, typ)
}
// findTime returns the time from an NMEA sentence:
@@ -100,7 +96,10 @@ func findTime(val string) time.Time {
h, _ := strconv.ParseInt(val[0:2], 10, 8)
m, _ := strconv.ParseInt(val[2:4], 10, 8)
s, _ := strconv.ParseInt(val[4:6], 10, 8)
ms, _ := strconv.ParseInt(val[7:10], 10, 16)
ms := int64(0)
if len(val) == 10 {
ms, _ = strconv.ParseInt(val[7:10], 10, 16)
}
t := time.Date(0, 0, 0, int(h), int(m), int(s), int(ms), time.UTC)
return t