Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2136 - 1
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3
 
4
//############################################################################
5
//# HISTORY  nmea.c
6
//#
7
//# 08.08.2015 CB
8
//# - add: Einführung eines neuen Interruptbasierten NMEA Parsers
9
//#        cpp-Code auf c geändert und an PKT angepasst
10
//#
11
//############################################################################
12
 
13
/* URSPRUNG:
14
    File:       nmea.cpp
15
    Version:    0.1.0
16
    Date:       Feb. 23, 2013
17
        License:        GPL v2
18
 
19
        NMEA GPS content parser
20
 
21
    ****************************************************************************
22
    Copyright (C) 2013 Radu Motisan  <radu.motisan@gmail.com>
23
 
24
        http://www.pocketmagic.net
25
 
26
    This program is free software; you can redistribute it and/or modify
27
    it under the terms of the GNU General Public License as published by
28
    the Free Software Foundation; either version 2 of the License, or
29
    (at your option) any later version.
30
 
31
    This program is distributed in the hope that it will be useful,
32
    but WITHOUT ANY WARRANTY; without even the implied warranty of
33
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34
    GNU General Public License for more details.
35
 
36
    You should have received a copy of the GNU General Public License
37
    along with this program; if not, write to the Free Software
38
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
39
    ****************************************************************************
40
 */
41
 
42
#include "nmea.h"
43
#include "../timer/timer.h"
44
//#include <stdio.h>
45
//#include <stdlib.h>
46
//#include <util/delay.h>
47
#include  <stdbool.h>
48
 
49
 
50
  #define MAX_POWER   10
51
  #define getPower(x) (int32_t)pgm_read_dword(&powers[x])
52
  static const int32_t    powers[MAX_POWER] PROGMEM = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
53
 
54
  uint8_t m_bFlagRead,                                      // flag used by the parser, when a valid sentence has begun
55
          m_bFlagDataReady = false;                         // valid GPS fix and data available, user can call reader functions
56
  volatile uint32_t res_nNMEAcounter = 0;                   // NMEA Datensatzzähler
57
  volatile uint32_t res_nCRCerror = 0;                      // zählt die CRC Errors beim dekodieren
58
  uint8_t receiveNMEA = false;
59
  char tmp_words[20][15],                                   //      hold parsed words for one given NMEA sentence
60
       tmp_szChecksum[15];                                  //      hold the received checksum for one given NMEA sentence
61
 
62
  // will be set to true for characters between $ and * only
63
  bool m_bFlagComputedCks ;                                 // used to compute checksum and indicate valid checksum interval (between $ and * in a given sentence)
64
  int  m_nChecksum ;                                        // numeric checksum, computed for a given sentence
65
  bool m_bFlagReceivedCks ;                                 // after getting  * we start cuttings the received checksum
66
  int  index_received_checksum ;                            // used to parse received checksum
67
 
68
  // word cutting variables
69
  int  m_nWordIdx ,                                         // the current word in a sentence
70
       m_nPrevIdx,                                          // last character index where we did a cut
71
       m_nNowIdx ;                                          // current character index
72
 
73
  // globals to store parser results
74
  int32_t res_fLongitude;                                   // GPRMC and GPGGA
75
  int32_t res_fLatitude;                                    // GPRMC and GPGGA
76
  unsigned char   res_nUTCHour, res_nUTCMin, res_nUTCSec,   // GPRMC and GPGGA
77
                  res_nUTCDay, res_nUTCMonth, res_nUTCYear; // GPRMC
78
  char     Time[9];                                         // GPRMC and GPGGA
79
  uint8_t res_nSatellitesUsed;                              // GPGGA
80
  uint8_t res_nGPSfix;                                      // GPGGA
81
  int16_t res_nHDOP;                                        // GPGGA
82
  int16_t res_fAltitude;                                    // GPGGA
83
  float res_fSpeed;                                         // GPRMC
84
  int16_t res_fBearing;                                       // GPRMC
85
 
86
  NMEA_GPGGA_t    NMEA;                                     // Variable mit der Struktur für die NMEA Daten für PKT
87
 
88
  // the parser, currently handling GPRMC and GPGGA, but easy to add any new sentences
89
  //              void parsedata();
90
  // aux functions
91
  int digit2dec(char hexdigit);
92
  float string2float(char* s);
93
  int   mstrcmp(const char *s1, const char *s2);
94
 
95
 
96
 
97
 
