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 +7 -0
- package/examples/ntripclient.json +1 -0
- package/examples/sapos.json +1 -0
- package/examples/upload.json +1 -0
- package/ntrip/99-ntrip.html +54 -9
- package/ntrip/99-ntrip.js +5 -189
- package/ntrip/lib/ntrip-client.js +106 -0
- package/ntrip/nodes/nmea-decoder-node.js +62 -0
- package/ntrip/nodes/ntrip-client-node.js +159 -0
- package/ntrip/nodes/rtcm-decoder-node.js +72 -0
- package/package.json +2 -2
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"]]}]
|
package/ntrip/99-ntrip.html
CHANGED
|
@@ -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:
|
|
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
|
|
151
|
+
<p>Content is directly passed to the ntrip server.</p>
|
|
107
152
|
|
|
108
153
|
<h3>Outputs</h3>
|
|
109
|
-
<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
|
|
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,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
|
|
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",
|