node-red-contrib-ntrip 0.1.2 → 0.2.0
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 +3 -0
- package/examples/ntripclient.json +1 -0
- package/examples/sapos.json +1 -0
- package/examples/upload.json +1 -0
- package/ntrip/99-ntrip.html +34 -9
- package/ntrip/99-ntrip.js +5 -189
- package/ntrip/lib/ntrip-client.js +87 -0
- package/ntrip/nodes/nmea-decoder-node.js +62 -0
- package/ntrip/nodes/ntrip-client-node.js +136 -0
- package/ntrip/nodes/rtcm-decoder-node.js +72 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -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"]]}]
|
package/ntrip/99-ntrip.html
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
color: '#C0DEED',
|
|
9
9
|
defaults: {
|
|
10
10
|
description: { value:"" },
|
|
11
|
+
mode: { value: "download", required: true },
|
|
11
12
|
port: { value: 2101, required: false, validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 65535))) }},
|
|
12
13
|
host: { value:"" },
|
|
13
14
|
mountpoint: { value:"" },
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
username: { type: "text" },
|
|
21
22
|
password: { type: "password" },
|
|
22
23
|
},
|
|
23
|
-
inputs:
|
|
24
|
+
inputs: 1,
|
|
24
25
|
outputs: 1,
|
|
25
26
|
icon: "bridge.png",
|
|
26
27
|
paletteLabel: "NTRIP Client",
|
|
@@ -29,6 +30,21 @@
|
|
|
29
30
|
},
|
|
30
31
|
labelStyle: function() {
|
|
31
32
|
return this.description?"node_label_italic":"";
|
|
33
|
+
},
|
|
34
|
+
oneditprepare: function() {
|
|
35
|
+
// download or upload
|
|
36
|
+
var updateMode = function() {
|
|
37
|
+
var mode = $("#node-input-mode").val();
|
|
38
|
+
if (mode == "download") {
|
|
39
|
+
//$("#coordinates").show();
|
|
40
|
+
} else if (mode == "upload"){
|
|
41
|
+
//$("#coordinates").hide();
|
|
42
|
+
} else {
|
|
43
|
+
//$("#coordinates").hide();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
updateMode();
|
|
47
|
+
$("#node-input-mode").change(updateMode);
|
|
32
48
|
}
|
|
33
49
|
});
|
|
34
50
|
|
|
@@ -36,11 +52,26 @@
|
|
|
36
52
|
|
|
37
53
|
<script type="text/x-red" data-template-name="NtripClient">
|
|
38
54
|
|
|
55
|
+
<div class="form-row">
|
|
56
|
+
<label for="node-input-description"><i class="fa fa-comment"></i> Description</label>
|
|
57
|
+
<input type="text" id="node-input-description" placeholder="The description of the node (optional)">
|
|
58
|
+
</div>
|
|
59
|
+
|
|
39
60
|
<div class="form-row">
|
|
40
61
|
<label for="node-input-host"><i class="fa fa-tag"></i> NTRIP Server</label>
|
|
41
62
|
<input type="text" id="node-input-host" placeholder="The hostname or the IP address of the NTRIP server">
|
|
42
63
|
</div>
|
|
43
64
|
|
|
65
|
+
<div class="form-row">
|
|
66
|
+
<label for="node-input-mode"><i class="fa fa-link"></i> Mode</label>
|
|
67
|
+
<select id="node-input-mode" style="width:70%">
|
|
68
|
+
<option value="download">Download</option>
|
|
69
|
+
<option value="upload">Upload</option>
|
|
70
|
+
</select>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<hr align="middle"/>
|
|
74
|
+
|
|
44
75
|
<div class="form-row">
|
|
45
76
|
<label for="node-input-port"><i class="fa fa-phone"></i> Port</label>
|
|
46
77
|
<input type="text" id="node-input-port" placeholder="The network port to open e.g. 2101">
|
|
@@ -87,12 +118,6 @@
|
|
|
87
118
|
</div>
|
|
88
119
|
</div>
|
|
89
120
|
|
|
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
121
|
</script>
|
|
97
122
|
|
|
98
123
|
<script type="text/x-red" data-help-name="NtripClient">
|
|
@@ -103,10 +128,10 @@
|
|
|
103
128
|
<p>Description of the device.</p>
|
|
104
129
|
|
|
105
130
|
<h3>Inputs</h3>
|
|
106
|
-
<p
|
|
131
|
+
<p>Content is directly passed to the ntrip server.</p>
|
|
107
132
|
|
|
108
133
|
<h3>Outputs</h3>
|
|
109
|
-
<p
|
|
134
|
+
<p>Messages received from the ntrip server.</p>
|
|
110
135
|
<p><code>msg.payload</code></p>
|
|
111
136
|
</script>
|
|
112
137
|
|
package/ntrip/99-ntrip.js
CHANGED
|
@@ -5,76 +5,10 @@
|
|
|
5
5
|
module.exports = function (RED) {
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
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,87 @@
|
|
|
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
|
+
}
|
|
12
|
+
|
|
13
|
+
_connect() {
|
|
14
|
+
if (this.isClose) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// init connection of client
|
|
19
|
+
this.client = net.createConnection({
|
|
20
|
+
host: this.host,
|
|
21
|
+
port: this.port
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// on timeout event
|
|
25
|
+
this.client.on('timeout', () => {
|
|
26
|
+
this._onError('socket timeouted');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// on connect event
|
|
30
|
+
this.client.on('connect', () => {
|
|
31
|
+
const mountpoint = this.mountpoint;
|
|
32
|
+
const username = this.username;
|
|
33
|
+
const password = this.password;
|
|
34
|
+
|
|
35
|
+
let data;
|
|
36
|
+
if (this.username === '' && this.password === '') {
|
|
37
|
+
data = `SOURCE ${mountpoint}\r\n\r\n`;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const authorization = Buffer.from(
|
|
41
|
+
username + ':' + password,
|
|
42
|
+
'utf8'
|
|
43
|
+
).toString('base64');
|
|
44
|
+
|
|
45
|
+
data = `SOURCE ${mountpoint}\r\nAuthorization: Basic ${authorization}\r\n\r\n`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.client.write(data);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// on data event
|
|
52
|
+
this.client.on('data', (data) => {
|
|
53
|
+
this._onData(data);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// on end event
|
|
57
|
+
this.client.on('end', () => {
|
|
58
|
+
this._onError('socket ended');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// on close event
|
|
62
|
+
this.client.on('close', () => {
|
|
63
|
+
this._onError('socket closed');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// on error event
|
|
67
|
+
this.client.on('error', (err) => {
|
|
68
|
+
// TODO: check for code = ECONNREFUSED
|
|
69
|
+
this._onError(err);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
this.client.setTimeout(this.timeout);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function createDownloader(options) {
|
|
77
|
+
return new NtripClient(options)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function createUploader(options) {
|
|
81
|
+
return new NtripClientUploader(options)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
createDownloader,
|
|
86
|
+
createUploader
|
|
87
|
+
};
|
|
@@ -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,136 @@
|
|
|
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 NtripServerMissingMountpoint = 'SOURCETABLE 200 OK';
|
|
7
|
+
|
|
8
|
+
function NtripClientNode(config) {
|
|
9
|
+
RED.nodes.createNode(this, config);
|
|
10
|
+
let node = this;
|
|
11
|
+
node.messagesReceived = 0;
|
|
12
|
+
let host = config.host;
|
|
13
|
+
let port = config.port || 2101;
|
|
14
|
+
let mountpoint = config.mountpoint;
|
|
15
|
+
let mode = config.mode || "download";
|
|
16
|
+
|
|
17
|
+
let client;
|
|
18
|
+
if(host !== undefined ) {
|
|
19
|
+
|
|
20
|
+
let options = {
|
|
21
|
+
host: host,
|
|
22
|
+
port: port,
|
|
23
|
+
mountpoint: mountpoint,
|
|
24
|
+
username: node.credentials.username,
|
|
25
|
+
password: node.credentials.password,
|
|
26
|
+
interval: config.interval,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Depending on the caster the clients need to provide the location via GGA sentence.
|
|
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
|
+
if (mode === 'download') {
|
|
41
|
+
// Clients only consume ntrip data.
|
|
42
|
+
client = NtripClient.createDownloader(options);
|
|
43
|
+
}
|
|
44
|
+
else if (mode === 'upload') {
|
|
45
|
+
// Uploaders send RTCM data to a mountpoint so that clients can consume it.
|
|
46
|
+
client = NtripClient.createUploader(options);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
node.error('Mode not supported ' + mode);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
client.on('data', (data) => {
|
|
53
|
+
node.messagesReceived++;
|
|
54
|
+
node.status({
|
|
55
|
+
fill: 'green',
|
|
56
|
+
shape: 'ring',
|
|
57
|
+
text: 'Messages ' + node.messagesReceived,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
let msg = {
|
|
61
|
+
payload : data
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// check if ICY 200 OK
|
|
65
|
+
const response = data.toString();
|
|
66
|
+
if (response.startsWith(NtripServerOkReply)) {
|
|
67
|
+
node.status({
|
|
68
|
+
fill: 'green',
|
|
69
|
+
shape: 'ring',
|
|
70
|
+
text: 'NTRIP server connected.',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else if (response.startsWith(NtripServerMissingMountpoint)) {
|
|
74
|
+
node.status({
|
|
75
|
+
fill: 'red',
|
|
76
|
+
shape: 'ring',
|
|
77
|
+
text: 'No mountpont ' + mountpoint,
|
|
78
|
+
});
|
|
79
|
+
node.error('No mountpoint: ' + response);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
node.send(msg);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
client.on('close', () => {
|
|
87
|
+
node.log('Closed NTRIP connection.');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
client.on('error', (error) => {
|
|
91
|
+
if (error instanceof AggregateError) {
|
|
92
|
+
error.errors.forEach((e, i) => {
|
|
93
|
+
node.log(`Error ${i + 1}: ${e.message}`);
|
|
94
|
+
node.error('Error: ' + e.message);
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
node.log(error);
|
|
98
|
+
node.error('Error: ' + error);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
client.run();
|
|
103
|
+
node.status({
|
|
104
|
+
fill: 'green',
|
|
105
|
+
shape: 'ring',
|
|
106
|
+
text: 'ok',
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.on('input', async function (msg) {
|
|
110
|
+
if (msg.payload) {
|
|
111
|
+
try {
|
|
112
|
+
let data = msg.payload;
|
|
113
|
+
if (Array.isArray(data)) {
|
|
114
|
+
client.setXYZ(data);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
client.write(data);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
} catch (error) {
|
|
121
|
+
node.status({ fill: 'red', shape: 'ring', text: error });
|
|
122
|
+
node.error('Failed to write data: ' + error, error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.on('close', function(done) {
|
|
128
|
+
client.close();
|
|
129
|
+
node.status({});
|
|
130
|
+
done();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return NtripClientNode;
|
|
136
|
+
};
|
|
@@ -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.
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|