98
/*
99
 * The serial data is assembled on the fly, without using any redundant buffers.
100
 * When a sentence is complete (one that starts with $, ending in EOL), all processing is done on
101
 * this temporary buffer that we've built: checksum computation, extracting sentence "words" (the CSV values),
102
 * and so on.
103
 * When a new sentence is fully assembled using the fusedata function, the code calls parsedata.
104
 * This function in turn, splits the sentences and interprets the data. Here is part of the parser function,
105
 * handling both the $GPRMC NMEA sentence:
106
 */
107
uint8_t fusedata(char c)
108
 
109
         {
110
 
111
        if (c == '$') {
112
                m_bFlagRead = true;
113
                // init parser vars
114
                m_bFlagComputedCks = false;
115
                m_nChecksum = 0;
116
                // after getting  * we start cuttings the received m_nChecksum
117
                m_bFlagReceivedCks = false;
118
                index_received_checksum = 0;
119
                // word cutting variables
120
                m_nWordIdx = 0; m_nPrevIdx = 0; m_nNowIdx = 0;
121
                timer_nmea_timeout = NMEA_TIMEOUT;                      // Timeout Timer setzen
122
        }
123
 
124
        if (m_bFlagRead) {
125
                // check ending
126
                if (c == '\r' || c== '\n') {
127
                        // catch last ending item too
128
                        tmp_words[m_nWordIdx][m_nNowIdx - m_nPrevIdx] = 0;
129
                        m_nWordIdx++;
130
                        // cut received m_nChecksum
131
                        tmp_szChecksum[index_received_checksum] = 0;
132
                        // sentence complete, read done
133
                        m_bFlagRead = false;
134
                        // parse
135
                        parsedata();
136
                } else {
137
                        // computed m_nChecksum logic: count all chars between $ and * exclusively
138
                        if (m_bFlagComputedCks && c == '*') m_bFlagComputedCks = false;
139
                        if (m_bFlagComputedCks) m_nChecksum ^= c;
140
                        if (c == '$') m_bFlagComputedCks = true;
141
                        // received m_nChecksum
142
                        if (m_bFlagReceivedCks)  {
143
                                tmp_szChecksum[index_received_checksum] = c;
144
                                index_received_checksum++;
145
                        }
146
                        if (c == '*') m_bFlagReceivedCks = true;
147
                        // build a word
148
                        tmp_words[m_nWordIdx][m_nNowIdx - m_nPrevIdx] = c;
149
                        if (c == ',') {
150
                                tmp_words[m_nWordIdx][m_nNowIdx - m_nPrevIdx] = 0;
151
                                m_nWordIdx++;
152
                                m_nPrevIdx = m_nNowIdx;
153
                        }
154
                        else m_nNowIdx++;
155
                }                              
156
        }
157
        return m_nWordIdx;
158
}
159
 
160
 
161
/*
162
 * parse internal tmp_ structures, fused by pushdata, and set the data flag when done
163
 */
