node-red-contrib-ntrip 0.1.2 → 0.2.1

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [0.2.1]
5
+ ### Added several auth modes for uploading data to the caster.
6
+ ### added pass through mode - [#10](https://github.com/windkh/node-red-contrib-ntrip/issues/10)
7
+
8
+ ## [0.2.0]
9
+ ### Refactored nodes: data can be forwarded to NTRIP caster now.
10
+
4
11
  ## [0.1.2]
5
12
  ### Fixed readme
6
13
 
@@ -0,0 +1 @@
1
+ [{"id":"86db72067d6de6eb","type":"debug","z":"95fe791a9c2e02fb","name":"debug 42","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":260,"wires":[]},{"id":"bcb4f78f38f73614","type":"debug","z":"95fe791a9c2e02fb","name":"debug 44","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":300,"wires":[]},{"id":"ce248cbbb81fedc7","type":"debug","z":"95fe791a9c2e02fb","name":"debug 45","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":360,"wires":[]},{"id":"1e5f02ac026876e5","type":"debug","z":"95fe791a9c2e02fb","name":"debug 46","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":420,"wires":[]},{"id":"51cd0b92e167630a","type":"function","z":"95fe791a9c2e02fb","name":"1005","func":"if(msg.payload.rtcm == 1005)\n{\n return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1070,"y":140,"wires":[["19d95eb7c3fc0838"]]},{"id":"19d95eb7c3fc0838","type":"debug","z":"95fe791a9c2e02fb","name":"debug 48","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1220,"y":140,"wires":[]},{"id":"307341cb4389ee70","type":"NtripClient","z":"95fe791a9c2e02fb","description":"RTK2Go Westendorf","port":"2101","host":"rtk2go.com","mountpoint":"Westendorf","interval":1000,"x":590,"y":340,"wires":[["91bc83d1480be9ce","fa8ecd8584cd2497"]]},{"id":"91bc83d1480be9ce","type":"RtcmDecoder","z":"95fe791a9c2e02fb","description":"","x":880,"y":260,"wires":[["86db72067d6de6eb","51cd0b92e167630a"],["bcb4f78f38f73614"]]},{"id":"fa8ecd8584cd2497","type":"NmeaDecoder","z":"95fe791a9c2e02fb","description":"","x":880,"y":400,"wires":[["ce248cbbb81fedc7"],["1e5f02ac026876e5"]]}]
@@ -0,0 +1 @@
1
+ [{"id":"86db72067d6de6eb","type":"debug","z":"95fe791a9c2e02fb","name":"debug 42","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":260,"wires":[]},{"id":"bcb4f78f38f73614","type":"debug","z":"95fe791a9c2e02fb","name":"debug 44","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":300,"wires":[]},{"id":"ce248cbbb81fedc7","type":"debug","z":"95fe791a9c2e02fb","name":"debug 45","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":360,"wires":[]},{"id":"1e5f02ac026876e5","type":"debug","z":"95fe791a9c2e02fb","name":"debug 46","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1100,"y":420,"wires":[]},{"id":"51cd0b92e167630a","type":"function","z":"95fe791a9c2e02fb","name":"1005","func":"if(msg.payload.rtcm == 1005)\n{\n return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1070,"y":140,"wires":[["19d95eb7c3fc0838"]]},{"id":"19d95eb7c3fc0838","type":"debug","z":"95fe791a9c2e02fb","name":"debug 48","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1220,"y":140,"wires":[]},{"id":"91bc83d1480be9ce","type":"RtcmDecoder","z":"95fe791a9c2e02fb","description":"","x":880,"y":260,"wires":[["86db72067d6de6eb","51cd0b92e167630a"],["bcb4f78f38f73614"]]},{"id":"fa8ecd8584cd2497","type":"NmeaDecoder","z":"95fe791a9c2e02fb","description":"","x":880,"y":400,"wires":[["ce248cbbb81fedc7"],["1e5f02ac026876e5"]]},{"id":"ae86ea193a1bd420","type":"NtripClient","z":"95fe791a9c2e02fb","description":"Sapos Bayern","port":"2101","host":"sapos-rtk.bayern.de","mountpoint":"VRS_3_4G_BY","interval":1000,"x":560,"y":320,"wires":[["fa8ecd8584cd2497","91bc83d1480be9ce"]]},{"id":"41c59401a86de38e","type":"function","z":"95fe791a9c2e02fb","name":"SetXYZ","func":"// Setting XYZ sends a GGA sentence to the NTRIP caster.\n// This is needed to start RTCM streaming.\n// These are only example coordinates. Replace them with yours.\nmsg.payload = [4200023.620, 778279.450, 4721519.716];\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":360,"y":320,"wires":[["ae86ea193a1bd420"]]},{"id":"9694b5cce6bfc44c","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":320,"wires":[["41c59401a86de38e"]]}]
@@ -0,0 +1 @@
1
+ [{"id":"381976b4de0e6794","type":"tcp in","z":"95fe791a9c2e02fb","name":"","server":"client","host":"r4c.fritz.box","port":"5016","datamode":"stream","datatype":"buffer","newline":"","topic":"","trim":false,"base64":false,"tls":"","x":210,"y":100,"wires":[["bb4a5f23f761c055"]]},{"id":"9f96be356ff5bf3f","type":"debug","z":"95fe791a9c2e02fb","name":"debug 58","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":660,"y":100,"wires":[]},{"id":"bb4a5f23f761c055","type":"NtripClient","z":"95fe791a9c2e02fb","description":"SNIP","mode":"upload","port":"3000","host":"localhost","mountpoint":"Westendorf","interval":1000,"x":430,"y":100,"wires":[["9f96be356ff5bf3f"]]}]
@@ -8,6 +8,10 @@
8
8
  color: '#C0DEED',
9
9
  defaults: {
10
10
  description: { value:"" },
11
+ mode: { value: "download", required: true },
12
+ authmode: { value: "legacy", required: true },
13
+ passthrough: { value: false, required: false },
14
+
11
15
  port: { value: 2101, required: false, validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 65535))) }},
