diff --git a/.github/workflows/nmea-parser.yml b/.github/workflows/nmea-parser.yml index b4e5e7b..0237d42 100644 --- a/.github/workflows/nmea-parser.yml +++ b/.github/workflows/nmea-parser.yml @@ -30,6 +30,9 @@ jobs: - name: 🧑‍🔬 Tests run: "npm run nmea-parser:test" + + - name: 🛠️ Build + run: "npm run nmea-parser:build" publish: name: 🚀 Publish diff --git a/packages/nmea-parser/src/nmea-metadata.ts b/packages/nmea-parser/src/nmea-metadata.ts new file mode 100644 index 0000000..8d2616b --- /dev/null +++ b/packages/nmea-parser/src/nmea-metadata.ts @@ -0,0 +1,76 @@ +import { Float64, NMEASentence, Uint8 } from './types' + +const metadataGGA = (sentence: NMEASentence): NMEASentence => { + const getLatitudeDegrees = (latitude: string, letter: string): Float64 => { + const [left, minutesRight] = latitude.split('.') + const degrees = left.slice(0, -2) + // console.log(`Lat: Degrees = ${degrees}`) + const minutesLeft = left.slice(-2) + const sign = (letter === 'S') ? -1 : 1 + const minutes = `${minutesLeft}.${minutesRight}` + // console.log(`Lat: Minutes ${minutes}`) + return sign * (Number(degrees) + (Number(minutes) / 60)) + } + + const getLongitudeDegrees = (longitude: string, letter: string): Float64 => { + const [left, minutesRight] = longitude.split('.') + const degrees = left.slice(0, -2) + // console.log(`Lon: Degrees = ${degrees}`) + const minutesLeft = left.slice(-2) + const sign = (letter === 'W') ? -1 : 1 + const minutes = `${minutesLeft}.${minutesRight}` + // console.log(`Lon: Minutes ${minutes}`) + return sign * (Number(degrees) + (Number(minutes) / 60)) + } + + const getQuality = (quality: Uint8): string => { + const QUALITIES = { + 0: 'Fix not valid', + 1: 'GPS fix', + 2: 'Differential GPS fix (DGNSS), SBAS, OmniSTAR VBS, Beacon, RTX in GVBS mode', + 3: 'Not applicable', + 4: 'RTK Fixed, xFill', + 5: 'RTK Float, OmniSTAR XP/HP, Location RTK, RTX', + 6: 'INS Dead reckoning', + 7: 'Manual Input Mode', + 8: 'Simulator Mode' + } + return QUALITIES[quality as keyof typeof QUALITIES] ?? 'unknown' + } + + sentence.payload.forEach((field, index) => { + // Latitude + if (field.name === 'latitude') { + const latitude = field.value as string + const letter = sentence.payload[index + 1].value as string + const degrees = getLatitudeDegrees(latitude, letter) + sentence.metadata = { latitude: degrees } + return + } + // Longitude + if (field.name === 'longitude') { + const longitude = field.value as string + const letter = sentence.payload[index + 1].value as string + const degrees = getLongitudeDegrees(longitude, letter) + sentence.metadata = { longitude: degrees } + return + } + // Quality + if (field.name === 'quality') { + sentence.metadata = { quality: getQuality(field.value as Uint8) } + // return + } + // Rest + // return + }) + return { ...sentence } +} + +const METADATA = { + GGA: metadataGGA +} + +export const addMetadata = (sentence: NMEASentence): NMEASentence => { + if (sentence.id in METADATA) { return METADATA[sentence.id as keyof typeof METADATA](sentence) } + return sentence +} diff --git a/packages/nmea-parser/src/nmea-sentences.ts b/packages/nmea-parser/src/nmea-sentences.ts index 05ec475..db1ab07 100644 --- a/packages/nmea-parser/src/nmea-sentences.ts +++ b/packages/nmea-parser/src/nmea-sentences.ts @@ -48,7 +48,7 @@ export const NMEA_SENTENCES: ProtocolsFileContent = { { name: 'latitude', type: 'string', - units: 'deg' + units: 'deg-min' }, { name: 'latitude_direction', @@ -58,7 +58,7 @@ export const NMEA_SENTENCES: ProtocolsFileContent = { { name: 'longitude', type: 'string', - units: 'deg' + units: 'deg-min' }, { name: 'longitude_direction', @@ -66,7 +66,7 @@ export const NMEA_SENTENCES: ProtocolsFileContent = { description: 'E - East\n W - West' }, { - name: 'gps_quality', + name: 'quality', type: 'int8', description: '0: Fix not valid\n 1: GPS fix\n 2: Differential GPS fix (DGNSS), SBAS, OmniSTAR VBS, Beacon, RTX in GVBS mode\n 3: Not applicable\n 4: RTK Fixed, xFill\n 5: RTK Float, OmniSTAR XP/HP, Location RTK, RTX\n 6: INS Dead reckoning\n 7: Manual Input Mode\n 8: Simulator Mode' }, diff --git a/packages/nmea-parser/src/nmea.ts b/packages/nmea-parser/src/nmea.ts deleted file mode 100644 index 8b13789..0000000 --- a/packages/nmea-parser/src/nmea.ts +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/nmea-parser/src/schemas.ts b/packages/nmea-parser/src/schemas.ts index 4024f0f..f5ae03e 100644 --- a/packages/nmea-parser/src/schemas.ts +++ b/packages/nmea-parser/src/schemas.ts @@ -186,6 +186,7 @@ const ValibotNMEASentenceSchema = v.object({ description: v.optional(ValibotStringSchema), checksum: ValibotChecksumSchema, payload: v.array(ValibotNMEAParsedFieldSchema), + metadata: v.optional(v.any()), protocol: v.optional( v.object({ name: ValibotStringSchema, diff --git a/packages/nmea-parser/src/sentences.ts b/packages/nmea-parser/src/sentences.ts index 06ad26c..92c55bd 100644 --- a/packages/nmea-parser/src/sentences.ts +++ b/packages/nmea-parser/src/sentences.ts @@ -4,6 +4,7 @@ import { CHECKSUM_LENGTH, DELIMITER, END_FLAG, END_FLAG_LENGTH, MINIMAL_LENGTH, import { Float32Schema, Float64Schema, Int16Schema, Int32Schema, Int64Schema, Int8Schema, NMEASentenceSchema, Uint16Schema, Uint32Schema, Uint64Schema, Uint8Schema } from './schemas' import type { Checksum, NMEALike, NMEASentence, Payload, ProtocolFieldType, StoredSentence, Talker, Value } from './types' import { isLowerCharASCII, isNumberCharASCII, isUpperCharASCII } from './utils' +import { addMetadata } from './nmea-metadata' export const lastUncompletedFrame = (text: string): string | null => { // Start of last possible frame @@ -137,7 +138,7 @@ export const getKnownNMEASentence = ( }) const { protocol } = model // TODO: Metada -> GGA Latitude-Longitude degrees - const sent: NMEASentence = { + const nmeaSentence: NMEASentence = { received, sample, id: sentenceID, @@ -145,7 +146,8 @@ export const getKnownNMEASentence = ( payload, protocol } - return NMEASentenceSchema.parse(sent) + const nmeaSentenceWithMetadata = addMetadata(nmeaSentence) + return NMEASentenceSchema.parse(nmeaSentenceWithMetadata) } export const getTalker = (sentenceID: string): Talker | null => { diff --git a/packages/nmea-parser/tests/index.test.ts b/packages/nmea-parser/tests/index.test.ts index 82a2672..0a761be 100644 --- a/packages/nmea-parser/tests/index.test.ts +++ b/packages/nmea-parser/tests/index.test.ts @@ -107,7 +107,7 @@ describe('Parser', () => { const parser = new Parser() parser.addProtocols({ file: NORSUB_FILE }) const storedSentences = parser.getSentences() - const fakeinput = storedSentences.reduce((acc, curr) => acc += createFakeSentence(curr), '') + const fakeinput = storedSentences.reduce((acc, curr) => acc + createFakeSentence(curr), '') const output = parser.parseData(fakeinput) expect(output.length).toBe(storedSentences.length) })