164
void parsedata(void) {
165
        int received_cks = 16*digit2dec(tmp_szChecksum[0]) + digit2dec(tmp_szChecksum[1]);
166
        //uart1.Send("seq: [cc:%X][words:%d][rc:%s:%d]\r\n", m_nChecksum,m_nWordIdx, tmp_szChecksum, received_cks);
167
        // check checksum, and return if invalid!
168
        if (m_nChecksum != received_cks) {
169
            m_bFlagDataReady = false;
170
            res_nCRCerror = res_nCRCerror+1;
171
            return;
172
        }
173
        /* $GPGGA
174
         * $GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
175
         * ex: $GPGGA,230600.501,4543.8895,N,02112.7238,E,1,03,3.3,96.7,M,39.0,M,,0000*6A,
176
         *
177
         * WORDS:
178
         *  1    = UTC of Position
179
         *  2    = Latitude
180
         *  3    = N or S
181
         *  4    = Longitude
182
         *  5    = E or W
183
         *  6    = GPS quality indicator (0=invalid; 1=GPS fix; 2=Diff. GPS fix)
184
         *  7    = Number of satellites in use [not those in view]
185
         *  8    = Horizontal dilution of position
186
         *  9    = Antenna altitude above/below mean sea level (geoid)
187
         *  10   = Meters  (Antenna height unit)
188
         *  11   = Geoidal separation (Diff. between WGS-84 earth ellipsoid and mean sea level.  
189
         *      -geoid is below WGS-84 ellipsoid)
190
         *  12   = Meters  (Units of geoidal separation)
191
         *  13   = Age in seconds since last update from diff. reference station
192
         *  14   = Diff. reference station ID#
193
         *  15   = Checksum
194
         */
195
        if (mstrcmp(tmp_words[0], "$GPGGA") == 0) {
196
                // Check GPS Fix: 0=no fix, 1=GPS fix, 2=Dif. GPS fix
197
                res_nGPSfix = atoi(&tmp_words[6][0]);
198
                NMEA.SatFix = res_nGPSfix;
199
                if (tmp_words[6][0] == '0') {
200
                        // clear data
201
                        res_fLatitude = 0;
202
                        res_fLongitude = 0;
203
//                      m_bFlagDataReady = false;
204
 
205
//                      return;
206
                }                      
207
                // parse time
208
                res_nUTCHour = digit2dec(tmp_words[1][0]) * 10 + digit2dec(tmp_words[1][1]);
209
                res_nUTCMin = digit2dec(tmp_words[1][2]) * 10 + digit2dec(tmp_words[1][3]);
210
                res_nUTCSec = digit2dec(tmp_words[1][4]) * 10 + digit2dec(tmp_words[1][5]);
211
                NMEA_getTime (tmp_words[1]);
212
 
213
                // parse latitude and longitude in NMEA format
214
//              res_fLatitude = string2float(tmp_words[2]);
215
//              res_fLongitude = string2float(tmp_words[4]);
216
 
217
                // get decimal format
218
//              if (tmp_words[3][0] == 'S') res_fLatitude  *= -1.0;
219
//              if (tmp_words[5][0] == 'W') res_fLongitude *= -1.0;
220
//              float degrees = trunc(res_fLatitude / 100.0f);
221
//              float minutes = res_fLatitude - (degrees * 100.0f);
222
//              res_fLatitude = degrees + minutes / 60.0f;
223
//              degrees = trunc(res_fLongitude / 100.0f);
224
//              minutes = res_fLongitude - (degrees * 100.0f);
225
//              res_fLongitude = degrees + minutes / 60.0f;
226
 
227
                res_fLatitude = NMEA_calcLatitude(tmp_words[2],tmp_words[3]);
228
                res_fLongitude = NMEA_calcLongitude(tmp_words[4],tmp_words[5]);
229
                NMEA.Latitude = res_fLatitude;
230
                NMEA.Longitude = res_fLongitude;
231
                // parse number of satellites
232
                res_nSatellitesUsed = (int)string2float(tmp_words[7]);
233
                NMEA.SatsInUse = atoi(tmp_words[7]);
234
                // parse HDOP
235
                res_nHDOP = NMEA_floatStrToInt(tmp_words[8],1);
236
                NMEA.HDOP = res_nHDOP;
237
 
238
                // parse altitude
239
//              res_fAltitude = string2float(tmp_words[9]);
240
                res_fAltitude = NMEA_floatStrToInt( tmp_words[9], 1);
241
                NMEA.Altitude = res_fAltitude;
242
                // data ready
243
                m_bFlagDataReady = true;
244
                res_nNMEAcounter = res_nNMEAcounter +1;
245
                NMEA.Counter =  res_nNMEAcounter;
246
 
247
        }
248
 
249
// 8.8.2015 CB wird zur Zeit nicht benötigt
250
        /* $GPRMC
251
         * note: a siRF chipset will not support magnetic headers.
252
         * $GPRMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,ddmmyy,x.x,a*hh
253
         * ex: $GPRMC,230558.501,A,4543.8901,N,02112.7219,E,1.50,181.47,230213,,,A*66,
254
         *
255
         * WORDS:
256
         *  1    = UTC of position fix
257
         *  2    = Data status (V=navigation receiver warning)
258
         *  3    = Latitude of fix
259
         *  4    = N or S
260
         *  5    = Longitude of fix
261
         *  6    = E or W
262
         *  7    = Speed over ground in knots
263
         *  8    = Track made good in degrees True, Bearing This indicates the direction that the device is currently moving in,
264
         *       from 0 to 360, measured in “azimuth”.
265
         *  9    = UT date
266
         *  10   = Magnetic variation degrees (Easterly var. subtracts from true course)
267
         *  11   = E or W
268
         *  12   = Checksum
269
         */
270
//      if (mstrcmp(tmp_words[0], "$GPRMC") == 0) {
271
//              // Check data status: A-ok, V-invalid
272
//              if (tmp_words[2][0] == 'V') {
273
//                      // clear data
274
//                      res_fLatitude = 0;
275
//                      res_fLongitude = 0;
276
////                    m_bFlagDataReady = false;
277
//                      return;
278
//              }
279
////            // parse time
280
////            res_nUTCHour = digit2dec(tmp_words[1][0]) * 10 + digit2dec(tmp_words[1][1]);
281
////            res_nUTCMin = digit2dec(tmp_words[1][2]) * 10 + digit2dec(tmp_words[1][3]);
282
////            res_nUTCSec = digit2dec(tmp_words[1][4]) * 10 + digit2dec(tmp_words[1][5]);
283
////            // parse latitude and longitude in NMEA format
284
////            res_fLatitude = string2float(tmp_words[3]);
285
////            res_fLongitude = string2float(tmp_words[5]);
286
////            // get decimal format
287
////            if (tmp_words[4][0] == 'S') res_fLatitude  *= -1.0;
288
////            if (tmp_words[6][0] == 'W') res_fLongitude *= -1.0;
289
////            float degrees = trunc(res_fLatitude / 100.0f);
290
////            float minutes = res_fLatitude - (degrees * 100.0f);
291
////            res_fLatitude = degrees + minutes / 60.0f;
292
////            degrees = trunc(res_fLongitude / 100.0f);
293
////            minutes = res_fLongitude - (degrees * 100.0f);
294
////            res_fLongitude = degrees + minutes / 60.0f;
295
//              //parse speed
296
//              // The knot (pronounced not) is a unit of speed equal to one nautical mile (1.852 km) per hour
297
//              res_fSpeed = NMEA_floatStrToInt(tmp_words[7],1);
298
//              res_fSpeed /= 1.852; // convert to km/h
299
//              // parse bearing
300
//              res_fBearing = NMEA_floatStrToInt(tmp_words[8],1);
301
////            // parse UTC date
302
////            res_nUTCDay = digit2dec(tmp_words[9][0]) * 10 + digit2dec(tmp_words[9][1]);
303
////            res_nUTCMonth = digit2dec(tmp_words[9][2]) * 10 + digit2dec(tmp_words[9][3]);
304
////            res_nUTCYear = digit2dec(tmp_words[9][4]) * 10 + digit2dec(tmp_words[9][5]);
305
//
306
//              // data ready
307
//                res_nNMEAcounter = res_nNMEAcounter +1;
308
//                NMEA.Counter =  res_nNMEAcounter;
309
//              m_bFlagDataReady = true;
310
//      }
311
}
312
 
