Skip to content

Commit 0c836e0

Browse files
authored
Merge pull request #42 from bmurray/master
Support for GNGNS Strings
2 parents a32116e + c7d459b commit 0c836e0

8 files changed

+202
-4
lines changed

.travis.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@
55
language: go
66

77
go:
8-
- 1.7.x
9-
- 1.8.x
108
- 1.9.x
119
- 1.10.x
10+
- 1.11.x
1211
- tip
1312

1413
matrix:
1514
fast_finish: true
1615

1716
before_install:
1817
- go get golang.org/x/tools/cmd/cover
19-
- go get github.com/golang/lint/golint
18+
- go get golang.org/x/lint/golint
2019
- go get github.com/mattn/goveralls
2120
- go get github.com/stretchr/testify/assert
2221

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ At this moment, this library supports the following sentence types:
2828
- [GPZDA](http://aprs.gids.nl/nmea/#zda) - Date & time data
2929
- [PGRME](http://aprs.gids.nl/nmea/#rme) - Estimated Position Error (Garmin proprietary sentence)
3030
- [GPHDT](http://aprs.gids.nl/nmea/#hdt) - Actual vessel heading in degrees True
31+
- [GNGNS](https://www.trimble.com/oem_receiverhelp/v4.44/en/NMEA-0183messages_GNS.html) - Combined GPS fix for GPS, Glonass, Galileo, and BeiDou
3132

3233

3334
## Example

gngns.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package nmea
2+
3+
const (
4+
// PrefixGNGNS prefix
5+
PrefixGNGNS = "GNGNS"
6+
// NoFixGNGNS Character
7+
NoFixGNGNS = "N"
8+
// AutonomousGNGNS Character
9+
AutonomousGNGNS = "A"
10+
// DifferentialGNGNS Character
11+
DifferentialGNGNS = "D"
12+
// PreciseGNGNS Character
13+
PreciseGNGNS = "P"
14+
// RealTimeKinematicGNGNS Character
15+
RealTimeKinematicGNGNS = "R"
16+
// FloatRTKGNGNS RealTime Kinematic Character
17+
FloatRTKGNGNS = "F"
18+
// EstimatedGNGNS Fix Character
19+
EstimatedGNGNS = "E"
20+
// ManualGNGNS Fix Character
21+
ManualGNGNS = "M"
22+
// SimulatorGNGNS Character
23+
SimulatorGNGNS = "S"
24+
)
25+
26+
// GNGNS is standard GNSS sentance that combined multiple constellations
27+
type GNGNS struct {
28+
BaseSentence
29+
Time Time
30+
Latitude float64
31+
Longitude float64
32+
Mode []string
33+
SVs int64
34+
HDOP float64
35+
Altitude float64
36+
Separation float64
37+
Age float64
38+
Station int64
39+
}
40+
41+
// newGNGNS Constructor
42+
func newGNGNS(s BaseSentence) (GNGNS, error) {
43+
p := newParser(s, PrefixGNGNS)
44+
m := GNGNS{
45+
BaseSentence: s,
46+
Time: p.Time(0, "time"),
47+
Latitude: p.LatLong(1, 2, "latitude"),
48+
Longitude: p.LatLong(3, 4, "longitude"),
49+
Mode: p.EnumChars(5, "mode", NoFixGNGNS, AutonomousGNGNS, DifferentialGNGNS, PreciseGNGNS, RealTimeKinematicGNGNS, FloatRTKGNGNS, EstimatedGNGNS, ManualGNGNS, SimulatorGNGNS),
50+
SVs: p.Int64(6, "SVs"),
51+
HDOP: p.Float64(7, "HDOP"),
52+
Altitude: p.Float64(8, "altitude"),
53+
Separation: p.Float64(9, "separation"),
54+
Age: p.Float64(10, "age"),
55+
Station: p.Int64(11, "station"),
56+
}
57+
return m, p.Err()
58+
}

gngns_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package nmea
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
var gngnstests = []struct {
10+
name string
11+
raw string
12+
err string
13+
msg GNGNS
14+
}{
15+
{
16+
name: "good sentence A",
17+
raw: "$GNGNS,014035.00,4332.69262,S,17235.48549,E,RR,13,0.9,25.63,11.24,,*70",
18+
msg: GNGNS{
19+
Time: Time{true, 1, 40, 35, 0},
20+
Latitude: MustParseGPS("4332.69262 S"),
21+
Longitude: MustParseGPS("17235.48549 E"),
22+
Mode: []string{"R", "R"},
23+
SVs: 13,
24+
HDOP: 0.9,
25+
Altitude: 25.63,
26+
Separation: 11.24,
27+
Age: 0,
28+
Station: 0,
29+
},
30+
},
31+
{
32+
name: "good sentence B",
33+
raw: "$GNGNS,094821.0,4849.931307,N,00216.053323,E,AA,14,0.6,161.5,48.0,,*6D",
34+
msg: GNGNS{
35+
Time: Time{true, 9, 48, 21, 0},
36+
Latitude: MustParseGPS("4849.931307 N"),
37+
Longitude: MustParseGPS("00216.053323 E"),
38+
Mode: []string{"A", "A"},
39+
SVs: 14,
40+
HDOP: 0.6,
41+
Altitude: 161.5,
42+
Separation: 48.0,
43+
Age: 0,
44+
Station: 0,
45+
},
46+
},
47+
{
48+
name: "good sentence B",
49+
raw: "$GNGNS,094821.0,4849.931307,N,00216.053323,E,AAN,14,0.6,161.5,48.0,,*23",
50+
msg: GNGNS{
51+
Time: Time{true, 9, 48, 21, 0},
52+
Latitude: MustParseGPS("4849.931307 N"),
53+
Longitude: MustParseGPS("00216.053323 E"),
54+
Mode: []string{"A", "A", "N"},
55+
SVs: 14,
56+
HDOP: 0.6,
57+
Altitude: 161.5,
58+
Separation: 48.0,
59+
Age: 0,
60+
Station: 0,
61+
},
62+
},
63+
{
64+
name: "bad sentence",
65+
raw: "$GNGNS,094821.0,4849.931307,N,00216.053323,E,AAX,14,0.6,161.5,48.0,,*35",
66+
err: "nmea: GNGNS invalid mode: AAX",
67+
},
68+
}
69+
70+
func TestGNGNS(t *testing.T) {
71+
for _, tt := range gngnstests {
72+
t.Run(tt.name, func(t *testing.T) {
73+
m, err := Parse(tt.raw)
74+
if tt.err != "" {
75+
assert.Error(t, err)
76+
assert.EqualError(t, err, tt.err)
77+
} else {
78+
assert.NoError(t, err)
79+
gngns := m.(GNGNS)
80+
gngns.BaseSentence = BaseSentence{}
81+
assert.Equal(t, tt.msg, gngns)
82+
}
83+
})
84+
}
85+
}

gprmc_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var gprmctests = []struct {
1616
name: "good sentence A",
1717
raw: "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70",
1818
msg: GPRMC{
19-
Time: Time{true, 22, 05, 16, 0},
19+
Time: Time{true, 22, 5, 16, 0},
2020
Validity: "A",
2121
Speed: 173.8,
2222
Course: 231.8,

parser.go

+26
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@ func (p *parser) EnumString(i int, context string, options ...string) string {
6363
return ""
6464
}
6565

66+
// EnumChars returns an array of strings that are matched in the Mode field.
67+
// It will only match the number of characters that are in the Mode field.
68+
// If the value is empty, it will return an empty array
69+
func (p *parser) EnumChars(i int, context string, options ...string) []string {
70+
s := p.String(i, context)
71+
if p.err != nil || s == "" {
72+
return []string{}
73+
}
74+
strs := []string{}
75+
for _, r := range s {
76+
rs := string(r)
77+
for _, o := range options {
78+
if o == rs {
79+
strs = append(strs, o)
80+
break
81+
}
82+
}
83+
}
84+
if len(strs) != len(s) {
85+
86+
p.SetErr(context, s)
87+
return []string{}
88+
}
89+
return strs
90+
}
91+
6692
// Int64 returns the int64 value at the specified index.
6793
// If the value is an empty string, 0 is returned.
6894
func (p *parser) Int64(i int, context string) int64 {

parser_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,33 @@ var parsertests = []struct {
6666
return p.EnumString(1, "context", "a", "b")
6767
},
6868
},
69+
{
70+
name: "EnumChars",
71+
fields: []string{"AA", "AB", "BA", "BB"},
72+
expected: []string{"A", "B"},
73+
parse: func(p *parser) interface{} {
74+
return p.EnumChars(1, "context", "A", "B")
75+
},
76+
},
77+
{
78+
name: "EnumChars invalid",
79+
fields: []string{"a", "AB", "c"},
80+
expected: []string{},
81+
hasErr: true,
82+
parse: func(p *parser) interface{} {
83+
return p.EnumChars(1, "context", "X", "Y")
84+
},
85+
},
86+
{
87+
name: "EnumChars with existing error",
88+
fields: []string{"a", "AB", "c"},
89+
expected: []string{},
90+
hasErr: true,
91+
parse: func(p *parser) interface{} {
92+
p.SetErr("context", "value")
93+
return p.EnumChars(1, "context", "A", "B")
94+
},
95+
},
6996
{
7097
name: "Int64",
7198
fields: []string{"123"},

sentence.go

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ func Parse(raw string) (Sentence, error) {
106106
return newGLGSV(s)
107107
case PrefixGPHDT:
108108
return newGPHDT(s)
109+
case PrefixGNGNS:
110+
return newGNGNS(s)
109111
default:
110112
return nil, fmt.Errorf("nmea: sentence type '%s' not implemented", s.Type)
111113
}

0 commit comments

Comments
 (0)