UNPKG

gps

Version:
29 lines (27 loc) 10.9 kB
/* GPS.js v0.8.1 8/16/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(x){function n(b,a=null){if(!b)return null;const c=new Date;if(a){var d=a.slice(4),g=a.slice(2,4)-1;a=a.slice(0,2);4===d.length?c.setUTCFullYear(+d,+g,+a):c.setUTCFullYear(Number("20"+d),+g,+a)}c.setUTCHours(+b.slice(0,2));c.setUTCMinutes(+b.slice(2,4));c.setUTCSeconds(+b.slice(4,6));g=b.indexOf(".");d=0;-1!==g&&g+1<b.length&&(b=b.slice(g+1),3<=b.length?d=+b.slice(0,3):2===b.length?d=10*+b:1===b.length&&(d=100*+b));c.setUTCMilliseconds(d);return c}function p(b,a){if(""===b)return null; const c="N"===a||"S"===a?2:3;return("S"===a||"W"===a?-1:1)*(parseFloat(b.slice(0,c))+parseFloat(b.slice(c))/60)}function e(b){return""===b?null:parseFloat(b)}function u(b){return""===b?null:1.852*parseFloat(b)}function y(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 r(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 z(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 A(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 t(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 e(b);throw Error("Unknown unit: "+a);}function k(){if(!(this instanceof k))return new k;this.events=Object.create(null);this.state={errors:0,processed:0,txtBuffer:{}};this._collectSats=Object.create(null);this._collectActiveSats=Object.create(null);this._lastSeenSat=Object.create(null);this.partial=""}const q=Math.PI/180;k.parsers={GGA:function(b,a){if(16!==a.length&&14!==a.length)throw Error("Invalid GGA length: "+b);return{time:n(a[1]),lat:p(a[2],a[3]),lon:p(a[4],a[5]),alt:w(a[9],a[10]),quality:z(a[6]),satellites:e(a[7]),hdop:e(a[8]),geoidal:w(a[11],a[12]),age:void 0=== a[13]?null:e(a[13]),stationID:void 0===a[14]?null:e(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));c=19<a.length?e(a[18]):null;a:{var d=a[1];switch(d){case "M":d="manual";break a;case "A":d="automatic";break a;case "":d=null;break a}throw Error("INVALID GSA MODE: "+d);}return{mode:d,fix:A(a[2]),satellites:b,pdop:e(a[15]),hdop:e(a[16]),vdop:e(a[17]),systemId:c,system:null!==c?y(c):"unknown"}}, RMC:function(b,a){if(13!==a.length&&14!==a.length&&15!==a.length)throw Error("Invalid RMC length: "+b);b=n(a[1],a[9]);var c=v(a[2]),d=p(a[3],a[4]),g=p(a[5],a[6]),f=u(a[7]),h=e(a[8]),l=a[10],m=a[11];return{time:b,status:c,lat:d,lon:g,speed:f,track:h,variation:""===l||""===m?null:parseFloat(l)*("W"===m?-1:1),faa:13<a.length?t(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, trackMagnetic: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:e(a[1]),trackMagnetic:""===a[3]?null:e(a[3]),speed:u(a[5]),faa:11===a.length?t(a[9]):null}},GSV:function(b,a){if(0===a.length%4)throw Error("Invalid GSV length: "+b);const c=[],d=b.slice(1,3);for(let g=4;g<a.length-3;g+=4){const f=e(a[g]),h=e(a[g+3]);c.push({prn:f,elevation:e(a[g+1]),azimuth:e(a[g+2]),snr:h,status:null!==f? null!==h?"tracking":"in view":null,system:r(b),key:d+f})}return{msgNumber:e(a[2]),msgsTotal:e(a[1]),satsInView:e(a[3]),satellites:c,signalId:2===a.length%4?e(a[a.length-2]):null,system:r(b)}},GLL:function(b,a){if(9!==a.length&&8!==a.length)throw Error("Invalid GLL length: "+b);return{time:n(a[5]),status:v(a[6]),lat:p(a[1],a[2]),lon:p(a[3],a[4]),faa:9===a.length?t(a[7]):null}},ZDA:function(b,a){return{time:n(a[1],a[2]+a[3]+a[4]),offsetMin:""===a[5]||""===a[6]?null:60*parseInt(a[5],10)+parseInt(a[6], 10)}},GST:function(b,a){if(10!==a.length)throw Error("Invalid GST length: "+b);return{time:n(a[1]),rms:e(a[2]),ellipseMajor:e(a[3]),ellipseMinor:e(a[4]),ellipseOrientation:e(a[5]),latitudeError:e(a[6]),longitudeError:e(a[7]),heightError:e(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(let c=3;14>=c;c++){const d=e(a[c]);null!==d&& b.push(d)}return{time:n(a[1]),mode:e(a[2]),res:b}},GBS:function(b,a){if(10!==a.length&&12!==a.length)throw Error("Invalid GBS length: "+b);return{time:n(a[1]),errLat:e(a[2]),errLon:e(a[3]),errAlt:e(a[4]),failedSat:e(a[5]),probFailedSat:e(a[6]),biasFailedSat:e(a[7]),stdFailedSat:e(a[8]),systemId:12===a.length?e(a[9]):null,signalId:12===a.length?e(a[10]):null}},GNS:function(b,a){if(14!==a.length&&15!==a.length)throw Error("Invalid GNS length: "+b);return{time:n(a[1]),lat:p(a[2],a[3]),lon:p(a[4],a[5]), mode:a[6],satsUsed:e(a[7]),hdop:e(a[8]),alt:e(a[9]),sep:e(a[10]),diffAge:e(a[11]),diffStation:e(a[12]),navStatus:15===a.length?a[13]:null}},TXT:function(b,a){if(6!==a.length)throw Error("Invalid TXT length: "+b);var c=parseInt(a[1],10),d=parseInt(a[2],10),g=parseInt(a[3],10),f=a[4]||"";if(!(1<=c&&99>=c))throw Error("Invalid TXT total: "+a[1]);if(!(1<=d&&d<=c))throw Error("Invalid TXT index: "+a[2]);if(!(0<=g&&99>=g))throw Error("Invalid TXT id: "+a[3]);if(61<f.length)throw Error("Invalid TXT message length: "+ f.length);if(null==f)f="";else{a="\r\n$*,!\\~\u007f".split("");for(var h=0;h<a.length;h++)if(-1!==f.indexOf(a[h]))throw Error("Message may not contain invalid character '"+a[h]+"'");a="";for(h=0;h<f.length;h++)if(94!==f.charCodeAt(h))a+=f[h];else{var l=f[h+1],m=f[h+2];"^"===l?(a+="^",h+=1):l&&m&&("0"<=l&&"9">=l||"A"<=l&&"F">=l||"a"<=l&&"f">=l)&&("0"<=m&&"9">=m||"A"<=m&&"F">=m||"a"<=m&&"f">=m)?(a+=String.fromCharCode(parseInt(l+m,16)),h+=2):a+="^"}f=a}if(""===f)throw Error("Invalid empty TXT message"); return{total:c,index:d,id:g,part:f,message:1===c?f:null,completed:1===c,rawMessages:1===c?[f]:[],system:r(b)}}};k.Parse=function(b){if("string"!==typeof b||6>b.length||36!==b.charCodeAt(0))return!1;var a=b.indexOf("*",1);if(-1===a||a+2>=b.length)return!1;var c=[],d=b.indexOf(",",1);if(-1===d||d>a)return!1;c.push("$"+b.slice(1,d));let g=0;for(var f=1;f<a;f++)g^=b.charCodeAt(f);d+=1;for(f=d;f<a;f++)44===b.charCodeAt(f)&&(c.push(b.slice(d,f)),d=f+1);c.push(b.slice(d,a));f=b.slice(a+1).trim();a=parseInt(f.slice(0, 2),16);if(!(0<=a&&255>=a))return!1;c[0]=c[0].slice(3);d=c[0];const h=k.parsers[d];if(void 0===h)return!1;c.push(f.slice(0,2));c=h(b,c);c.raw=b;c.valid=g===a;c.type=d;return c};k.Heading=function(b,a,c,d){a=(d-a)*q;b*=q;c*=q;d=Math.cos(c);return(180*Math.atan2(Math.sin(a)*d,Math.cos(b)*Math.sin(c)-Math.sin(b)*d*Math.cos(a))/Math.PI+360)%360};k.Distance=function(b,a,c,d){var g=(c-b)*q*.5;a=(d-a)*q*.5;b*=q;c*=q;g=Math.sin(g);a=Math.sin(a);return 12745.6*Math.asin(Math.sqrt(g*g+Math.cos(b)*Math.cos(c)* a*a))};k.TotalDistance=function(b){if(2>b.length)return 0;let a=0;for(let c=0;c<b.length-1;c++){const d=b[c],g=b[c+1];a+=k.Distance(d.lat,d.lon,g.lat,g.lon)}return a};k.prototype={constructor:k,_updateState:function(b){const 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);if("RMC"===b.type||"VTG"===b.type)null!= b.speed&&(a.speed=b.speed),null!=b.track&&(a.track=b.track);if("GSA"===b.type){var c=b.systemId;null!=c&&(this._collectActiveSats[c]=b.satellites);c=[];var d=this._collectActiveSats;for(var g in d)if(Object.prototype.hasOwnProperty.call(d,g)){var f=d[g];for(let h=0,l=f.length;h<l;h++)c.push(f[h])}a.satsActive=c;a.fix=b.fix;a.hdop=b.hdop;a.pdop=b.pdop;a.vdop=b.vdop}if("GSV"===b.type){g=Date.now();d=b.satellites;b=this._collectSats;c=this._lastSeenSat;for(let h=0,l=d.length;h<l;h++)f=d[h].key,c[f]= g,b[f]=d[h];d=[];for(const h in b)Object.prototype.hasOwnProperty.call(b,h)&&(3E3>g-c[h]?d.push(b[h]):(delete b[h],delete c[h]));a.satsVisible=d}},_assembleTXT:function(b){if(1===b.total)return b;const a=(b.system||"")+"#"+b.id;let c=this.state.txtBuffer[a];if(!c){c=this.state.txtBuffer[a]={total:b.total,parts:Array(b.total).fill(null),received:0,timer:null};const g=this;c.timer=setTimeout(function(){g.state.errors++;delete g.state.txtBuffer[a]},1E4)}const d=b.index-1;0<=d&&d<c.total&&(c.parts[d]= b.part,c.received++);c.received===c.total?(clearTimeout(c.timer),delete this.state.txtBuffer[a],b.message=c.parts.join(""),b.completed=!0,b.rawMessages=c.parts):(b.message=null,b.completed=!1,b.rawMessages=[]);return b},update:function(b){b=k.Parse(b);this.state.processed++;if(!1===b)return this.state.errors++,!1;"TXT"===b.type&&this._assembleTXT(b);this._updateState(b);this.emit("data",b);this.emit(b.type,b);return!0},updatePartial:function(b){for(b&&(this.partial+=b);;){b=this.partial.indexOf("\r\n"); var a=this.partial.indexOf("\n");let c=-1;-1!==b?c=b:-1!==a&&(c=a);if(-1===c)break;a=this.partial.slice(0,c);this.partial=this.partial.slice(c+(b===c?2:1));if("$"===a.charAt(0))try{this.update(a)}catch(d){throw this.state.errors++,d;}}},on:function(b,a){const c=this.events[b];void 0===c?this.events[b]=[a]:"function"===typeof c?this.events[b]=[c,a]:this.events[b].push(a);return this},off:function(b,a){const c=this.events[b];if(void 0===c)return this;if(!a)return delete this.events[b],this;if("function"=== typeof c)return c===a&&delete this.events[b],this;for(let d=c.length-1;0<=d;d--)c[d]===a&&c.splice(d,1);0===c.length&&delete this.events[b];return this},emit:function(b,a){b=this.events[b];if(void 0!==b)if("function"===typeof b)b.call(this,a);else for(let c=0,d=b.length;c<d;c++)b[c].call(this,a)}};"function"===typeof define&&define.amd?define([],function(){return k}):"object"===typeof exports?(Object.defineProperty(k,"__esModule",{value:!0}),k["default"]=k,k.GPS=k,module.exports=k):x.GPS=k})(this);