/* * This file is part of Espruino, a JavaScript interpreter for Microcontrollers * * Copyright (C) 2019 Gordon Williams * * 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; }