313
 
314
 
315
//--------------------------------------------------------------
316
// NMEA latitudes are in the form ddmm.mmmmm, we want an integer in 1E-7 degree steps
317
//--------------------------------------------------------------
318
int32_t NMEA_calcLatitude( const char *s, const char *NS)
319
{
320
    int32_t deg;
321
    int32_t min;
322
 
323
    //lcdx_puts_at(0,5,NS,0,0,0);
324
    deg = (s[0] - '0') * 10 + s[1] - '0';                           // First 2 chars are full degrees
325
    min = NMEA_floatStrToInt( &s[2], 6) / 6;                         // Minutes * 1E5 * 100 / 60 = Minutes * 1E6 / 6 = 1E-7 degree steps
326
 
327
    deg = deg * 10000000 + min;
328
 
329
    if( *NS == 'S' )
330
    {
331
        deg = -deg;
332
    }
333
 
334
    return deg;
335
}
336
 
337
 
338
 
339
//--------------------------------------------------------------
340
// NMEA longitudes are in the form dddmm.mmmmm, we want an integer in 1E-7 degree steps
341
//--------------------------------------------------------------
342
int32_t NMEA_calcLongitude( const char *s, const char *WE)
343
{
344
    int32_t deg;
345
    int32_t min;
346
 
347
    //lcdx_puts_at(10,5,WE,0,0,0);
348
    deg = ((s[0] - '0') * 10 + s[1] - '0') * 10 + s[2] - '0';       // First 3 chars are full degrees
349
    min = NMEA_floatStrToInt( &s[3], 6) / 6;                         // Minutes * 1E5 * 100 / 60 = Minutes * 1E6 / 6 = 1E-7 degree steps
350
 
351
    deg = deg * 10000000 + min;
352
 
353
    if( *WE == 'W' )
354
    {
355
        deg = -deg;
356
    }
357
 
358
    return deg;
359
}
360
 
361
//--------------------------------------------------------------
362
void NMEA_getTime( const char *s)
363
{
364
    uint8_t sem = 0;
365
    uint8_t i;
366
 
367
    for( i=0; i<6; i++ )
368
    {
369
        NMEA.Time[sem++] = s[i];
370
 
371
        if( i==1 || i==3)
372
            NMEA.Time[sem++] = ':';
373
 
374
    }
375
    NMEA.Time[sem] = '\0';
376
}
377
 
