diff --git a/Gopkg.lock b/Gopkg.lock index 72601c44..e9b3d851 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,11 +2,12 @@ [[projects]] - digest = "1:fa526d5f6ec66a1833c687768639251d6db3bc3b7f32abd0265ae9625a9233de" + digest = "1:4132a4623657c2ba93a7cf83dccc6869b3e3bb91dc2afefa7c7032e10ceeaa12" name = "github.com/adrianmo/go-nmea" packages = ["."] pruneopts = "UT" - revision = "22095aa1b48050243d3eb9a001ca80eb91a0c6fa" + revision = "a32116e4989e2b0e17c057ee378b4d5246add74e" + version = "v1.1.0" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index c6289dbd..c3721b51 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -93,6 +93,10 @@ branch = "master" name = "github.com/tarm/serial" +[[constraint]] + name = "github.com/adrianmo/go-nmea" + version = "1.1.0" + [prune] go-tests = true unused-packages = true diff --git a/modules/gps.go b/modules/gps.go index 50ee1ed0..88ecd305 100644 --- a/modules/gps.go +++ b/modules/gps.go @@ -129,13 +129,10 @@ func (gps *GPS) Start() error { for gps.Running() { if line, err := gps.readLine(); err == nil { - if info, err := nmea.Parse(line); err == nil { - s := info.Sentence() + if s, err := nmea.Parse(line); err == nil { // http://aprs.gids.nl/nmea/#gga - if s.Type == "GNGGA" { - gps.Session.GPS = info.(nmea.GNGGA) - } else { - log.Debug("Skipping message %s: %v", s.Type, s) + if m, ok := s.(nmea.GNGGA); ok { + gps.Session.GPS = m } } else { log.Debug("Error parsing line '%s': %s", line, err) diff --git a/vendor/github.com/adrianmo/go-nmea/.travis.yml b/vendor/github.com/adrianmo/go-nmea/.travis.yml index 8ad3a7f8..24256360 100644 --- a/vendor/github.com/adrianmo/go-nmea/.travis.yml +++ b/vendor/github.com/adrianmo/go-nmea/.travis.yml @@ -5,7 +5,10 @@ language: go go: - - 1.7 + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x - tip matrix: diff --git a/vendor/github.com/adrianmo/go-nmea/README.md b/vendor/github.com/adrianmo/go-nmea/README.md index b0db9f69..e60d1445 100644 --- a/vendor/github.com/adrianmo/go-nmea/README.md +++ b/vendor/github.com/adrianmo/go-nmea/README.md @@ -27,6 +27,7 @@ At this moment, this library supports the following sentence types: - [GPVTG](http://aprs.gids.nl/nmea/#vtg) - Track Made Good and Ground Speed - [GPZDA](http://aprs.gids.nl/nmea/#zda) - Date & time data - [PGRME](http://aprs.gids.nl/nmea/#rme) - Estimated Position Error (Garmin proprietary sentence) +- [GPHDT](http://aprs.gids.nl/nmea/#hdt) - Actual vessel heading in degrees True ## Example @@ -35,18 +36,50 @@ At this moment, this library supports the following sentence types: package main import ( - "fmt" - "github.com/adrianmo/go-nmea" + "fmt" + "log" + "github.com/adrianmo/go-nmea" ) func main() { - m, err := nmea.Parse("$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70") - if err == nil { - fmt.Printf("%+v\n", m) - } + sentence := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70" + s, err := nmea.Parse(sentence) + if err != nil { + log.Fatal(err) + } + m := s.(nmea.GPRMC) + fmt.Printf("Raw sentence: %v\n", m) + fmt.Printf("Time: %s\n", m.Time) + fmt.Printf("Validity: %s\n", m.Validity) + fmt.Printf("Latitude GPS: %s\n", nmea.FormatGPS(m.Latitude)) + fmt.Printf("Latitude DMS: %s\n", nmea.FormatDMS(m.Latitude)) + fmt.Printf("Longitude GPS: %s\n", nmea.FormatGPS(m.Longitude)) + fmt.Printf("Longitude DMS: %s\n", nmea.FormatDMS(m.Longitude)) + fmt.Printf("Speed: %f\n", m.Speed) + fmt.Printf("Course: %f\n", m.Course) + fmt.Printf("Date: %s\n", m.Date) + fmt.Printf("Variation: %f\n", m.Variation) } ``` +Output: + +``` +$ go run main/main.go + +Raw sentence: $GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70 +Time: 22:05:16.0000 +Validity: A +Latitude GPS: 5133.8200 +Latitude DMS: 51° 33' 49.200000" +Longitude GPS: 042.2400 +Longitude DMS: 0° 42' 14.400000" +Speed: 173.800000 +Course: 231.800000 +Date: 13/06/94 +Variation: -4.200000 +``` + ## Contributions Please, feel free to implement support for new sentences, fix bugs, refactor code, etc. and send a pull-request to update the library. diff --git a/vendor/github.com/adrianmo/go-nmea/VERSION b/vendor/github.com/adrianmo/go-nmea/VERSION new file mode 100644 index 00000000..795460fc --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/VERSION @@ -0,0 +1 @@ +v1.1.0 diff --git a/vendor/github.com/adrianmo/go-nmea/glgsv.go b/vendor/github.com/adrianmo/go-nmea/glgsv.go index 62a0ac3b..be215e45 100644 --- a/vendor/github.com/adrianmo/go-nmea/glgsv.go +++ b/vendor/github.com/adrianmo/go-nmea/glgsv.go @@ -8,7 +8,7 @@ const ( // GLGSV represents the GPS Satellites in view // http://aprs.gids.nl/nmea/#glgsv type GLGSV struct { - Sent + BaseSentence TotalMessages int64 // Total number of messages of this type in this cycle MessageNumber int64 // Message number NumberSVsInView int64 // Total number of SVs in view @@ -23,11 +23,11 @@ type GLGSVInfo struct { SNR int64 // SNR, 00-99 dB (null when not tracking) } -// NewGLGSV constructor -func NewGLGSV(s Sent) (GLGSV, error) { +// newGLGSV constructor +func newGLGSV(s BaseSentence) (GLGSV, error) { p := newParser(s, PrefixGLGSV) m := GLGSV{ - Sent: s, + BaseSentence: s, TotalMessages: p.Int64(0, "total number of messages"), MessageNumber: p.Int64(1, "message number"), NumberSVsInView: p.Int64(2, "number of SVs in view"), diff --git a/vendor/github.com/adrianmo/go-nmea/gngga.go b/vendor/github.com/adrianmo/go-nmea/gngga.go index e0754f7b..dac99a1c 100644 --- a/vendor/github.com/adrianmo/go-nmea/gngga.go +++ b/vendor/github.com/adrianmo/go-nmea/gngga.go @@ -7,10 +7,10 @@ const ( // GNGGA is the Time, position, and fix related data of the receiver. type GNGGA struct { - Sent + BaseSentence Time Time // Time of fix. - Latitude LatLong // Latitude. - Longitude LatLong // Longitude. + Latitude float64 // Latitude. + Longitude float64 // Longitude. FixQuality string // Quality of fix. NumSatellites int64 // Number of satellites in use. HDOP float64 // Horizontal dilution of precision. @@ -20,16 +20,16 @@ type GNGGA struct { DGPSId string // DGPS reference station ID. } -// NewGNGGA constructor -func NewGNGGA(s Sent) (GNGGA, error) { +// newGNGGA constructor +func newGNGGA(s BaseSentence) (GNGGA, error) { p := newParser(s, PrefixGNGGA) return GNGGA{ - Sent: s, + BaseSentence: s, Time: p.Time(0, "time"), Latitude: p.LatLong(1, 2, "latitude"), Longitude: p.LatLong(3, 4, "longitude"), - FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS), - NumSatellites: p.Int64(6, "number of satelites"), + FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS, PPS, RTK, FRTK), + NumSatellites: p.Int64(6, "number of satellites"), HDOP: p.Float64(7, "hdop"), Altitude: p.Float64(8, "altitude"), Separation: p.Float64(10, "separation"), diff --git a/vendor/github.com/adrianmo/go-nmea/gnrmc.go b/vendor/github.com/adrianmo/go-nmea/gnrmc.go index ee5004b9..163bd4ef 100644 --- a/vendor/github.com/adrianmo/go-nmea/gnrmc.go +++ b/vendor/github.com/adrianmo/go-nmea/gnrmc.go @@ -8,30 +8,30 @@ const ( // GNRMC is the Recommended Minimum Specific GNSS data. // http://aprs.gids.nl/nmea/#rmc type GNRMC struct { - Sent + BaseSentence Time Time // Time Stamp Validity string // validity - A-ok, V-invalid - Latitude LatLong // Latitude - Longitude LatLong // Longitude + Latitude float64 // Latitude + Longitude float64 // Longitude Speed float64 // Speed in knots Course float64 // True course Date Date // Date Variation float64 // Magnetic variation } -// NewGNRMC constructor -func NewGNRMC(s Sent) (GNRMC, error) { +// newGNRMC constructor +func newGNRMC(s BaseSentence) (GNRMC, error) { p := newParser(s, PrefixGNRMC) m := GNRMC{ - Sent: s, - Time: p.Time(0, "time"), - Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC), - Latitude: p.LatLong(2, 3, "latitude"), - Longitude: p.LatLong(4, 5, "longitude"), - Speed: p.Float64(6, "speed"), - Course: p.Float64(7, "course"), - Date: p.Date(8, "date"), - Variation: p.Float64(9, "variation"), + BaseSentence: s, + Time: p.Time(0, "time"), + Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC), + Latitude: p.LatLong(2, 3, "latitude"), + Longitude: p.LatLong(4, 5, "longitude"), + Speed: p.Float64(6, "speed"), + Course: p.Float64(7, "course"), + Date: p.Date(8, "date"), + Variation: p.Float64(9, "variation"), } if p.EnumString(10, "direction", West, East) == West { m.Variation = 0 - m.Variation diff --git a/vendor/github.com/adrianmo/go-nmea/go.mod b/vendor/github.com/adrianmo/go-nmea/go.mod new file mode 100644 index 00000000..c3bbdfee --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/go.mod @@ -0,0 +1,3 @@ +module github.com/adrianmo/go-nmea + +require github.com/stretchr/testify v1.2.1 diff --git a/vendor/github.com/adrianmo/go-nmea/gpgga.go b/vendor/github.com/adrianmo/go-nmea/gpgga.go index 2b27c2bf..23f752d8 100644 --- a/vendor/github.com/adrianmo/go-nmea/gpgga.go +++ b/vendor/github.com/adrianmo/go-nmea/gpgga.go @@ -9,15 +9,21 @@ const ( GPS = "1" // DGPS fix quality DGPS = "2" + // PPS fix + PPS = "3" + // RTK real time kinematic fix + RTK = "4" + // FRTK float RTK fix + FRTK = "5" ) // GPGGA represents fix data. // http://aprs.gids.nl/nmea/#gga type GPGGA struct { - Sent + BaseSentence Time Time // Time of fix. - Latitude LatLong // Latitude. - Longitude LatLong // Longitude. + Latitude float64 // Latitude. + Longitude float64 // Longitude. FixQuality string // Quality of fix. NumSatellites int64 // Number of satellites in use. HDOP float64 // Horizontal dilution of precision. @@ -27,17 +33,17 @@ type GPGGA struct { DGPSId string // DGPS reference station ID. } -// NewGPGGA parses the GPGGA sentence into this struct. +// newGPGGA parses the GPGGA sentence into this struct. // e.g: $GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*58 -func NewGPGGA(s Sent) (GPGGA, error) { +func newGPGGA(s BaseSentence) (GPGGA, error) { p := newParser(s, PrefixGPGGA) return GPGGA{ - Sent: s, + BaseSentence: s, Time: p.Time(0, "time"), Latitude: p.LatLong(1, 2, "latitude"), Longitude: p.LatLong(3, 4, "longitude"), - FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS), - NumSatellites: p.Int64(6, "number of satelites"), + FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS, PPS, RTK, FRTK), + NumSatellites: p.Int64(6, "number of satellites"), HDOP: p.Float64(7, "hdap"), Altitude: p.Float64(8, "altitude"), Separation: p.Float64(10, "separation"), diff --git a/vendor/github.com/adrianmo/go-nmea/gpgll.go b/vendor/github.com/adrianmo/go-nmea/gpgll.go index 803e63c9..c49b5536 100644 --- a/vendor/github.com/adrianmo/go-nmea/gpgll.go +++ b/vendor/github.com/adrianmo/go-nmea/gpgll.go @@ -12,21 +12,21 @@ const ( // GPGLL is Geographic Position, Latitude / Longitude and time. // http://aprs.gids.nl/nmea/#gll type GPGLL struct { - Sent - Latitude LatLong // Latitude - Longitude LatLong // Longitude + BaseSentence + Latitude float64 // Latitude + Longitude float64 // Longitude Time Time // Time Stamp Validity string // validity - A-valid } -// NewGPGLL constructor -func NewGPGLL(s Sent) (GPGLL, error) { +// newGPGLL constructor +func newGPGLL(s BaseSentence) (GPGLL, error) { p := newParser(s, PrefixGPGLL) return GPGLL{ - Sent: s, - Latitude: p.LatLong(0, 1, "latitude"), - Longitude: p.LatLong(2, 3, "longitude"), - Time: p.Time(4, "time"), - Validity: p.EnumString(5, "validity", ValidGLL, InvalidGLL), + BaseSentence: s, + Latitude: p.LatLong(0, 1, "latitude"), + Longitude: p.LatLong(2, 3, "longitude"), + Time: p.Time(4, "time"), + Validity: p.EnumString(5, "validity", ValidGLL, InvalidGLL), }, p.Err() } diff --git a/vendor/github.com/adrianmo/go-nmea/gpgsa.go b/vendor/github.com/adrianmo/go-nmea/gpgsa.go index 79d152f9..a13db2b0 100644 --- a/vendor/github.com/adrianmo/go-nmea/gpgsa.go +++ b/vendor/github.com/adrianmo/go-nmea/gpgsa.go @@ -18,7 +18,7 @@ const ( // GPGSA represents overview satellite data. // http://aprs.gids.nl/nmea/#gsa type GPGSA struct { - Sent + BaseSentence Mode string // The selection mode. FixType string // The fix type. SV []string // List of satellite PRNs used for this fix. @@ -27,17 +27,17 @@ type GPGSA struct { VDOP float64 // Vertical dilution of precision. } -// NewGPGSA parses the GPGSA sentence into this struct. -func NewGPGSA(s Sent) (GPGSA, error) { +// newGPGSA parses the GPGSA sentence into this struct. +func newGPGSA(s BaseSentence) (GPGSA, error) { p := newParser(s, PrefixGPGSA) m := GPGSA{ - Sent: s, - Mode: p.EnumString(0, "selection mode", Auto, Manual), - FixType: p.EnumString(1, "fix type", FixNone, Fix2D, Fix3D), + BaseSentence: s, + Mode: p.EnumString(0, "selection mode", Auto, Manual), + FixType: p.EnumString(1, "fix type", FixNone, Fix2D, Fix3D), } // Satellites in view. for i := 2; i < 14; i++ { - if v := p.String(i, "satelite in view"); v != "" { + if v := p.String(i, "satellite in view"); v != "" { m.SV = append(m.SV, v) } } diff --git a/vendor/github.com/adrianmo/go-nmea/gpgsv.go b/vendor/github.com/adrianmo/go-nmea/gpgsv.go index 85e51145..395ea121 100644 --- a/vendor/github.com/adrianmo/go-nmea/gpgsv.go +++ b/vendor/github.com/adrianmo/go-nmea/gpgsv.go @@ -8,7 +8,7 @@ const ( // GPGSV represents the GPS Satellites in view // http://aprs.gids.nl/nmea/#gpgsv type GPGSV struct { - Sent + BaseSentence TotalMessages int64 // Total number of messages of this type in this cycle MessageNumber int64 // Message number NumberSVsInView int64 // Total number of SVs in view @@ -23,11 +23,11 @@ type GPGSVInfo struct { SNR int64 // SNR, 00-99 dB (null when not tracking) } -// NewGPGSV constructor -func NewGPGSV(s Sent) (GPGSV, error) { +// newGPGSV constructor +func newGPGSV(s BaseSentence) (GPGSV, error) { p := newParser(s, PrefixGPGSV) m := GPGSV{ - Sent: s, + BaseSentence: s, TotalMessages: p.Int64(0, "total number of messages"), MessageNumber: p.Int64(1, "message number"), NumberSVsInView: p.Int64(2, "number of SVs in view"), diff --git a/vendor/github.com/adrianmo/go-nmea/gphdt.go b/vendor/github.com/adrianmo/go-nmea/gphdt.go new file mode 100644 index 00000000..785642d9 --- /dev/null +++ b/vendor/github.com/adrianmo/go-nmea/gphdt.go @@ -0,0 +1,25 @@ +package nmea + +const ( + // PrefixGPHDT prefix of GPHDT sentence type + PrefixGPHDT = "GPHDT" +) + +// GPHDT is the Actual vessel heading in degrees True. +// http://aprs.gids.nl/nmea/#hdt +type GPHDT struct { + BaseSentence + Heading float64 // Heading in degrees + True bool // Heading is relative to true north +} + +// newGPHDT constructor +func newGPHDT(s BaseSentence) (GPHDT, error) { + p := newParser(s, PrefixGPHDT) + m := GPHDT{ + BaseSentence: s, + Heading: p.Float64(0, "heading"), + True: p.EnumString(1, "true", "T") == "T", + } + return m, p.Err() +} diff --git a/vendor/github.com/adrianmo/go-nmea/gprmc.go b/vendor/github.com/adrianmo/go-nmea/gprmc.go index 72be1584..18a24fec 100644 --- a/vendor/github.com/adrianmo/go-nmea/gprmc.go +++ b/vendor/github.com/adrianmo/go-nmea/gprmc.go @@ -12,30 +12,30 @@ const ( // GPRMC is the Recommended Minimum Specific GNSS data. // http://aprs.gids.nl/nmea/#rmc type GPRMC struct { - Sent + BaseSentence Time Time // Time Stamp Validity string // validity - A-ok, V-invalid - Latitude LatLong // Latitude - Longitude LatLong // Longitude + Latitude float64 // Latitude + Longitude float64 // Longitude Speed float64 // Speed in knots Course float64 // True course Date Date // Date Variation float64 // Magnetic variation } -// NewGPRMC constructor -func NewGPRMC(s Sent) (GPRMC, error) { +// newGPRMC constructor +func newGPRMC(s BaseSentence) (GPRMC, error) { p := newParser(s, PrefixGPRMC) m := GPRMC{ - Sent: s, - Time: p.Time(0, "time"), - Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC), - Latitude: p.LatLong(2, 3, "latitude"), - Longitude: p.LatLong(4, 5, "longitude"), - Speed: p.Float64(6, "speed"), - Course: p.Float64(7, "course"), - Date: p.Date(8, "date"), - Variation: p.Float64(9, "variation"), + BaseSentence: s, + Time: p.Time(0, "time"), + Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC), + Latitude: p.LatLong(2, 3, "latitude"), + Longitude: p.LatLong(4, 5, "longitude"), + Speed: p.Float64(6, "speed"), + Course: p.Float64(7, "course"), + Date: p.Date(8, "date"), + Variation: p.Float64(9, "variation"), } if p.EnumString(10, "variation", West, East) == West { m.Variation = 0 - m.Variation diff --git a/vendor/github.com/adrianmo/go-nmea/gpvtg.go b/vendor/github.com/adrianmo/go-nmea/gpvtg.go index 097d806a..b068af2b 100644 --- a/vendor/github.com/adrianmo/go-nmea/gpvtg.go +++ b/vendor/github.com/adrianmo/go-nmea/gpvtg.go @@ -8,19 +8,19 @@ const ( // GPVTG represents track & speed data. // http://aprs.gids.nl/nmea/#vtg type GPVTG struct { - Sent + BaseSentence TrueTrack float64 MagneticTrack float64 GroundSpeedKnots float64 GroundSpeedKPH float64 } -// NewGPVTG parses the GPVTG sentence into this struct. +// newGPVTG parses the GPVTG sentence into this struct. // e.g: $GPVTG,360.0,T,348.7,M,000.0,N,000.0,K*43 -func NewGPVTG(s Sent) (GPVTG, error) { +func newGPVTG(s BaseSentence) (GPVTG, error) { p := newParser(s, PrefixGPVTG) return GPVTG{ - Sent: s, + BaseSentence: s, TrueTrack: p.Float64(0, "true track"), MagneticTrack: p.Float64(2, "magnetic track"), GroundSpeedKnots: p.Float64(4, "ground speed (knots)"), diff --git a/vendor/github.com/adrianmo/go-nmea/gpzda.go b/vendor/github.com/adrianmo/go-nmea/gpzda.go index 16a4450f..71bbae5d 100644 --- a/vendor/github.com/adrianmo/go-nmea/gpzda.go +++ b/vendor/github.com/adrianmo/go-nmea/gpzda.go @@ -8,7 +8,7 @@ const ( // GPZDA represents date & time data. // http://aprs.gids.nl/nmea/#zda type GPZDA struct { - Sent + BaseSentence Time Time Day int64 Month int64 @@ -17,11 +17,11 @@ type GPZDA struct { OffsetMinutes int64 // Local time zone offset from GMT, minutes } -// NewGPZDA constructor -func NewGPZDA(s Sent) (GPZDA, error) { +// newGPZDA constructor +func newGPZDA(s BaseSentence) (GPZDA, error) { p := newParser(s, PrefixGPZDA) return GPZDA{ - Sent: s, + BaseSentence: s, Time: p.Time(0, "time"), Day: p.Int64(1, "day"), Month: p.Int64(2, "month"), diff --git a/vendor/github.com/adrianmo/go-nmea/parser.go b/vendor/github.com/adrianmo/go-nmea/parser.go index e73d6106..f2f123b8 100644 --- a/vendor/github.com/adrianmo/go-nmea/parser.go +++ b/vendor/github.com/adrianmo/go-nmea/parser.go @@ -8,21 +8,21 @@ import ( // parser provides a simple way of accessing and parsing // sentence fields type parser struct { - Sent + BaseSentence prefix string err error } // newParser constructor -func newParser(s Sent, prefix string) *parser { - p := &parser{Sent: s, prefix: prefix} +func newParser(s BaseSentence, prefix string) *parser { + p := &parser{BaseSentence: s, prefix: prefix} if p.Type != prefix { p.SetErr("prefix", p.Type) } return p } -// Err returns the first error encounterd during the parser's usage. +// Err returns the first error encountered during the parser's usage. func (p *parser) Err() error { return p.err } @@ -48,10 +48,10 @@ func (p *parser) String(i int, context string) string { } // EnumString returns the field value at the specified index. -// An error occurs if the value is not one of the options. +// An error occurs if the value is not one of the options and not empty. func (p *parser) EnumString(i int, context string, options ...string) string { s := p.String(i, context) - if p.err != nil { + if p.err != nil || s == "" { return "" } for _, o := range options { @@ -64,7 +64,7 @@ func (p *parser) EnumString(i int, context string, options ...string) string { } // Int64 returns the int64 value at the specified index. -// If the value is an emtpy string, 0 is returned. +// If the value is an empty string, 0 is returned. func (p *parser) Int64(i int, context string) int64 { s := p.String(i, context) if p.err != nil { @@ -126,7 +126,7 @@ func (p *parser) Date(i int, context string) Date { } // LatLong returns the coordinate value of the specified fields. -func (p *parser) LatLong(i, j int, context string) LatLong { +func (p *parser) LatLong(i, j int, context string) float64 { a := p.String(i, context) b := p.String(j, context) if p.err != nil { diff --git a/vendor/github.com/adrianmo/go-nmea/pgrme.go b/vendor/github.com/adrianmo/go-nmea/pgrme.go index 2de745e3..a1186d89 100644 --- a/vendor/github.com/adrianmo/go-nmea/pgrme.go +++ b/vendor/github.com/adrianmo/go-nmea/pgrme.go @@ -10,14 +10,14 @@ const ( // PGRME is Estimated Position Error (Garmin proprietary sentence) // http://aprs.gids.nl/nmea/#rme type PGRME struct { - Sent + BaseSentence Horizontal float64 // Estimated horizontal position error (HPE) in metres Vertical float64 // Estimated vertical position error (VPE) in metres Spherical float64 // Overall spherical equivalent position error in meters } -// NewPGRME constructor -func NewPGRME(s Sent) (PGRME, error) { +// newPGRME constructor +func newPGRME(s BaseSentence) (PGRME, error) { p := newParser(s, PrefixPGRME) horizontal := p.Float64(0, "horizontal error") @@ -30,9 +30,9 @@ func NewPGRME(s Sent) (PGRME, error) { _ = p.EnumString(5, "spherical error unit", ErrorUnit) return PGRME{ - Sent: s, - Horizontal: horizontal, - Vertical: vertial, - Spherical: spherical, + BaseSentence: s, + Horizontal: horizontal, + Vertical: vertial, + Spherical: spherical, }, p.Err() } diff --git a/vendor/github.com/adrianmo/go-nmea/sentence.go b/vendor/github.com/adrianmo/go-nmea/sentence.go index a611524a..bed8a18e 100644 --- a/vendor/github.com/adrianmo/go-nmea/sentence.go +++ b/vendor/github.com/adrianmo/go-nmea/sentence.go @@ -16,43 +16,35 @@ const ( ChecksumSep = "*" ) -// Message interface for all NMEA sentence -type Message interface { +// Sentence interface for all NMEA sentence +type Sentence interface { fmt.Stringer - Sentence() Sent Prefix() string - Validate() error } -// Sent contains the information about the NMEA sentence -type Sent struct { +// 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 } -// Sentence returns the Messages Sent -func (s Sent) Sentence() Sent { return s } - // Prefix returns the type of the message -func (s Sent) Prefix() string { return s.Type } +func (s BaseSentence) Prefix() string { return s.Type } // String formats the sentence into a string -func (s Sent) String() string { return s.Raw } +func (s BaseSentence) String() string { return s.Raw } -// Validate returns an error if the sentence is not valid -func (s Sent) Validate() error { return nil } - -// ParseSentence parses a raw message into it's fields -func ParseSentence(raw string) (Sent, error) { +// parseSentence parses a raw message into it's fields +func parseSentence(raw string) (BaseSentence, error) { startIndex := strings.Index(raw, SentenceStart) if startIndex != 0 { - return Sent{}, fmt.Errorf("nmea: sentence does not start with a '$'") + return BaseSentence{}, fmt.Errorf("nmea: sentence does not start with a '$'") } sumSepIndex := strings.Index(raw, ChecksumSep) if sumSepIndex == -1 { - return Sent{}, fmt.Errorf("nmea: sentence does not contain checksum separator") + return BaseSentence{}, fmt.Errorf("nmea: sentence does not contain checksum separator") } var ( fieldsRaw = raw[startIndex+1 : sumSepIndex] @@ -62,10 +54,10 @@ func ParseSentence(raw string) (Sent, error) { ) // Validate the checksum if checksum != checksumRaw { - return Sent{}, fmt.Errorf( + return BaseSentence{}, fmt.Errorf( "nmea: sentence checksum mismatch [%s != %s]", checksum, checksumRaw) } - return Sent{ + return BaseSentence{ Type: fields[0], Fields: fields[1:], Checksum: checksumRaw, @@ -84,34 +76,36 @@ func xorChecksum(s string) string { } // Parse parses the given string into the correct sentence type. -func Parse(raw string) (Message, error) { - s, err := ParseSentence(raw) +func Parse(raw string) (Sentence, error) { + s, err := parseSentence(raw) if err != nil { return nil, err } switch s.Type { case PrefixGPRMC: - return NewGPRMC(s) + return newGPRMC(s) case PrefixGNRMC: - return NewGNRMC(s) + return newGNRMC(s) case PrefixGPGGA: - return NewGPGGA(s) + return newGPGGA(s) case PrefixGNGGA: - return NewGNGGA(s) + return newGNGGA(s) case PrefixGPGSA: - return NewGPGSA(s) + return newGPGSA(s) case PrefixGPGLL: - return NewGPGLL(s) + return newGPGLL(s) case PrefixGPVTG: - return NewGPVTG(s) + return newGPVTG(s) case PrefixGPZDA: - return NewGPZDA(s) + return newGPZDA(s) case PrefixPGRME: - return NewPGRME(s) + return newPGRME(s) case PrefixGPGSV: - return NewGPGSV(s) + return newGPGSV(s) case PrefixGLGSV: - return NewGLGSV(s) + return newGLGSV(s) + case PrefixGPHDT: + return newGPHDT(s) default: return nil, fmt.Errorf("nmea: sentence type '%s' not implemented", s.Type) } diff --git a/vendor/github.com/adrianmo/go-nmea/types.go b/vendor/github.com/adrianmo/go-nmea/types.go index 7ee189f2..90399caa 100644 --- a/vendor/github.com/adrianmo/go-nmea/types.go +++ b/vendor/github.com/adrianmo/go-nmea/types.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" "unicode" - // "unicode/utf8" ) const ( @@ -31,42 +30,6 @@ const ( West = "W" ) -// LatLong type -type LatLong float64 - -// PrintGPS returns the GPS format for the given LatLong. -func (l LatLong) PrintGPS() string { - padding := "" - value := float64(l) - degrees := math.Floor(math.Abs(value)) - fraction := (math.Abs(value) - degrees) * 60 - if fraction < 10 { - padding = "0" - } - return fmt.Sprintf("%d%s%.4f", int(degrees), padding, fraction) -} - -// PrintDMS returns the degrees, minutes, seconds format for the given LatLong. -func (l LatLong) PrintDMS() string { - val := math.Abs(float64(l)) - degrees := int(math.Floor(val)) - minutes := int(math.Floor(60 * (val - float64(degrees)))) - seconds := 3600 * (val - float64(degrees) - (float64(minutes) / 60)) - - return fmt.Sprintf("%d\u00B0 %d' %f\"", degrees, minutes, seconds) -} - -//ValidRange validates if the range is between -180 and +180. -func (l LatLong) ValidRange() bool { - return -180.0 <= l && l <= 180.0 -} - -// IsNear returns whether the coordinate is near the other coordinate, -// by no further than the given distance away. -func (l LatLong) IsNear(o LatLong, max float64) bool { - return math.Abs(float64(l-o)) <= max -} - // ParseLatLong parses the supplied string into the LatLong. // // Supported formats are: @@ -74,27 +37,30 @@ func (l LatLong) IsNear(o LatLong, max float64) bool { // - Decimal (e.g. 33.23454) // - GPS (e.g 15113.4322S) // -func ParseLatLong(s string) (LatLong, error) { - var l LatLong - var err error - invalid := LatLong(0.0) // The invalid value to return. - if l, err = ParseDMS(s); err == nil { - return l, nil - } else if l, err = ParseGPS(s); err == nil { - return l, nil - } else if l, err = ParseDecimal(s); err == nil { - return l, nil +func ParseLatLong(s string) (float64, error) { + var l float64 + if v, err := ParseDMS(s); err == nil { + l = v + } else if v, err := ParseGPS(s); err == nil { + l = v + } else if v, err := ParseDecimal(s); err == nil { + l = v + } else { + return 0, fmt.Errorf("cannot parse [%s], unknown format", s) } - if !l.ValidRange() { - return invalid, errors.New("coordinate is not in range -180, 180") + if l < -180.0 || 180.0 < l { + return 0, errors.New("coordinate is not in range -180, 180") } - return invalid, fmt.Errorf("cannot parse [%s], unknown format", s) + return l, nil } // ParseGPS parses a GPS/NMEA coordinate. // e.g 15113.4322S -func ParseGPS(s string) (LatLong, error) { +func ParseGPS(s string) (float64, error) { parts := strings.Split(s, " ") + if len(parts) != 2 { + return 0, fmt.Errorf("invalid format: %s", s) + } dir := parts[1] value, err := strconv.ParseFloat(parts[0], 64) if err != nil { @@ -106,28 +72,39 @@ func ParseGPS(s string) (LatLong, error) { value = degrees + minutes/60 if dir == North || dir == East { - return LatLong(value), nil + return value, nil } else if dir == South || dir == West { - return LatLong(0 - value), nil + return 0 - value, nil } else { return 0, fmt.Errorf("invalid direction [%s]", dir) } } +// FormatGPS formats a GPS/NMEA coordinate +func FormatGPS(l float64) string { + padding := "" + degrees := math.Floor(math.Abs(l)) + fraction := (math.Abs(l) - degrees) * 60 + if fraction < 10 { + padding = "0" + } + return fmt.Sprintf("%d%s%.4f", int(degrees), padding, fraction) +} + // ParseDecimal parses a decimal format coordinate. // e.g: 151.196019 -func ParseDecimal(s string) (LatLong, error) { +func ParseDecimal(s string) (float64, error) { // Make sure it parses as a float. l, err := strconv.ParseFloat(s, 64) if err != nil || s[0] != '-' && len(strings.Split(s, ".")[0]) > 3 { - return LatLong(0.0), errors.New("parse error (not decimal coordinate)") + return 0.0, errors.New("parse error (not decimal coordinate)") } - return LatLong(l), nil + return l, nil } // ParseDMS parses a coordinate in degrees, minutes, seconds. // - e.g. 33° 23' 22" -func ParseDMS(s string) (LatLong, error) { +func ParseDMS(s string) (float64, error) { degrees := 0 minutes := 0 seconds := 0.0 @@ -138,42 +115,55 @@ func ParseDMS(s string) (LatLong, error) { var err error for i, r := range s { - if unicode.IsNumber(r) || r == '.' { + switch { + case unicode.IsNumber(r) || r == '.': if !endNumber { tmpBytes = append(tmpBytes, s[i]) } else { return 0, errors.New("parse error (no delimiter)") } - } else if unicode.IsSpace(r) && len(tmpBytes) > 0 { + case unicode.IsSpace(r) && len(tmpBytes) > 0: endNumber = true - } else if r == Degrees { + case r == Degrees: if degrees, err = strconv.Atoi(string(tmpBytes)); err != nil { return 0, errors.New("parse error (degrees)") } tmpBytes = tmpBytes[:0] endNumber = false - } else if s[i] == Minutes { + case s[i] == Minutes: if minutes, err = strconv.Atoi(string(tmpBytes)); err != nil { return 0, errors.New("parse error (minutes)") } tmpBytes = tmpBytes[:0] endNumber = false - } else if s[i] == Seconds { + case s[i] == Seconds: if seconds, err = strconv.ParseFloat(string(tmpBytes), 64); err != nil { return 0, errors.New("parse error (seconds)") } tmpBytes = tmpBytes[:0] endNumber = false - } else if unicode.IsSpace(r) && len(tmpBytes) == 0 { + case unicode.IsSpace(r) && len(tmpBytes) == 0: continue - } else { + default: return 0, fmt.Errorf("parse error (unknown symbol [%d])", s[i]) } } - val := LatLong(float64(degrees) + (float64(minutes) / 60.0) + (float64(seconds) / 60.0 / 60.0)) + if len(tmpBytes) > 0 { + return 0, fmt.Errorf("parse error (trailing data [%s])", string(tmpBytes)) + } + val := float64(degrees) + (float64(minutes) / 60.0) + (float64(seconds) / 60.0 / 60.0) return val, nil } +// FormatDMS returns the degrees, minutes, seconds format for the given LatLong. +func FormatDMS(l float64) string { + val := math.Abs(l) + degrees := int(math.Floor(val)) + minutes := int(math.Floor(60 * (val - float64(degrees)))) + seconds := 3600 * (val - float64(degrees) - (float64(minutes) / 60)) + return fmt.Sprintf("%d\u00B0 %d' %f\"", degrees, minutes, seconds) +} + // Time type type Time struct { Valid bool