nmea0183-signalk
Advanced tools
Comparing version 0.2.3 to 0.3.0
@@ -0,1 +1,19 @@ | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
/** AIS? **/ |
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
/* | ||
GSA - GPS DOP and active satellites | ||
@@ -3,0 +20,0 @@ |
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
/* | ||
GSV - Satellites in view | ||
@@ -3,0 +20,0 @@ |
@@ -1,11 +0,4 @@ | ||
/* | ||
* BWC codec | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* @repository https://github.com/signalk/nmea-signalk | ||
* @author Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* | ||
* | ||
* Copyright 2014, Fabian Tollenaar | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
@@ -22,2 +15,10 @@ * you may not use this file except in compliance with the License. | ||
* limitations under the License. | ||
*/ | ||
/* | ||
* BWC codec | ||
* | ||
* @repository https://github.com/signalk/nmea-signalk | ||
* @author Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
@@ -24,0 +25,0 @@ */ |
@@ -1,11 +0,4 @@ | ||
/* | ||
* ABT codec | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* @repository https://github.com/signalk/nmea-signalk | ||
* @author Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* | ||
* | ||
* Copyright 2014, Fabian Tollenaar | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
@@ -22,2 +15,11 @@ * you may not use this file except in compliance with the License. | ||
* limitations under the License. | ||
*/ | ||
/* | ||
* ABT codec | ||
* | ||
* @repository https://github.com/signalk/nmea-signalk | ||
* @author Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
@@ -34,3 +36,3 @@ */ | ||
where: | ||
APB Autopilot format B | ||
APB Autopilot format B | ||
0 A Loran-C blink/SNR warning, general warning | ||
@@ -45,5 +47,5 @@ 1 A Loran-C cycle warning | ||
9 DEST destination waypoint ID | ||
10,11 011,M magnetic bearing, present position to destination | ||
12,13 011,M magnetic heading to steer (bearings could True as 033,T) | ||
*3C Checksum | ||
10,11 011,M magnetic bearing, present position to destination | ||
12,13 011,M magnetic heading to steer (bearings could True as 033,T) | ||
*3C Checksum | ||
*/ | ||
@@ -68,3 +70,3 @@ | ||
var xte = this.transform(values[2], (values[4].toUpperCase() === 'N' ? 'nm' : 'km'), 'nm'); // value, inputFormat, outputFormat | ||
var xte = this.transform(values[2], (values[4].toUpperCase() === 'N' ? 'nm' : 'km'), 'm'); // value, inputFormat, outputFormat | ||
@@ -75,8 +77,8 @@ multiplexer | ||
.set('currentRoute', { | ||
source: this.source(), | ||
source: this.source(input.instrument), | ||
timestamp: this.timestamp(), | ||
steer: (values[3].toUpperCase() == 'R' ? 'right' : 'left'), | ||
bearingActual: this.float(values[10]), | ||
bearingDirect: this.float(values[7]), | ||
courseRequired: this.float(values[12]), | ||
bearingActual: this.transform(this.float(values[10]), 'deg', 'rad'), | ||
bearingDirect: this.transform(this.float(values[7]), 'deg', 'rad'), | ||
courseRequired: this.transform(this.float(values[12]), 'deg', 'rad'), | ||
waypoint: { | ||
@@ -83,0 +85,0 @@ next: values[9], |
@@ -52,15 +52,17 @@ /* | ||
multiplexer | ||
.self() | ||
.group('environment') | ||
.set('depth', { | ||
belowTransducer: { | ||
source: this.source(), | ||
timestamp: this.timestamp(), | ||
value: this.float(values[2]) | ||
} | ||
}) | ||
; | ||
multiplexer.self(); | ||
multiplexer.add({ | ||
"updates": [{ | ||
"source": this.source(input.instrument), | ||
"timestamp": this.timestamp(), | ||
"values": [{ | ||
"path": "environment.depth.belowTransducer", | ||
"value": this.float(values[2]) | ||
}] | ||
}], | ||
"context": multiplexer._context | ||
}); | ||
return true; | ||
}); |
@@ -76,3 +76,3 @@ /* | ||
var ts = this.timestamp(values[0]); | ||
var src = this.source(); | ||
var src = this.source(input.instrument); | ||
var self = this; | ||
@@ -85,3 +85,3 @@ | ||
.set('position', { | ||
source: this.source(), | ||
source: this.source(input.instrument), | ||
timestamp: ts, | ||
@@ -98,3 +98,3 @@ longitude: this.coordinate(values[3], values[4]), | ||
.set('gnss', { | ||
source: this.source(), | ||
source: this.source(input.instrument), | ||
timestamp: ts, | ||
@@ -101,0 +101,0 @@ quality: this.int(values[5]), |
@@ -52,3 +52,3 @@ /* | ||
if(values[5].toUpperCase() == 'V') { | ||
if(values[5].toUpperCase() == 'V') { | ||
// Don't parse this sentence as it's void, but report the exception to the main Codec. | ||
@@ -78,14 +78,21 @@ this.reportError(this.errors.VOID, "Not parsing sentence for it's void."); | ||
// Position | ||
multiplexer | ||
.self() | ||
.group('navigation') | ||
.set('position', { | ||
source: this.source(), | ||
timestamp: ts, | ||
longitude: self.coordinate(values[2], values[3]), | ||
latitude: self.coordinate(values[0], values[1]) | ||
}) | ||
; | ||
multiplexer.self(); | ||
multiplexer.add({ | ||
"updates": [{ | ||
"source": this.source(input.instrument), | ||
"timestamp": ts, | ||
"values": [{ | ||
"path": "navigation.position", | ||
"value": { | ||
longitude: self.coordinate(values[2], values[3]), | ||
latitude: self.coordinate(values[0], values[1]) | ||
} | ||
}] | ||
}], | ||
"context": multiplexer._context | ||
}); | ||
return true; | ||
return true; | ||
@@ -92,0 +99,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
/* | ||
/* | ||
* index.js | ||
@@ -15,3 +15,3 @@ * | ||
* You may obtain a copy of the License at | ||
* | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
@@ -27,16 +27,27 @@ * | ||
var codecs = {}; | ||
var codecs = { | ||
APB: require('./APB'), | ||
DBT: require('./DBT'), | ||
DSC: require('./DSC'), | ||
GGA: require('./GGA'), | ||
GLL: require('./GLL'), | ||
HDM: require('./HDM'), | ||
HDG: require('./HDG'), | ||
HDT: require('./HDT'), | ||
MTW: require('./MTW'), | ||
MWV: require('./MWV'), | ||
RMC: require('./RMC'), | ||
VDM: require('./VDM'), | ||
VDO: require('./VDO'), | ||
VHW: require('./VHW'), | ||
VPW: require('./VPW'), | ||
VTG: require('./VTG'), | ||
RPM: require('./RPM'), | ||
ROT: require('./ROT'), | ||
VDR: require('./VDR'), | ||
VPW: require('./VPW'), | ||
VWR: require('./VWR'), | ||
STALK: require('./STALK'), | ||
}; | ||
require("fs").readdirSync(__dirname).forEach(function(codec) { | ||
if(codec !== 'index.js' && codec.charAt(0) !== '.' && codec.charAt(0) !== '_') { | ||
var module = require('./' + codec); | ||
if(typeof module === 'function') { | ||
codecs[codec.replace('.js', '').toUpperCase()] = module(); | ||
} else { | ||
codecs[codec.replace('.js', '').toUpperCase()] = module; | ||
} | ||
} | ||
}); | ||
module.exports = codecs; | ||
module.exports = codecs; |
@@ -49,7 +49,7 @@ /* | ||
function convertToWindAngle(self, angle) { | ||
var numAngle = self.float(angle) % 360; | ||
if (numAngle > 180 && numAngle <= 360) { | ||
return numAngle - 360; | ||
} | ||
return numAngle; | ||
var numAngle = self.float(angle) % 360; | ||
if (numAngle > 180 && numAngle <= 360) { | ||
return numAngle - 360; | ||
} | ||
return numAngle; | ||
} | ||
@@ -60,4 +60,4 @@ | ||
if(values[4].toUpperCase() != 'A') { | ||
// Don't parse this sentence as it's void, but report the exception to the main Codec. | ||
if(!values[4] || values[4].toUpperCase() != 'A') { | ||
// Don't parse this sentence as it's void/has no data, but report the exception to the main Codec. | ||
this.reportError(this.errors.VOID, "Not parsing sentence for it's void."); | ||
@@ -68,3 +68,3 @@ return null; | ||
var ts = this.timestamp(); | ||
var source = this.source(); | ||
var source = this.source(input.instrument); | ||
var wsu = values[3].toUpperCase(); | ||
@@ -80,40 +80,25 @@ | ||
var angle = convertToWindAngle(this, values[0]); | ||
var speed = this.transform(values[2], wsu, 'ms'); | ||
var angle = convertToWindAngle(this, values[0]); | ||
var speed = this.transform(values[2], wsu, 'ms'); | ||
multiplexer | ||
.self() | ||
.group('environment') | ||
; | ||
multiplexer.self(); | ||
if(values[1].toUpperCase() == "R") { | ||
multiplexer.set('wind', { | ||
angleApparent: { | ||
timestamp: ts, | ||
source: source, | ||
value: angle | ||
}, | ||
var valueType = values[1].toUpperCase() == "R" ? 'Apparent' : 'True'; | ||
var angleType = values[1].toUpperCase() == "R" ? 'Apparent' : 'TrueWater'; | ||
speedApparent: { | ||
timestamp: ts, | ||
source: source, | ||
value: speed | ||
} | ||
}); | ||
} else { | ||
multiplexer.set('wind', { | ||
angleTrue: { | ||
timestamp: ts, | ||
source: source, | ||
value: angle | ||
}, | ||
speedTrue: { | ||
timestamp: ts, | ||
source: source, | ||
value: speed | ||
} | ||
}); | ||
} | ||
multiplexer.add({ | ||
"updates": [{ | ||
"source": source, | ||
"timestamp": ts, | ||
"values": [{ | ||
"path": "environment.wind.speed" + valueType, | ||
"value": speed | ||
}, { | ||
"path": "environment.wind.angle" + angleType, | ||
"value": this.transform(angle, 'deg', 'rad') | ||
}] | ||
}], | ||
"context": multiplexer._context | ||
}); | ||
return true; | ||
}); | ||
}); |
@@ -72,3 +72,3 @@ /* | ||
.set('position', { | ||
source: this.source(), | ||
source: this.source(input.instrument), | ||
timestamp: ts, | ||
@@ -81,3 +81,3 @@ longitude: self.coordinate(values[4], String(values[5]).toUpperCase()), | ||
var vals = [ | ||
{ path: 'courseOverGroundTrue', value: self.float(values[7]) }, | ||
{ path: 'courseOverGroundTrue', value: self.transform(self.float(values[7]), 'deg', 'rad') }, | ||
{ path: 'speedOverGround', value: self.transform(values[6], 'knots', 'ms') } | ||
@@ -87,3 +87,3 @@ ]; | ||
if(typeof values[9] !== 'undefined' && typeof values[10] === 'string') { | ||
vals.push({ path: 'magneticVariation', value: this.magneticVariaton(values[9], values[10]) }); | ||
vals.push({ path: 'magneticVariation', value: self.transform(this.magneticVariaton(values[9], values[10]), 'deg', 'rad') }); | ||
} | ||
@@ -95,3 +95,3 @@ | ||
.group('navigation') | ||
.source(this.source()) | ||
.source(this.source(input.instrument)) | ||
.timestamp(ts) | ||
@@ -98,0 +98,0 @@ .values(vals) |
@@ -0,46 +1,98 @@ | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
"use strict"; | ||
var Codec = require('../lib/NMEA0183'); | ||
var Decoder = require ("ggencoder").AisDecode; | ||
var Codec = require('../lib/NMEA0183'); | ||
var Decoder = require("ggencoder").AisDecode; | ||
/* | ||
A QUICK HACK THAT WILL NOT WORK WITH MULTIPLE | ||
AIS NMEA STREAMS. Implements ggencoder session | ||
as a global singleton. Proper implementation would be | ||
a per stream constructer NMEA0183 encoder that would be able | ||
to keep a coherent internal parsing state. | ||
*/ | ||
var session = {}; | ||
module.exports = new Codec('VDM', function(multiplexer, input, line) { | ||
var data = new Decoder(line); | ||
var data = new Decoder(line, session); | ||
if(!data.valid) { | ||
return this.reportError(this.errors.VOID, "Not parsing sentence for it isn't valid."); | ||
if (!data.valid) { | ||
return this.reportError(this.errors.VOID, "Not parsing sentence for it isn't valid."); | ||
} | ||
// console.log(JSON.stringify(data, null, 2)); | ||
// console.log('\n'); | ||
// MMSI | ||
multiplexer.vessel(data.mmsi).set('mmsi', data.mmsi); | ||
// Position | ||
multiplexer | ||
.vessel(data.mmsi) | ||
.group('navigation') | ||
.set('position', { | ||
source: this.source(), | ||
timestamp: this.timestamp(), | ||
longitude: data.lon, | ||
latitude: data.lat | ||
var values = []; | ||
if (data.mmsi) { | ||
values.push({ | ||
path: "", | ||
value: { | ||
mmsi: data.mmsi | ||
} | ||
}) | ||
; | ||
} | ||
if (data.shipname) { | ||
values.push({ | ||
path: "", | ||
value: { | ||
name: data.shipname | ||
} | ||
}) | ||
} | ||
if (typeof data.sog != 'undefined') { | ||
values.push({ | ||
path: "navigation.speedOverGround", | ||
value: this.transform(data.sog, 'knots', 'ms') | ||
}) | ||
} | ||
if (typeof data.cog != 'undefined') { | ||
values.push({ | ||
path: "navigation.courseOverGroundTrue", | ||
value: this.transform(data.cog, 'deg', 'rad') | ||
}) | ||
} | ||
if (typeof data.hdg != 'undefined') { | ||
values.push({ | ||
path: "navigation.headingTrue", | ||
value: this.transform(data.hdg, 'deg', 'rad') | ||
}) | ||
} | ||
if (data.lon && data.lat) { | ||
values.push({ | ||
path: "navigation.position", | ||
value: { | ||
longitude: data.lon, | ||
latitude: data.lat | ||
} | ||
}) | ||
} | ||
// Other values | ||
multiplexer | ||
.vessel(data.mmsi) | ||
.group('navigation') | ||
.timestamp(this.timestamp()) | ||
.source(this.source()) | ||
.values([ | ||
{ path: "speedOverGround", value: data.sog }, | ||
{ path: "courseOverGround", value: data.cog }, | ||
{ path: "state", value: data.GetNavStatus() }, | ||
{ path: "headingTrue", value: data.hdg } | ||
]) | ||
; | ||
if (values.length > 0) { | ||
multiplexer.self(); | ||
multiplexer.add({ | ||
"updates": [{ | ||
"source": this.source(input.instrument), | ||
"timestamp": this.timestamp(), | ||
"values": values | ||
}], | ||
"context": 'vessels.urn:mrn:imo:mmsi:' + data.mmsi | ||
}); | ||
} | ||
return true; | ||
}); | ||
}); |
@@ -0,1 +1,18 @@ | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
module.exports = require('./VDM'); |
@@ -54,38 +54,36 @@ /* | ||
function hasValue(value) { | ||
return (typeof value !== 'undefined' && | ||
value !== ''); | ||
return (typeof value !== 'undefined' && value !== '' && value !== null); | ||
} | ||
module.exports = new Codec('VHW', function(multiplexer, input) { | ||
var values = input.values; | ||
var values = input.values; | ||
var speed; | ||
var parsedValues = []; | ||
var speed; | ||
var parsedValues = []; | ||
if (hasValue(values[0])) { | ||
parsedValues.push({ path: 'headingTrue', value: parseFloat(values[0]) }); | ||
} | ||
if (hasValue(values[2])) { | ||
parsedValues.push({ path: 'headingMagnetic', value: parseFloat(values[2]) }); | ||
} | ||
if(hasValue(values[0])) { | ||
parsedValues.push({ path: 'headingTrue', value: this.transform(this.float(values[0]), 'deg', 'rad') }); | ||
} | ||
if(hasValue(values[2])) { | ||
parsedValues.push({ path: 'headingMagnetic', value: this.transform(this.float(values[2]), 'deg', 'rad') }); | ||
} | ||
if (hasValue(values[6])) { | ||
speed = this.transform(values[6], 'kph', 'ms'); | ||
} | ||
else if (hasValue(values[4])) { | ||
speed = this.transform(values[4], 'knots', 'ms'); | ||
} | ||
if(hasValue(values[6])) { | ||
speed = this.transform(values[6], 'kph', 'ms'); | ||
} else if(hasValue(values[4])) { | ||
speed = this.transform(values[4], 'knots', 'ms'); | ||
} | ||
if (typeof speed !== 'undefined') { | ||
parsedValues.push({ path: 'speedThroughWater', value: speed }); | ||
} | ||
if (typeof speed !== 'undefined') { | ||
parsedValues.push({ path: 'speedThroughWater', value: speed }); | ||
} | ||
multiplexer | ||
.self() | ||
.group('navigation') | ||
.timestamp(this.timestamp()) | ||
.source(this.source()) | ||
.values(parsedValues); | ||
multiplexer | ||
.self() | ||
.group('navigation') | ||
.timestamp(this.timestamp()) | ||
.source(this.source(input.instrument)) | ||
.values(parsedValues); | ||
return true; | ||
return true; | ||
}); |
@@ -68,6 +68,6 @@ /* | ||
.timestamp(this.timestamp()) | ||
.source(this.source()) | ||
.source(this.source(input.instrument)) | ||
.values([ | ||
{ path: 'courseOverGroundMagnetic', value: this.float(values[2]) }, | ||
{ path: 'courseOverGroundTrue', value: this.float(values[0]) }, | ||
{ path: 'courseOverGroundMagnetic', value: this.transform(this.float(values[2]), 'deg', 'rad') }, | ||
{ path: 'courseOverGroundTrue', value: this.transform(this.float(values[0]), 'deg', 'rad') }, | ||
{ path: 'speedOverGround', value: speed } | ||
@@ -74,0 +74,0 @@ ]); |
16
index.js
@@ -0,1 +1,17 @@ | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
module.exports = require('./lib'); |
244
lib/index.js
@@ -1,41 +0,61 @@ | ||
var Transform = require('stream').Transform; | ||
var util = require('util'); | ||
var _ = require('lodash'); | ||
var codecs = require('../codecs'); | ||
var debug = require('debug')('signalk-parser-nmea0183'); | ||
var uuid = require('node-uuid').v4; | ||
var Multiplexer = require('signalk-multiplexer'); | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
var Transform = require('stream').Transform; | ||
var util = require('util'); | ||
var _ = require('lodash'); | ||
var codecs = require('../codecs'); | ||
var debug = require('debug')('signalk-parser-nmea0183'); | ||
var uuid = require('uuid').v4; | ||
var Multiplexer = require('signalk-multiplexer'); | ||
function Parser(opts) { | ||
if(!(this instanceof Parser)) { | ||
if (!(this instanceof Parser)) { | ||
return new Parser(opts); | ||
} | ||
var self = this; | ||
var options = opts || {}; | ||
var self = this; | ||
var options = opts || {}; | ||
if(!options.stream) { | ||
options.stream = {}; | ||
} | ||
if (!options.stream) { | ||
options.stream = {}; | ||
} | ||
options.stream.objectMode = true; | ||
Transform.call(this, options.stream); | ||
options.stream.objectMode = true; | ||
Transform.call(this, options.stream); | ||
this._linesParsed = 0; | ||
this._linesProcessed = 0; | ||
this._options = options; | ||
this._lineBuffer = ""; | ||
this.self = {}; | ||
this.self.id = this._options.selfId || String(uuid().split('-')[0]).toUpperCase(); | ||
this.self.type = this._options.selfType || 'uuid'; | ||
this._linesParsed = 0; | ||
this._linesProcessed = 0; | ||
this._options = options; | ||
this._lineBuffer = ""; | ||
this.self = {}; | ||
this.self.id = this._options.selfId || String(uuid().split('-')[0]).toUpperCase(); | ||
this.self.type = this._options.selfType || 'uuid'; | ||
this._multiplexer = new Multiplexer(this.self.id, this.self.type); | ||
this._codecs = require('../codecs'); | ||
this._multiplexer = new Multiplexer(this.self.id, this.self.type); | ||
this._codecs = require('../codecs'); | ||
this._multiplexer.on('change', function() { | ||
var data = self._multiplexer.retrieve(); | ||
if (self.listeners('sentence').length > 0 || | ||
self.listeners('signalk').length > 0 || | ||
self.listeners('data').length > 0) { | ||
var data = self._multiplexer.retrieve(); | ||
self.emit('sentence', data, self._linesParsed, self._linesProcessed); | ||
self.emit('signalk', data, self._linesParsed, self._linesProcessed); | ||
self.push(data); | ||
self.emit('sentence', data, self._linesParsed, self._linesProcessed); | ||
self.emit('signalk', data, self._linesParsed, self._linesProcessed); | ||
self.push(data); | ||
} | ||
}); | ||
@@ -51,9 +71,9 @@ | ||
Parser.prototype._validSentence = function(sentence) { | ||
sentence = String(sentence).trim(); | ||
sentence = String(sentence).trim(); | ||
if(sentence === "") { | ||
if (sentence === "") { | ||
return false; | ||
} | ||
if((sentence.charAt(0) == '$' || sentence.charAt(0) == '!') && sentence.charAt(sentence.length - 3) == '*') { | ||
if ((sentence.charAt(0) == '$' || sentence.charAt(0) == '!') && sentence.charAt(sentence.length - 3) == '*') { | ||
var check = 0; | ||
@@ -67,113 +87,129 @@ var split = sentence.split('*'); | ||
return (parseInt(split[1], 16) == check); | ||
} else { | ||
return false; | ||
} | ||
} else { | ||
return false; | ||
} | ||
} | ||
Parser.prototype._lineData = function(sentence) { | ||
var split = sentence.split('*'); | ||
var raw = split[0].slice(1); | ||
var values = raw.split(','); | ||
this.emit('nmea0183', sentence); | ||
var split = sentence.split('*'); | ||
var raw = split[0].slice(1); | ||
var values = raw.split(','); | ||
var data = { | ||
instrument: values[0].slice(0, 2), | ||
type: values[0].slice(-3), | ||
values: [] | ||
}; | ||
if (values[0][0] == "P") { | ||
var data = { | ||
instrument: values[0].slice(0, 4), | ||
type: values[0].slice(-3), | ||
values: [] | ||
}; | ||
} else if (values[0] == "STALK") { | ||
var data = { | ||
instrument: "seatalk", | ||
type: values[0], | ||
values: [] | ||
}; | ||
} else { | ||
var data = { | ||
instrument: values[0].slice(0, 2), | ||
type: values[0].slice(-3), | ||
values: [] | ||
}; | ||
} | ||
for(var i = 1; i < values.length; i++) { | ||
data.values.push(values[i]); | ||
} | ||
return data; | ||
for (var i = 1; i < values.length; i++) { | ||
data.values.push(values[i]); | ||
} | ||
return data; | ||
} | ||
Parser.prototype._decode = function(data, line) { | ||
if(typeof codecs[data.type.toUpperCase()] !== 'undefined') { | ||
if (typeof codecs[data.type.toUpperCase()] !== 'undefined') { | ||
return codecs[data.type.toUpperCase()].decode(this._multiplexer, data, line); | ||
} | ||
} | ||
} | ||
Parser.prototype._transform = function(chunk, encoding, done) { | ||
if(Buffer.isBuffer(chunk)) { | ||
if (Buffer.isBuffer(chunk)) { | ||
chunk = chunk.toString(); | ||
} | ||
var self = this; | ||
var self = this; | ||
this._lineBuffer += chunk; | ||
this._lineBuffer += chunk; | ||
this._lineBuffer = this._lineBuffer.replace(/\\r\\n/g, "\n"); | ||
if(this._lineBuffer.indexOf('\n') !== -1 || (this._lineBuffer.indexOf('\n') === -1 && this._lineBuffer.indexOf('$') !== -1 && this._lineBuffer.indexOf('*') !== -1)) { | ||
var split = this._lineBuffer.split('\n'); | ||
var unfinished = ""; | ||
var lines = []; | ||
if (this._lineBuffer.indexOf('\n') !== -1 || (this._lineBuffer.indexOf('\n') === -1 && this._lineBuffer.indexOf('$') !== -1 && this._lineBuffer.indexOf('*') !== -1)) { | ||
var split = this._lineBuffer.split('\n'); | ||
var unfinished = ""; | ||
var lines = []; | ||
_.each(split, function(line) { | ||
line = line.trim(); | ||
_.each(split, function(line) { | ||
line = line.trim(); | ||
if(line !== '') { | ||
if(line.charAt(0) == '$' || line.charAt(0) == '!') { | ||
if(line.charAt(line.length - 3) == '*') { | ||
// we have a full line | ||
lines.push(line); | ||
} else { | ||
unfinished = line; | ||
} | ||
} else { | ||
unfinished += line; | ||
} | ||
} | ||
if (line !== '') { | ||
if (line.charAt(0) == '$' || line.charAt(0) == '!') { | ||
if (line.charAt(line.length - 3) == '*') { | ||
// we have a full line | ||
lines.push(line); | ||
} else { | ||
unfinished = line; | ||
} | ||
} else { | ||
unfinished += line; | ||
} | ||
} | ||
if(unfinished.trim() != '' && (unfinished.charAt(0) == '$' || unfinished.charAt(0) == '!') && unfinished.charAt(unfinished.length - 3) == '*') { | ||
// Unfinished is a full line | ||
lines.push(unfinished); | ||
} | ||
}); | ||
if (unfinished.trim() != '' && (unfinished.charAt(0) == '$' || unfinished.charAt(0) == '!') && unfinished.charAt(unfinished.length - 3) == '*') { | ||
// Unfinished is a full line | ||
lines.push(unfinished); | ||
} | ||
}); | ||
_.each(lines, function(line) { | ||
if(self._validSentence(line)) { | ||
var data = self._lineData(line); | ||
var valid = self._decode(data, line); | ||
_.each(lines, function(line) { | ||
if (self._validSentence(line)) { | ||
var data = self._lineData(line); | ||
var valid = self._decode(data, line); | ||
// Internal counter counting lines that were processed | ||
self._linesProcessed++; | ||
// Internal counter counting lines that were processed | ||
self._linesProcessed++; | ||
if(valid) { | ||
// Internal counter counting lines that were actually parsed. | ||
// Lines that weren't parsed are either of an unsupported type (see codecs) or VOID. | ||
self._linesParsed++; | ||
} | ||
} | ||
}); | ||
if (valid) { | ||
// Internal counter counting lines that were actually parsed. | ||
// Lines that weren't parsed are either of an unsupported type (see codecs) or VOID. | ||
self._linesParsed++; | ||
} | ||
} | ||
}); | ||
if(split[split.length - 1].charAt(0) == '$' && split[split.length - 1].charAt(split[split.length - 1].length - 3) !== '*') { | ||
this._lineBuffer = split[split.length - 1]; | ||
} else { | ||
this._lineBuffer = ""; | ||
} | ||
} | ||
if (split[split.length - 1].charAt(0) == '$' && split[split.length - 1].charAt(split[split.length - 1].length - 3) !== '*') { | ||
this._lineBuffer = split[split.length - 1]; | ||
} else { | ||
this._lineBuffer = ""; | ||
} | ||
} | ||
return done(); | ||
return done(); | ||
} | ||
Parser.prototype._flush = function(done) { | ||
return done(); | ||
return done(); | ||
} | ||
function parseLine(line, cb, opts) { | ||
var parser = new Parser(opts); | ||
var parser = new Parser(opts); | ||
parser.on('sentence', function(signal) { | ||
cb(null, signal); | ||
parser.end(); | ||
parser.on('sentence', function(signal) { | ||
cb(null, signal); | ||
parser.end(); | ||
parser = undefined; | ||
}); | ||
}); | ||
parser.write(line); | ||
parser.write(line); | ||
}; | ||
module.exports = { | ||
Parser: Parser, | ||
parse: parseLine | ||
}; | ||
Parser: Parser, | ||
parse: parseLine | ||
}; |
@@ -1,225 +0,264 @@ | ||
!function() { | ||
"use strict"; | ||
/* | ||
* Copyright 2015 Fabian Tollenaar <fabian@starting-point.nl> | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
! function() { | ||
"use strict"; | ||
Math.radians = function(degrees) { | ||
return degrees * Math.PI / 180; | ||
}; | ||
var NMEA0183 = function(name, decoder) { | ||
if(!(this instanceof NMEA0183)) return new NMEA0183(); | ||
if (!(this instanceof NMEA0183)) return new NMEA0183(); | ||
this.name = name; | ||
this._decoder = decoder; | ||
this.exceptions = []; | ||
this.name = name; | ||
this._decoder = decoder; | ||
this.exceptions = []; | ||
this.errors = { | ||
GENERAL: 0, | ||
MALFORMED: 1, | ||
VOID: 2, | ||
VOID_MODE: 3 | ||
}; | ||
this.errors = { | ||
GENERAL: 0, | ||
MALFORMED: 1, | ||
VOID: 2, | ||
VOID_MODE: 3 | ||
}; | ||
this.RATIOS = { | ||
// DISTANCE | ||
NM_IN_KM: 1.852, | ||
KM_IN_NM: 0.539956803, | ||
// SPEED | ||
// Knots | ||
KNOTS_IN_MS: 0.514444, | ||
KNOTS_IN_MPH: 1.150779, | ||
KNOTS_IN_KPH: 1.852, | ||
// MPH | ||
MPH_IN_MS: 0.44704, | ||
MPH_IN_KPH: 1.609344, | ||
MPH_IN_KNOTS: 0.868976, | ||
// KPH | ||
KPH_IN_MS: 0.277778, | ||
KPH_IN_MPH: 0.621371, | ||
KPH_IN_KNOTS: 0.539957, | ||
// MS | ||
MS_IN_KPH: 3.6, | ||
MS_IN_MPH: 2.236936, | ||
MS_IN_KNOTS: 1.943844, | ||
}; | ||
}; | ||
this.RATIOS = { | ||
// DISTANCE | ||
NM_IN_KM: 1.852, | ||
KM_IN_NM: 0.539956803, | ||
// SPEED | ||
// Knots | ||
KNOTS_IN_MS: 0.514444, | ||
KNOTS_IN_MPH: 1.150779, | ||
KNOTS_IN_KPH: 1.852, | ||
// MPH | ||
MPH_IN_MS: 0.44704, | ||
MPH_IN_KPH: 1.609344, | ||
MPH_IN_KNOTS: 0.868976, | ||
// KPH | ||
KPH_IN_MS: 0.277778, | ||
KPH_IN_MPH: 0.621371, | ||
KPH_IN_KNOTS: 0.539957, | ||
// MS | ||
MS_IN_KPH: 3.6, | ||
MS_IN_MPH: 2.236936, | ||
MS_IN_KNOTS: 1.943844, | ||
}; | ||
}; | ||
NMEA0183.prototype.source = function() { | ||
return { | ||
type: 'NMEA0183', | ||
src: this.name, | ||
label: 'signalk-parser-nmea0183' | ||
}; | ||
}; | ||
NMEA0183.prototype.source = function(talker) { | ||
var source = { | ||
type: 'NMEA0183', | ||
sentence: this.name, | ||
label: 'signalk-parser-nmea0183' | ||
}; | ||
NMEA0183.prototype.transform = function(value, inputFormat, outputFormat) { | ||
value = this.float(value); | ||
if(typeof talker === 'string' && talker.trim().length > 0) { | ||
source.talker = talker.toUpperCase() | ||
} | ||
inputFormat = inputFormat.toLowerCase(); | ||
outputFormat = outputFormat.toLowerCase(); | ||
return source | ||
}; | ||
if(inputFormat === outputFormat) { | ||
return value; | ||
} | ||
NMEA0183.prototype.transform = function(value, inputFormat, outputFormat) { | ||
value = this.float(value); | ||
// KM | ||
if(inputFormat == 'km') { | ||
if(outputFormat == 'nm') return value / this.RATIOS.NM_IN_KM; | ||
} | ||
inputFormat = inputFormat.toLowerCase(); | ||
outputFormat = outputFormat.toLowerCase(); | ||
// NM | ||
if(inputFormat == 'nm') { | ||
if(outputFormat == 'km') return value / this.RATIOS.KM_IN_NM; | ||
} | ||
// KNOTS | ||
if(inputFormat == 'knots') { | ||
if(outputFormat == 'kph') return value / this.RATIOS.KPH_IN_KNOTS; | ||
if(outputFormat == 'ms') return value / this.RATIOS.MS_IN_KNOTS; | ||
if(outputFormat == 'mph') return value / this.RATIOS.MPH_IN_KNOTS; | ||
} | ||
if (inputFormat === outputFormat) { | ||
return value; | ||
} | ||
// KPH | ||
if(inputFormat == 'kph') { | ||
if(outputFormat == 'knots') return value / this.RATIOS.KNOTS_IN_KPH; | ||
if(outputFormat == 'ms') return value / this.RATIOS.MS_IN_KPH; | ||
if(outputFormat == 'mph') return value / this.RATIOS.MPH_IN_KPH; | ||
} | ||
// M | ||
if (inputFormat == 'm') { | ||
if (outputFormat == 'nm') return (value / 1000) / this.RATIOS.NM_IN_KM; | ||
} | ||
// MPH | ||
if(inputFormat == 'mph') { | ||
if(outputFormat == 'knots') return value / this.RATIOS.KNOTS_IN_MPH; | ||
if(outputFormat == 'ms') return value / this.RATIOS.MS_IN_MPH; | ||
if(outputFormat == 'kph') return value / this.RATIOS.KPH_IN_MPH; | ||
} | ||
// KM | ||
if (inputFormat == 'km') { | ||
if (outputFormat == 'nm') return value / this.RATIOS.NM_IN_KM; | ||
if (outputFormat == 'm') return value * 1000; | ||
} | ||
// MS | ||
if(inputFormat == 'ms') { | ||
if(outputFormat == 'knots') return value / this.RATIOS.KNOTS_IN_MS; | ||
if(outputFormat == 'mph') return value / this.RATIOS.MPH_IN_MS; | ||
if(outputFormat == 'kph') return value / this.RATIOS.KPH_IN_MS; | ||
} | ||
// NM | ||
if (inputFormat == 'nm') { | ||
if (outputFormat == 'km') return value / this.RATIOS.KM_IN_NM; | ||
if (outputFormat == 'm') return (value / this.RATIOS.KM_IN_NM) * 1000; | ||
} | ||
// Just return input if input/output formats are not recognised. | ||
return value; | ||
}; | ||
// KNOTS | ||
if (inputFormat == 'knots') { | ||
if (outputFormat == 'kph') return value / this.RATIOS.KPH_IN_KNOTS; | ||
if (outputFormat == 'ms') return value / this.RATIOS.MS_IN_KNOTS; | ||
if (outputFormat == 'mph') return value / this.RATIOS.MPH_IN_KNOTS; | ||
} | ||
NMEA0183.prototype.reportError = function(errorCode, errorMsg) { | ||
/* | ||
* @TODO | ||
*/ | ||
// KPH | ||
if (inputFormat == 'kph') { | ||
if (outputFormat == 'knots') return value / this.RATIOS.KNOTS_IN_KPH; | ||
if (outputFormat == 'ms') return value / this.RATIOS.MS_IN_KPH; | ||
if (outputFormat == 'mph') return value / this.RATIOS.MPH_IN_KPH; | ||
} | ||
this.exceptions.push({ | ||
source: this.name, | ||
code: errorCode, | ||
message: errorMsg, | ||
time: new Date().toISOString() | ||
}); | ||
}; | ||
// MPH | ||
if (inputFormat == 'mph') { | ||
if (outputFormat == 'knots') return value / this.RATIOS.KNOTS_IN_MPH; | ||
if (outputFormat == 'ms') return value / this.RATIOS.MS_IN_MPH; | ||
if (outputFormat == 'kph') return value / this.RATIOS.KPH_IN_MPH; | ||
} | ||
NMEA0183.prototype.magneticVariaton = function(degrees, pole) { | ||
// MS | ||
if (inputFormat == 'ms') { | ||
if (outputFormat == 'knots') return value / this.RATIOS.KNOTS_IN_MS; | ||
if (outputFormat == 'mph') return value / this.RATIOS.MPH_IN_MS; | ||
if (outputFormat == 'kph') return value / this.RATIOS.KPH_IN_MS; | ||
} | ||
// DEGREES | ||
if (inputFormat == 'deg') { | ||
if (outputFormat == 'rad') return Math.radians(value) | ||
} | ||
// Celsius | ||
if (inputFormat == 'c') { | ||
if (outputFormat == 'k') return value + 273.15 | ||
} | ||
// Just return input if input/output formats are not recognised. | ||
return value; | ||
}; | ||
NMEA0183.prototype.reportError = function(errorCode, errorMsg) { | ||
this.exceptions.push({ | ||
source: this.name, | ||
code: errorCode, | ||
message: errorMsg, | ||
time: new Date().toISOString() | ||
}); | ||
}; | ||
NMEA0183.prototype.magneticVariaton = function(degrees, pole) { | ||
pole = pole.toUpperCase(); | ||
degrees = this.float(degrees); | ||
degrees = this.float(degrees); | ||
if(pole == "S" || pole == "W") { | ||
degrees *= -1; | ||
} | ||
if (pole == "S" || pole == "W") { | ||
degrees *= -1; | ||
} | ||
return degrees; | ||
} | ||
return degrees; | ||
} | ||
NMEA0183.prototype.timestamp = function(time, date) { | ||
/* TIME (UTC) */ | ||
if(time) { | ||
var hours, minutes, seconds; | ||
hours = this.int(time.slice(0, 2), true); | ||
minutes = this.int(time.slice(2, 4), true); | ||
seconds = this.int(time.slice(-2), true); | ||
} else { | ||
var dt, hours, minutes, seconds; | ||
dt = new Date(); | ||
hours = dt.getUTCHours(); | ||
minutes = dt.getUTCMinutes(); | ||
seconds = dt.getUTCSeconds(); | ||
} | ||
NMEA0183.prototype.timestamp = function(time, date) { | ||
/* TIME (UTC) */ | ||
if (time) { | ||
var hours, minutes, seconds; | ||
hours = this.int(time.slice(0, 2), true); | ||
minutes = this.int(time.slice(2, 4), true); | ||
seconds = this.int(time.slice(-2), true); | ||
} else { | ||
var dt, hours, minutes, seconds; | ||
dt = new Date(); | ||
hours = dt.getUTCHours(); | ||
minutes = dt.getUTCMinutes(); | ||
seconds = dt.getUTCSeconds(); | ||
} | ||
/* DATE (UTC) */ | ||
if(date) { | ||
var year, month, day; | ||
day = this.int(date.slice(0, 2), true); | ||
month = this.int(date.slice(2, 4), true); | ||
year = this.int(date.slice(-2)); | ||
/* DATE (UTC) */ | ||
if (date) { | ||
var year, month, day; | ||
day = this.int(date.slice(0, 2), true); | ||
month = this.int(date.slice(2, 4), true); | ||
year = this.int(date.slice(-2)); | ||
// HACK copied from jamesp/node-nmea | ||
if(year < 73) { | ||
year = this.int("20" + year); | ||
} else { | ||
year = this.int("19" + year); | ||
} | ||
} else { | ||
var dt, year, month, day; | ||
// HACK copied from jamesp/node-nmea | ||
if (year < 73) { | ||
year = this.int("20" + year); | ||
} else { | ||
year = this.int("19" + year); | ||
} | ||
} else { | ||
var dt, year, month, day; | ||
dt = new Date(); | ||
year = dt.getUTCFullYear(); | ||
month = dt.getUTCMonth() + 1; | ||
day = dt.getUTCDate(); | ||
} | ||
dt = new Date(); | ||
year = dt.getUTCFullYear(); | ||
month = dt.getUTCMonth(); | ||
day = dt.getUTCDate(); | ||
} | ||
/* construct */ | ||
var d = new Date(Date.UTC(year, month, day, hours, minutes, seconds)); | ||
return d.toISOString(); | ||
} | ||
/* construct */ | ||
var d = new Date(Date.UTC(year, month, day, hours, minutes, seconds)); | ||
return d.toISOString(); | ||
} | ||
NMEA0183.prototype.coordinate = function(value, pole) { | ||
// N 5222.3277 should be read as 52°22.3277' | ||
// E 454.5824 should be read as 4°54.5824' | ||
// | ||
// 1. split at . | ||
// 2. last two characters of split[0] (.slice(-2)) + everything after . (split[1]) are the minutes | ||
// 3. degrees: split[0][a] | ||
// 4. minutes: split[0][b] + '.' + split[1] | ||
// | ||
// 52°22'19.662'' N -> 52.372128333 | ||
// 4°54'34.944'' E -> 4.909706667 | ||
// S & W should be negative. | ||
NMEA0183.prototype.coordinate = function(value, pole) { | ||
// N 5222.3277 should be read as 52°22.3277' | ||
// E 454.5824 should be read as 4°54.5824' | ||
// | ||
// 1. split at . | ||
// 2. last two characters of split[0] (.slice(-2)) + everything after . (split[1]) are the minutes | ||
// 3. degrees: split[0][a] | ||
// 4. minutes: split[0][b] + '.' + split[1] | ||
// | ||
// 52°22'19.662'' N -> 52.372128333 | ||
// 4°54'34.944'' E -> 4.909706667 | ||
// S & W should be negative. | ||
pole = pole.toUpperCase(); | ||
pole = pole.toUpperCase(); | ||
var split = value.split('.'); | ||
var degrees = this.float(split[0].slice(0, -2)); | ||
var minsec = this.float(split[0].slice(-2) + '.' + split[1]); | ||
var decimal = this.float(degrees + (minsec / 60)); | ||
var split = value.split('.'); | ||
var degrees = this.float(split[0].slice(0, -2)); | ||
var minsec = this.float(split[0].slice(-2) + '.' + split[1]); | ||
var decimal = this.float(degrees + (minsec / 60)); | ||
if (pole == "S" || pole == "W") { | ||
decimal *= -1; | ||
} | ||
return this.float(decimal); | ||
} | ||
if (pole == "S" || pole == "W") { | ||
decimal *= -1; | ||
} | ||
NMEA0183.prototype.zero = function(n) { | ||
if(this.float(n) < 10) { | ||
return "0" + n; | ||
} else { | ||
return "" + n; | ||
} | ||
} | ||
return this.float(decimal); | ||
} | ||
NMEA0183.prototype.int = function(n) { | ||
if(("" + n).trim() === '') { | ||
return 0; | ||
} else { | ||
return parseInt(n, 10); | ||
} | ||
} | ||
NMEA0183.prototype.zero = function(n) { | ||
if (this.float(n) < 10) { | ||
return "0" + n; | ||
} else { | ||
return "" + n; | ||
} | ||
} | ||
NMEA0183.prototype.float = function(n) { | ||
if(("" + n).trim() === '') { | ||
return 0.0; | ||
} else { | ||
return parseFloat(n); | ||
} | ||
} | ||
NMEA0183.prototype.int = function(n) { | ||
if (("" + n).trim() === '') { | ||
return 0; | ||
} else { | ||
return parseInt(n, 10); | ||
} | ||
} | ||
NMEA0183.prototype.decode = function() { | ||
return this._decoder.apply(this, Array.prototype.slice.call(arguments)); | ||
} | ||
NMEA0183.prototype.float = function(n) { | ||
if (("" + n).trim() === '') { | ||
return 0.0; | ||
} else { | ||
return parseFloat(n); | ||
} | ||
} | ||
module.exports = NMEA0183; | ||
NMEA0183.prototype.decode = function() { | ||
return this._decoder.apply(this, Array.prototype.slice.call(arguments)); | ||
} | ||
}(); | ||
module.exports = NMEA0183; | ||
}(); |
{ | ||
"name": "nmea0183-signalk", | ||
"version": "0.2.3", | ||
"description": "NMEA0183 to Signal K parser", | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "node ./index", | ||
"test": "mocha" | ||
}, | ||
"bin": { | ||
"nmea0183-signalk": "./bin/nmea0183-signalk", | ||
"nmea2signalk": "./bin/nmea0183-signalk" | ||
}, | ||
"dependencies": { | ||
"debug": "^2.1.0", | ||
"ggencoder": "^0.1.5", | ||
"lodash": "~2.4.1", | ||
"mocha": "^2.1.0", | ||
"node-uuid": "^1.4.1", | ||
"signalk-multiplexer": "^0.1.7" | ||
}, | ||
"devDependencies": { | ||
"signalk-schema": "SignalK/specification", | ||
"mocha": "^2.1.0" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/signalk/nmea0183-signalk" | ||
}, | ||
"keywords": [ | ||
"nmea", | ||
"nmea0183", | ||
"signalk", | ||
"signal", | ||
"k", | ||
"parser" | ||
], | ||
"author": "Fabian Tollenaar <fabian@starting-point.nl>", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/signalk/nmea0183-signalk/issues" | ||
}, | ||
"homepage": "https://github.com/signalk/nmea0183-signalk" | ||
} | ||
"name": "nmea0183-signalk", | ||
"version": "0.3.0", | ||
"description": "NMEA0183 to Signal K parser", | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "node ./index", | ||
"test": "mocha" | ||
}, | ||
"bin": { | ||
"nmea0183-signalk": "./bin/nmea0183-signalk", | ||
"nmea2signalk": "./bin/nmea0183-signalk" | ||
}, | ||
"dependencies": { | ||
"ggencoder": "^0.1.5", | ||
"lodash": "^4.0.0", | ||
"signalk-multiplexer": "git://github.com/SignalK/signalk-multiplexer-node.git", | ||
"uuid": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.4.1", | ||
"chai-things": "^0.2.0", | ||
"debug": "^2.2.0", | ||
"mocha": "^2.3.4", | ||
"signalk-schema": "0.0.1-0" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/signalk/nmea0183-signalk" | ||
}, | ||
"keywords": [ | ||
"nmea", | ||
"nmea0183", | ||
"signalk", | ||
"signal", | ||
"k", | ||
"parser" | ||
], | ||
"author": "Fabian Tollenaar <fabian@starting-point.nl>", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/signalk/nmea0183-signalk/issues" | ||
}, | ||
"homepage": "https://github.com/signalk/nmea0183-signalk" | ||
} |
106
README.md
@@ -1,54 +0,78 @@ | ||
signalk-parser-nmea0183 | ||
======================= | ||
# signalk-parser-nmea0183 | ||
[](https://travis-ci.org/SignalK/signalk-parser-nmea0183) | ||
A parser written in node.js to parse NMEA 0183 sentences into Signal K. | ||
A Node.js [stream.Transform](https://nodejs.org/api/stream.html#stream_class_stream_transform) | ||
which converts [NMEA 0183](http://www.nmea.org/content/nmea_standards/nmea_0183_v_410.asp) | ||
sentences into Signal K delta messages. | ||
SUPPORTED NMEA SENTENCES | ||
------------------------ | ||
- APB | ||
- DBT | ||
- GGA | ||
- GLL | ||
- MWV | ||
- RMC | ||
- VDM | ||
- VDO | ||
- VTG | ||
## Supported Sentences | ||
The following is the list of sentences the parser supports. Pull requests welcome! | ||
- [APB - Autopilot Sentence "B"](http://www.catb.org/gpsd/NMEA.html#_apb_autopilot_sentence_b) | ||
- [DBT - Depth Below Transducer](http://www.catb.org/gpsd/NMEA.html#_dbt_depth_below_transducer) | ||
- [DSC - Digital Selective Calling Class-D Radios](http://continuouswave.com/whaler/reference/DSC_Datagrams.html) | ||
- [GGA - Global Positioning System Fix Data](http://www.catb.org/gpsd/NMEA.html#_gga_global_positioning_system_fix_data) | ||
- [GLL - Geographic Position - Latitude/Longitude](http://www.catb.org/gpsd/NMEA.html#_gll_geographic_position_latitude_longitude) | ||
- [HDM - Heading - Magnetic](http://www.catb.org/gpsd/NMEA.html#_hdm_heading_magnetic) | ||
- [HDT - Heading - True](http://www.catb.org/gpsd/NMEA.html#_hdt_heading_true) | ||
- [MTW - Mean Temperature of Water](http://catb.org/gpsd/NMEA.html#_mtw_mean_temperature_of_water) | ||
- [MWV - Wind Speed and Angle](http://www.catb.org/gpsd/NMEA.html#_mwv_wind_speed_and_angle) | ||
- [RMC - Recommended Minimum Navigation Information](http://www.catb.org/gpsd/NMEA.html#_rmc_recommended_minimum_navigation_information) | ||
- [ROT - Rate of Turn](http://www.catb.org/gpsd/NMEA.html#_rot_rate_of_turn) | ||
- [RPM - Revolutions](http://www.catb.org/gpsd/NMEA.html#_rpm_revolutions) | ||
- [VDM - AIS Other Vessel Data](http://catb.org/gpsd/AIVDM.html) | ||
- [VDO - AIS Own Vessel Data](http://catb.org/gpsd/AIVDM.html) | ||
- [VDR - Set and Drift](http://www.catb.org/gpsd/NMEA.html#_vdr_set_and_drift) | ||
- [VHW - Water Speed and Heading](http://www.catb.org/gpsd/NMEA.html#_vhw_water_speed_and_heading) | ||
- [VPW - Speed - Measured Parallel to Wind](http://www.catb.org/gpsd/NMEA.html#_vpw_speed_measured_parallel_to_wind) | ||
- [VTG - Track Made Good and Ground Speed](http://www.catb.org/gpsd/NMEA.html#_vtg_track_made_good_and_ground_speed) | ||
- [VWR - Relative Wind Speed and Angle](http://www.catb.org/gpsd/NMEA.html#_vwr_relative_wind_speed_and_angle) | ||
TODO (SENTENCES) | ||
---------------- | ||
- GSA* | ||
- GBS* | ||
- GSV* | ||
- BWC* (Started work, but needs an update) | ||
- ZDA | ||
- AAM | ||
- BOD | ||
- RMB | ||
- XTE | ||
- DPT | ||
- MTW | ||
- VLW | ||
- VHW | ||
- HDG | ||
- MWD | ||
- XDR | ||
## Installation and Use | ||
``` | ||
$ git clone https://github.com/SignalK/signalk-parser-nmea0183.git | ||
$ cd signalk-parser-nmea0183 | ||
$ npm install | ||
$ echo '$IIDBT,035.53,f,010.83,M,005.85,F*23' | ./bin/nmea0183-signalk | ||
``` | ||
Acknowledgements | ||
--------------- | ||
Should return something like this: | ||
Special thanks to my fellow collaborators on [Signal K](http://signalk.github.io). | ||
``` | ||
{ | ||
"self": "D344B1D0", | ||
"version": "1", | ||
"vessels": { | ||
"D344B1D0": { | ||
"uuid": "D344B1D0", | ||
"environment": { | ||
"depth": { | ||
"belowTransducer": { | ||
"value": 10.83, | ||
"source": { | ||
"type": "NMEA0183", | ||
"sentence": "DBT", | ||
"label": "signalk/signalk-parser-nmea0183", | ||
"talker": "II" | ||
}, | ||
"timestamp": "2016-04-15T17:56:52.000Z" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
Thanks to [@jamesp](https://github.com/jamesp) for his work on node-nmea (his codecs provided a starting point for the codecs included in this Parser). | ||
You can also pipe a file into the parser CLI: | ||
``` | ||
$ cat some-nmea-file.log | ./bin/nmea0183-signalk | ||
``` | ||
## Use as a Node Module | ||
See https://github.com/SignalK/signalk-server-node/blob/master/providers/nmea0183-signalk.js for an | ||
example in a Node application. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable and can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
3890329
4
57
0
2665
79
1
5
1
2
+ Addeduuid@^3.0.0
+ Addedlodash@4.17.21(transitive)
+ Addeduuid@3.4.0(transitive)
- Removeddebug@^2.1.0
- Removedmocha@^2.1.0
- Removednode-uuid@^1.4.1
- Removedcommander@0.6.12.3.0(transitive)
- Removeddebug@2.2.02.6.9(transitive)
- Removeddiff@1.4.0(transitive)
- Removedescape-string-regexp@1.0.2(transitive)
- Removedglob@3.2.11(transitive)
- Removedgrowl@1.9.2(transitive)
- Removedinherits@2.0.4(transitive)
- Removedjade@0.26.3(transitive)
- Removedlodash@2.4.2(transitive)
- Removedlru-cache@2.7.3(transitive)
- Removedminimatch@0.3.0(transitive)
- Removedminimist@0.0.8(transitive)
- Removedmkdirp@0.3.00.5.1(transitive)
- Removedmocha@2.5.3(transitive)
- Removedms@0.7.12.0.0(transitive)
- Removednode-uuid@1.4.8(transitive)
- Removedsigmund@1.0.1(transitive)
- Removedsignalk-multiplexer@0.1.10(transitive)
- Removedsupports-color@1.2.0(transitive)
- Removedto-iso-string@0.0.2(transitive)
Updatedlodash@^4.0.0
Updatedsignalk-multiplexer@git://github.com/SignalK/signalk-multiplexer-node.git