Comparing version 0.4.1 to 0.4.2
{ | ||
"name": "gps", | ||
"main": "gps.js", | ||
"version": "0.4.1", | ||
"version": "0.4.2", | ||
"homepage": "https://github.com/infusion/GPS.js", | ||
@@ -6,0 +6,0 @@ "description": "A GPS NMEA parser library", |
@@ -10,2 +10,8 @@ | ||
/* | ||
SerialPort.list(function (err, ports) { | ||
console.log(ports); | ||
}); | ||
*/ | ||
const parser = new parsers.Readline({ | ||
@@ -12,0 +18,0 @@ delimiter: '\r\n' |
231
gps.js
@@ -12,9 +12,15 @@ /** | ||
'use strict'; | ||
var D2R = Math.PI / 180; | ||
var collectSats = []; | ||
const D2R = Math.PI / 180; | ||
const MIN_LON = -Math.PI; | ||
const MAX_LON = Math.PI; | ||
const MIN_LAT = -Math.PI / 2; | ||
const MAX_LAT = Math.PI / 2; | ||
let collectSats = []; | ||
function updateState(state, data) { | ||
// TODO: can we really use RMC time here or is it the time of fix? | ||
if (data['type'] === 'RMC' || data['type'] === 'GGA' || data['type'] === 'GLL') { | ||
@@ -48,7 +54,9 @@ state['time'] = data['time']; | ||
// TODO: better merge algorithm | ||
// TODO: better merge algorithm: | ||
// 1. update every sat and mark as updated. | ||
// 2. If last msg, delete all unmarked sats & reset mark | ||
if (data['type'] === 'GSV') { | ||
var sats = data['satellites']; | ||
for (var i = 0; i < sats.length; i++) { | ||
const sats = data['satellites']; | ||
for (let i = 0; i < sats.length; i++) { | ||
collectSats.push(sats[i]); | ||
@@ -71,9 +79,9 @@ } | ||
var ret = new Date; | ||
const ret = new Date; | ||
if (date) { | ||
var year = date.slice(4); | ||
var month = date.slice(2, 4) - 1; | ||
var day = date.slice(0, 2); | ||
const year = date.slice(4); | ||
const month = date.slice(2, 4) - 1; | ||
const day = date.slice(0, 2); | ||
@@ -85,2 +93,3 @@ if (year.length === 4) { | ||
// year < 73 ? 2000+year : 1900+year | ||
// Since GPS appeared in 1973 | ||
ret.setUTCFullYear('20' + year, month, day); | ||
@@ -95,7 +104,7 @@ } | ||
// Extract the milliseconds, since they can be not present, be 3 decimal place, or 2 decimal places, or other? | ||
var msStr = time.slice(7); | ||
var msExp = msStr.length; | ||
var ms = 0; | ||
const msStr = time.slice(7); | ||
const msExp = msStr.length; | ||
let ms = 0; | ||
if (msExp !== 0) { | ||
ms = parseFloat(msStr) * Math.pow(10, 3 - msExp); | ||
ms = parseFloat(msStr) * Math.pow(10, 3 - msExp); | ||
} | ||
@@ -109,3 +118,3 @@ ret.setUTCMilliseconds(ms); | ||
// Latitude can go from 0 to 90; longitude can go from 0 to 180. | ||
// Latitude can go from 0 to 90; longitude can go from -180 to 180. | ||
@@ -115,3 +124,3 @@ if (coord === '') | ||
var n, sgn = 1; | ||
let n, sgn = 1; | ||
@@ -170,3 +179,3 @@ switch (dir) { | ||
} | ||
throw 'INVALID GSA MODE: ' + mode; | ||
throw new Error('INVALID GSA MODE: ' + mode); | ||
} | ||
@@ -197,3 +206,3 @@ | ||
} | ||
throw 'INVALID GGA FIX: ' + fix; | ||
throw new Error('INVALID GGA FIX: ' + fix); | ||
} | ||
@@ -212,3 +221,3 @@ | ||
} | ||
throw 'INVALID GSA FIX: ' + fix; | ||
throw new Error('INVALID GSA FIX: ' + fix); | ||
} | ||
@@ -226,3 +235,3 @@ | ||
} | ||
throw 'INVALID RMC/GLL STATUS: ' + status; | ||
throw new Error('INVALID RMC/GLL STATUS: ' + status); | ||
} | ||
@@ -250,3 +259,3 @@ | ||
} | ||
throw 'INVALID FAA MODE: ' + faa; | ||
throw new Error('INVALID FAA MODE: ' + faa); | ||
} | ||
@@ -259,3 +268,3 @@ | ||
var q = (dir === 'W') ? -1.0 : 1.0; | ||
const q = (dir === 'W') ? -1.0 : 1.0; | ||
@@ -267,5 +276,5 @@ return parseFloat(vari) * q; | ||
var checksum = 0; | ||
for (var i = 1; i < str.length; i++) { | ||
var c = str.charCodeAt(i); | ||
let checksum = 0; | ||
for (let i = 1; i < str.length; i++) { | ||
const c = str.charCodeAt(i); | ||
@@ -285,3 +294,3 @@ if (c === 42) // Asterisk: * | ||
} | ||
throw 'Unknown unit: ' + unit; | ||
throw new Error('Unknown unit: ' + unit); | ||
} | ||
@@ -291,2 +300,7 @@ | ||
function GPS() { | ||
if (!(this instanceof GPS)) { | ||
return new GPS; | ||
} | ||
this['events'] = {}; | ||
@@ -304,3 +318,3 @@ this['state'] = {}; | ||
if (gga.length !== 16) { | ||
throw 'Invalid GGA length: ' + str; | ||
throw new Error('Invalid GGA length: ' + str); | ||
} | ||
@@ -313,3 +327,3 @@ | ||
$--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh | ||
1) Time (UTC) | ||
@@ -344,3 +358,3 @@ 2) Latitude | ||
'geoidal': parseDist(gga[11], gga[12]), // aboveGeoid | ||
'age': parseNumber(gga[13]), // dgpsUpdate??? | ||
'age': parseNumber(gga[13]), // dgps time since update | ||
'stationID': parseNumber(gga[14]) // dgpsReference?? | ||
@@ -353,3 +367,3 @@ }; | ||
if (gsa.length !== 19) { | ||
throw 'Invalid GSA length: ' + str; | ||
throw new Error('Invalid GSA length: ' + str); | ||
} | ||
@@ -360,4 +374,4 @@ | ||
eg2. $GPGSA,A,3,19,28,14,18,27,22,31,39,,,,,1.7,1.0,1.3*35 | ||
1 = Mode: | ||
@@ -377,4 +391,4 @@ M=Manual, forced to operate in 2D or 3D | ||
var sats = []; | ||
for (var i = 3; i < 12 + 3; i++) { | ||
const sats = []; | ||
for (let i = 3; i < 15; i++) { | ||
@@ -399,3 +413,3 @@ if (gsa[i] !== '') { | ||
if (rmc.length !== 13 && rmc.length !== 14) { | ||
throw 'Invalid RMC length: ' + str; | ||
throw new Error('Invalid RMC length: ' + str); | ||
} | ||
@@ -405,3 +419,3 @@ | ||
$GPRMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,ddmmyy,x.x,a*hh | ||
RMC = Recommended Minimum Specific GPS/TRANSIT Data | ||
@@ -429,3 +443,3 @@ 1 = UTC of position fix | ||
'speed': parseKnots(rmc[7]), | ||
'track': parseNumber(rmc[8]), | ||
'track': parseNumber(rmc[8]), // heading | ||
'variation': parseRMCVariation(rmc[10], rmc[11]), | ||
@@ -439,3 +453,3 @@ 'faa': rmc.length === 14 ? parseFAA(rmc[12]) : null | ||
if (vtg.length !== 10 && vtg.length !== 11) { | ||
throw 'Invalid VTG length: ' + str; | ||
throw new Error('Invalid VTG length: ' + str); | ||
} | ||
@@ -449,7 +463,7 @@ | ||
------------------------------------------------------------------------------ | ||
1 = Track degrees | ||
1 = Track made good (degrees true) | ||
2 = Fixed text 'T' indicates that track made good is relative to true north | ||
3 = not used | ||
4 = not used | ||
3 = optional: Track made good (degrees magnetic) | ||
4 = optional: M: track made good is relative to magnetic north | ||
5 = Speed over ground in knots | ||
@@ -467,2 +481,3 @@ 6 = Fixed text 'N' indicates that speed over ground in in knots | ||
'track': null, | ||
'trackMagetic': null, | ||
'speed': null, | ||
@@ -474,11 +489,12 @@ 'faa': null | ||
if (vtg[2] !== 'T') { | ||
throw 'Invalid VTG track mode: ' + str; | ||
throw new Error('Invalid VTG track mode: ' + str); | ||
} | ||
if (vtg[8] !== 'K' || vtg[6] !== 'N') { | ||
throw 'Invalid VTG speed tag: ' + str; | ||
throw new Error('Invalid VTG speed tag: ' + str); | ||
} | ||
return { | ||
'track': parseNumber(vtg[1]), | ||
'track': parseNumber(vtg[1]), // heading | ||
'trackMagnetic': vtg[3] === '' ? null : parseNumber(vtg[3]), // heading uncorrected to magnetic north | ||
'speed': parseKnots(vtg[5]), | ||
@@ -492,3 +508,3 @@ 'faa': vtg.length === 11 ? parseFAA(vtg[9]) : null | ||
if (gsv.length < 9 || gsv.length % 4 !== 1) { | ||
throw 'Invalid GSV length: ' + str; | ||
throw new Error('Invalid GSV length: ' + str); | ||
} | ||
@@ -498,3 +514,3 @@ | ||
$GPGSV,1,1,13,02,02,213,,03,-3,000,,11,00,121,,14,13,172,05*67 | ||
1 = Total number of messages of this type in this cycle | ||
@@ -513,8 +529,8 @@ 2 = Message number | ||
var sats = []; | ||
const sats = []; | ||
for (var i = 4; i < gsv.length - 1; i += 4) { | ||
for (let i = 4; i < gsv.length - 1; i += 4) { | ||
var prn = parseNumber(gsv[i]); | ||
var snr = parseNumber(gsv[i + 3]); | ||
const prn = parseNumber(gsv[i]); | ||
const snr = parseNumber(gsv[i + 3]); | ||
@@ -541,3 +557,3 @@ sats.push({ | ||
if (gll.length !== 9) { | ||
throw 'Invalid GLL length: ' + str; | ||
throw new Error('Invalid GLL length: ' + str); | ||
} | ||
@@ -551,3 +567,3 @@ | ||
------------------------------------------------------------------------------ | ||
1. Latitude | ||
@@ -588,2 +604,31 @@ 2. N or S (North or South) | ||
}; | ||
}, | ||
'GST': function(str, gst) { | ||
if (gst.length !== 10) { | ||
throw new Error('Invalid GST length: ' + str); | ||
} | ||
/* | ||
1 = Time (UTC) | ||
2 = RMS value of the pseudorange residuals; includes carrier phase residuals during periods of RTK (float) and RTK (fixed) processing | ||
3 = Error ellipse semi-major axis 1 sigma error, in meters | ||
4 = Error ellipse semi-minor axis 1 sigma error, in meters | ||
5 = Error ellipse orientation, degrees from true north | ||
6 = Latitude 1 sigma error, in meters | ||
7 = Longitude 1 sigma error, in meters | ||
8 = Height 1 sigma error, in meters | ||
9 = Checksum | ||
*/ | ||
return { | ||
'time': parseTime(gst[1]), | ||
'rms': parseNumber(gst[2]), | ||
'ellipseMajor': parseNumber(gst[3]), | ||
'ellipseMinor': parseNumber(gst[4]), | ||
'ellipseOrientation': parseNumber(gst[5]), | ||
'latitudeError': parseNumber(gst[6]), | ||
'longitudeError': parseNumber(gst[7]), | ||
'heightError': parseNumber(gst[8]) | ||
}; | ||
} | ||
@@ -598,5 +643,5 @@ | ||
var nmea = line.split(','); | ||
const nmea = line.split(','); | ||
var last = nmea.pop(); | ||
let last = nmea.pop(); | ||
@@ -616,3 +661,3 @@ if (nmea.length < 4 || line.charAt(0) !== '$' || last.indexOf('*') === -1) { | ||
// set raw data here as well? | ||
var data = this['mod'][nmea[0]](line, nmea); | ||
const data = this['mod'][nmea[0]](line, nmea); | ||
data['raw'] = line; | ||
@@ -630,3 +675,3 @@ data['valid'] = isValid(line, nmea[nmea.length - 1]); | ||
var dlon = (lon2 - lon1) * D2R; | ||
const dlon = (lon2 - lon1) * D2R; | ||
@@ -636,15 +681,15 @@ lat1 = lat1 * D2R; | ||
var sdlon = Math.sin(dlon); | ||
var cdlon = Math.cos(dlon); | ||
const sdlon = Math.sin(dlon); | ||
const cdlon = Math.cos(dlon); | ||
var slat1 = Math.sin(lat1); | ||
var clat1 = Math.cos(lat1); | ||
const slat1 = Math.sin(lat1); | ||
const clat1 = Math.cos(lat1); | ||
var slat2 = Math.sin(lat2); | ||
var clat2 = Math.cos(lat2); | ||
const slat2 = Math.sin(lat2); | ||
const clat2 = Math.cos(lat2); | ||
var n = sdlon * clat2; | ||
var d = clat1 * slat2 - slat1 * clat2 * cdlon; | ||
const y = sdlon * clat2; | ||
const x = clat1 * slat2 - slat1 * clat2 * cdlon; | ||
var head = Math.atan2(n, d) * 180 / Math.PI; | ||
const head = Math.atan2(y, x) * 180 / Math.PI; | ||
@@ -660,8 +705,8 @@ return (head + 360) % 360; | ||
// Because Earth is no exact sphere, rounding errors may be up to 0.5%. | ||
// var RADIUS = 6371; // Earth radius average | ||
// var RADIUS = 6378.137; // Earth radius at equator | ||
var RADIUS = 6372.8; // Earth radius in km | ||
// const RADIUS = 6371; // Earth radius average | ||
// const RADIUS = 6378.137; // Earth radius at equator | ||
const RADIUS = 6372.8; // Earth radius in km | ||
var hLat = (lat2 - lat1) * D2R * 0.5; // Half of lat difference | ||
var hLon = (lon2 - lon1) * D2R * 0.5; // Half of lon difference | ||
const hLat = (lat2 - lat1) * D2R * 0.5; // Half of lat difference | ||
const hLon = (lon2 - lon1) * D2R * 0.5; // Half of lon difference | ||
@@ -671,8 +716,8 @@ lat1 = lat1 * D2R; | ||
var shLat = Math.sin(hLat); | ||
var shLon = Math.sin(hLon); | ||
var clat1 = Math.cos(lat1); | ||
var clat2 = Math.cos(lat2); | ||
const shLat = Math.sin(hLat); | ||
const shLon = Math.sin(hLon); | ||
const clat1 = Math.cos(lat1); | ||
const clat2 = Math.cos(lat2); | ||
var tmp = shLat * shLat + clat1 * clat2 * shLon * shLon; | ||
const tmp = shLat * shLat + clat1 * clat2 * shLon * shLon; | ||
@@ -683,5 +728,19 @@ //return RADIUS * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a)); | ||
GPS['TotalDistance'] = function(path) { | ||
if (path.length < 2) | ||
return 0; | ||
let len = 0; | ||
for (let i = 0; i < path.length - 1; i++) { | ||
const c = path[i]; | ||
const n = path[i + 1]; | ||
len += GPS['Distance'](c['lat'], c['lon'], n['lat'], n['lon']); | ||
} | ||
return len; | ||
}; | ||
GPS.prototype['update'] = function(line) { | ||
var parsed = GPS['Parse'](line); | ||
const parsed = GPS['Parse'](line); | ||
@@ -693,9 +752,5 @@ if (parsed === false) | ||
if (this['events']['data'] !== undefined) { | ||
this['events']['data'].call(this, parsed); | ||
} | ||
this['emit']('data', parsed); | ||
this['emit'](parsed.type, parsed); | ||
if (this['events'][parsed.type] !== undefined) { | ||
this['events'][parsed.type].call(this, parsed); | ||
} | ||
return true; | ||
@@ -712,3 +767,3 @@ }; | ||
var pos = this['partial'].indexOf("\r\n"); | ||
const pos = this['partial'].indexOf("\r\n"); | ||
@@ -718,3 +773,3 @@ if (pos === -1) | ||
var line = this['partial'].slice(0, pos); | ||
const line = this['partial'].slice(0, pos); | ||
@@ -745,2 +800,8 @@ if (line.charAt(0) === '$') { | ||
GPS.prototype['emit'] = function(ev, data) { | ||
if (this['events'][ev] !== undefined) { | ||
this['events'][ev].call(this, data); | ||
} | ||
}; | ||
if (typeof exports === 'object') { | ||
@@ -747,0 +808,0 @@ module.exports = GPS; |
{ | ||
"name": "gps", | ||
"title": "gps.js", | ||
"version": "0.4.1", | ||
"version": "0.4.2", | ||
"homepage": "https://github.com/infusion/GPS.js", | ||
@@ -11,2 +11,3 @@ "bugs": "https://github.com/infusion/GPS.js/issues", | ||
"gps", | ||
"glonass", | ||
"serial", | ||
@@ -16,2 +17,3 @@ "parser", | ||
"geo", | ||
"stream", | ||
"location", | ||
@@ -30,3 +32,4 @@ "rmc", | ||
"directories": { | ||
"example": "examples" | ||
"example": "examples", | ||
"test": "tests" | ||
}, | ||
@@ -33,0 +36,0 @@ "license": "MIT OR GPL-2.0", |
@@ -55,2 +55,5 @@ | ||
Register device on a BeagleBone | ||
--- | ||
If you find yourself on a BeagleBone, the serial device must be registered manually. Luckily, this can be done within node quite easily using [octalbonescript](https://www.npmjs.com/package/octalbonescript): | ||
@@ -100,3 +103,3 @@ | ||
After that you can open the browser and go to http://localhost:3000 The result should look like, which in principle is just a visualiziation of the state object `gps.state` | ||
After that you can open the browser and go to http://localhost:3000. The result should look like the following, which in principle is just a visualiziation of the state object `gps.state` | ||
@@ -138,3 +141,3 @@ ![GPS TU Dresden](https://github.com/infusion/GPS.js/blob/master/res/dashboard.png?raw=true) | ||
``` | ||
node set-time | ||
node set-date | ||
``` | ||
@@ -261,2 +264,18 @@ | ||
GST - Position error statistics | ||
--- | ||
The parsed object will have the following attributes: | ||
- type: "GST" | ||
- time: The time given as a JavaScript Date object | ||
- rms: RMS value of the pseudorange residuals; includes carrier phase residuals during periods of RTK (float) and RTK (fixed) | ||
- ellipseMajor: Error ellipse semi-major axis 1 sigma error, in meters | ||
- ellipseMinor: Error ellipse semi-minor axis 1 sigma error, in meters | ||
- ellipseOrientation: Error ellipse orientation, degrees from true north | ||
- latitudeError: Latitude 1 sigma error, in meters | ||
- longitudeError: Longitude 1 sigma error, in meters | ||
- heightError: Height 1 sigma error, in meters | ||
- valid: Indicates if the checksum is okay | ||
GPS State | ||
@@ -295,2 +314,6 @@ === | ||
GPS.TotalDistance(points) | ||
--- | ||
Calculates the length of a traveled route, given as an array of {lat: x, lon: y} point objects | ||
GPS.Heading(latFrom, lonFrom, latTo, lonTo) | ||
@@ -297,0 +320,0 @@ --- |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
353719
24
1506
349