forked from adrianmo/go-nmea
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sentence.go
114 lines (103 loc) · 2.77 KB
/
sentence.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package nmea
import (
"fmt"
"strings"
)
const (
// SentenceStart is the token to indicate the start of a sentence.
SentenceStart = "$"
// FieldSep is the token to delimit fields of a sentence.
FieldSep = ","
// ChecksumSep is the token to delimit the checksum of a sentence.
ChecksumSep = "*"
)
// Sentence interface for all NMEA sentence
type Sentence interface {
fmt.Stringer
Prefix() string
}
// BaseSentence contains the information about the NMEA sentence
type BaseSentence struct {
Type string // The sentence type (e.g $GPGSA)
Fields []string // Array of fields
Checksum string // The Checksum
Raw string // The raw NMEA sentence received
}
// Prefix returns the type of the message
func (s BaseSentence) Prefix() string { return s.Type }
// String formats the sentence into a string
func (s BaseSentence) String() string { return s.Raw }
// parseSentence parses a raw message into it's fields
func parseSentence(raw string) (BaseSentence, error) {
startIndex := strings.Index(raw, SentenceStart)
if startIndex != 0 {
return BaseSentence{}, fmt.Errorf("nmea: sentence does not start with a '$'")
}
sumSepIndex := strings.Index(raw, ChecksumSep)
if sumSepIndex == -1 {
return BaseSentence{}, fmt.Errorf("nmea: sentence does not contain checksum separator")
}
var (
fieldsRaw = raw[startIndex+1 : sumSepIndex]
fields = strings.Split(fieldsRaw, FieldSep)
checksumRaw = strings.ToUpper(raw[sumSepIndex+1:])
checksum = xorChecksum(fieldsRaw)
)
// Validate the checksum
if checksum != checksumRaw {
return BaseSentence{}, fmt.Errorf(
"nmea: sentence checksum mismatch [%s != %s]", checksum, checksumRaw)
}
return BaseSentence{
Type: fields[0],
Fields: fields[1:],
Checksum: checksumRaw,
Raw: raw,
}, nil
}
// xor all the bytes in a string an return it
// as an uppercase hex string
func xorChecksum(s string) string {
var checksum uint8
for i := 0; i < len(s); i++ {
checksum ^= s[i]
}
return fmt.Sprintf("%02X", checksum)
}
// Parse parses the given string into the correct sentence type.
func Parse(raw string) (Sentence, error) {
s, err := parseSentence(raw)
if err != nil {
return nil, err
}
switch s.Type {
case PrefixGPRMC:
return newGPRMC(s)
case PrefixGNRMC:
return newGNRMC(s)
case PrefixGPGGA:
return newGPGGA(s)
case PrefixGNGGA:
return newGNGGA(s)
case PrefixGPGSA:
return newGPGSA(s)
case PrefixGPGLL:
return newGPGLL(s)
case PrefixGPVTG:
return newGPVTG(s)
case PrefixGPZDA:
return newGPZDA(s)
case PrefixPGRME:
return newPGRME(s)
case PrefixGPGSV:
return newGPGSV(s)
case PrefixGLGSV:
return newGLGSV(s)
case PrefixGPHDT:
return newGPHDT(s)
case PrefixGNGNS:
return newGNGNS(s)
default:
return nil, fmt.Errorf("nmea: sentence type '%s' not implemented", s.Type)
}
}