12
16
  host: { value:"" },
13
17
  mountpoint: { value:"" },
@@ -20,7 +24,7 @@
20
24
  username: { type: "text" },
21
25
  password: { type: "password" },
22
26
  },
23
- inputs: 0,
27
+ inputs: 1,
24
28
  outputs: 1,
25
29
  icon: "bridge.png",
26
30
  paletteLabel: "NTRIP Client",
@@ -29,6 +33,21 @@
29
33
  },
30
34
  labelStyle: function() {
31
35
  return this.description?"node_label_italic":"";
36
+ },
37
+ oneditprepare: function() {
38
+ // download or upload
39
+ var updateMode = function() {
40
+ var mode = $("#node-input-mode").val();
41
+ if (mode == "download") {
42
+ $("#uploadoptions").hide();
43
+ } else if (mode == "upload"){
44
+ $("#uploadoptions").show();
45
+ } else {
46
+ $("#uploadoptions").hide();
47
+ }
48
+ };
49
+ updateMode();
50
+ $("#node-input-mode").change(updateMode);
32
51
  }
33
52
  });
34
53
 
@@ -36,11 +55,43 @@
36
55
 
37
56
  <script type="text/x-red" data-template-name="NtripClient">
38
57
 
58
+ <div class="form-row">
59
+ <label for="node-input-description"><i class="fa fa-comment"></i> Description</label>
60
+ <input type="text" id="node-input-description" placeholder="The description of the node (optional)">
61
+ </div>
62
+
39
63
  <div class="form-row">
40
64
  <label for="node-input-host"><i class="fa fa-tag"></i> NTRIP Server</label>
41
65
  <input type="text" id="node-input-host" placeholder="The hostname or the IP address of the NTRIP server">
42
66
  </div>
43
67
 
68
+ <div class="form-row">
69
+ <label for="node-input-mode"><i class="fa fa-link"></i> Mode</label>
70
+ <select id="node-input-mode" style="width:70%">
71
+ <option value="download">Download</option>
72
+ <option value="upload">Upload</option>
73
+ </select>
74
+ </div>
75
+
76
+ <div class="form-row" id="uploadoptions" style="background: var(--red-ui-tertiary-background)">
77
+ <div class="form-row">
78
+ <label for="node-input-authmode"><i class="fa fa-id-card-o"></i> Authentication Mode</label>
79
+ <select id="node-input-authmode" style="width:70%">
80
+ <option value="legacy">Legacy (Plain Text)</option>
81
+ <option value="hybrid">Legacy (Basic Auth)</option>
82
+ <option value="ntripv1">NTRIP V1</option>
83
+ <option value="ntripv2">NTRIP V2</option>
84
+ </select>
85
+ </div>
86
+
87
+ <div class="form-row">
88
+ <label for="node-input-passthrough"><i class="fa fa-long-arrow-right"></i> Pass through data to output.</label>
89
+ <input type="checkbox" id="node-input-passthrough" style="display: inline-block; width: auto; vertical-align: top;">
90
+ </div>
91
+ </div>
92
+
93
+ <hr align="middle"/>
94
+
44
95
  <div class="form-row">
