Espruino/libs/misc/nmea.c
2019-11-06 13:09:52 +00:00

147 lines
5.2 KiB
C

/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2019 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* NMEA decoder
* ----------------------------------------------------------------------------
*/
#include "nmea.h"
#include "jswrap_date.h"
char *nmea_next_comma(char *nmea) {
while (*nmea && *nmea!=',') nmea++; // find the comma
return nmea;
}
double nmea_decode_latlon(char *nmea, char *comma) {
if (*nmea==',') return NAN; // no reading
char *dp = nmea;
while (*dp && *dp!='.' && *dp!=',') dp++; // find decimal pt
*comma = 0;
double minutes = stringToFloat(&dp[-2]);
*comma = ',';
dp[-2] = 0;
int x = stringToInt(nmea);
return x+(minutes/60);
}
double nmea_decode_float(char *nmea, char *comma) {
*comma = 0;
double r = stringToFloat(nmea);
*comma = ',';
return r;
}
uint8_t nmea_decode_1(char *nmea) {
return chtod(nmea[0]);
}
uint8_t nmea_decode_2(char *nmea) {
return chtod(nmea[0])*10 + chtod(nmea[1]);
}
bool nmea_decode(NMEAFixInfo *gpsFix, const char *nmeaLine) {
char buf[NMEA_MAX_SIZE];
strcpy(buf, nmeaLine);
char *nmea = buf, *nextComma;
if (nmea[0]!='$' || nmea[1]!='G') return false; // not valid
if (nmea[3]=='R' && nmea[4]=='M' && nmea[5]=='C') {
// $GNRMC,161945.00,A,5139.11397,N,00116.07202,W,1.530,,190919,,,A*7E
nmea = nmea_next_comma(nmea)+1;
nextComma = nmea_next_comma(nmea);
// time
gpsFix->hour = nmea_decode_2(&nmea[0]);
gpsFix->min = nmea_decode_2(&nmea[2]);
gpsFix->sec = nmea_decode_2(&nmea[4]);
gpsFix->ms = nmea_decode_2(&nmea[7]);
// status
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);//?
// lat + NS
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// lon + EW
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// speed
gpsFix->speed = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// course
gpsFix->course = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// date
gpsFix->day = nmea_decode_2(&nmea[0]);
gpsFix->month = nmea_decode_2(&nmea[2]);
gpsFix->year = nmea_decode_2(&nmea[4]);
// ....
}
if (nmea[3]=='G' && nmea[4]=='G' && nmea[5]=='A') {
// $GNGGA,161945.00,5139.11397,N,00116.07202,W,1,06,1.29,71.1,M,47.0,M,,*64
nmea = nmea_next_comma(nmea)+1;
nextComma = nmea_next_comma(nmea);
// time
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// LAT
gpsFix->lat = nmea_decode_latlon(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
if (*nmea=='S') gpsFix->lat=-gpsFix->lat;
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// LON
gpsFix->lon = nmea_decode_latlon(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
if (*nmea=='W') gpsFix->lon=-gpsFix->lon;
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// quality
gpsFix->quality = nmea_decode_1(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// num satellites
gpsFix->satellites = nmea_decode_2(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// dilution of precision
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// altitude
gpsFix->alt = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// ....
}
if (nmea[3]=='G' && nmea[4]=='S' && nmea[5]=='V') {
// loads of cool data about what satellites we have
}
if (nmea[3]=='G' && nmea[4]=='L' && nmea[5]=='L') {
// Complete set of data received
return true;
}
return false;
}
JsVar *nmea_to_jsVar(NMEAFixInfo *gpsFix) {
JsVar *o = jsvNewObject();
if (o) {
jsvObjectSetChildAndUnLock(o, "lat", jsvNewFromFloat(gpsFix->lat));
jsvObjectSetChildAndUnLock(o, "lon", jsvNewFromFloat(gpsFix->lon));
jsvObjectSetChildAndUnLock(o, "alt", jsvNewFromFloat(gpsFix->alt));
jsvObjectSetChildAndUnLock(o, "speed", jsvNewFromFloat(gpsFix->speed));
jsvObjectSetChildAndUnLock(o, "course", jsvNewFromFloat(gpsFix->course));
CalendarDate date;
date.day = gpsFix->day;
date.month = gpsFix->month-1; // 1 based to 0 based
date.year = 2000+gpsFix->year;
TimeInDay td;
td.daysSinceEpoch = fromCalenderDate(&date);
td.hour = gpsFix->hour;
td.min = gpsFix->min;
td.sec = gpsFix->sec;
td.ms = gpsFix->ms;
td.zone = 0; // jsdGetTimeZone(); - no! GPS time is always in UTC :)
jsvObjectSetChildAndUnLock(o, "time", jswrap_date_from_milliseconds(fromTimeInDay(&td)));
jsvObjectSetChildAndUnLock(o, "satellites", jsvNewFromInteger(gpsFix->satellites));
jsvObjectSetChildAndUnLock(o, "fix", jsvNewFromInteger(gpsFix->quality));
}
return o;
}