@signalk/nmea0183-signalk
Version:
A node.js/javascript parser for NMEA0183 sentences. Sentences are parsed to Signal K format.
269 lines (249 loc) • 7.29 kB
JavaScript
/**
* Copyright 2016 Signal K and Fabian Tollenaar <fabian@signalk.org>.
* Based on the work by Philip J Freeman
*
* 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.
*/
const debug = require('debug')('signalk-parser-nmea0183/DSC')
const utils = require('@signalk/nmea0183-utilities')
var delta = {}
function isEmpty(mixed) {
return (
(typeof mixed !== 'string' && typeof mixed !== 'number') ||
(typeof mixed === 'string' && mixed.trim() === '')
)
}
function parsePosition(line) {
/*
* Position Format:
*
* / Quadrant Id ("0" = NE, "1" = NW, "2" = SE, and "3" = SW
* |
* |/ Degrees Latitude
* ||
* || / Minutes Latitude
* || |
* || | / Degrees Longitude
* || | |
* || | | / Minutes Longitude
* || | | |
* 1YYyyXXXxx
*/
var lat = parseFloat(line.substring(1, 3))
var lat_min = parseFloat(line.substring(3, 5))
var lat_dec = lat + lat_min / 60
var lon = parseFloat(line.substring(5, 8))
var lon_min = parseFloat(line.substring(8, 10))
var lon_dec = lon + lon_min / 60
var quadrant = parseInt(line.substring(0, 1))
if (quadrant == 1 || quadrant == 3) {
lon_dec = lon_dec * -1
}
if (quadrant == 2 || quadrant == 3) {
lat_dec = lat_dec * -1
}
debug('lat: ' + lat_dec + ' ,lon: ' + lon_dec)
return { longitude: lon_dec, latitude: lat_dec }
}
module.exports = function (input) {
const { id, sentence, parts, tags } = input
var values = []
const empty = parts.reduce((e, val) => {
if (isEmpty(val)) {
++e
}
return e
}, 0)
if (empty > 3) {
return null
}
// for some reason, it seems the sender identification is mmsi+'0', so we
// strip the trailing zero to get a 9 digit mmsi
var mmsi = parts[1].substring(0, 9)
debug('mmsi: ' + mmsi)
var handled = false
var get_position = false
var distress = false
var distress_nature = ''
switch (parts[2]) {
case '00': // routine category
switch (parts[3]) {
case '21': // ship position
handled = true
get_position = true
break
//case '??': // other telecommands
}
break
case '08': // * 108 = safety
break
case '10': // * 110 = urgency
break
case '12': // * 112 = distress
handled = true
get_position = true
distress = true
switch (
parts[3] // Nature of Distress
) {
case '00': // = Fire, explosion
distress_nature = 'fire'
break
case '01': // = Flooding
distress_nature = 'flooding'
break
case '02': // = Collision
distress_nature = 'collision'
break
case '03': // = Grounding
distress_nature = 'grounding'
break
case '04': // = Listing, in danger of capsize
distress_nature = 'listing'
break
case '05': // = Sinking
distress_nature = 'sinking'
break
case '06': // = Disabled and adrift
distress_nature = 'adrift'
break
case '07': // = Undesignated distres
distress_nature = 'undesignated'
break
case '08': // = Abandoning ship
distress_nature = 'abandon'
break
case '09': // = Piracy/armed robbery attack
distress_nature = 'piracy'
break
case '10': // = Man overboard
distress_nature = 'mob'
break
case '12': // = EPRIB emission
distress_nature = 'epirb'
break
default:
// unassigned symbol; take no action
distress_nature = 'unassigned'
}
}
/*values.push({
path: "",
value: {
mmsi: parts[1]
}
})*/
if (get_position) {
var position = parsePosition(parts[5])
values.push({
path: 'navigation.position',
value: {
latitude: position.latitude,
longitude: position.longitude,
},
})
}
if (distress) {
values.push({
path: 'notifications.' + distress_nature,
value: {
message:
'DSC Distress Recieved! Nature of distress: ' + distress_nature,
},
})
}
if (!handled) {
console.log('DSC Message Not Handled: ' + line)
values.push({
path: 'notifications.dsc_parser',
value: {
message: 'DSC Message Not Handled: ' + line,
},
})
}
if (values.length > 0) {
//multiplexer.self();
delta = {
updates: [
{
source: tags.source, //this.source(input.instrument),
timestamp: tags.timestamp,
values: values,
},
],
context: 'vessels.urn:mrn:imo:mmsi:' + mmsi,
}
}
return delta
}
/*
* DSC Codec - Some DSC Capable VHF Radios output DSC Sentences
*
* This codec currently contains basic support for distress messages and
* position messages.
*
* NOTE: The position in the DSC sentence is only accurate to the minute,
* however, there is an extended sentence that provides further detail. The
* DSE Sentence (which can follow the DSC sentence) contains further position
* detail.
*
*
* Documentation for DSC Sentences:
*
* * http://continuouswave.com/whaler/reference/DSC_Datagrams.html
*
* Distress Alert Example:
* $CDDSC,12,3380400790,12,06,00,1423108312,2019,,,S,E*6A
* $CDDSE,1,1,A,3380400790,00,45894494*1B
*
* Distress Cancelation (unsupported):
* $CDDSC,12,3381581370,12,06,00,1423108312,0236,3381581370,,S,*20
*
* Example of Non-Distress Call:
* $CDDSC,20,3381581370,00,21,26,1423108312,1902,,,B,E*7B
*
*
*
* 0 1 2 3 4 5 6 9 10
* | | | | | | | | |
* $--DSC,XX,XXXXXXXXXX,XX,XX,XX,XXXXXXXXXX,XXXX,,,A,C*hh<CR><LF>
*
* Field Number:
* 0. Format Specifier (without first digit)
* 102 = selective call to a group of ships in particular geographic area
* 112 = distress alert call
* 114 = selective call to a group of ships having common interest
* 116 = all ships call
* 120 = selective call to particular individual station
* 123 = selective call to a particular individual using automatic service
*
* 1. Sender MMSI
* 2. Category Element (without first digit)
* 100 = Routine
* 108 = Safety
* 110 = Urgency
* 112 = Distress
*
* 3. variable based on Category
* 4. variable based on category
* 5. Sender Position
* 6. time in UTC
* 7. address of vessel in distress (if different than sending vessel?)
* 8. Unknown
* 9. Unknown (It may be a representation of a service command)
* 10. Expansion message follows
* E = true
* ' '= false
*
*/