-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnmea.c
259 lines (225 loc) · 7.09 KB
/
nmea.c
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*! \file nmea.c \brief NMEA protocol function library. */
//*****************************************************************************
//
// File Name : 'nmea.c'
// Title : NMEA protocol function library
// Author : Pascal Stang - Copyright (C) 2002
// Created : 2002.08.27
// Revised : 2002.08.27
// Version : 0.1
// Target MCU : Atmel AVR Series
// Editor Tabs : 4
//
// NOTE: This code is currently below version 1.0, and therefore is considered
// to be lacking in some functionality or documentation, or may not be fully
// tested. Nonetheless, you can expect most functions to work.
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
#ifndef WIN32
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "global.h"
#include "buffer.h"
#include "rprintf.h"
#include "gps.h"
#include "nmea.h"
// Program ROM constants
// Global variables
extern GpsInfoType GpsInfo;
u08 NmeaPacket[NMEA_BUFFERSIZE];
void nmeaInit(void)
{
}
u08* nmeaGetPacketBuffer(void)
{
return NmeaPacket;
}
u08 nmeaProcess(cBuffer* rxBuffer)
{
u08 foundpacket = NMEA_NODATA;
u08 startFlag = FALSE;
//u08 data;
u16 i,j;
// process the receive buffer
// go through buffer looking for packets
while(rxBuffer->datalength)
{
// look for a start of NMEA packet
if(bufferGetAtIndex(rxBuffer,0) == '$')
{
// found start
startFlag = TRUE;
// when start is found, we leave it intact in the receive buffer
// in case the full NMEA string is not completely received. The
// start will be detected in the next nmeaProcess iteration.
// done looking for start
break;
}
else
bufferGetFromFront(rxBuffer);
}
// if we detected a start, look for end of packet
if(startFlag)
{
for(i=1; i<(rxBuffer->datalength)-1; i++)
{
// check for end of NMEA packet <CR><LF>
if((bufferGetAtIndex(rxBuffer,i) == '\r') && (bufferGetAtIndex(rxBuffer,i+1) == '\n'))
{
// have a packet end
// dump initial '$'
bufferGetFromFront(rxBuffer);
// copy packet to NmeaPacket
for(j=0; j<(i-1); j++)
{
// although NMEA strings should be 80 characters or less,
// receive buffer errors can generate erroneous packets.
// Protect against packet buffer overflow
if(j<(NMEA_BUFFERSIZE-1))
NmeaPacket[j] = bufferGetFromFront(rxBuffer);
else
bufferGetFromFront(rxBuffer);
}
// null terminate it
NmeaPacket[j] = 0;
// dump <CR><LF> from rxBuffer
bufferGetFromFront(rxBuffer);
bufferGetFromFront(rxBuffer);
#ifdef NMEA_DEBUG_PKT
rprintf("Rx NMEA packet type: ");
rprintfStrLen(NmeaPacket, 0, 5);
rprintfStrLen(NmeaPacket, 5, (i-1)-5);
rprintfCRLF();
#endif
// found a packet
// done with this processing session
foundpacket = NMEA_UNKNOWN;
break;
}
}
}
if(foundpacket)
{
// check message type and process appropriately
if(!strncmp(NmeaPacket, "GPGGA", 5))
{
// process packet of this type
nmeaProcessGPGGA(NmeaPacket);
// report packet type
foundpacket = NMEA_GPGGA;
}
else if(!strncmp(NmeaPacket, "GPVTG", 5))
{
// process packet of this type
nmeaProcessGPVTG(NmeaPacket);
// report packet type
foundpacket = NMEA_GPVTG;
}
}
else if(rxBuffer->datalength >= rxBuffer->size)
{
// if we found no packet, and the buffer is full
// we're logjammed, flush entire buffer
bufferFlush(rxBuffer);
}
return foundpacket;
}
void nmeaProcessGPGGA(u08* packet)
{
u08 i;
char* endptr;
double degrees, minutesfrac;
#ifdef NMEA_DEBUG_GGA
rprintf("NMEA: ");
rprintfStr(packet);
rprintfCRLF();
#endif
// start parsing just after "GPGGA,"
i = 6;
// attempt to reject empty packets right away
if(packet[i]==',' && packet[i+1]==',')
return;
// get UTC time [hhmmss.sss]
GpsInfo.PosLLA.TimeOfFix.f = strtod(&packet[i], &endptr);
while(packet[i++] != ','); // next field: latitude
// get latitude [ddmm.mmmmm]
GpsInfo.PosLLA.lat.f = strtod(&packet[i], &endptr);
// convert to pure degrees [dd.dddd] format
minutesfrac = modf(GpsInfo.PosLLA.lat.f/100, °rees);
GpsInfo.PosLLA.lat.f = degrees + (minutesfrac*100)/60;
// convert to radians
GpsInfo.PosLLA.lat.f *= (M_PI/180);
while(packet[i++] != ','); // next field: N/S indicator
// correct latitute for N/S
if(packet[i] == 'S') GpsInfo.PosLLA.lat.f = -GpsInfo.PosLLA.lat.f;
while(packet[i++] != ','); // next field: longitude
// get longitude [ddmm.mmmmm]
GpsInfo.PosLLA.lon.f = strtod(&packet[i], &endptr);
// convert to pure degrees [dd.dddd] format
minutesfrac = modf(GpsInfo.PosLLA.lon.f/100, °rees);
GpsInfo.PosLLA.lon.f = degrees + (minutesfrac*100)/60;
// convert to radians
GpsInfo.PosLLA.lon.f *= (M_PI/180);
while(packet[i++] != ','); // next field: E/W indicator
// correct latitute for E/W
if(packet[i] == 'W') GpsInfo.PosLLA.lon.f = -GpsInfo.PosLLA.lon.f;
while(packet[i++] != ','); // next field: position fix status
// position fix status
// 0 = Invalid, 1 = Valid SPS, 2 = Valid DGPS, 3 = Valid PPS
// check for good position fix
if( (packet[i] != '0') && (packet[i] != ',') )
GpsInfo.PosLLA.updates++;
while(packet[i++] != ','); // next field: satellites used
// get number of satellites used in GPS solution
GpsInfo.numSVs = atoi(&packet[i]);
while(packet[i++] != ','); // next field: HDOP (horizontal dilution of precision)
while(packet[i++] != ','); // next field: altitude
// get altitude (in meters)
GpsInfo.PosLLA.alt.f = strtod(&packet[i], &endptr);
while(packet[i++] != ','); // next field: altitude units, always 'M'
while(packet[i++] != ','); // next field: geoid seperation
while(packet[i++] != ','); // next field: seperation units
while(packet[i++] != ','); // next field: DGPS age
while(packet[i++] != ','); // next field: DGPS station ID
while(packet[i++] != '*'); // next field: checksum
}
void nmeaProcessGPVTG(u08* packet)
{
u08 i;
char* endptr;
#ifdef NMEA_DEBUG_VTG
rprintf("NMEA: ");
rprintfStr(packet);
rprintfCRLF();
#endif
// start parsing just after "GPVTG,"
i = 6;
// attempt to reject empty packets right away
if(packet[i]==',' && packet[i+1]==',')
return;
// get course (true north ref) in degrees [ddd.dd]
GpsInfo.VelHS.heading.f = strtod(&packet[i], &endptr);
while(packet[i++] != ','); // next field: 'T'
while(packet[i++] != ','); // next field: course (magnetic north)
// get course (magnetic north ref) in degrees [ddd.dd]
//GpsInfo.VelHS.heading.f = strtod(&packet[i], &endptr);
while(packet[i++] != ','); // next field: 'M'
while(packet[i++] != ','); // next field: speed (knots)
// get speed in knots
//GpsInfo.VelHS.speed.f = strtod(&packet[i], &endptr);
while(packet[i++] != ','); // next field: 'N'
while(packet[i++] != ','); // next field: speed (km/h)
// get speed in km/h
GpsInfo.VelHS.speed.f = strtod(&packet[i], &endptr);
while(packet[i++] != ','); // next field: 'K'
while(packet[i++] != '*'); // next field: checksum
GpsInfo.VelHS.updates++;
}