nmea-streamer
Version:
The plugin streams a file with NMEA0183 messages to the SignalK server at a configurable speed. It also provides a web app with play controls.
106 lines (83 loc) • 2.83 kB
JavaScript
// nmeaUtils.js
// file functions
const fs = require('fs');
const readline = require('readline');
async function getDateTimeRange(path) {
let start = null;
let end = null;
try {
// Check if the file exists and is readable
await fs.promises.access(path, fs.constants.R_OK);
const fileStream = fs.createReadStream(path);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
for await (const line of rl) {
const date = getDate(line);
if (date) {
start = start ?? date;
end = date;
}
}
return { start, end };
} catch (error) {
throw new Error(`Failed to get date range from file: ${path}`);
}
}
// line functions
/**
* Validates if a line is a properly formatted NMEA0183 sentence.
*/
function validCheckSum(line) {
const parts = line.split('*');
if (parts.length !== 2) return false;
const message = parts[0].substring(1); // Remove the starting '$'
const checksum = parts[1];
let calculatedChecksum = 0;
for (let i = 0; i < message.length; i++) {
calculatedChecksum ^= message.charCodeAt(i);
}
const hexChecksum = calculatedChecksum.toString(16).toUpperCase().padStart(2, '0');
return hexChecksum === checksum;
}
function isAisMessage(line) {
if (!line.startsWith('$AIVDM') && !line.startsWith('$AIVDO')) return false;
if (!validCheckSum(line)) return false;
return true;
}
function isNmeaMessage(line) {
if (!(line.startsWith('$') )) return false;
if (!validCheckSum(line)) return false;
return true;
}
/**
* Converts an NMEA date/time to a JavaScript Date object.
*/
function toDate(time, datum) {
const day = parseInt(datum.substr(0, 2), 10);
const month = parseInt(datum.substr(2, 2), 10) - 1;
const year = parseInt(datum.substr(4, 2), 10) + 2000;
const hours = parseInt(time.substr(0, 2), 10);
const minutes = parseInt(time.substr(2, 2), 10);
const seconds = parseInt(time.substr(4, 2), 10);
let milliseconds = parseInt(time.substr(7, 2), 10) * 10;
if (isNaN(milliseconds)) milliseconds = 0;
return new Date(Date.UTC(year, month, day, hours, minutes, seconds, milliseconds));
}
/**
* Extracts a Date object from a nmea0183 line if possible. Otherwise return null.
*/
function getDate(line) {
if (line.substr(3, 3) != "RMC") return null;
const fields = line.split(',');
if (!fields[1] || !fields[9]) return null;
return toDate(fields[1], fields[9]);
}
module.exports = {
getDate,
getDateTimeRange,
isNmeaMessage,
isAisMessage,
validCheckSum
};