libjuju-go/src/juju.net.nz/gps/gps.go
Michael Hope 8479106022 Split the GPS parser out from nppilot.
Split the GPS parser into smaller source files.
Added tests to the parser.
2014-06-24 22:03:42 +02:00

145 lines
2.6 KiB
Go

package gps
import (
"bytes"
"io"
"regexp"
"strconv"
"strings"
)
var matcher *regexp.Regexp
func init() {
matcher = regexp.MustCompile(`\$([A-Z]+,.+)\*(\w\w)$`)
}
type Stats struct {
Received uint
UnrecognisedType uint
ChecksumError uint
ParseError uint
EmptyFields uint
DecodeError uint
}
type Link struct {
Stats Stats
Sentences chan Sentence
}
func checksum(frame string) uint8 {
var sum uint8
for _, ch := range frame {
sum ^= uint8(ch)
}
return sum
}
func (link *Link) decode(frame string) (msg Sentence, err error, emptyField bool) {
parser := &Parser{strings.Split(frame, ","), false, nil}
switch parser.GetString(0) {
case "GPGGA":
msg = &GGA{
Time: parser.GetTime(1),
Latitude: parser.GetLatLong(2),
Longitude: parser.GetLatLong(4),
Quality: parser.GetInt(6),
NumSatellites: parser.GetInt(7),
HDOP: parser.GetFloat32(8),
Altitude: parser.GetFloat(9),
}
case "GPVTG":
msg = &VTG{
TrueCourse: parser.GetFloat32(1),
Speed: parser.GetFloat32(5),
SpeedInKmh: parser.GetFloat32(7),
Mode: parser.GetString(9),
}
case "GPRMC":
msg = &RMC{
Time: parser.GetTime(1),
Status: parser.GetString(2),
Latitude: parser.GetLatLong(3),
Longitude: parser.GetLatLong(5),
Speed: parser.GetFloat32(7),
Track: parser.GetFloat32(8),
Date: parser.GetString(9),
Mode: parser.GetString(12),
}
}
return msg, parser.Err, parser.EmptyField
}
func (link *Link) dispatch(frame *bytes.Buffer) {
if frame.Len() == 0 {
return
}
parts := matcher.FindStringSubmatch(frame.String())
if len(parts) == 0 {
link.Stats.ParseError += 1
return
}
link.Stats.Received += 1
sum := checksum(parts[1])
got, err := strconv.ParseUint(parts[2], 16, 8)
if err != nil || uint8(got) != sum {
link.Stats.ChecksumError += 1
return
}
msg, err, emptyField := link.decode(parts[1])
if err != nil {
link.Stats.DecodeError += 1
} else if emptyField {
link.Stats.EmptyFields += 1
} else if msg != nil {
link.Sentences <- msg
}
}
func (link *Link) Read(port io.Reader) {
got := make([]byte, 128)
var frame bytes.Buffer
for {
n, _ := port.Read(got)
if n > 0 {
for _, ch := range got[:n] {
switch ch {
case '$':
frame.Reset()
frame.WriteByte(ch)
case '\r':
case '\n':
link.dispatch(&frame)
frame.Reset()
default:
frame.WriteByte(ch)
}
}
}
// TODO(michaelh): handle errors.
}
}
func (link *Link) Watch(port io.Reader) {
link.Read(port)
}
func New() *Link {
link := &Link{}
link.Sentences = make(chan Sentence)
return link
}