gps
Version:
A GPS NMEA parser library
24 lines (22 loc) • 8.2 kB
JavaScript
/*
GPS.js v0.7.4 1/27/2025
https://raw.org/article/using-gps-with-node-js-and-javascript/
Copyright (c) 2025, Robert Eisele (https://raw.org/)
Licensed under the MIT license.
*/
'use strict';(function(A){function k(b,a){if(""===b)return null;var c=new Date;if(a){var e=a.slice(4),f=a.slice(2,4)-1;a=a.slice(0,2);4===e.length?c.setUTCFullYear(Number(e),Number(f),Number(a)):c.setUTCFullYear(Number("20"+e),Number(f),Number(a))}c.setUTCHours(Number(b.slice(0,2)));c.setUTCMinutes(Number(b.slice(2,4)));c.setUTCSeconds(Number(b.slice(4,6)));b=b.slice(7);e=b.length;f=0;0!==e&&(f=parseFloat(b)*Math.pow(10,3-e));c.setUTCMilliseconds(Number(f));return c}function l(b,a){if(""===b)return null;
var c=1;switch(a){case "S":c=-1;case "N":var e=2;break;case "W":c=-1;case "E":e=3}return c*(parseFloat(b.slice(0,e))+parseFloat(b.slice(e))/60)}function d(b){return""===b?null:parseFloat(b)}function t(b){return""===b?null:1.852*parseFloat(b)}function B(b){switch(b){case 0:return"QZSS";case 1:return"GPS";case 2:return"GLONASS";case 3:return"Galileo";case 4:return"BeiDou";default:return"unknown"}}function u(b){b=b.slice(1,3);switch(b){case "GP":return"GPS";case "GQ":return"QZSS";case "GL":return"GLONASS";
case "GA":return"Galileo";case "GB":return"BeiDou";default:return b}}function C(b){if(""===b)return null;switch(parseInt(b,10)){case 0:return null;case 1:return"fix";case 2:return"dgps-fix";case 3:return"pps-fix";case 4:return"rtk";case 5:return"rtk-float";case 6:return"estimated";case 7:return"manual";case 8:return"simulated"}throw Error("INVALID GGA FIX: "+b);}function D(b){if(""===b)return null;switch(parseInt(b,10)){case 1:return null;case 2:return"2D";case 3:return"3D"}throw Error("INVALID GSA FIX: "+
b);}function v(b){switch(b){case "":return null;case "A":return"active";case "V":return"void"}throw Error("INVALID RMC/GLL STATUS: "+b);}function p(b){switch(b){case "":return null;case "A":return"autonomous";case "D":return"differential";case "E":return"estimated";case "M":return"manual input";case "S":return"simulated";case "N":return"not valid";case "P":return"precise";case "R":return"rtk";case "F":return"rtk-float"}throw Error("INVALID FAA MODE: "+b);}function w(b,a){if("M"===a||""===a)return d(b);
throw Error("Unknown unit: "+a);}function g(){if(!(this instanceof g))return new g;this.events={};this.state={errors:0,processed:0}}var m=Math.PI/180,q={},r={},x={};g.prototype.events=null;g.prototype.state=null;g.mod={GGA:function(b,a){if(16!==a.length&&14!==a.length)throw Error("Invalid GGA length: "+b);return{time:k(a[1]),lat:l(a[2],a[3]),lon:l(a[4],a[5]),alt:w(a[9],a[10]),quality:C(a[6]),satellites:d(a[7]),hdop:d(a[8]),geoidal:w(a[11],a[12]),age:void 0===a[13]?null:d(a[13]),stationID:void 0===
a[14]?null:d(a[14])}},GSA:function(b,a){if(19!==a.length&&20!==a.length)throw Error("Invalid GSA length: "+b);b=[];for(var c=3;15>c;c++)""!==a[c]&&b.push(parseInt(a[c],10));a:{c=a[1];switch(c){case "M":c="manual";break a;case "A":c="automatic";break a;case "":c=null;break a}throw Error("INVALID GSA MODE: "+c);}return{mode:c,fix:D(a[2]),satellites:b,pdop:d(a[15]),hdop:d(a[16]),vdop:d(a[17]),systemId:19<a.length?d(a[18]):null,system:19<a.length?B(d(a[18])):"unknown"}},RMC:function(b,a){if(13!==a.length&&
14!==a.length&&15!==a.length)throw Error("Invalid RMC length: "+b);b=k(a[1],a[9]);var c=v(a[2]),e=l(a[3],a[4]),f=l(a[5],a[6]),h=t(a[7]),n=d(a[8]),y=a[10],z=a[11];return{time:b,status:c,lat:e,lon:f,speed:h,track:n,variation:""===y||""===z?null:parseFloat(y)*("W"===z?-1:1),faa:13<a.length?p(a[12]):null,navStatus:14<a.length?a[13]:null}},VTG:function(b,a){if(10!==a.length&&11!==a.length)throw Error("Invalid VTG length: "+b);if(""===a[2]&&""===a[8]&&""===a[6])return{track:null,trackMagetic:null,speed:null,
faa:null};if("T"!==a[2])throw Error("Invalid VTG track mode: "+b);if("K"!==a[8]||"N"!==a[6])throw Error("Invalid VTG speed tag: "+b);return{track:d(a[1]),trackMagnetic:""===a[3]?null:d(a[3]),speed:t(a[5]),faa:11===a.length?p(a[9]):null}},GSV:function(b,a){if(0===a.length%4)throw Error("Invalid GSV length: "+b);for(var c=[],e=b.slice(1,3),f=4;f<a.length-3;f+=4){var h=d(a[f]),n=d(a[f+3]);c.push({prn:h,elevation:d(a[f+1]),azimuth:d(a[f+2]),snr:n,status:null!==h?null!==n?"tracking":"in view":null,system:u(b),
key:e+h})}return{msgNumber:d(a[2]),msgsTotal:d(a[1]),satsInView:d(a[3]),satellites:c,signalId:2===a.length%4?d(a[a.length-2]):null,system:u(b)}},GLL:function(b,a){if(9!==a.length&&8!==a.length)throw Error("Invalid GLL length: "+b);return{time:k(a[5]),status:v(a[6]),lat:l(a[1],a[2]),lon:l(a[3],a[4]),faa:9===a.length?p(a[7]):null}},ZDA:function(b,a){return{time:k(a[1],a[2]+a[3]+a[4])}},GST:function(b,a){if(10!==a.length)throw Error("Invalid GST length: "+b);return{time:k(a[1]),rms:d(a[2]),ellipseMajor:d(a[3]),
ellipseMinor:d(a[4]),ellipseOrientation:d(a[5]),latitudeError:d(a[6]),longitudeError:d(a[7]),heightError:d(a[8])}},HDT:function(b,a){if(4!==a.length)throw Error("Invalid HDT length: "+b);return{heading:parseFloat(a[1]),trueNorth:"T"===a[2]}},GRS:function(b,a){if(18!==a.length)throw Error("Invalid GRS length: "+b);b=[];for(var c=3;14>=c;c++){var e=d(a[c]);null!==e&&b.push(e)}return{time:k(a[1]),mode:d(a[2]),res:b}},GBS:function(b,a){if(10!==a.length&&12!==a.length)throw Error("Invalid GBS length: "+
b);return{time:k(a[1]),errLat:d(a[2]),errLon:d(a[3]),errAlt:d(a[4]),failedSat:d(a[5]),probFailedSat:d(a[6]),biasFailedSat:d(a[7]),stdFailedSat:d(a[8]),systemId:12===a.length?d(a[9]):null,signalId:12===a.length?d(a[10]):null}},GNS:function(b,a){if(14!==a.length&&15!==a.length)throw Error("Invalid GNS length: "+b);return{time:k(a[1]),lat:l(a[2],a[3]),lon:l(a[4],a[5]),mode:a[6],satsUsed:d(a[7]),hdop:d(a[8]),alt:d(a[9]),sep:d(a[10]),diffAge:d(a[11]),diffStation:d(a[12]),navStatus:15===a.length?a[13]:
null}}};g.Parse=function(b){if("string"!==typeof b)return!1;var a=b.split(","),c=a.pop();if(2>a.length||"$"!==b.charAt(0)||-1===c.indexOf("*"))return!1;c=c.split("*");a.push(c[0]);a.push(c[1]);a[0]=a[0].slice(3);if(void 0!==g.mod[a[0]]){c=this.mod[a[0]](b,a);c.raw=b;for(var e=0,f=1;f<b.length;f++){var h=b.charCodeAt(f);if(42===h)break;e^=h}c.valid=e===parseInt(a[a.length-1],16);c.type=a[0];return c}return!1};g.Heading=function(b,a,c,e){a=(e-a)*m;b*=m;c*=m;e=Math.cos(c);return(180*Math.atan2(Math.sin(a)*
e,Math.cos(b)*Math.sin(c)-Math.sin(b)*e*Math.cos(a))/Math.PI+360)%360};g.Distance=function(b,a,c,e){var f=(c-b)*m*.5;a=(e-a)*m*.5;b*=m;c*=m;f=Math.sin(f);a=Math.sin(a);return 12745.6*Math.asin(Math.sqrt(f*f+Math.cos(b)*Math.cos(c)*a*a))};g.TotalDistance=function(b){if(2>b.length)return 0;for(var a=0,c=0;c<b.length-1;c++){var e=b[c],f=b[c+1];a+=g.Distance(e.lat,e.lon,f.lat,f.lon)}return a};g.prototype.update=function(b){b=g.Parse(b);this.state.processed++;if(!1===b)return this.state.errors++,!1;var a=
this.state;if("RMC"===b.type||"GGA"===b.type||"GLL"===b.type||"GNS"===b.type)a.time=b.time,a.lat=b.lat,a.lon=b.lon;"HDT"===b.type&&(a.heading=b.heading,a.trueNorth=b.trueNorth);"ZDA"===b.type&&(a.time=b.time);"GGA"===b.type&&(a.alt=b.alt);"RMC"===b.type&&(a.speed=b.speed,a.track=b.track);if("GSA"===b.type){r[b.systemId]=b.satellites;var c=[];for(e in r)c.push(...r[e]);a.satsActive=c;a.fix=b.fix;a.hdop=b.hdop;a.pdop=b.pdop;a.vdop=b.vdop}if("GSV"===b.type){var e=(new Date).getTime();c=b.satellites;
for(var f=0;f<c.length;f++){var h=c[f].key;x[h]=e;q[h]=c[f]}c=[];for(h in q)3E3>e-x[h]&&c.push(q[h]);a.satsVisible=c}this.emit("data",b);this.emit(b.type,b);return!0};g.prototype.partial="";g.prototype.updatePartial=function(b){for(this.partial+=b;;){b=this.partial.indexOf("\r\n");if(-1===b)break;var a=this.partial.slice(0,b);if("$"===a.charAt(0))try{this.update(a)}catch(c){throw this.partial="",Error(c);}this.partial=this.partial.slice(b+2)}};g.prototype.on=function(b,a){return void 0===this.events[b]?
(this.events[b]=a,this):null};g.prototype.off=function(b){void 0!==this.events[b]&&(this.events[b]=void 0);return this};g.prototype.emit=function(b,a){void 0!==this.events[b]&&this.events[b].call(this,a)};"function"===typeof define&&define.amd?define([],function(){return g}):"object"===typeof exports?(Object.defineProperty(g,"__esModule",{value:!0}),g["default"]=g,g.GPS=g,module.exports=g):A.GPS=g})(this);