378
//--------------------------------------------------------------
379
/*
380
 * returns base-16 value of chars '0'-'9' and 'A'-'F';
381
 * does not trap invalid chars!
382
 */    
383
int digit2dec(char digit)
384
  {
385
        if ((int)digit >= 65)
386
                return ((int)digit - 55);
387
        else
388
         return ((int)digit - 48);
389
 }
390
 
391
//--------------------------------------------------------------
392
/* returns base-10 value of zero-terminated string
393
 * that contains only chars '+','-','0'-'9','.';
394
 * does not trap invalid strings!
395
 */
396
float string2float(char* s) {
397
        long  integer_part = 0;
398
        float decimal_part = 0.0;
399
        float decimal_pivot = 0.1;
400
        bool isdecimal = false, isnegative = false;
401
 
402
        char c;
403
        while ( ( c = *s++) )  {
404
                // skip special/sign chars
405
                if (c == '-') { isnegative = true; continue; }
406
                if (c == '+') continue;
407
                if (c == '.') { isdecimal = true; continue; }
408
 
409
                if (!isdecimal) {
410
                        integer_part = (10 * integer_part) + (c - 48);
411
                }
412
                else {
413
                        decimal_part += decimal_pivot * (float)(c - 48);
414
                        decimal_pivot /= 10.0;
415
                }
416
        }
417
        // add integer part
418
        decimal_part += (float)integer_part;
419
 
420
        // check negative
421
        if (isnegative)  decimal_part = - decimal_part;
422
 
423
        return decimal_part;
424
}
425
 
426
 
427
//--------------------------------------------------------------
428
// Trying to avoid floating point maths here. Converts a floating point string to an integer with a smaller unit
429
// i.e. floatStrToInt("4.5", 2) = 4.5 * 1E2 = 450
430
//--------------------------------------------------------------
431
int32_t NMEA_floatStrToInt( const char *s, int32_t power1 )
432
{
433
    char    *endPtr;
434
    int32_t  v;
435
 
436
    v = strtol(s, &endPtr, 10);
437
 
438
    if( *endPtr == '.' )
439
    {
440
        for (s = endPtr + 1; *s && power1; s++)
441
        {
442
            v = v * 10 + (*s - '0');
443
            --power1;
444
        }
445
    }
446
 
447
    if( power1 )
448
    {
449
        // Table to avoid multiple multiplications
450
        v = v * getPower(power1);
451
    }
452
 
453
    return v;
454
}
455
 
456
 
457
//--------------------------------------------------------------
458
int mstrcmp(const char *s1, const char *s2)
459
{
460
        while((*s1 && *s2) && (*s1 == *s2))
461
        s1++,s2++;
462
        return *s1 - *s2;
463
}
464
 
465
//--------------------------------------------------------------
466
bool NMEA_isdataready() {
467
        return m_bFlagDataReady;
468
}
469
 
470
int NMEA_getHour() {
471
        return res_nUTCHour;
472
}      
473
int NMEA_getMinute() {
474
        return res_nUTCMin;
475
}
476
int NMEA_getSecond() {
477
        return res_nUTCSec;
478
}
479
int NMEA_getDay() {
480
        return res_nUTCDay;
481
}
482
int NMEA_getMonth() {
483
        return res_nUTCMonth;
484
}
485
int NMEA_getYear() {
486
        return res_nUTCYear;
487
}
488
 
489
int32_t NMEA_getLatitude() {
490
        return res_fLatitude;
491
}
492
 
493
int32_t NMEA_getLongitude() {
494
        return res_fLongitude;
495
}
496
 
497
uint8_t NMEA_getSatellites() {
498
        return res_nSatellitesUsed;
499
}
500
 
501
uint8_t NMEA_getGPSfix() {
502
        return res_nGPSfix;
503
}
504
int16_t NMEA_getHDOP() {
505
        return res_nHDOP;
506
}
507
 
508
int16_t  NMEA_getAltitude() {
509
        return res_fAltitude;
510
}
511
 
512
float NMEA_getSpeed() {
513
        return res_fSpeed;
514
}
515
 
516
int16_t NMEA_getBearing() {
517
        return res_fBearing;
518
}
519
uint32_t NMEA_getNMEAcounter() {
520
        return res_nNMEAcounter;
521
}
522
 
523
uint32_t NMEA_getCRCerror() {
524
        return res_nCRCerror;
525
}