node-red-contrib-ntrip 0.2.1 → 0.2.3

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,12 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [0.2.3]
5
+ ### Added NMEA encoder node - [#13](https://github.com/windkh/node-red-contrib-ntrip/issues/13)
6
+
7
+ ## [0.2.2]
8
+ ### Added documentation - [#3](https://github.com/windkh/node-red-contrib-ntrip/issues/3)
9
+
4
10
  ## [0.2.1]
5
11
  ### Added several auth modes for uploading data to the caster.
6
12
  ### added pass through mode - [#10](https://github.com/windkh/node-red-contrib-ntrip/issues/10)
package/README.md CHANGED
@@ -10,15 +10,6 @@
10
10
  [![Closed Issues](https://img.shields.io/github/issues-closed-raw/windkh/node-red-contrib-ntrip.svg)](https://github.com/windkh/node-red-contrib-ntrip/issues?q=is%3Aissue+is%3Aclosed)
11
11
  ...
12
12
 
13
- This package contains nodes for connecting to a NTRIP server.
14
-
15
- # Dependencies
16
- This package depends on the following libraries
17
- @gnss/nmea
18
- @gnss/rtcm
19
- ntrip-client
20
-
21
-
22
13
  # Thanks for your donation
23
14
  If you want to support this free project. Any help is welcome. You can donate by clicking one of the following links:
24
15
 
@@ -28,22 +19,162 @@ If you want to support this free project. Any help is welcome. You can donate by
28
19
  <a href="https://www.buymeacoffee.com/windka" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
29
20
 
30
21
 
22
+ This package contains nodes for connecting to a NTRIP server.
23
+
24
+ # Installation
25
+ [![NPM](https://nodei.co/npm/node-red-contrib-ntrip.png?downloads=true)](https://nodei.co/npm/node-red-contrib-ntrip/)
26
+
27
+ You can install the nodes using node-red's "Manage palette" in the side bar.
28
+
29
+ Or run the following command in the root directory of your Node-RED installation
30
+
31
+ npm install node-red-contrib-ntrip --save
32
+
33
+ # Dependencies
34
+ This package depends on the following libraries
35
+ - [@gnss/nmea](https://github.com/node-ntrip/nmea)
36
+ - [@gnss/rtcm](https://github.com/node-ntrip/rtcm)
37
+ - [ntrip-client](https://github.com/dxhbiz/ntrip-client)
38
+
39
+
31
40
  # Credits
32
41
 
42
+ # 👥 Contributors
43
+
44
+ <p align="center">
45
+ <a href="https://github.com/windkh/node-red-contrib-ntrip/graphs/contributors">
46
+ <img src="https://contrib.rocks/image?repo=windkh/node-red-contrib-ntrip" />
47
+ </a>
48
+ </p>
33
49
 
34
50
  # Changelog
35
51
  Changes can be followed [here](/CHANGELOG.md)
36
52
 
37
-
38
53
  # NTRIP Client Node
39
- ...
40
-
54
+ This node connects to a NTRIP caster and can be used in two different modes:
55
+ - upload
56
+ - download
57
+
58
+ ## Download Mode
59
+ ### Configuration
60
+ For downloading data from a NTRIP caster you need to provide the following information
61
+ - __host__ - the IP address or hostname of the NTRIP server (e.g. rtk2go.com)
62
+ - __port__ - the Port number of the NTRIP server (usually this is 2101)
63
+ - __mountpoint__ - the mountpoint of the stream data at the NTRIP server (enter name without /)
64
+
65
+ Depending on the version of the NTRIP caster you need to provide
66
+ - __username__ - the username (e.g. your e-mail address when using rtk2go.com)
67
+ - __password__ - the password (e.g. none)
68
+
69
+ Some NTRIP casters only send data after the client provided its location via a GGA sentence.
70
+ In this case you can provide the X Y Z coordinate here or inject it like illustrated in the example flow
71
+ - __X__ - the X coordinate
72
+ - __Y__ - the Y coordinate
73
+ - __Z__ - the Z coordinate
74
+ - __interval__ - the interval in milliseconds to transmit the location to the NTRIP server.
75
+
76
+ ### Provide Client Location (XYZ) to NTRIP caster
77
+ If you want more control about sending your location to the NTRIP server, then you
78
+ can inject the values as described in the following example:
79
+ ![Alt text](images/SetXYZFlow.png?raw=true "SetXYZ Flow")
80
+
81
+ The function node simply creates an array of X Y Z coordinates in msg.payload.
82
+ Sapos NTRIP casters for example only needs one GGA sentence to initiate streaming.
83
+
84
+ ![Alt text](images/SetXYZFunctionNode.png?raw=true "SetXYZ Function Node")
85
+ See also example flow [**SetXYZ flow**](examples/sapos.json)
86
+
87
+ ## Upload Mode
88
+ ### Configuration
89
+ The configuation properties are the same as described for the download mode.
90
+ You need to provide these additional properties:
91
+ - __Authentication Mode__ - the mode the NTRIP caster accepts.
92
+ - __Pass through data__ - if checked all data is sent to the output for further processing.
93
+
94
+ ### Authentication Modes
95
+ #### Legacy
96
+ This mode initiates the uploading to a mountpoint using plan text.
97
+ ```javascript
98
+ SOURCE ${mountpoint}\r\n\r\n
99
+ ```
100
+ or with password and unsername
101
+ ```javascript
102
+ SOURCE ${password} /${mountpoint}\r\nSource-Agent: NTRIP ${username}\r\n\r\n
103
+ ```
104
+
105
+ #### Legacy (Basic Auth)
106
+ This hybrid mode initiates the uploading to a mountpoint using plan text and Basic Auth.
107
+ ```javascript
108
+ SOURCE ${mountpoint}\r\nAuthorization: Basic ${authorization}\r\n\r\n
109
+ ```
110
+
111
+ #### NTRIP V1
112
+ This mode initiates the uploading to a mountpoint using HTTP with Basic Auth.
113
+ ```javascript
114
+ POST /${mountpoint} HTTP/1.0\r\nUser-Agent: NTRIP ${userAgent}\r\nAuthorization: Basic ${authorization}\r\nContent-Type: gnss/data\r\n\r\n
115
+ ```
116
+
117
+ #### NTRIP V2
118
+ THis mode is equal to NTRIP V1 with some more data.
119
+ ```javascript
120
+ 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
121
+ ```
41
122
 
42
123
  # RTCM Decoder Node
43
-
124
+ This node accepts binary RTCM data. The node accepts either a single or multiple RTCN messages.
125
+ The converted messages are sent one by one to the output in the following format.
126
+ ```javascript
127
+ msg.payload =
128
+ {
129
+ rtcm, // RTCm message number
130
+ messageType, // a string indicating a readyble RTCM message name
131
+ message, // a structure containing all decoded data
132
+ input, // the raw input message
133
+ };
134
+ ```
44
135
 
45
136
  # NMEA Decoder Node
46
-
137
+ This node accepts binary NMEA data. The node accepts either a single or multiple NMEA messages.
138
+ The converted messages are sent one by one to the output in the following format.
139
+ ```javascript
140
+ msg.payload =
141
+ {
142
+ messageType, // a string indicating a readyble RTCM message name
143
+ message, // a structure containing all decoded data
144
+ input, // the raw input message
145
+ };
146
+ ```
147
+
148
+ # Examples
149
+ Examples are stored in the examples folder an can be imported from within node-red's sidebar via import.
150
+
151
+ ## Download data from NTRIP caster
152
+ Consuming Data is straight forward. Connect to a NTRIP caster, select download mode and
153
+ pass the data to other nodes for further processing: in this example 2 decoder are used.
154
+ The NMEA node is only to show that the stream can be split.
155
+ (It is useless as RTK2Go does not send any NMEA strings)
156
+ See also example flow [**RTK2Go flow**](examples/ntripclient.json)
157
+
158
+ ## Provide XYZ dato to NTRIP caster
159
+ This example is the same as the RTK2Go one except for the fact that Sapos requires sending a GGA sentence
160
+ before transmitting RTCM sentences.
161
+ See also example flow [**Sapos flow**](examples/sapos.json)
162
+
163
+ ## Consume data from TCP source (RTKBase)
164
+ Next to retrieving data from a remote NTRIP caster you can also consume the RTCM stream from a local
165
+ TCP source like a RTKBase Station (e.g. https://github.com/Stefal/rtkbase).
166
+ You can upload the data to any NTRIP caster like Onocoy, Centipede, RTKOnline, ... and use the same data
167
+ on your own. To accomplish this you can use node-red's TCP receiver node.
168
+ You need to activate a TCP server either on your ESP32 XBee or RTKBase.
169
+ See also example flow [**TCP flow**](examples/tcp.json)
170
+
171
+ ## Upload data to NTRIP caster
172
+ Uploading is straight forward, however authentication might be tricky.
173
+ You need to know the mountpoint and credentials before you can initiat an upload.
174
+ Not all NTRIP casters accept the same way of authentication. You can test with
175
+ a local demo version of SNIP by creating a raw TCP input stream.
176
+ (SNIP can be found here https://www.use-snip.com)
177
+ See also example flow [**Upload flow**](examples/upload.json)
47
178
 
48
179
  # License
49
180
 
@@ -0,0 +1 @@
1
+ [{"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":"fa8ecd8584cd2497","type":"NmeaDecoder","z":"95fe791a9c2e02fb","description":"","x":780,"y":380,"wires":[["ce248cbbb81fedc7","395ad916396d2633"],["1e5f02ac026876e5"]]},{"id":"458040210a13ca79","type":"serial in","z":"95fe791a9c2e02fb","name":"COM8 USB","serial":"02508e00360cfd65","x":590,"y":380,"wires":[["028e8c8c34a7cb4f","fa8ecd8584cd2497"]]},{"id":"028e8c8c34a7cb4f","type":"debug","z":"95fe791a9c2e02fb","name":"debug 67","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":760,"y":420,"wires":[]},{"id":"02508e00360cfd65","type":"serial-port","name":"","serialport":"COM8","serialbaud":"115200","databits":"8","parity":"none","stopbits":"1","waitfor":"","dtr":"none","rts":"none","cts":"none","dsr":"none","newline":"\\n","bin":"false","out":"char","addchar":"","responsetimeout":"10000"}]
@@ -0,0 +1 @@
1
+ [{"id":"395ad916396d2633","type":"NmeaEncoder","z":"95fe791a9c2e02fb","description":"","x":1100,"y":660,"wires":[["b1d594a6a88edfca","0451732ab38bafa9"],["0aacddfbe23ce47d"]]},{"id":"b1d594a6a88edfca","type":"debug","z":"95fe791a9c2e02fb","name":"debug 65","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1280,"y":620,"wires":[]},{"id":"0aacddfbe23ce47d","type":"debug","z":"95fe791a9c2e02fb","name":"debug 66","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1280,"y":680,"wires":[]},{"id":"8288987f3959d44e","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":540,"wires":[["311378b9c7996b1f"]]},{"id":"311378b9c7996b1f","type":"function","z":"95fe791a9c2e02fb","name":"GPDTM","func":"// https://github.com/Node-NTRIP/nmea/blob/master/src/messages/dtm.ts\nmsg.payload = {\n messageType : 'DTM',\n nmeaMessage : {\n talker: 'GP', \n datum: 'W84',\n // subDatum: string;\n latitudeOffset : 0,\n longitudeOffset: 0,\n altitudeOffset: 0,\n // refDatum: 'W84'\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":540,"wires":[["395ad916396d2633"]]},{"id":"81c6cb1be666c10c","type":"function","z":"95fe791a9c2e02fb","name":"GNGBS","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gbs.ts\nmsg.payload = {\n messageType : 'GBS',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n latitudeError: 0,\n longitudeError: 0,\n altitudeError: 0,\n satelliteId: 1,\n probability: 1,\n bias: 0,\n standardDeviation: 0,\n systemId: 1,\n signalId: 0,\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":580,"wires":[["395ad916396d2633"]]},{"id":"0e982631e6b36163","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":580,"wires":[["81c6cb1be666c10c"]]},{"id":"0451732ab38bafa9","type":"NmeaDecoder","z":"95fe791a9c2e02fb","description":"","x":1320,"y":760,"wires":[["ccc599cb0e9addf6"],[]]},{"id":"ccc599cb0e9addf6","type":"debug","z":"95fe791a9c2e02fb","name":"debug 68","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1480,"y":740,"wires":[]},{"id":"7ea7ecdb2b40ae9c","type":"function","z":"95fe791a9c2e02fb","name":"GNGLL","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gll.ts\nmsg.payload = {\n messageType : 'GLL',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n latitude: 66.66,\n longitude: 33.33,\n status: 'A',\n modeIndicator: 'R',\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":660,"wires":[["395ad916396d2633"]]},{"id":"fa082ed5b7d0ed5a","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":660,"wires":[["7ea7ecdb2b40ae9c"]]},{"id":"9cb944ace7f6806e","type":"function","z":"95fe791a9c2e02fb","name":"GNGGA","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gga.ts\nmsg.payload = {\n messageType : 'GGA',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n latitude: 66.66,\n longitude: 33.33,\n quality: 1,\n numberSatellites: 10,\n hdop: 2.5,\n altitude: 180.00,\n geoidalSeparation: 90.00,\n differentialAge: 5,\n differentialStationId: 'differentialStationId',\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":620,"wires":[["395ad916396d2633"]]},{"id":"0e3c3f77a0bc9553","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":620,"wires":[["9cb944ace7f6806e"]]},{"id":"78e689b321ae3f8f","type":"comment","z":"95fe791a9c2e02fb","name":"","info":"// see also https://github.com/node-ntrip/nmea#readme\n// put your payload into msg.payload.message see output of decoder node","x":740,"y":500,"wires":[]},{"id":"9fb5ecd91eba0f94","type":"function","z":"95fe791a9c2e02fb","name":"GNGNS","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gns.ts\nmsg.payload = {\n messageType : 'GNS',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n latitude: 66.66,\n longitude: 33.33,\n modeIndicators: ['R'],\n numberSatellites: 1,\n hdop: 0,\n altitude: 123,\n geoidalSeparation: 0,\n differentialAge: 0,\n differentialStationId: 'differentialStationId',\n navigationStatus: 'S',\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":700,"wires":[["395ad916396d2633"]]},{"id":"b4c368a6ec3fd9b0","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":700,"wires":[["9fb5ecd91eba0f94"]]},{"id":"346c9bfb24eca4bf","type":"function","z":"95fe791a9c2e02fb","name":"GNGRS","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/grs.ts\nmsg.payload = {\n messageType : 'GRS',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n residualsMode: 0,\n residuals: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n systemId: 1,\n signalId: 1,\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":740,"wires":[["395ad916396d2633"]]},{"id":"a42bcfe61f797be3","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":740,"wires":[["346c9bfb24eca4bf"]]},{"id":"24fc3108ef208f3b","type":"function","z":"95fe791a9c2e02fb","name":"GNGSA","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gsa.ts\nmsg.payload = {\n messageType : 'GSA',\n nmeaMessage : {\n talker: 'GN',\n operationMode: 'A',\n navMode: 3,\n satellites: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n pdop: 0,\n hdop: 0,\n vdop: 0,\n systemId: 1,\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":780,"wires":[["395ad916396d2633"]]},{"id":"79f77e548a594e86","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":780,"wires":[["24fc3108ef208f3b"]]},{"id":"946e93e6b5d5bd99","type":"function","z":"95fe791a9c2e02fb","name":"GNGST","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gst.ts\nmsg.payload = {\n messageType : 'GST',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n rangeRms: 1,\n standardDeviationMajor: 0,\n standardDeviationMinor: 0,\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":820,"wires":[["395ad916396d2633"]]},{"id":"45b4a35d34900485","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":820,"wires":[["946e93e6b5d5bd99"]]},{"id":"cd0e6bb7b2ebe038","type":"function","z":"95fe791a9c2e02fb","name":"GNGSV","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/gsv.ts\nmsg.payload = {\n messageType : 'GSV',\n nmeaMessage : {\n talker: 'GN',\n sentenceCount: 1,\n sentenceNumber: 1,\n numberSatellites: 1,\n satellites: [{ \n satelliteId: 1,\n elevationDegrees: 1,\n azimuthDegrees: 0,\n signalNoiseRatio: 1,\n }],\n signalId: 1,\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":860,"wires":[["395ad916396d2633"]]},{"id":"95fbbdfc6e3339cf","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":860,"wires":[["cd0e6bb7b2ebe038"]]},{"id":"a8daeb2afa192350","type":"function","z":"95fe791a9c2e02fb","name":"GNRMC","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/rmc.ts\nmsg.payload = {\n messageType : 'RMC',\n nmeaMessage : {\n talker: 'GN',\n time: new Date(0, 0, 0, 1, 2, 3, 0),\n status: 'A',\n latitude: 10,\n longitude: 11,\n speedOverGroundKnots: 0,\n courseOverGroundTrue: 1,\n date: new Date(0, 0, 0),\n magneticVariation: 0,\n modeIndicator: 'R',\n navStatus: 'S',\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":900,"wires":[["395ad916396d2633"]]},{"id":"aabbb873df0609ca","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":900,"wires":[["a8daeb2afa192350"]]},{"id":"54fe198c52d51c3d","type":"function","z":"95fe791a9c2e02fb","name":"GNTHS","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/ths.ts\nmsg.payload = {\n messageType : 'THS',\n nmeaMessage : {\n talker: 'GN',\n headingTrue: 1,\n modeIndicator: 'R'\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":940,"wires":[["395ad916396d2633"]]},{"id":"2227f94cab8861f7","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":940,"wires":[["54fe198c52d51c3d"]]},{"id":"53b59280cfaa354c","type":"function","z":"95fe791a9c2e02fb","name":"GNTXT","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/txt.ts\nmsg.payload = {\n messageType : 'TXT',\n nmeaMessage : {\n talker: 'GN',\n messageCount: 1,\n messageNumber: 1,\n messageType: 1,\n text: 'Bigfoot'\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":980,"wires":[["395ad916396d2633"]]},{"id":"5db9e0dee97ef9e0","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":980,"wires":[["53b59280cfaa354c"]]},{"id":"4aeabe8dfd5e58de","type":"function","z":"95fe791a9c2e02fb","name":"GNVHW","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/vhw.ts\nmsg.payload = {\n messageType : 'VHW',\n nmeaMessage : {\n talker: 'GN',\n headingTrue: 1,\n headingMagnetic: 2,\n speedKnots: 3,\n speedKmh: 4\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":1020,"wires":[["395ad916396d2633"]]},{"id":"d835de357bdbb233","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":1020,"wires":[["4aeabe8dfd5e58de"]]},{"id":"95767237d022ba6f","type":"function","z":"95fe791a9c2e02fb","name":"GNVLW","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/vlw.ts\nmsg.payload = {\n messageType : 'VLW',\n nmeaMessage : {\n talker: 'GN',\n totalCumulativeWaterDistance: 1,\n resetWaterDistance: 2,\n totalCumulativeGroundDistance: 3,\n resetGroundDistance: 4\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":1060,"wires":[["395ad916396d2633"]]},{"id":"d6f529187402ec50","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":1060,"wires":[["95767237d022ba6f"]]},{"id":"f72f5b6462f257c4","type":"function","z":"95fe791a9c2e02fb","name":"GNVPW","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/vpw.ts\nmsg.payload = {\n messageType : 'VPW',\n nmeaMessage : {\n talker: 'GN',\n speedKnots: 1,\n speedMs: 2\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":1100,"wires":[["395ad916396d2633"]]},{"id":"238c581bcb5a2ecf","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":1100,"wires":[["f72f5b6462f257c4"]]},{"id":"80bfe72db5dee054","type":"function","z":"95fe791a9c2e02fb","name":"GNVTG","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/vtg.ts\nmsg.payload = {\n messageType : 'VTG',\n nmeaMessage : {\n talker: 'GN',\n courseOverGroundTrue: 1,\n courseOverGroundMagnetic: 2,\n speedOverGroundKnots: 3,\n speedOverGroundKmh: 4,\n modeIndicator: 'R',\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":1140,"wires":[["395ad916396d2633"]]},{"id":"8a811f6ae216f934","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":1140,"wires":[["80bfe72db5dee054"]]},{"id":"0cfc1b0b7af0efa1","type":"function","z":"95fe791a9c2e02fb","name":"GNZDA","func":"// see https://github.com/Node-NTRIP/nmea/blob/master/src/messages/zda.ts\nmsg.payload = {\n messageType : 'ZDA',\n nmeaMessage : {\n talker: 'GN',\n date: new Date(0, 0, 0, 1, 2, 3, 0),\n localTimeZoneHours: 1,\n localTimeZoneMinutes: 2\n }\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":1180,"wires":[["395ad916396d2633"]]},{"id":"5a48b0e6d7d8d4c0","type":"inject","z":"95fe791a9c2e02fb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":680,"y":1180,"wires":[["0cfc1b0b7af0efa1"]]}]
Binary file
Binary file
@@ -4,7 +4,7 @@
4
4
 
5
5
  <script type="text/javascript">
6
6
  RED.nodes.registerType('NtripClient', {
7
- category: 'NTRIP',
7
+ category: 'GNSS',
8
8
  color: '#C0DEED',
9
9
  defaults: {
10
10
  description: { value:"" },
@@ -110,12 +110,6 @@
110
110
  <label for="node-input-password"><i class="fa fa-key"></i> Password</label>
111
111
  <input type="text" id="node-input-password" placeholder="Enter the password here)">
112
112
  </div>
113
-
114
- <div class="form-row">
115
- <label for="node-input-interval"><i class="fa fa-comment"></i> Interval (ms)</label>
116
- <input type="text" id="node-input-interval" placeholder="The interval in milliseconds or 0">
117
- </div>
118
-
119
113
 
120
114
  <hr align="middle"/>
121
115
 
@@ -123,18 +117,23 @@
123
117
  <label style="width: auto"><i class="fa fa-crosshairs"></i> Optional location (if set a NMEA GGA sentence is sent to the server)</label>
124
118
 
125
119
  <div class="form-row">
126
- <label for="node-input-x"> X</label>
127
- <input type="text" id="node-input-x" placeholder="The X position or 0 (optional)">
120
+ <label for="node-input-xcoordinate"> X</label>
121
+ <input type="text" id="node-input-xcoordinate" placeholder="The X position or 0 (optional)">
128
122
  </div>
129
123
 
130
124
  <div class="form-row">
131
- <label for="node-input-y"> Y</label>
132
- <input type="text" id="node-input-y" placeholder="The Y position or 0 (optional)">
125
+ <label for="node-input-ycoordinate"> Y</label>
126
+ <input type="text" id="node-input-ycoordinate" placeholder="The Y position or 0 (optional)">
133
127
  </div>
134
128
 
135
129
  <div class="form-row">
136
- <label for="node-input-z"> Z</label>
137
- <input type="text" id="node-input-z" placeholder="The Z position or 0 (optional)">
130
+ <label for="node-input-zcoordinate"> Z</label>
131
+ <input type="text" id="node-input-zcoordinate" placeholder="The Z position or 0 (optional)">
132
+ </div>
133
+
134
+ <div class="form-row">
135
+ <label for="node-input-interval"><i class="fa fa-comment"></i> Interval (ms)</label>
136
+ <input type="text" id="node-input-interval" placeholder="The interval in milliseconds or 0">
138
137
  </div>
139
138
  </div>
140
139
 
@@ -160,7 +159,7 @@
160
159
 
161
160
  <script type="text/javascript">
162
161
  RED.nodes.registerType('RtcmDecoder', {
163
- category: 'NTRIP',
162
+ category: 'GNSS',
164
163
  color: '#C0DEED',
165
164
  defaults: {
166
165
  description: { value:"" },
@@ -205,7 +204,7 @@
205
204
 
206
205
  <script type="text/javascript">
207
206
  RED.nodes.registerType('NmeaDecoder', {
208
- category: 'NTRIP',
207
+ category: 'GNSS',
209
208
  color: '#C0DEED',
210
209
  defaults: {
211
210
  description: { value:"" },
@@ -245,3 +244,48 @@
245
244
  <p>.</p>
246
245
  <p><code>msg.payload</code></p>
247
246
  </script>
247
+
248
+ <!-- ------------------------------------------------------------------------------------------ -->
249
+
250
+ <script type="text/javascript">
251
+ RED.nodes.registerType('NmeaEncoder', {
252
+ category: 'GNSS',
253
+ color: '#C0DEED',
254
+ defaults: {
255
+ description: { value:"" },
256
+ },
257
+ inputs: 1,
258
+ outputs: 2,
259
+ icon: "function.png",
260
+ paletteLabel: "NMEA Encoder",
261
+ label: function () {
262
+ return this.description || "NMEA Encoder";
263
+ },
264
+ labelStyle: function() {
265
+ return this.description?"node_label_italic":"";
266
+ }
267
+ });
268
+
269
+ </script>
270
+
271
+ <script type="text/x-red" data-template-name="NmeaEncoder">
272
+ <div class="form-row">
273
+ <label for="node-input-description"><i class="fa fa-comment"></i> Description</label>
274
+ <input type="text" id="node-input-description" placeholder="The description of the node (optional)">
275
+ </div>
276
+ </script>
277
+
278
+ <script type="text/x-red" data-help-name="NmeaEncoder">
279
+ <p>A node that encodes NMEA messages.</p>
280
+
281
+ <h3>Configuration</h3>
282
+
283
+ <p>Description of the device.</p>
284
+
285
+ <h3>Inputs</h3>
286
+ <p>.</p>
287
+
288
+ <h3>Outputs</h3>
289
+ <p>.</p>
290
+ <p><code>msg.payload</code></p>
291
+ </script>
package/ntrip/99-ntrip.js CHANGED
@@ -21,4 +21,7 @@ module.exports = function (RED) {
21
21
 
22
22
  const NmeaDecoderNode = require('./nodes/nmea-decoder-node.js')(RED);
23
23
  RED.nodes.registerType("NmeaDecoder", NmeaDecoderNode);
24
+
25
+ const NmeaEncoderNode = require('./nodes/nmea-encoder-node.js')(RED);
26
+ RED.nodes.registerType("NmeaEncoder", NmeaEncoderNode);
24
27
  }
@@ -1,7 +1,7 @@
1
1
  module.exports = function (RED) {
2
2
  'use strict';
3
3
 
4
- const { NmeaTransport, NmeaMessageUnknown } = require('@gnss/nmea');
4
+ const { NmeaTransport } = require('@gnss/nmea');
5
5
 
6
6
  function NmeaDecoderNode(config) {
7
7
  RED.nodes.createNode(this, config);
@@ -11,20 +11,20 @@ module.exports = function (RED) {
11
11
 
12
12
  this.on('input', function (msg) {
13
13
 
14
- let buffer = msg.payload;
14
+ let buffer = msg.payload.nmeaMessage ?? msg.payload;
15
15
  if(Buffer.isBuffer(buffer)){
16
- buffer = buffer.toString();
16
+ buffer = buffer.toString('utf8');
17
17
  }
18
18
 
19
19
  try
20
- {
21
- let message = NmeaTransport.decode(buffer);
22
- let messageType = message.constructor.name.replace('NmeaMessage', '').toUpperCase();
20
+ {
21
+ let nmeaMessage = NmeaTransport.decode(buffer);
22
+ let messageType = nmeaMessage.constructor.name.replace('NmeaMessage', '').toUpperCase();
23
23
 
24
24
  let msg = {
25
25
  payload : {
26
26
  messageType : messageType,
27
- message : message,
27
+ nmeaMessage : nmeaMessage,
28
28
  input : buffer
29
29
  },
30
30
  };
@@ -0,0 +1,151 @@
1
+ module.exports = function (RED) {
2
+ 'use strict';
3
+
4
+ const {
5
+ NmeaTransport,
6
+ NmeaMessageUnknown,
7
+ NmeaMessage,
8
+ NmeaMessageDtm,
9
+ NmeaMessageGbs,
10
+ NmeaMessageGga,
11
+ NmeaMessageGll,
12
+ NmeaMessageGns,
13
+ NmeaMessageGrs,
14
+ NmeaMessageGsa,
15
+ NmeaMessageGst,
16
+ NmeaMessageGsv,
17
+ NmeaMessageRmc,
18
+ NmeaMessageThs,
19
+ NmeaMessageTxt,
20
+ NmeaMessageVhw,
21
+ NmeaMessageVlw,
22
+ NmeaMessageVpw,
23
+ NmeaMessageVtg,
24
+ NmeaMessageZda,
25
+ } = require('@gnss/nmea');
26
+
27
+ function NmeaEncoderNode(config) {
28
+ RED.nodes.createNode(this, config);
29
+ let node = this;
30
+ node.nmeaMessagesReceived = 0;
31
+ node.invalidMessagesReceived = 0;
32
+
33
+ this.on('input', function (msg) {
34
+
35
+ let input = msg.payload.nmeaMessage;
36
+ let isNmeaMessage = input.prototype instanceof NmeaMessage;
37
+ let messageType = msg.payload.messageType;
38
+ messageType = messageType.toUpperCase();
39
+
40
+ try
41
+ {
42
+ let message;
43
+ let nmeaMessage;
44
+ if(isNmeaMessage){
45
+ nmeaMessage = input; // is already a NmeaMessage
46
+ }
47
+ else {
48
+
49
+ switch (messageType) {
50
+ case 'Object':
51
+ nmeaMessage = NmeaMessageUnknown.construct(input);
52
+ break;
53
+ case 'DTM':
54
+ nmeaMessage = NmeaMessageDtm.construct(input);
55
+ break;
56
+ case 'GBS':
57
+ nmeaMessage = NmeaMessageGbs.construct(input);
58
+ break;
59
+ case 'GGA':
60
+ nmeaMessage = NmeaMessageGga.construct(input);
61
+ break;
62
+ case 'GLL':
63
+ nmeaMessage = NmeaMessageGll.construct(input);
64
+ break;
65
+ case 'GNS':
66
+ nmeaMessage = NmeaMessageGns.construct(input);
67
+ break;
68
+ case 'GRS':
69
+ nmeaMessage = NmeaMessageGrs.construct(input);
70
+ break;
71
+ case 'GSA':
72
+ nmeaMessage = NmeaMessageGsa.construct(input);
73
+ break;
74
+ case 'GST':
75
+ nmeaMessage = NmeaMessageGst.construct(input);
76
+ break;
77
+ case 'GSV':
78
+ nmeaMessage = NmeaMessageGsv.construct(input);
79
+ break;
80
+ case 'RMC':
81
+ nmeaMessage = NmeaMessageRmc.construct(input);
82
+ break;
83
+ case 'THS':
84
+ nmeaMessage = NmeaMessageThs.construct(input);
85
+ break;
86
+ case 'TXT':
87
+ nmeaMessage = NmeaMessageTxt.construct(input);
88
+ break;
89
+ case 'VHW':
90
+ nmeaMessage = NmeaMessageVhw.construct(input);
91
+ break;
92
+ case 'VLW':
93
+ nmeaMessage = NmeaMessageVlw.construct(input);
94
+ break;
95
+ case 'VPW':
96
+ nmeaMessage = NmeaMessageVpw.construct(input);
97
+ break;
98
+ case 'VTG':
99
+ nmeaMessage = NmeaMessageVtg.construct(input);
100
+ break;
101
+ case 'ZDA':
102
+ nmeaMessage = NmeaMessageZda.construct(input);
103
+ break;
104
+ default:
105
+ // message could not be converted.
106
+ break;
107
+ }
108
+ }
109
+
110
+ if (nmeaMessage !== undefined) {
111
+ message = NmeaTransport.encode(nmeaMessage);
112
+ }
113
+
114
+ let msg = {
115
+ payload : {
116
+ nmeaMessage : message,
117
+ input : input
118
+ },
119
+ };
120
+
121
+ node.send([msg, null]);
122
+ node.nmeaMessagesReceived++;
123
+ }
124
+ catch(ex)
125
+ {
126
+ let msg = {
127
+ payload : {
128
+ error : ex,
129
+ input : input,
130
+ inputString : input.toString()
131
+ }
132
+ };
133
+ node.send([null, msg]);
134
+ node.invalidMessagesReceived++;
135
+ }
136
+
137
+ node.status({
138
+ fill: 'green',
139
+ shape: 'ring',
140
+ text: 'NMEA: ' + node.nmeaMessagesReceived + ' Invalid: ' + node.invalidMessagesReceived,
141
+ });
142
+ });
143
+
144
+ this.on('close', function(done) {
145
+ node.status({});
146
+ done();
147
+ });
148
+ }
149
+
150
+ return NmeaEncoderNode;
151
+ };
@@ -10,12 +10,13 @@ module.exports = function (RED) {
10
10
  RED.nodes.createNode(this, config);
11
11
  let node = this;
12
12
  node.messagesReceived = 0;
13
+ node.passthrough = config.passthrough || false;
14
+
13
15
  let host = config.host;
14
16
  let port = config.port || 2101;
15
17
  let mountpoint = config.mountpoint;
16
18
  let mode = config.mode || 'download';
17
19
  let authmode = config.authmode || 'legacy';
18
- node.passthrough = config.passthrough || false;
19
20
 
20
21
  let client;
21
22
  if(host !== undefined ) {
@@ -30,13 +31,11 @@ module.exports = function (RED) {
30
31
  };
31
32
 
32
33
  // 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;
34
+ let x = parseFloat(config.xcoordinate || 0);
35
+ let y = parseFloat(config.ycoordinate || 0);
36
+ let z = parseFloat(config.zcoordinate || 0);
36
37
 
37
- if (x !== undefined && x !== 0 &&
38
- y !== undefined && y !== 0 &&
39
- z !== undefined && z !== 0) {
38
+ if (x !== 0 && y !== 0 && z !== 0) {
40
39
  options.xyz = [x, y, z];
41
40
  }
42
41
 
@@ -27,8 +27,7 @@ module.exports = function (RED) {
27
27
  rtcm : message.messageType,
28
28
  messageType : messageType,
29
29
  message : message,
30
- input : buffer,
31
- length : length
30
+ input : buffer
32
31
  }
33
32
  };
34
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-ntrip",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Node for ntrip and rtcm handling.",
5
5
  "node-red": {
6
6
  "version": ">=0.1.0",
@@ -15,7 +15,8 @@
15
15
  "node-red",
16
16
  "rtcm",
17
17
  "ntrip",
18
- "gnss"
18
+ "gnss",
19
+ "mmea"
19
20
  ],
20
21
  "repository": {
21
22
  "type": "git",