Skip to content

Commit

Permalink
implement support for $GPGSV
Browse files Browse the repository at this point in the history
  • Loading branch information
tzneal committed Mar 6, 2017
1 parent 0f076f5 commit 7eb0338
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ At this moment, this library supports the following sentence types:
- [GPRMC](http://aprs.gids.nl/nmea/#rmc) - Recommended minimum specific GPS/Transit data
- [GPGGA](http://aprs.gids.nl/nmea/#gga) - Global Positioning System Fix Data
- [GPGSA](http://aprs.gids.nl/nmea/#gsa) - GPS DOP and active satellites
- [GPGSV](http://aprs.gids.nl/nmea/#gsa) - GPS Satellites in view
- [GPGLL](http://aprs.gids.nl/nmea/#gll) - Geographic Position, Latitude / Longitude and time
- [GPVTG](http://aprs.gids.nl/nmea/#vtg) - Track Made Good and Ground Speed
- [GPZDA](http://aprs.gids.nl/nmea/#zda) - Date & time data
Expand Down
107 changes: 107 additions & 0 deletions gpgsv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package nmea

import (
"fmt"
"strconv"
)

const (
// PrefixGPGSV prefix
PrefixGPGSV = "GPGSV"
)

// GPGSV represents the GPS Satellites in view
// http://aprs.gids.nl/nmea/#gpgsv
type GPGSV struct {
Sentence
TotalMessages int64 // Total number of messages of this type in this cycle
MessageNumber int64 // Message number
NumberSVsInView int64 // Total number of SVs in view

Info []GPGSVInfo // visible satellite info (0-4 of these)
}

// GPGSVInfo represents information about a visible satellite
type GPGSVInfo struct {
SVPRNNumber int64 // SV PRN number, pseudo-random noise or gold code
Elevation int64 // Elevation in degrees, 90 maximum
Azimuth int64 // Azimuth, degrees from true north, 000 to 359
SNR int64 // SNR, 00-99 dB (null when not tracking)
}

// NewGPGSV constructor
func NewGPGSV(sentence Sentence) GPGSV {
return GPGSV{Sentence: sentence}
}

// GetSentence getter
func (s GPGSV) GetSentence() Sentence {
return s.Sentence
}

func (s *GPGSV) parse() error {

if s.Type != PrefixGPGSV {
return fmt.Errorf("%s is not a %s", s.Type, PrefixGPGSV)
}
var err error
if s.Fields[0] != "" {
s.TotalMessages, err = strconv.ParseInt(s.Fields[0], 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode total number of messages error: %s", s.Fields[0])
}
}

if s.Fields[1] != "" {
s.MessageNumber, err = strconv.ParseInt(s.Fields[1], 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode message number error: %s", s.Fields[1])
}
}

if s.Fields[2] != "" {
s.NumberSVsInView, err = strconv.ParseInt(s.Fields[2], 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode number of SVs in view error: %s", s.Fields[2])
}
}

s.Info = nil
for i := 0; i < 4; i++ {
info := GPGSVInfo{}
field := s.Fields[3+i*4]
if s.Fields[3+i*4] != "" {
info.SVPRNNumber, err = strconv.ParseInt(field, 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode SV prn number error: %s", field)
}
}

field = s.Fields[4+i*4]
if field != "" {
info.Elevation, err = strconv.ParseInt(field, 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode elevation error: %s", field)
}
}

field = s.Fields[5+i*4]
if field != "" {
info.Azimuth, err = strconv.ParseInt(field, 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode azimuth error: %s", field)
}
}

field = s.Fields[6+i*4]
if field != "" {
info.SNR, err = strconv.ParseInt(field, 10, 64)
if err != nil {
return fmt.Errorf("GPGSV decode SNR error: %s", field)
}
}
s.Info = append(s.Info, info)
}

return nil
}
71 changes: 71 additions & 0 deletions gpgsv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package nmea

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGPGSVGoodSentence(t *testing.T) {
goodMsg := "$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*77"
s, err := Parse(goodMsg)

assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPGSV, s.GetSentence().Type, "Prefix does not match")

sentence := s.(GPGSV)
assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match")
assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match")
assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match")

assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match")
assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match")
assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match")

assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match")
assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match")
assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match")

assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match")
assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match")
assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match")
assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match")

assert.Equal(t, int64(13), sentence.Info[3].SVPRNNumber, "Number of Info[3] SV PRN does not match")
assert.Equal(t, int64(6), sentence.Info[3].Elevation, "Number of Info[3] Elevation does not match")
assert.Equal(t, int64(292), sentence.Info[3].Azimuth, "Number of Info[3] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[3].SNR, "Number of Info[3] SNR does not match")
}

func TestGPGSVBadSentence(t *testing.T) {
tests := []struct {
Input string
Error string
}{
{"$GPGSV,3,1,11.2,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*6b", "GPGSV decode number of SVs in view error: 11.2"},
{"$GPGSV,A3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "GPGSV decode total number of messages error: A3"},
{"$GPGSV,3,A1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "GPGSV decode message number error: A1"},
{"$GPGSV,3,1,11,A03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "GPGSV decode SV prn number error: A03"},
{"$GPGSV,3,1,11,03,A03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "GPGSV decode elevation error: A03"},
{"$GPGSV,3,1,11,03,03,A111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "GPGSV decode azimuth error: A111"},
{"$GPGSV,3,1,11,03,03,111,A00,04,15,270,00,06,01,010,12,13,06,292,00*36", "GPGSV decode SNR error: A00"},
}
for _, tc := range tests {
_, err := Parse(tc.Input)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, tc.Error, err.Error(), "Incorrect error message")
}

}

func TestGPGSVWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
sent := Sentence{}
sent.parse(wrongMsg)
msg := GPGSV{Sentence: sent}
err := msg.parse()
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "GPXTE is not a GPGSV", err.Error(), "Incorrect error message")
}
6 changes: 6 additions & 0 deletions nmea.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ func Parse(s string) (SentenceI, error) {
return nil, err
}
return pgrme, nil
} else if sentence.Type == PrefixGPGSV {
gpgsv := NewGPGSV(sentence)
if err := gpgsv.parse(); err != nil {
return nil, err
}
return gpgsv, nil
}

err := fmt.Errorf("Sentence type '%s' not implemented", sentence.Type)
Expand Down

0 comments on commit 7eb0338

Please sign in to comment.