-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc.go
181 lines (153 loc) · 6.96 KB
/
doc.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
Package loc converts an NMEA-0183 stream into compiled fixes.
At the end of each NMEA cycle, a new fix is compiled and a LocInfo
structure is delivered on a channel. This channel is created on user's
behalf when the Init function is called.
The LocInfo structure gives information about the quality of the fix
(navigation mode, DOPs, etc.), time, actual location (latitude,
longitude, elevation), speed, heading as well as the characteristics of
the satellites in view and used for the solution.
Example usage
A typical usage would look like the following code.
package main
import (
"io"
"log"
"os"
"github.com/rdeg/loc"
)
func main() {
// Start a go routine to handle GNSS fixes from the loc package.
done := make(chan struct {}) // channel used to terminate locHandler
defer close(done)
work := loc.Init("", 0) // let loc package determine lsdt
defer loc.Exit()
go locHandler(work, done)
// Open the GNSS device.
// Please note that some serial line tuning may be needed before
// operating the port, such as suppressing the echo and the conversion
// of CR into LF (i.e. 'stty -F /dev/ttyACM0 -echo -icrlf').
file, err := os.Open("/dev/ttyACM0")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Read the GNSS device stream and feed-back the loc package.
buf := make([]byte, 1024)
for {
n, err := file.Read(buf)
if n > 0 {
loc.Feed(buf[:n])
}
if err == io.EOF {
break
}
if err != nil {
log.Printf("Error: read %d bytes: %v\n", n, err)
break
}
}
}
// This goroutine handles the LocData sent by the loc package.
func locHandler(work chan *loc.LocInfo, done chan struct{}) {
for {
select {
case li := <-work:
log.Printf("LocInfo: %v\n\n", li)
case <-done:
return // exit the handler
}
}
}
NMEA-0183 stream
The NMEA-0183 stream generated by GPS/GNSS receivers is well known and
documented. In general, a number of NMEA 'sentences' are output every
second or couple of seconds by the receiver on a serial or USB link.
For a given receiver, the sentences are usually repeated identically in
each cycle, at least as far as their 'types' (GPRMC, GPGGA and so) are
concerned. For instance:
$GPRMC,093907,A,4729.3207,N,01904.7852,E,000.0,005.9,310305,002.9,E*7B
$GPGGA,093907,4729.3207,N,01904.7852,E,1,05,2.6,103.8,M,41.0,M,,*42
$GPGSA,A,3,03,,15,16,18,,,22,,,,,4.7,2.6,1.5*38
$GPGSV,3,1,11,03,67,292,44,08,03,333,,15,73,047,45,16,33,202,38*76
$GPGSV,3,2,11,18,56,077,44,19,37,301,31,21,29,069,31,22,62,145,43*78
$GPGSV,3,3,11,26,11,036,,27,02,312,,29,05,022,,,,,*41
$GPRMC,093908,A,4729.3206,N,01904.7851,E,000.0,005.9,310305,002.9,E*76
$GPGGA,093908,4729.3206,N,01904.7851,E,1,05,2.8,104.1,M,41.0,M,,*4F
$GPGSA,A,3,03,,,15,16,18,,,22,,,,4.7,2.8,1.0*33
$GPGSV,3,1,12,03,67,292,45,08,03,333,,14,01,149,,15,73,047,45*70
$GPGSV,3,2,12,16,33,202,38,18,56,077,43,19,37,301,31,21,29,069,31*73
$GPGSV,3,3,12,22,62,145,43,26,11,036,,27,02,312,,29,05,022,*71
In this example, each cycle consists in a sequence of GPRMC, GPCCA, GPGSA
and GPGSV sentences issued every second. The number of GPGSV sentences
may vary, depending on the actual number of satellites sensed by the
receiver (in-view satellites).
The output of loc for the two cycles given above could print as:
09:39:07: Lat = 47.488682, Lon = 19.079752, Quality = 1, Mode = 3, HDOP = 2.600000, Level = 5
Sats (*5/11): *3 8 *15 *16 *18 19 21 *22 26 27 29
09:39:08: Lat = 47.488682, Lon = 19.079752, Quality = 1, Mode = 3, HDOP = 2.800000, Level = 5
Sats (*5/12): *3 8 14 *15 *16 *18 19 21 *22 26 27 29
See examples/feed/feed.go for details.
Satellites
The NMEA standard was originally designed for 4800 baud serial links.
The communication was certainly robust but not really adapted to the
supply of a quantity of information a little bigger than the bare minimum.
As a result, the display of information on satellites in view quickly
posed a problem to manufacturers of GPS receivers.
At that time, it was difficult to expect to process more than a dozen
satellites, but this still involved sending up to 3 GPGSV messages on the
serial link, in addition to the almost mandatory GPGGA, GPRMC and GPGSA:
at 2 milliseconds per character, it was tricky to pass all the information
of a GPS fix in less than a second. As a result, each manufacturer developed
its own strategy to solve the problem.
Some chose to send all the information but only every two seconds. To
further relieve the serial link, they could also decide to send the GPGSV
messages only once in five. Finally, some manufacturers chose to send
only one GPGSV per cycle, thereby creating a sort of 'super cycle' during
which all GPGSVs are sent.
The output of some old GPS receivers that issue a single GPGSV message per
NMEA cycle is not handled very gracefully by this package. In this situation,
the satellites are cleared when the first GPGSV sentence of a 'super cycle'
is encoutered and only the cycle containing the last GPGSV sentence will give
a fix with the actual number of satellites.
For illustration, here is the output of a Garmin GPS receiver made in 2005
and its interpretation by loc:
$GPRMC,183455,A,4124.7339,N,08152.2463,W,000.0,000.0,010305,007.9,W*71
$GPGGA,183455,4124.7339,N,08152.2463,W,2,04,2.5,259.3,M,-34.0,M,,*79
$GPGSA,A,2,,04,,,07,10,,,,,,35,2.7,2.5,1.0*34
$GPGSV,3,1,12,02,71,317,00,04,50,056,36,05,43,263,00,06,02,307,00*7B
$PGRME,13.6,M,28.3,M,31.5,M*14
$PGRMM,WGS 84*06
18:34:55: Lat = 41.412231, Lon = -81.870766, Quality = 2, Mode = 2, HDOP = 2.500000, Level = 5
Sats (*1/4): 2 *4 5 6
$GPRMC,183456,A,4124.7342,N,08152.2464,W,000.0,000.0,010305,007.9,W*79
$GPGGA,183456,4124.7342,N,08152.2464,W,2,04,2.5,259.0,M,-34.0,M,,*72
$GPGSA,A,2,,04,,,07,10,,,,,,35,2.7,2.5,1.0*34
$GPGSV,3,2,12,07,20,126,31,10,44,197,50,13,20,067,00,23,09,040,00*7F
$PGRME,13.6,M,28.3,M,31.5,M*14
$PGRMM,WGS 84*06
18:34:56: Lat = 41.412239, Lon = -81.870773, Quality = 2, Mode = 2, HDOP = 2.500000, Level = 5
Sats (*3/8): 2 *4 5 6 *7 *10 13 23
$GPRMC,183457,A,4124.7342,N,08152.2464,W,000.0,000.0,010305,007.9,W*78
$GPGGA,183457,4124.7342,N,08152.2464,W,2,04,2.5,258.8,M,-34.0,M,,*7A
$GPGSA,A,2,,04,,,07,10,,,,,,35,2.7,2.5,1.0*34
$GPGSV,3,3,12,24,17,068,00,30,29,304,00,33,09,106,41,35,34,141,43*72
$PGRME,13.8,M,30.4,M,33.6,M*15
$PGRMM,WGS 84*06
18:34:57: Lat = 41.412239, Lon = -81.870773, Quality = 2, Mode = 2, HDOP = 2.500000, Level = 5
Sats (*4/12): 2 *4 5 6 *7 *10 13 23 24 30 33 *35
Credits
This package has been loosely inspired by the work of the NMEA library
project at SourceForge:
NMEA library
URL: http://nmea.sourceforge.net
Author: Tim ([email protected])
Licence: http://www.gnu.org/licenses/lgpl.html
It is also based on work carried out at ACTIA PCs (http://www.actia-pcs.fr/en/)
as part of the EBSF 2 project (http://ebsf2.eu/).
*/
package loc
/* BUG(rdeg): The output of some old GPS receivers that issue a single
GPGSV sentence per NMEA cycle is not handled very gracefully. See the
Overview section for details.
*/