The following is the code for the node-red function to parse the $GPGGA sentence. There is a similar function for the $GPVTG sentence, but it is a little simpler. This code is still in beta, so some other improvements are likely.
if ( isValid(msg.payload) ) {
let degrees = 0;
let garminTimeOfFix = 0;
let garminLatitude = 0;
let garminLongitude = 0;
let garminQuality = 0;
let garminAltitude = 0;
let garminSatellites = 0;
let garminHDOP = 0;
let sentence = msg.payload.split(",");
if ((sentence.length > 10) && (sentence[0] == "$GPGGA" )) {
if (sentence[1] !== "" && !isNaN(parseInt(sentence[1]))) {
garminTimeOfFix = sentence[1];
} else {
garminTimeOfFix = "n/a";
}
if (sentence[2] !== "" && sentence[3] !== "" && !isNaN(parseFloat(sentence[2]))) {
degrees = Math.floor(parseFloat(sentence[2]) / 100);
if (sentence[3] == "N") {
garminLatitude = degrees + (parseFloat(sentence[2]) / 100 - degrees) / 60 * 100;
}
if (sentence[3] == "S") {
garminLatitude = - degrees - (parseFloat(sentence[2]) / 100 - degrees) / 60 * 100;
}
} else {
garminLatitude = 0;
}
if (sentence[4] !== "" && sentence[5] !== "" && !isNaN(parseFloat(sentence[4]))) {
degrees = Math.floor(parseFloat(sentence[4]) / 100);
if (sentence[5] == "E") {
garminLongitude = degrees + (parseFloat(sentence[4]) / 100 - degrees) / 60 * 100;
}
if (sentence[5] == "W") {
garminLongitude = - degrees - (parseFloat(sentence[4]) / 100 - degrees) / 60 * 100;
}
} else {
garminLongitude = 0;
}
if (sentence[6] !== "" && !isNaN(parseInt(sentence[6]))) {
garminQuality = parseFloat(sentence[6]);
} else {
garminQuality = 0;
}
if (sentence[7] !== "" && !isNaN(parseInt(sentence[7]))) {
garminSatellites = parseFloat(sentence[7]);
if ( garminSatellites > 30) {
garminSatellites = 0;
}
} else {
garminSatellites = 0;
}
if (sentence[8] !== "" && !isNaN(parseFloat(sentence[8]))) {
garminHDOP = parseFloat(sentence[8]);
} else {
garminHDOP = 0;
}
if (sentence[9] !== "" && sentence[10] == "M" && !isNaN(parseFloat(sentence[9]))) {
garminAltitude = parseFloat(sentence[9]);
} else {
garminAltitude = 0;
}
msg.payload = [{
fixtime: garminTimeOfFix,
latitude: garminLatitude,
longitude: garminLongitude,
quality: garminQuality,
satellites: garminSatellites,
hdop: garminHDOP,
altitude: garminAltitude,
},
{
source: "GPS",
sensor: "garmin18x-GGA"
}];
return msg;
}
}
function checksum(_command) {
var command = _command[0] === '$' ? _command.slice(1) : _command;
var checksum = 0;
for(var i = 0; i < command.length; i++) {
checksum = checksum ^ command.charCodeAt(i);
}
var hex = Number(checksum).toString(16).toUpperCase();
if (hex.length < 2) {
hex = ("00" + hex).slice(-2);
}
return hex;
}
function isValid(_command) {
var _checksum = _command.slice(-4,-2);
var command = _command.slice(1).slice(0, -5);
return _checksum === checksum(command);
}
Many thanks to the following open source project (https://github.com/vesteraas/nmea-checksum)