UNPKG

ggencoder

Version:

GEOGATE-geojson handles AIS/NMEA encoding/decoding for GEOgate GPS/AIS/GTS framework

528 lines (498 loc) 20.9 kB
/* ** Copyright 2014 Fulup Ar Foll. * * 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. * * References: * Gpsd : http://catb.org/gpsd/AIVDM.html [best doc] * OpenCPN: https://github.com/OpenCPN/OpenCPN [file: AIS_Bitstring.cpp] * http://fossies.org/linux/misc/gpsd-3.11.tar.gz/gpsd-3.11/test/sample.aivdm * online AIS decoder http://www.maritec.co.za/aisvdmvdodecoding/ */ /* * Basic Test for Encoding/Decoding of AIS packet */ 'use strict'; var AisEncode= require ('../ApiExport').AisEncode; var AisDecode= require ('../ApiExport').AisDecode; var fs = require('fs'); function AisEncodeDecodeTest (args) { if (args !== undefined) this.testSet = args; else this.testSet = { msg24a: {// class B static info aistype : 24, part : 0, nmea : "!AIVDM,1,1,,A,H42O55i18tMET00000000000000,0*6F", cargo : 60, callsign : "AB1234", mmsi : "271041815", shipname : "PROGUY" } ,msg24b: {// class AB static info aistype : 24, part : 1, nmea : "!AIVDM,1,1,,A,H42O55lt0000000D3nink000?0500,0*70", mmsi : "271041815", cargo : 60, callsign : "TC6163", dimA : 0, dimB : 15, dimC : 0, dimD : 5 } ,msg18: { // standard class B Position report aistype : 18, nmea : '!AIVDM,1,1,,A,B69>7mh0?B<:>05B0`0e8TN000000,0*72', cog : 72.2, sog : 6.1, dsc : false, repeat : false, accuracy : true, lon : 122.47338666666667, lat : 36.91968, second : 50, mmsi : "412321751" } ,msg19: { // Extended class B Position report aistype : 19, nmea : ['!AIVDM,2,1,9,B,C43NbT0008VGWDVHNs0000N10PHb`NL00000,0*6D', '!AIVDM,2,2,9,B,00000000N0`90RPP,0*59'], mmsi : "272083600", cog : 0, sog : 0, lon : 33.527321666666666, lat : 44.61725333333333, second : 60, shipname : "PLUTON" } ,msg5: { // class A static info aistype : 5, nmea : "!AIVDM,1,1,,A,55?MbV42;H;s<HtKR20EHE:0@T4@Dn2222222216L961O0000i000000000000000000000,0*2D", //"!AIVDM,2,2,1,A,88888888880,2*25"], // [extentions for destination not implemented] mmsi : "351759000", imo : 9134270, callsign : "3FOF8", shipname : "EVER DIADEM", destination: "", cargo : 70, dimA : 225, dimB : 70, dimC : 1, dimD : 31, fixaistype : 1, etamn : 0, etaho : 0, etaday : 0, etamonth : 0, draught : 19.6 } ,msg5_2: { // class A static info aistype : 5, nmea : ["!AIVDM,2,1,3,B,59NWwC@2>6th7Q`7800858l8Dd00000000000018Cp:A:6a=0G@TQCADR0EQ,0*09", "!AIVDM,2,2,3,B,CP000000000,2*37"], mmsi : "235074703", imo : 12894435639, callsign : "A8ZA2", shipname : "BARMBEK", destination: "BREMERHAVEN", cargo : 72, dimA : 159, dimB : 10, dimC : 17, dimD : 10, fixaistype : 1, etamn : 0, etaho : 13, etaday : 18, etamonth : 10, draught : 9.3 } ,msg5_3: { // class A static info version 2 aistype : 5, nmea : ["!AIVDM,2,1,9,A,53Moi:81Qk8LLpQH000PD98T@D4r118Tp<E=<0153@f594ke07TSm21D,0*63", "!AIVDM,2,2,9,A,hF@000000000000,2*73"], mmsi : "235074703", imo : 6409351, callsign : "GNHV", shipname : "HEBRIDEAN PRINCESS", destination: "ROTHESAY", cargo : 69, dimA : 26, dimB : 46, dimC : 5, dimD : 9, fixaistype : 1, etamn : 0, etaho : 13, etaday : 7, etamonth : 3, draught : 3 } ,msg4: { // base station aistype : 4, nmea : "!AIVDM,1,1,,B,4@4k1EQutd87k:Etkmb:JM7P08Na,0*38", mmsi : "005030230", lon : 144.60521666666668, lat : -38.16343333333333 } ,msg21: { // aid of navigation aistype : 21, nmea : "!AIVDM,1,1,,B,ENlt;J@aSqP0000000000000000E;WUdm7Mu800003vP10,4*46", mmsi : "995036009", shipname : "SG3", aidtype : 1, lon : 144.88636666666667, lat : -38.03993166666667, txt : "", virtual : 1, offpos : 0 } ,msg21a: { // aid of navigation with extra text aistype : 21, nmea : "!AIVDM,1,1,,B,EvjO`>C2qHtq@8:W:0h9PW@1Pb0Paq`g;STu`10888N00313p12H31@hi@,4*0E", mmsi : "992471097", shipname : "E2192 PUNTA SAN CATA", aidtype : 6, lon : 18.306638333333332, lat : 40.390795, txt : "LDO DI LECCE", virtual : 0, offpos : 0 } ,msg9: { // sar aircraft aistype : 9, nmea : "!AIVDM,1,1,,B,900048wwTiJamA6Eu>B7Pd@20<6M,0*66", mmsi : "000001059", lon : -74.747675, lat : 38.37196, alt : 4094, sog : 305, cog : 192.2 } ,msg1: { aistype : 1, nmea : "!AIVDM,1,1,,A,133REv0P00P=K?TMDH6P0?vN289>,0*46", mmsi : "205035000", rot : -128, smi : 0, sog : 0, cog : 0, lon : 2.9328833333333333, lat : 51.23759 } ,msg1_1: { // sample with rot aistype : 1, nmea : "!AIVDM,1,1,,A,13u?etPv2;0n:dDPwUM1U1Cb069D,0*24", mmsi : "265547250", rot : -8, smi : 0, sog : 13.9, cog : 40.4, lon : 11.832976666666667, lat : 57.66035333333333 } ,msg1_2: { // position for mob aistype : 1, nmea : "!AIVDM,1,1,,B,1>O5`4wP01:F?39b6mD>4?w81P00,0*0D", mmsi : "972122131", lon : 144.66747333333333, lat : -38.2612, rot : -128, smi : 0, sog : 0.1, cog : 360, navstatus : 15 } ,msg14: { // text msg aistype : 14, nmea : "!AIVDM,1,1,,A,>>O5`4tlt:1@E=@,2*15", mmsi : "972122131", txt : "MOB TEST" } ,msg8_200_10: { // dac 200 fid 10 msg static inland ship aistype : 8, nmea : "!AIVDM,1,1,,A,85Mv070j2d>=<e<<=PQhhg`59P00,0*26", mmsi : "366968860", length : 27, width : 9.7, draught : 3.04, shiptypeERI: 8000 } ,msg8_001_11: { // dac 001 fid 11 meteorological and hydrographic data aistype : 800111, nmea : "!AIVDM,1,1,,A,802R5Ph0BkCwP0E<>jGaPPTHS7wwwwwwwk6wwwwwwwwwwwwwwwwwwtPwwwt,2*72", mmsi : "002655619", lon : 11.573166666666667, lat : 57.88800, avgwindspd : 2, winddir : 280, airtemp : undefined, watertemp : 13.1 } ,msg8_001_31: { // dac 001 fid 31 meteorological and hydrographic data aistype : 800131, nmea : "!AIVDM,1,1,1,B,8>h8nkP0Glr=<hFI0D6??wvlFR06EuOwgwl?wnSwe7wvlOw?sAwwnSGmwvh0,0*17", mmsi : "990000846", lon : 171.5985, lat : 12.2283, avgwindspd : undefined, winddir : undefined, airtemp : undefined, watertemp : undefined, waterlevel : undefined } ,msg8_001_31_2: { // dac 001 fid 31 meteorological and hydrographic data aistype : 800131, nmea : "!AIVDM,1,1,,A,8@2R5Ph0GhEUJiaWPFkt4RqUdf06EuFPB22p1Pd3S@h>:WwwsAwwnS@vwvwt,0*57", mmsi : "002655619", lon : 11.7881, lat : 57.6811, avgwindspd : 36, winddir : 203, airtemp : undefined, watertemp : 6.2, waterlevel : 0.47 } ,msg8_367_33_0: { // dac 367 fid 33 meteorological and hydrographic data location aistype : 836733, nmea : "!AIVDM,1,1,,A,8P3QiWAKp@dw8>5LlaB1aQkhCr@P,0*28", mmsi : "003699101", siteid : 3, lon : -122.954, lat : 46.106 } ,msg8_367_33_2: { // dac 367 fid 33 meteorological and hydrographic data wind aistype : 836733, nmea : "!AIVDM,1,1,,B,8>k1oCQKpBdvs:750l;7mre0<N00,0*4C", mmsi : "993032014", siteid : 50, avgwindspd : 7, winddir : 13 } ,msg27: { // position lon range aistype : 27, nmea : "!AIVDM,1,1,,B,K9TJi5H@o9jiPP2D,0*3E", mmsi : "642167061", lon : 23.531666666666666, lat : 37.86833333333333, sog : 0, cog : 37, navstatus : 1 } }} // compare input with decoded outputs AisEncodeDecodeTest.prototype.CheckResult = function (test, aisin, aisout, controls) { var slot; var count=0; console.log ("\nChecking: [%s] --> [%s]", test, aisin.nmea); for (var element in controls){ slot = controls[element]; if (aisout[slot] !== aisin[slot]) { count ++; console.log ("--> FX (%s) in:[%s] != out:[%s]", slot, aisin[slot], aisout [slot]); } else { console.log ("--> OK (%s) in:[%s] == out:[%s]", slot, aisin[slot], aisout [slot]); } } if (count > 0) console.log ("** FX Test [%s] Count=%d **", test, count); else console.log ("## OK Test [%s] ##", test); }; AisEncodeDecodeTest.prototype.CheckDecode = function () { // make sure we get expected output from reference messages for (var test in this.testSet) { var aisTest = this.testSet [test]; // Require a string or an array. Turn string into an array. Return for // anything else. if(aisTest.nmea instanceof Object) { var session={}; var aisDecoded = new AisDecode(aisTest.nmea[0], session); var aisDecoded = new AisDecode(aisTest.nmea[1], session); } else { var aisDecoded = new AisDecode(aisTest.nmea); } if (aisDecoded.valid !== true) { console.log ("\n[%s] invalid AIS payload: %s", test, aisDecoded.error); } else { switch (aisTest.aistype) { case 1: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'sog', 'cog', 'rot', 'smi']); break; case 4: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat']); break; case 5: this.CheckResult (test, aisTest, aisDecoded, ["shipname", 'callsign', 'destination', 'cargo', 'draught', 'dimA', 'dimB', "dimC", 'dimD']); break; case 9: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'alt', 'sog', 'cog']); break; case 14: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'txt']); break; case 18: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'cog', "sog"]); break; case 19: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'cog', "sog", 'shipname']); break; case 21: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'shipname', 'aidtype', 'lat', 'lon', 'txt', 'offpos', 'virtual']); break; case 24: switch (aisTest.part) { case 0: this.CheckResult(test, aisTest, aisDecoded, ["shipname"]); break; case 1: this.CheckResult(test, aisTest, aisDecoded, ['callsign', 'cargo', 'dimA', 'dimB', "dimC", 'dimD']); break; default: console.log ("hoop test=[%s] message type=[%d] invalid part number [%s]", test, aisTest.type, aisDecoded.part); } break; case 8: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'length', 'width', 'draught', 'shiptypeERI']); break; case 800111: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'avgwindspd', 'winddir', 'airtemp', 'watertemp']); break; case 800131: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'avgwindspd', 'winddir', 'airtemp', 'watertemp', 'waterlevel']); break; case 836733: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'siteid', 'lon', 'lat', 'avgwindspd', 'winddir']); break; case 27: this.CheckResult (test, aisTest, aisDecoded, ["mmsi", 'lon', 'lat', 'cog', "sog", 'navstatus']); break; default: console.log ("hoop test=[%s] message type=[%d] not implemented", test, aisTest.type); } } } }; AisEncodeDecodeTest.prototype.CheckEncode = function () { // make sure we get expected output from reference messages for (var test in this.testSet) { var aisIn = this.testSet [test]; if (aisIn.nmea.lenght === 1) { var aisOut = new AisEncode (aisIn); // Warning: this test only to a string comparison on old result from www.maritec.co.za if (aisOut.valid) { console.log("\nTEST=%s --> http://www.maritec.co.za/ais", test); console.log(" --in=%s", aisIn.nmea); console.log(" --ou=%s", aisOut.nmea); } else { console.log ("Ais Input message [%s] invalid", test); } var error=0; for (var i=0; i< aisIn.nmea.length; i++) { if (aisIn.nmea [i] !== aisOut.nmea [i]) { error=1; console.log (' ** idx=%d in:%s != out:%s', i, aisIn.nmea [i], aisOut.nmea [i]); } } if (error === 0 )console.log (" ## OK ##"); else console.log (" ** ERROR **"); } } }; // Require/Autoload of method AisEncode/Decode fail when run from AisEncode/Decode themself why ??? AisEncodeDecodeTest.prototype.SetAisDecode = function (Method2Test) {AisDecode= Method2Test;} AisEncodeDecodeTest.prototype.SetAisEncode = function (Method2Test) {AisEncode= Method2Test;} AisEncodeDecodeTest.prototype.CheckFile = function (filename) { var buffer = fs.readFileSync (filename, "utf-8"); var line = ""; var count=0; var session={} for (var idx=0; idx < buffer.length; idx++) { switch (buffer [idx]) { case '\n': // new line count ++; console.log ("line[%d]=%s", count, line); var ais= new AisDecode (line, session); switch (ais.aistype) { case 1: case 2: case 3: case 18: console.log (' -->msg-18 mmsi=%s Lon=%d Lat=%d Speed=%d Course=%d, NavStatus=%s/%s' , ais.mmsi, ais.lon, ais.lat, ais.sog, ais.cog, ais.navstatus, ais.GetNavStatus()); break; case 24: console.log (' -->msg-24 mmsi=%s shipname=%s callsign=%s cargo=%s/%s length=%d width=%d' , ais.mmsi,ais.shipname, ais.callsign, ais.cargo, ais.GetVesselType(), ais.length, ais.width); break; case 5: console.log (' -->msg-05 mmsi=%s shipname=%s callsign=%s cargo=%s/%s draught=%d length=%d width=%d' , ais.mmsi,ais.shipname, ais.callsign, ais.cargo, ais.GetVesselType(),ais.draught, ais.length, ais.width); break; case 14: console.log (' -->msg-14 mmsi=%s text=%s' , ais.mmsi,ais.txt); break; case 4: case 11: console.log (' -->msg-%d mmsi=%s Lon=%d Lat=%d' , ais.aistype, ais.mmsi, ais.lon, ais.lat ); break; case 9: console.log (' -->msg-09 mmsi=%s Lon=%d Lat=%d Alt=%d' , ais.mmsi, ais.lon, ais.lat, ais.alt ); break; case 19: console.log (' -->msg-19 mmsi=%s Lon=%d Lat=%d Speed=%d Course=%d Name=%s' , ais.mmsi, ais.lon, ais.lat, ais.sog, ais.cog, ais.shipname ); break; case 21: console.log (' -->msg-21 mmsi=%s Lon=%d Lat=%d Name=%s' , ais.mmsi, ais.lon, ais.lat, ais.shipname ); break; case 8: if (ais.dac === 200 && ais.fid === 10) { console.log (' -->msg-08 mmsi=%s length=%d width=%d draught=%d shiptype=%s/%s' , ais.mmsi, ais.length, ais.width, ais.draught, ais.shiptypeERI, ais.GetERIShiptype(ais.shiptypeERI) ); } else if ((ais.dac === 1 && ais.fid === 11) || (ais.dac === 1 && ais.fid === 31) || (ais.dac === 367 && ais.fid === 33)) { //console.log (' -->msg-08 mmsi=%s dac=%d fid=%d meteo winddir=%d avgwindspd=%d airtemp=%d watertemp=%d' // , ais.mmsi, ais.dac, ais.fid, ais.winddir, ais.avgwindspd, ais.airtemp, ais.watertemp ); ais.bitarray = undefined; ais.payload = undefined; console.log (' -->msg-meteo ' + JSON.stringify(ais) ); } else { console.log (' -->msg-08 mmsi=%s dac=%d fid=%d shiptype=%s/%s' , ais.mmsi, ais.dac, ais.fid, ais.width,ais.shiptypeERI, ais.GetERIShiptype(ais.shiptypeERI) ); } break; case 27: console.log (' -->msg-27 mmsi=%s Lon=%d Lat=%d Speed=%d Course=%d state=%d/%s' , ais.mmsi, ais.lon, ais.lat, ais.sog, ais.cog, ais.navstatus, ais.GetNavStatus() ); break; default: console.log (" ### hoop Testing msg-%d ==> [%s] not implemented", ais.aistype, ais.Getaistype()); } line=''; break; case '\r': break; default: line += buffer [idx]; } } }; // if started as a main and not as module, then process test. if (process.argv[1] === __filename && process.argv.length === 3 ) { var test= new AisEncodeDecodeTest (); test.CheckFile (process.argv[2]); } else if (process.argv[1] === __filename) { var test= new AisEncodeDecodeTest (); test.CheckDecode(); test.CheckEncode(); //test.CheckFile ('FeedSample/AisHubSample.nmea'); } module.exports = AisEncodeDecodeTest; // http://openmymind.net/2012/2/3/Node-Require-and-Exports/