45
96
  <label for="node-input-port"><i class="fa fa-phone"></i> Port</label>
46
97
  <input type="text" id="node-input-port" placeholder="The network port to open e.g. 2101">
@@ -87,12 +138,6 @@
87
138
  </div>
88
139
  </div>
89
140
 
90
- <hr align="middle"/>
91
-
92
- <div class="form-row">
93
- <label for="node-input-description"><i class="fa fa-comment"></i> Description</label>
94
- <input type="text" id="node-input-description" placeholder="The description of the node (optional)">
95
- </div>
96
141
  </script>
97
142
 
98
143
  <script type="text/x-red" data-help-name="NtripClient">
@@ -103,10 +148,10 @@
103
148
  <p>Description of the device.</p>
104
149
 
105
150
  <h3>Inputs</h3>
106
- <p>.</p>
151
+ <p>Content is directly passed to the ntrip server.</p>
107
152
 
108
153
  <h3>Outputs</h3>
109
- <p>.</p>
154
+ <p>Messages received from the ntrip server.</p>
110
155
  <p><code>msg.payload</code></p>
111
156
  </script>
112
157
 
package/ntrip/99-ntrip.js CHANGED
@@ -5,76 +5,10 @@
5
5
  module.exports = function (RED) {
6
6
  "use strict";
7
7
 
8
- const { RtcmTransport, RtcmMessage, RtcmMessageType } = require('@gnss/rtcm');
9
- const { NmeaTransport, NmeaMessageUnknown } = require('@gnss/nmea');
10
- const { NtripClient } = require('ntrip-client');
8
+ const pkg = require('./../package.json');
9
+ RED.log.info('node-red-contrib-ntrip version: v' + pkg.version);
11
10
 
12
- // --------------------------------------------------------------------------------------------
13
- // NTRIP client node.
14
- function NtripClientNode(config) {
15
- RED.nodes.createNode(this, config);
16
- let node = this;
17
- node.messagesReceived = 0;
18
-
19
- if(config.host !== undefined ) {
20
-
21
- let options = {
22
- host: config.host,
23
- port: config.port || 2101,
24
- mountpoint: config.mountpoint,
25
- username: node.credentials.username,
26
- password: node.credentials.password,
27
- interval: config.interval,
28
- };
29
-
30
- let x = config.xcoordinate;
31
- let y = config.ycoordinate;
32
- let z = config.zcoordinate;
33
-
34
- if (x !== undefined && x !== 0 &&
35
- y !== undefined && y !== 0 &&
36
- z !== undefined && z !== 0) {
37
- options.xyz = [x, y, z];
38
- }
39
-
40
- const client = new NtripClient(options);
41
-
42
- client.on('data', (data) => {
43
- node.messagesReceived++;
44
- node.status({
45
- fill: 'green',
46
- shape: 'ring',
47
- text: 'Messages ' + node.messagesReceived,
48
- });
49
-
50
- let msg = {
51
- payload : data
52
- }
53
- node.send(msg);
54
- });
55
-
56
- client.on('close', () => {
57
- node.log('Closed NTRIP connection.');
58
- });
59
-
60
- client.on('error', (err) => {
61
- node.log(err);
62
- });
63
-
64
- client.run();
65
- node.status({
66
- fill: 'green',
67
- shape: 'ring',
68
- text: 'ok',
69
- });
70
-
71
- this.on('close', function(done) {
72
- client.close();
73
- node.status({});
74
- done();
75
- });
76
- }
77
- }
11
+ const NtripClientNode = require('./nodes/ntrip-client-node.js')(RED);
78
12
  RED.nodes.registerType("NtripClient", NtripClientNode, {
79
13
  credentials: {
80
14
  username: { type: "text" },
@@ -82,127 +16,9 @@ module.exports = function (RED) {
82
16
  }
83
17
  });
84
18
 
85
-
86
- // --------------------------------------------------------------------------------------------
87
- // RTCM decoder node.
88
- function RtcmDecoderNode(config) {
89
- RED.nodes.createNode(this, config);
90
- let node = this;
91
- node.rtcmMessagesReceived = 0;
92
- node.invalidMessagesReceived = 0;
93
-
94
- this.on('input', function (msg) {
95
-
96
- let buffer = msg.payload;
97
- try
98
- {
99
- do {
100
- let message;
101
- let length;
102
- [message, length] = RtcmTransport.decode(buffer);
103
-
104
- let messageType = message.constructor.name.replace('RtcmMessage', '');
105
-
106
- let msg = {
107
- payload : {
108
- rtcm : message.messageType,
109
- messageType : messageType,
110
- message : message,
111
- input : buffer,
112
- length : length
113
- }
114
- };
115
-
116
- node.send([msg, null]);
117
- node.rtcmMessagesReceived++;
118
-
119
- // find next message in buffer.
120
- buffer = buffer.slice(length);
121
- if(buffer.length == 0){
122
- done = true;
123
- }
124
- } while(!done);
125
- }
126
- catch(ex)
127
- {
128
- let msg = {
129
- payload : {
130
- error : ex,
131
- input : buffer,
132
- }
133
- };
134
- node.send([null, msg]);
135
- node.invalidMessagesReceived++;
136
- }
137
-
138
- node.status({
139
- fill: 'green',
140
- shape: 'ring',
141
- text: 'RTCM: ' + node.rtcmMessagesReceived + ' Invalid: ' + node.invalidMessagesReceived,
142
- });
143
- });
144
-
145
- this.on('close', function(done) {
146
- node.status({});
147
- done();
148
- });
149
- }
19
+ const RtcmDecoderNode = require('./nodes/rtcm-decoder-node.js')(RED);
150
20
  RED.nodes.registerType("RtcmDecoder", RtcmDecoderNode);
151
21
 
152
- // --------------------------------------------------------------------------------------------
153
- // NMEA decoder node.
154
- function NmeaDecoderNode(config) {
155
- RED.nodes.createNode(this, config);
156
- let node = this;
157
- node.nmeaMessagesReceived = 0;
158
- node.invalidMessagesReceived = 0;
159
-
160
- this.on('input', function (msg) {
161
-
162
- let buffer = msg.payload;
163
- if(Buffer.isBuffer(buffer)){
164
- buffer = buffer.toString();
165
- }
166
-
167
- try
168
- {
169
- let message = NmeaTransport.decode(buffer);
170
- let messageType = message.constructor.name.replace('NmeaMessage', '').toUpperCase();
171
-
172
- let msg = {
173
- payload : {
174
- messageType : messageType,
175
- message : message,
176
- input : buffer
177
- },
178
- };
179
-
180
- node.send([msg, null]);
181
- node.nmeaMessagesReceived++;
182
- }
183
- catch(ex)
184
- {
185
- let msg = {
186
- payload : {
187
- error : ex,
188
- input : buffer,
189
- }
190
- };
191
- node.send([null, msg]);
192
- node.invalidMessagesReceived++;
193
- }
194
-
195
- node.status({
196
- fill: 'green',
197
- shape: 'ring',
198
- text: 'NMEA: ' + node.nmeaMessagesReceived + ' Invalid: ' + node.invalidMessagesReceived,
199
- });
200
- });
201
-
202
- this.on('close', function(done) {
203
- node.status({});
204
- done();
205
- });
206
- }
22
+ const NmeaDecoderNode = require('./nodes/nmea-decoder-node.js')(RED);
207
23
  RED.nodes.registerType("NmeaDecoder", NmeaDecoderNode);
208
24
  }
@@ -0,0 +1,106 @@
1
+ // Code is a modified client taken from here https://github.com/dxhbiz/ntrip-client.
2
+ 'use strict';
3
+
4
+ const { NtripClient } = require('ntrip-client');
5
+ const net = require('net');
6
+
7
+ // Orginal class is extended to be able to send data to the caster.
8
+ class NtripClientUploader extends NtripClient {
9
+ constructor(options) {
10
+ super(options);
11
+ this.authmode = options.authmode || 'legacy';
12
+ }
13
+
14
+ _connect() {
15
+ if (this.isClose) {
16
+ return;
17
+ }
18
+
19
+ // init connection of client
20
+ this.client = net.createConnection({
21
+ host: this.host,
22
+ port: this.port
23
+ });
24
+
25
+ // on timeout event
26
+ this.client.on('timeout', () => {
27
+ this._onError('socket timeouted');
28
+ });
29
+
30
+ // on connect event
31
+ this.client.on('connect', () => {
32
+ const mountpoint = this.mountpoint;
33
+ const userAgent = this.userAgent;
34
+ const username = this.username;
35
+ const password = this.password;
36
+ const host = this.host;
37
+ const port = this.port;
38
+ const authmode = this.authmode;
39
+ const authorization = Buffer.from(
40
+ username + ':' + password,
41
+ 'utf8'
42
+ ).toString('base64');
43
+
44
+ let data;
45
+ switch (authmode) {
46
+ case 'legacy': // Legacy --> rtk2Go accepts this.
47
+ if (username === '' && password === '') {
48
+ data = `SOURCE ${mountpoint}\r\n\r\n`;
49
+ }
50
+ else {
51
+ data = `SOURCE ${password} /${mountpoint}\r\nSource-Agent: NTRIP ${username}\r\n\r\n`;
52
+ }
53
+ break;
54
+ case 'hybrid': // hybrid (legacy + http auth) --> SNIP accepts this.
55
+ data = `SOURCE ${mountpoint}\r\nAuthorization: Basic ${authorization}\r\n\r\n`;
56
+ break;
57
+ case 'ntripv1': // NTRIP V1 POST
58
+ data = `POST /${mountpoint} HTTP/1.0\r\nUser-Agent: NTRIP ${userAgent}\r\nAuthorization: Basic ${authorization}\r\nContent-Type: gnss/data\r\n\r\n`;
59
+ break;
60
+ case 'ntripv2': // NTRIP V2 POST
61
+ data = `POST /${mountpoint} HTTP/1.1\r\nHost: ${host}:${port}\r\nUser-Agent: NTRIP ${userAgent}\r\nAuthorization: Basic ${authorization}\r\nNtrip-Version: Ntrip/2.0\r\nContent-Type: gnss/data\r\nConnection: keep-alive\r\n\r\n`;
62
+ break;
63
+ default:
64
+ throw new Error("Auth mode is not supported " + authmode);
65
+ }
66
+
67
+ this.client.write(data);
68
+ });
69
+
70
+ // on data event
71
+ this.client.on('data', (data) => {
72
+ this._onData(data);
73
+ });
74
+
75
+ // on end event
76
+ this.client.on('end', () => {
77
+ this._onError('socket ended');
78
+ });
79
+
80
+ // on close event
81
+ this.client.on('close', () => {
82
+ this._onError('socket closed');
83
+ });
84
+
85
+ // on error event
86
+ this.client.on('error', (err) => {
87
+ // TODO: check for code = ECONNREFUSED
88
+ this._onError(err);
89
+ });
90
+
91
+ this.client.setTimeout(this.timeout);
92
+ }
93
+ }
94
+
95
+ function createDownloader(options) {
96
+ return new NtripClient(options)
97
+ }
98
+
99
+ function createUploader(options) {
100
+ return new NtripClientUploader(options)
101
+ }
102
+
103
+ module.exports = {
104
+ createDownloader,
105
+ createUploader
106
+ };
@@ -0,0 +1,62 @@
1
+ module.exports = function (RED) {
2
+ 'use strict';
3
+
4
+ const { NmeaTransport, NmeaMessageUnknown } = require('@gnss/nmea');
5
+
6
+ function NmeaDecoderNode(config) {
7
+ RED.nodes.createNode(this, config);
8
+ let node = this;
9
+ node.nmeaMessagesReceived = 0;
10
+ node.invalidMessagesReceived = 0;
11
+
12
+ this.on('input', function (msg) {
13
+
14
+ let buffer = msg.payload;
15
+ if(Buffer.isBuffer(buffer)){
16
+ buffer = buffer.toString();
17
+ }
18
+
19
+ try
20
+ {
21
+ let message = NmeaTransport.decode(buffer);
22
+ let messageType = message.constructor.name.replace('NmeaMessage', '').toUpperCase();
23
+
24
+ let msg = {
25
+ payload : {
26
+ messageType : messageType,
27
+ message : message,
28
+ input : buffer
29
+ },
30
+ };
31
+
32
+ node.send([msg, null]);
33
+ node.nmeaMessagesReceived++;
34
+ }
35
+ catch(ex)
36
+ {
37
+ let msg = {
38
+ payload : {
39
+ error : ex,
40
+ input : buffer,
41
+ inputString : buffer.toString()
42
+ }
43
+ };
44
+ node.send([null, msg]);
45
+ node.invalidMessagesReceived++;
46
+ }
47
+
48
+ node.status({
49
+ fill: 'green',
50
+ shape: 'ring',
51
+ text: 'NMEA: ' + node.nmeaMessagesReceived + ' Invalid: ' + node.invalidMessagesReceived,
52
+ });
53
+ });
54
+
55
+ this.on('close', function(done) {
56
+ node.status({});
57
+ done();
58
+ });
59
+ }
60
+
61
+ return NmeaDecoderNode;
62
+ };
@@ -0,0 +1,159 @@
1
+ module.exports = function (RED) {
2
+ 'use strict';
3
+
4
+ const NtripClient = require("../lib/ntrip-client.js");
5
+ const NtripServerOkReply = 'ICY 200 OK';
6
+ const NtripServerNotOkReply = 'ICY 406';
7
+ const NtripServerMissingMountpoint = 'SOURCETABLE 200 OK';
8
+
9
+ function NtripClientNode(config) {
10
+ RED.nodes.createNode(this, config);
11
+ let node = this;
12
+ node.messagesReceived = 0;
13
+ let host = config.host;
14
+ let port = config.port || 2101;
15
+ let mountpoint = config.mountpoint;
16
+ let mode = config.mode || 'download';
17
+ let authmode = config.authmode || 'legacy';
18
+ node.passthrough = config.passthrough || false;
19
+
20
+ let client;
21
+ if(host !== undefined ) {
22
+
23
+ let options = {
24
+ host: host,
25
+ port: port,
26
+ mountpoint: mountpoint,
27
+ username: node.credentials.username,
28
+ password: node.credentials.password,
29
+ interval: config.interval,
30
+ };
31
+
32
+ // Depending on the caster the clients need to provide the location via GGA sentence.
33
+ let x = config.xcoordinate;
34
+ let y = config.ycoordinate;
35
+ let z = config.zcoordinate;
36
+
37
+ if (x !== undefined && x !== 0 &&
38
+ y !== undefined && y !== 0 &&
39
+ z !== undefined && z !== 0) {
40
+ options.xyz = [x, y, z];
41
+ }
42
+
43
+ if (mode === 'download') {
44
+ // Clients only consume ntrip data.
45
+ client = NtripClient.createDownloader(options);
46
+ }
47
+ else if (mode === 'upload') {
48
+ // Uploaders send RTCM data to a mountpoint so that clients can consume it.
49
+ options.authmode = authmode;
50
+ client = NtripClient.createUploader(options);
51
+ }
52
+ else {
53
+ node.error('Mode not supported ' + mode);
54
+ }
55
+
56
+ client.on('data', (data) => {
57
+ node.messagesReceived++;
58
+ node.status({
59
+ fill: 'green',
60
+ shape: 'ring',
61
+ text: 'Messages ' + node.messagesReceived,
62
+ });
63
+
64
+ let msg = {
65
+ payload : data
66
+ }
67
+
68
+ // check if ICY 200 OK
69
+ const response = data.toString();
70
+ if (response.startsWith(NtripServerOkReply)) {
71
+ node.status({
72
+ fill: 'green',
73
+ shape: 'ring',
74
+ text: 'NTRIP server connected.',
75
+ });
76
+ }
77
+ // check if ICY 406 Not Acceptable
78
+ else if (response.startsWith(NtripServerNotOkReply)) {
79
+ node.status({
80
+ fill: 'red',
81
+ shape: 'ring',
82
+ text: 'NTRIP server rejected connection.',
83
+ });
84
+ node.error('Server rejected connect: ' + response);
85
+ }
86
+ else if (response.startsWith(NtripServerMissingMountpoint)) {
87
+ node.status({
88
+ fill: 'red',
89
+ shape: 'ring',
90
+ text: 'No mountpont ' + mountpoint,
91
+ });
92
+ node.error('No mountpoint: ' + response);
93
+ }
94
+ else {
95
+ node.send(msg);
96
+ }
97
+ });
98
+
99
+ client.on('close', () => {
100
+ node.log('Closed NTRIP connection.');
101
+ });
102
+
103
+ client.on('error', (error) => {
104
+ if (error instanceof AggregateError) {
105
+ error.errors.forEach((e, i) => {
106
+ node.log(`Error ${i + 1}: ${e.message}`);
107
+ node.error('Error: ' + e.message);
108
+ });
109
+ } else {
110
+ node.log(error);
111
+ node.error('Error: ' + error);
112
+ }
113
+ });
114
+
115
+ client.run();
116
+ node.status({
117
+ fill: 'green',
118
+ shape: 'ring',
119
+ text: 'ok',
120
+ });
121
+
122
+ this.on('input', async function (msg) {
123
+ if (msg.payload) {
124
+ node.messagesReceived++;
125
+ node.status({
126
+ fill: 'green',
127
+ shape: 'ring',
128
+ text: 'Messages ' + node.messagesReceived,
129
+ });
130
+
131
+ try {
132
+ let data = msg.payload;
133
+ if (Array.isArray(data)) {
134
+ client.setXYZ(data);
135
+ }
136
+ else {
137
+ client.write(data);
138
+
139
+ if(node.passthrough) {
140
+ node.send(msg);
141
+ }
142
+ }
143
+ } catch (error) {
144
+ node.status({ fill: 'red', shape: 'ring', text: error });
145
+ node.error('Failed to write data: ' + error, error);
146
+ }
147
+ }
148
+ });
149
+
150
+ this.on('close', function(done) {
151
+ client.close();
152
+ node.status({});
153
+ done();
154
+ });
155
+ }
156
+ }
157
+
158
+ return NtripClientNode;
159
+ };
@@ -0,0 +1,72 @@
1
+ module.exports = function (RED) {
2
+ 'use strict';
3
+
4
+ const { RtcmTransport, RtcmMessage, RtcmMessageType } = require('@gnss/rtcm');
5
+
6
+ function RtcmDecoderNode(config) {
7
+ RED.nodes.createNode(this, config);
8
+ let node = this;
9
+ node.rtcmMessagesReceived = 0;
10
+ node.invalidMessagesReceived = 0;
11
+
12
+ this.on('input', function (msg) {
13
+
14
+ let done = false;
15
+ let buffer = msg.payload;
16
+ try
17
+ {
18
+ do {
19
+ let message;
20
+ let length;
21
+ [message, length] = RtcmTransport.decode(buffer);
22
+
23
+ let messageType = message.constructor.name.replace('RtcmMessage', '');
24
+
25
+ let msg = {
26
+ payload : {
27
+ rtcm : message.messageType,
28
+ messageType : messageType,
29
+ message : message,
30
+ input : buffer,
31
+ length : length
32
+ }
33
+ };
34
+
35
+ node.send([msg, null]);
36
+ node.rtcmMessagesReceived++;
37
+
38
+ // find next message in buffer.
39
+ buffer = buffer.slice(length);
40
+ if(buffer.length == 0){
41
+ done = true;
42
+ }
43
+ } while(!done);
44
+ }
45
+ catch(ex)
46
+ {
47
+ let msg = {
48
+ payload : {
49
+ error : ex,
50
+ input : buffer,
51
+ inputString : buffer.toString()
52
+ }
53
+ };
54
+ node.send([null, msg]);
55
+ node.invalidMessagesReceived++;
56
+ }
57
+
58
+ node.status({
59
+ fill: 'green',
60
+ shape: 'ring',
61
+ text: 'RTCM: ' + node.rtcmMessagesReceived + ' Invalid: ' + node.invalidMessagesReceived,
62
+ });
63
+ });
64
+
65
+ this.on('close', function(done) {
66
+ node.status({});
67
+ done();
68
+ });
69
+ }
70
+
71
+ return RtcmDecoderNode;
72
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-ntrip",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Node for ntrip and rtcm handling.",
5
5
  "node-red": {
6
6
  "version": ">=0.1.0",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "https://github.com/windkh/node-red-contrib-ntrip"
22
+ "url": "git+https://github.com/windkh/node-red-contrib-ntrip.git"
23
23
  },
24
24
  "author": "Karl-Heinz Wind",
25
25
  "license": "MIT",