signalk-vessels-to-ais 1.1.4 → 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/index.js +193 -93
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -9,6 +9,7 @@ User can configure:
9
9
  - Own data can be added to AIS sending
10
10
 
11
11
  New:
12
+ - v1.1.5, updated vessels within selected timeframe are sent out, radius filtering around own vessel and tag-block option added
12
13
  - v1.1.4, small fix
13
14
  - v1.1.3, add: own vessel data and sending interval modified
14
15
  - v1.1.2, fix: http/https url selection and better error info
package/index.js CHANGED
@@ -22,8 +22,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  SOFTWARE.
23
23
  */
24
24
 
25
- const fetch = require('node-fetch');
25
+ const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
26
26
  const AisEncode = require('ggencoder').AisEncode;
27
+ const moment = require('moment');
28
+ const haversine = require('haversine-distance');
27
29
 
28
30
  module.exports = function createPlugin(app) {
29
31
  const plugin = {};
@@ -32,16 +34,25 @@ module.exports = function createPlugin(app) {
32
34
  plugin.description = 'SignalK server plugin to convert other vessel data to NMEA0183 AIS format and forward it out to 3rd party applications';
33
35
 
34
36
  let positionUpdate = null;
37
+ let distance;
35
38
  let sendOwn;
36
39
  let url;
37
- let intervalStart;
38
40
  let intervalRun;
39
41
  let readInfo;
40
42
  const setStatus = app.setPluginStatus || app.setProviderStatus;
41
43
 
44
+ let position_update;
45
+ let useTag;
46
+
42
47
  plugin.start = function (options, restartPlugin) {
48
+ position_update = options.position_update * 60;
49
+ useTag = options.useTag;
50
+
51
+ let port = options.port || 3000;
52
+ let portSec = options.portSec || 3443;
53
+
43
54
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
44
- url = 'https://localhost:3443/signalk/v1/api/vessels';
55
+ url = 'https://localhost:' + portSec + '/signalk/v1/api/vessels';
45
56
  fetch(url, { method: 'GET' })
46
57
  .then((res) => {
47
58
  console.log(`${plugin.id}: SSL enabled, using https`);
@@ -51,7 +62,7 @@ module.exports = function createPlugin(app) {
51
62
  }
52
63
  })
53
64
  .catch(() => {
54
- url = 'http://localhost:3000/signalk/v1/api/vessels';
65
+ url = 'http://localhost:' + port + '/signalk/v1/api/vessels';
55
66
  fetch(url, { method: 'GET' })
56
67
  .then((res) => {
57
68
  console.log(`${plugin.id}: SSL disabled, using http`);
@@ -65,16 +76,11 @@ module.exports = function createPlugin(app) {
65
76
  });
66
77
 
67
78
  positionUpdate = options.position_update;
79
+ distance = options.distance;
68
80
  sendOwn = options.sendOwn;
69
81
 
70
82
  app.debug('Plugin started');
71
83
 
72
- function clear() {
73
- clearInterval(intervalStart);
74
- }
75
-
76
- intervalStart = setInterval(readInfo, (15000));
77
- setTimeout(clear, 15000);
78
84
  intervalRun = setInterval(readInfo, (positionUpdate * 60000));
79
85
  };
80
86
 
@@ -113,21 +119,65 @@ module.exports = function createPlugin(app) {
113
119
  //----------------------------------------------------------------------------
114
120
  // nmea out
115
121
 
116
- function aisOut(encMsg) {
122
+ function aisOut(encMsg, aisTime) {
117
123
  const enc = new AisEncode(encMsg);
118
124
  const sentence = enc.nmea;
125
+ let taggString = '';
126
+ if (useTag) {
127
+ taggString = createTagBlock(aisTime)
128
+ }
119
129
  if (sentence && sentence.length > 0) {
120
- app.debug(sentence);
121
- app.emit('nmea0183out', sentence);
130
+ app.debug(taggString+sentence);
131
+ app.emit('nmea0183out', taggString+sentence);
132
+ }
133
+ }
134
+
135
+ const m_hex = [
136
+ '0',
137
+ '1',
138
+ '2',
139
+ '3',
140
+ '4',
141
+ '5',
142
+ '6',
143
+ '7',
144
+ '8',
145
+ '9',
146
+ 'A',
147
+ 'B',
148
+ 'C',
149
+ 'D',
150
+ 'E',
151
+ 'F'
152
+ ]
153
+
154
+ function toHexString (v) {
155
+ let msn = (v >> 4) & 0x0f
156
+ let lsn = (v >> 0) & 0x0f
157
+ return m_hex[msn] + m_hex[lsn]
158
+ }
159
+
160
+ function createTagBlock (aisTime) {
161
+ let tagBlock = ''
162
+ tagBlock += 's:SK0001,'
163
+ //tagBlock += 'c:' + aisTime + ','
164
+ tagBlock += 'c:' + Date.now(aisTime) + ','
165
+ tagBlock = tagBlock.slice(0, - 1)
166
+ let tagBlockChecksum = 0
167
+ for (let i = 0; i < tagBlock.length; i++) {
168
+ tagBlockChecksum ^= tagBlock.charCodeAt(i)
122
169
  }
170
+ return `\\${tagBlock}*` + toHexString(tagBlockChecksum) + `\\`
123
171
  }
124
172
 
125
173
  //----------------------------------------------------------------------------
126
174
  // Read and parse AIS data
127
175
 
128
176
  readInfo = function readData(options) {
129
- let i, mmsi, shipName, lat, lon, sog, cog, rot, navStat, hdg, dst, callSign, imo, id, type;
177
+ let i, mmsi, aisTime, aisDelay, shipName, lat, lon, sog, cog, rot, navStat, hdg, dst, callSign, imo, id, type;
130
178
  let draftCur, length, beam, ais, encMsg3, encMsg5, encMsg18, encMsg240, encMsg241, own;
179
+ let ownLat = app.getSelfPath('navigation.position.value.latitude');
180
+ let ownLon = app.getSelfPath('navigation.position.value.longitude');
131
181
  fetch(url, { method: 'GET' })
132
182
  .then((res) => res.json())
133
183
  .then((json) => {
@@ -136,6 +186,22 @@ module.exports = function createPlugin(app) {
136
186
  for (i = 0; i < numberAIS; i++) {
137
187
  const jsonKey = Object.keys(jsonContent)[i];
138
188
 
189
+ try {
190
+ aisTime = jsonContent[jsonKey].sensors.ais.class.timestamp;
191
+ if ((parseFloat((moment(new Date(Date.now())).diff(aisTime)/1000).toFixed(3))) < position_update) {
192
+ aisDelay = true;
193
+ } else {
194
+ aisDelay = false;
195
+ }
196
+ } catch (error) {
197
+ if (i === 0) {
198
+ aisTime = new Date(Date.now()).toISOString();
199
+ aisDelay = true;
200
+ } else {
201
+ aisTime = null;
202
+ aisDelay = false;
203
+ }
204
+ }
139
205
  try {
140
206
  mmsi = jsonContent[jsonKey].mmsi;
141
207
  } catch (error) { mmsi = null; }
@@ -174,7 +240,7 @@ module.exports = function createPlugin(app) {
174
240
  }
175
241
  } catch (error) { callSign = ''; }
176
242
  try {
177
- imo = (jsonContent[jsonKey].registrations.imo).substring(4, 20);
243
+ imo = (jsonContent[jsonKey].registrations.value.imo).substring(4, 20);
178
244
  } catch (error) { imo = null; }
179
245
  try {
180
246
  id = jsonContent[jsonKey].design.aisShipType.value.id;
@@ -211,89 +277,103 @@ module.exports = function createPlugin(app) {
211
277
  if (i === 0) {
212
278
  own = true;
213
279
  if (sendOwn) {
214
- ais = 'B';
280
+ ais = 'A';
281
+ } else {
282
+ ais = '';
215
283
  }
216
284
  } else {
217
285
  own = false;
218
286
  }
219
287
 
220
- encMsg3 = {
221
- own,
222
- aistype: 3, // class A position report
223
- repeat: 0,
224
- mmsi,
225
- navstatus: navStat,
226
- sog,
227
- lon,
228
- lat,
229
- cog,
230
- hdg,
231
- rot,
232
- };
233
-
234
- encMsg5 = {
235
- own,
236
- aistype: 5, // class A static
237
- repeat: 0,
238
- mmsi,
239
- imo,
240
- cargo: id,
241
- callsign: callSign,
242
- shipname: shipName,
243
- draught: draftCur,
244
- destination: dst,
245
- dimA: 0,
246
- dimB: length,
247
- dimC: beam,
248
- dimD: beam,
249
- };
250
-
251
- encMsg18 = {
252
- own,
253
- aistype: 18, // class B position report
254
- repeat: 0,
255
- mmsi,
256
- sog,
257
- accuracy: 0,
258
- lon,
259
- lat,
260
- cog,
261
- hdg,
262
- };
263
-
264
- encMsg240 = {
265
- own,
266
- aistype: 24, // class B static
267
- repeat: 0,
268
- part: 0,
269
- mmsi,
270
- shipname: shipName,
271
- };
272
-
273
- encMsg241 = {
274
- own,
275
- aistype: 24, // class B static
276
- repeat: 0,
277
- part: 1,
278
- mmsi,
279
- cargo: id,
280
- callsign: callSign,
281
- dimA: 0,
282
- dimB: length,
283
- dimC: beam,
284
- dimD: beam,
285
- };
286
-
287
- if (ais === 'A') {
288
- app.debug(`class A ${i}`);
289
- aisOut(encMsg3);
290
- aisOut(encMsg5);
291
- }
292
- if (ais === 'B') {
293
- app.debug(`class B ${i}`);
294
- aisOut(encMsg18);
295
- aisOut(encMsg240);
296
- aisOut(encMsg241);
288
+ const a = { lat: ownLat, lon: ownLon }
289
+ const b = { lat: lat, lon: lon }
290
+ let dist = (haversine(a, b)/1000).toFixed(2);
291
+
292
+ if (dist <= distance) {
293
+
294
+ encMsg3 = {
295
+ own,
296
+ aistype: 3, // class A position report
297
+ repeat: 0,
298
+ mmsi,
299
+ navstatus: navStat,
300
+ sog,
301
+ lon,
302
+ lat,
303
+ cog,
304
+ hdg,
305
+ rot,
306
+ };
307
+
308
+ encMsg5 = {
309
+ own,
310
+ aistype: 5, // class A static
311
+ repeat: 0,
312
+ mmsi,
313
+ imo,
314
+ cargo: id,
315
+ callsign: callSign,
316
+ shipname: shipName,
317
+ draught: draftCur,
318
+ destination: dst,
319
+ dimA: 0,
320
+ dimB: length,
321
+ dimC: beam,
322
+ dimD: beam,
323
+ };
324
+
325
+ encMsg18 = {
326
+ own,
327
+ aistype: 18, // class B position report
328
+ repeat: 0,
329
+ mmsi,
330
+ sog,
331
+ accuracy: 0,
332
+ lon,
333
+ lat,
334
+ cog,
335
+ hdg,
336
+ };
337
+
338
+ encMsg240 = {
339
+ own,
340
+ aistype: 24, // class B static
341
+ repeat: 0,
342
+ part: 0,
343
+ mmsi,
344
+ shipname: shipName,
345
+ };
346
+
347
+ encMsg241 = {
348
+ own,
349
+ aistype: 24, // class B static
350
+ repeat: 0,
351
+ part: 1,
352
+ mmsi,
353
+ cargo: id,
354
+ callsign: callSign,
355
+ dimA: 0,
356
+ dimB: length,
357
+ dimC: beam,
358
+ dimD: beam,
359
+ };
360
+
361
+ if (aisDelay && (ais === 'A' || ais === 'B')) {
362
+ app.debug("Distance range: " + distance + "km, AIS target distance: " + dist + "km" + ", Class " + ais + " Vessel" + ", MMSI:" + mmsi)
363
+ if (ais === 'A') {
364
+ app.debug(`class A, ${i}, time: ${aisTime}`);
365
+ aisOut(encMsg3, aisTime);
366
+ aisOut(encMsg5, aisTime);
367
+ }
368
+ if (ais === 'B') {
369
+ app.debug(`class B, ${i}, time: ${aisTime}`);
370
+ aisOut(encMsg18, aisTime);
371
+ aisOut(encMsg240, aisTime);
372
+ aisOut(encMsg241, aisTime);
373
+ }
374
+ app.debug("--------------------------------------------------------");
375
+
376
+ }
297
377
  }
298
378
  }
299
379
  const dateobj = new Date(Date.now());
@@ -323,11 +403,31 @@ module.exports = function createPlugin(app) {
323
403
  default: 1,
324
404
  title: 'How often AIS data is sent to NMEA0183 out (in minutes). E.g. 0.5 = 30s, 1 = 1min',
325
405
  },
406
+ port: {
407
+ type: 'number',
408
+ title: 'HTTP port',
409
+ default: 3000
410
+ },
411
+ portSec: {
412
+ type: 'number',
413
+ title: 'HTTPS port',
414
+ default: 3443
415
+ },
326
416
  sendOwn: {
327
417
  type: 'boolean',
328
418
  title: 'Send own AIS data, VDO',
329
419
  default: true
330
420
  },
421
+ useTag: {
422
+ type: 'boolean',
423
+ title: 'Add Tag-block',
424
+ default: false
425
+ },
426
+ distance: {
427
+ type: 'integer',
428
+ default: 100,
429
+ title: 'AIS target within range [km]',
430
+ },
331
431
  },
332
432
  };
333
433
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-vessels-to-ais",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "SignalK server plugin to convert other vessel data to NMEA0183 AIS format and forward it out to 3rd party applications",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -13,7 +13,9 @@
13
13
  "author": "Karl-Erik Gustafsson",
14
14
  "license": "MIT",
15
15
  "dependencies": {
16
- "node-fetch": "^2.6.0",
16
+ "haversine-distance": "^1.2.1",
17
+ "moment": "^2.29.1",
18
+ "node-fetch": "^3.1.1",
17
19
  "ggencoder": "^1.0.5"
18
20
  },
19
21
  "devDependencies": {