From bfc6ba247cf075ff02ff701818f898068f6dc822 Mon Sep 17 00:00:00 2001 From: Akos Becsey Date: Thu, 9 Feb 2023 16:52:04 +0100 Subject: [PATCH] FIX: RMC failed to parse milliseconds --- codecs/RMC.ts | 4 +-- helpers.ts | 67 +++++++++++++++++++++--------------------------- tests/RMCtest.ts | 11 +++++++- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/codecs/RMC.ts b/codecs/RMC.ts index 1eeaf57..878cbad 100644 --- a/codecs/RMC.ts +++ b/codecs/RMC.ts @@ -26,7 +26,7 @@ * 13. Checksum */ -import { parseDatetime, parseFloatSafe, parseLatitude, parseLongitude } from "../helpers"; +import { parseFloatSafe, parseLatitude, parseLongitude, parseTime } from "../helpers"; import { initStubFields, PacketStub } from "./PacketStub"; @@ -50,7 +50,7 @@ export interface RMCPacket extends PacketStub { export function decodeSentence(stub: PacketStub, fields: string[]): RMCPacket { return { ...initStubFields(stub, sentenceId, sentenceName), - datetime: parseDatetime(fields[9], fields[1]), + datetime: parseTime(fields[1], fields[9], true), status: fields[2] === "A" ? "valid" : "warning", latitude: parseLatitude(fields[3], fields[4]), longitude: parseLongitude(fields[5], fields[6]), diff --git a/helpers.ts b/helpers.ts index 6f26b25..dd837c3 100644 --- a/helpers.ts +++ b/helpers.ts @@ -337,6 +337,33 @@ export function parseLongitude(lon: string, hemi: string): number { return parseDmCoordinate(lon) * hemisphere; } +function getYearFromString(yearString: string, rmcCompatible: boolean): number { + if (yearString.length === 4) { + return Number(yearString); + } else if (yearString.length === 2) { + if (rmcCompatible) { + // GPRMC date doesn't specify century. GPS came out in 1973 so if the year + // is less than 73, assume it's 20xx, otherwise assume it is 19xx. + let year = Number(yearString); + + if (year < 73) { + year = 2000 + year; + } + else { + year = 1900 + year; + } + + return year; + } + else { + return Number("20" + yearString); + } + } + else { + throw Error(`Unexpected year string: ${yearString}`); + } +} + /** * Parses a UTC time and optionally a date and returns a Date * object. @@ -344,7 +371,7 @@ export function parseLongitude(lon: string, hemi: string): number { * @param {String=} date Optional date in format the ddmmyyyy or ddmmyy * @returns {Date} */ -export function parseTime(time: string, date?: string): Date { +export function parseTime(time: string, date?: string, rmcCompatible = false): Date { if (time === "") { return new Date(0); @@ -358,14 +385,7 @@ export function parseTime(time: string, date?: string): Date { const month = parseInt(date.slice(2, 4), 10) - 1; const day = date.slice(0, 2); - if (year.length === 4) { - ret.setUTCFullYear(Number(year), Number(month), Number(day)); - } else { - // If we need to parse older GPRMC data, we should hack something like - // year < 73 ? 2000+year : 1900+year - // Since GPS appeared in 1973 - ret.setUTCFullYear(Number("20" + year), Number(month), Number(day)); - } + ret.setUTCFullYear(getYearFromString(year, rmcCompatible), Number(month), Number(day)); } ret.setUTCHours(Number(time.slice(0, 2))); @@ -383,32 +403,3 @@ export function parseTime(time: string, date?: string): Date { return ret; } - - -/** - * Parses a date in the format "yyMMdd" along with a time in the format - * "hhmmss" or "hhmmss.ss" and returns a Date object. - */ -export function parseDatetime(date: string, time: string): Date { - const day = parseInt(date.slice(0, 2), 10); - const month = parseInt(date.slice(2, 4), 10); - let year = parseInt(date.slice(4, 6), 10); - // GPRMC date doesn't specify century. GPS came out in 1973 so if the year - // is less than 73, assume it's 20xx, otherwise assume it is 19xx. - if (year < 73) { - year = year + 2000; - } - else { - year = year + 1900; - } - - const hours = parseInt(time.slice(0, 2), 10); - const minutes = parseInt(time.slice(2, 4), 10); - const seconds = parseInt(time.slice(4, 6), 10); - let milliseconds = 0; - if (time.length === 9) { - milliseconds = parseInt(time.slice(7, 9), 10) * 10; - } - - return new Date(Date.UTC(year, month - 1, day, hours, minutes, seconds, milliseconds)); -} diff --git a/tests/RMCtest.ts b/tests/RMCtest.ts index 48b3da2..10a3840 100644 --- a/tests/RMCtest.ts +++ b/tests/RMCtest.ts @@ -1,6 +1,6 @@ import "should"; -import { parseNmeaSentence } from "../index"; +import { assertPacketIs, parseNmeaSentence } from "../index"; describe("RMC", (): void => { @@ -21,4 +21,13 @@ describe("RMC", (): void => { packet.should.have.property("variationPole", "W"); }); + it("miliseconds", (): void => { + const packet = parseNmeaSentence("$GNRMC,123411.200,A,4721.2973,N,01906.6971,E,16.19,142.59,040223,,,A,V*37"); + assertPacketIs("RMC", packet); + packet.datetime.getMilliseconds().should.equal(200); + + const packet2 = parseNmeaSentence("$GPRMC,123409.886,V,,,,,16.10,145.36,040223,,,N,V*08"); + assertPacketIs("RMC", packet2); + packet2.datetime.getMilliseconds().should.equal(886); + }); });