{"_id":"@coremarine/septentrio-sbf-nodered","_rev":"5-9e9d6364727ff4518c96d44b81437d23","name":"@coremarine/septentrio-sbf-nodered","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@coremarine/septentrio-sbf-nodered","version":"1.0.0","keywords":["node-red","sbf","septentrio","gnss","parser","binary","protocol"],"author":{"name":"CoreMarine"},"license":"MIT","_id":"@coremarine/septentrio-sbf-nodered@1.0.0","maintainers":[{"name":"valentina-rubane","email":"vru@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"coremarinedev","email":"dev@core-marine.com"}],"homepage":"https://github.com/core-marine-dev/devices/packages/septentrio-sbf-nodered#readme","bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"dist":{"shasum":"0fb29d55c626ed8e069746f9177c6b0140329757","tarball":"https://registry.npmjs.org/@coremarine/septentrio-sbf-nodered/-/septentrio-sbf-nodered-1.0.0.tgz","fileCount":8,"integrity":"sha512-xb5D/QlIPiTdLqPghKzQpq20wTLhTk4kosU82KHuCGjyh4jU/AjgOpE9owiLzrqMdo4khaAVU/4fkV/L2BwURA==","signatures":[{"sig":"MEUCIQDfAqBwxhv6JiKVvH7H41MhyBwlgpbiVqYPaNtfbBsMSQIgFAmcW5FqXJqpIEyHDXTHlGB99ZSqNv6ufuDR0+ypce0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":62209},"main":"index.js","engines":{"node":">=18.0.0"},"gitHead":"c2d74b432252f1a8623bc5e5fcbabf0e5f075029","scripts":{"test":"mocha \"tests/**/*.test.js\"","docker":"sh manual_tests.sh","test:vitest":"vitest"},"_npmUser":{"name":"coremarinedev","email":"dev@core-marine.com"},"node-red":{"nodes":{"cma-septentrio-parser":"src/parser.js"},"version":">=3.0.0"},"repository":{"url":"git+https://github.com/core-marine-dev/devices.git","type":"git"},"_npmVersion":"10.5.2","description":"A group of Node-Red components to work with Septentrio SBF protocol","directories":{},"_nodeVersion":"20.13.1","dependencies":{"@coremarine/septentrio-sbf":"^1.0.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/septentrio-sbf-nodered_1.0.0_1716221117738_0.6413876842747388","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@coremarine/septentrio-sbf-nodered","version":"1.0.1","keywords":["node-red","sbf","septentrio","gnss","parser","binary","protocol"],"author":{"name":"CoreMarine"},"license":"MIT","_id":"@coremarine/septentrio-sbf-nodered@1.0.1","maintainers":[{"name":"valentina-rubane","email":"vru@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"coremarinedev","email":"dev@core-marine.com"}],"homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/septentrio-sbf-nodered","bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"dist":{"shasum":"1a93b40711170693ea775d8a607da4c39e826694","tarball":"https://registry.npmjs.org/@coremarine/septentrio-sbf-nodered/-/septentrio-sbf-nodered-1.0.1.tgz","fileCount":8,"integrity":"sha512-I1sRN0Cq99dDK8zzYkVzPqMobM2aiN2LA0N1926OdFmlGG8GeGiPjyAhwvgYDva8OfJqXjA9zZ85eAPb+/ztNA==","signatures":[{"sig":"MEQCIGp6yqj5bm4JMeaZcaYRlfAVdWIgUFNsX7BrXZyy4D+AAiB4XShQJARF/V4vYukvpRcgqKKIs582tAKgmS1SVHpzFw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":62233},"main":"index.js","engines":{"node":">=18.0.0"},"gitHead":"b3208594121eeff3b37ea2c1e117f11b0a4b82cc","scripts":{"test":"mocha \"tests/**/*.test.js\"","docker":"sh manual_tests.sh","test:vitest":"vitest"},"_npmUser":{"name":"coremarinedev","email":"dev@core-marine.com"},"node-red":{"nodes":{"cma-septentrio-parser":"src/parser.js"},"version":">=3.0.0"},"repository":{"url":"git+https://github.com/core-marine-dev/devices.git","type":"git"},"_npmVersion":"10.5.2","description":"A group of Node-Red components to work with Septentrio SBF protocol","directories":{},"_nodeVersion":"20.13.1","dependencies":{"@coremarine/septentrio-sbf":"^1.0.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/septentrio-sbf-nodered_1.0.1_1716274142261_0.09524961089993456","host":"s3://npm-registry-packages"}},"2.0.0":{"name":"@coremarine/septentrio-sbf-nodered","version":"2.0.0","author":"CoreMarine","license":"MIT","description":"Node-RED component to read Septentrio SBF receiver output as CMA","homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/septentrio-sbf-nodered","repository":{"type":"git","url":"git+https://github.com/core-marine-dev/devices.git","directory":"packages/septentrio-sbf-nodered"},"bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"keywords":["node-red","iot","cma","septentrio","sbf","asterx","gnss","gps","rtk","parser","binary","protocol"],"engines":{"node":">=22"},"node-red":{"version":">=4.0.0","nodes":{"cma-septentrio-parser":"dist/parser.js"}},"main":"dist/parser.js","dependencies":{"@coremarine/septentrio-sbf":"^2.0.0"},"devDependencies":{"@types/node-red":"1.3.5","tsx":"4.23.1"},"scripts":{"build":"pnpm run format && tsup && node copy-assets.mjs","lint":"eslint","format":"eslint --fix","test":"node --import tsx --test \"tests/**/*.test.ts\"","test:coverage":"node --import tsx --test --experimental-test-coverage --test-coverage-include=\"src/**\" \"tests/**/*.test.ts\"","test:unit":"node --import tsx --test \"tests/*.unit.test.ts\"","test:integration":"node --import tsx --test \"tests/*.integration.test.ts\"","dev":"pnpm run build && node dev-server.mjs dev","examples":"pnpm run build && node dev-server.mjs examples"},"_nodeVersion":"24.18.0","_id":"@coremarine/septentrio-sbf-nodered@2.0.0","dist":{"integrity":"sha512-ghm2e3gUzxxjTMO0LSFfiCgieU75Zk+wyTEJ0j7UvJLUZRHm+EVMbqcOyY1BqDI/0wc8WPNFR1+pjyO6L4zriQ==","shasum":"bcc4ec21ae746f67116b4573d6e10e29813da487","tarball":"https://registry.npmjs.org/@coremarine/septentrio-sbf-nodered/-/septentrio-sbf-nodered-2.0.0.tgz","fileCount":8,"unpackedSize":99199,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@coremarine%2fseptentrio-sbf-nodered@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGOH0KjFdLVA0EDOwikKwzqSt2Zhl8a7h3ofiolpEST+AiEAtnRPD9UKn37dJ/7FSa2617VpNWOsDV/Rjhz9iVlT7W0="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:7b19902e-d2ff-4060-932d-57815b51cb07"}},"directories":{},"maintainers":[{"name":"coremarinedev","email":"dev@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"egr666","email":"egr@core-marine.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/septentrio-sbf-nodered_2.0.0_1785601964167_0.700283156492965"},"_hasShrinkwrap":false}},"time":{"created":"2024-05-20T16:05:17.616Z","modified":"2026-08-01T16:32:44.643Z","1.0.0":"2024-05-20T16:05:17.922Z","1.0.1":"2024-05-21T06:49:02.422Z","2.0.0":"2026-08-01T16:32:44.311Z"},"bugs":{"url":"https://github.com/core-marine-dev/devices/issues"},"author":"CoreMarine","license":"MIT","homepage":"https://github.com/core-marine-dev/devices/tree/main/packages/septentrio-sbf-nodered","keywords":["node-red","iot","cma","septentrio","sbf","asterx","gnss","gps","rtk","parser","binary","protocol"],"repository":{"type":"git","url":"git+https://github.com/core-marine-dev/devices.git","directory":"packages/septentrio-sbf-nodered"},"description":"Node-RED component to read Septentrio SBF receiver output as CMA","maintainers":[{"name":"coremarinedev","email":"dev@core-marine.com"},{"name":"crisconru","email":"crisconru@gmail.com"},{"name":"egr666","email":"egr@core-marine.com"}],"readme":"# @coremarine/septentrio-sbf-nodered\n\n[![npm version](https://img.shields.io/npm/v/@coremarine/septentrio-sbf-nodered.svg)](https://www.npmjs.com/package/@coremarine/septentrio-sbf-nodered)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nNode-RED node for **Septentrio** GNSS receivers speaking **SBF** (Septentrio Binary Format). It\nturns the receiver's binary output into **CMA**, the same JSON shape every CoreMarine device parser\nemits.\n\nWrapper of [@coremarine/septentrio-sbf](https://www.npmjs.com/package/@coremarine/septentrio-sbf).\n**All 108 blocks** of the AsteRx SB3 Pro+ firmware 4.10.1 reference guide are decoded.\nNode >= 22, Node-RED >= 4.\n\n## Install\n\nFrom the Node-RED palette manager, or:\n\n```bash\nnpm install @coremarine/septentrio-sbf-nodered\n```\n\nThe node appears in the **CoreMarine** palette category as **Septentrio SBF parser**.\n\n## Configuration\n\n| Field | Meaning |\n| --- | --- |\n| **Name** | Optional node label |\n| **Firmware** | `Default, corrected by the receiver` (default) or `4.10.1` |\n| **Memory** | On by default — append each input to the buffer instead of replacing it |\n\nThe firmware selects which knowledge base describes each block. You rarely need to pin it:\n`ReceiverSetup` (5902) reports the receiver's real version and the parser adopts it, so the device\nis normally the authority on itself. A version this build does not model is **reported, never\nsubstituted**.\n\n**Leave memory on for a live stream.** SBF framing is length-prefixed rather than terminated, so a\nblock only decodes once its *last* byte has arrived — with memory off, any block split across two\nmessages is lost.\n\n## msg API\n\nSend `payload` to parse. Every other key is optional; **a key you do not send is absent from the\noutput too**, and on a bad request the corresponding output key holds an error **string**.\n\n| Key | In | Out |\n| --- | --- | --- |\n| `payload` | on `sbf`: a **Buffer**, a base64 string or a byte array · on `nmea`: the **sentence as a string** | `CMA[]` |\n| `memory` | `{ command: 'get' }` / `{ command: 'set', payload: boolean }` | `{ memory, bytes }` |\n| `protocol` | `{ command: 'get' }` / `{ command: 'set', payload: 'sbf' }` | `{ protocol, protocols }` |\n| `firmware` | `{ command: 'get' }` / `{ command: 'set', payload: '4.10.1' }` | `{ firmware, firmwares, reported?, leapSeconds? }` |\n| `ids` | any truthy value | `string[]` — all 108 block numbers |\n| `definition` | `4007` or `{ id, protocol? }` | `SBFSentenceDefinition[]`, one per revision |\n| `fake` | `5938` or `{ id, protocol?, options? }` | a real wire frame, as a **Buffer** |\n\nFull details, tables and warnings are in the node's **help panel** inside Node-RED.\n\n### Input — what a string means depends on the protocol\n\n**On `nmea`, `payload` is the sentence as a string.** NMEA 0183 is an ASCII text protocol, so that is\nits natural form and it is what the examples use:\n\n```javascript\nmsg.payload = '$PSSN,HRP,104751.00,230324,23.455,1.954,0.0125,0.123,0.0234,0.03765,11,0,4.56453,W*20\\r\\n'\n```\n\n**On `sbf`, `payload` is normally a Buffer** — SBF is binary and has no text form. A **base64 string**\nis also accepted, and that is deliberate rather than generous: every `raw` in the CMA output is\nbase64, so it is this package's own vocabulary for bytes. It closes the diagnostic loop — copy a `raw`\nout of a debug node, inject it back, and you are re-parsing the exact frame that misbehaved. Base64 is\nvalidated strictly, so an ASCII string is rejected with a message rather than silently parsed as\ngarbage.\n\n**A Buffer or a byte array works on both**, and that matters: a serial, TCP or file node emits bytes\nwhichever protocol the receiver was configured for, so a byte-fed flow keeps working when you switch\nthe protocol. A byte array covers the JSON-only paths (an HTTP body, a stored fixture).\n\n### Three conventions worth knowing\n\n**`id` is the block NUMBER** — `'5938'`, not `'AttEuler'`. The human name is in `metadata.name`. The\nnumber is what the wire carries and what Septentrio's own documentation indexes by.\n\n**Values are the datasheet's, unscaled.** A field documented in units of `0.01 m` keeps\n`value: 812` with `units: '0.01 m'`, and the engineering value is in that field's `metadata` as\n`{ value: 8.12, units: 'm' }`. Angles in radians or semi-circles work the same way. Bitfields and\nenums keep their integer `value`, with the decode in `metadata`.\n\n**The timestamp is the receiver's clock.** `payload[i].timestamp` is the block's own GNSS time, not\nthis machine's — a GNSS receiver is disciplined to atomic time. Leap seconds are learned from the\ndevice itself (`ReceiverTime.DeltaLS`). The navigation-message blocks are the exception: they are\nstamped with when the *satellite transmitted* the bits, which can be far in the past, so those keep\nthe parse time. `metadata.timestamp` always has `received` and `parsed`.\n\n### Diagnostics\n\n`ids` and `definition` let a flow ask the running node what it expects — field names, types, units,\nDo-Not-Use values and descriptions, **one entry per revision**, because a receiver generation only\nsends the fields its revision defines.\n\nThat matters because these receivers sit on remote installations with restricted internet access for\nyears: asking the deployed node settles a question that would otherwise need the datasheets.\n\nSeven blocks are `opaque` — the five `Meas3*` and the two `PVTSupport*` — because Septentrio\npublishes no field layout for them at all. Their bodies are emitted as bytes rather than invented\ninto fields.\n\n### Fake frames\n\n`fake` builds a real wire frame from the same field table the parser reads, with a real CRC and a\nreal `Length`, so wiring it back into `payload` round-trips. It is **deterministic** — the same\nrequest returns the same bytes forever, which is what makes it committable into a flow:\n\n```javascript\nmsg.fake = 5938\n// -> a 44-byte Buffer: a valid AttEuler frame\n\nmsg.fake = { id: 4007, options: { revision: 2, fields: { NrSV: 12 } } }\n// -> a 96-byte PVTGeodetic revision 2, with NrSV forced to 12\n```\n\n## Two things a flow must handle\n\n**A field value can be `null`** — the receiver marked it Do-Not-Use. Its `metadata` then says\n`{ doNotUse: true, value: <the sentinel> }`. **`null` means \"not available\" and must never be read\nas zero**: a Do-Not-Use course-over-ground is not a heading of north, and a receiver in\nheading-and-pitch attitude mode has no roll solution at all.\n\n**Sentences can carry `errors`.** Nothing the parser receives is dropped silently:\n\n- A **bad CRC or a truncated body** still decodes every field it can, plus `errors` saying what is\n  wrong. Flagged data beats none.\n- Bytes that cannot start a block come back as a **garbage sentence** — `id` and `protocol` are\n  `'unknown'`, `payload` is empty, and `raw` keeps the discarded bytes. Adjacent junk is coalesced\n  into one report, so a noisy line does not flood the flow.\n- A block number this build does **not** know still produces a sentence, with `payload: []`,\n  `metadata.name: 'unknown'`, the body in `metadata.body` — and **no `errors`**, because nothing is\n  wrong. That is what keeps a newer firmware's blocks from vanishing.\n\nCheck `errors` and `id === 'unknown'` before trusting a reading.\n\n## Examples\n\nImport `examples/septentrio-sbf-examples.json` from the Node-RED menu → Import → Examples, or open\nit directly:\n\n```bash\npnpm run septentrio-sbf:nodered:examples\n```\n\nNine groups, **every frame a real one from a Septentrio receiver**: decoded blocks (AttEuler,\nPVTGeodetic, ReceiverTime), the Do-Not-Use case, failed and garbage input (a corrupted CRC, line\nnoise), memory with a frame split in two, firmware and protocol channels, diagnostics, fake\nframes, and **both protocols on one node** — switch to `nmea`, then feed it a proprietary `$PSSN`\nsentence and a standard one to see `SEPTENTRIO SBF` / `SEPTENTRIO NMEA` / `NMEA` in the output.\nIt uses **only built-in node types**, so it imports cleanly with no extra contrib nodes.\n\nEvery inject was verified by booting a real Node-RED against the flow file and firing it — fire\n*ReceiverTime* and then *firmware: get* to watch `leapSeconds` appear, learned from the device.\n\n## Development\n\n```bash\npnpm run septentrio-sbf:nodered:build    # tsup -> CJS + copy html/icons\npnpm run septentrio-sbf:nodered:test     # node:test — unit + real headless node-red\npnpm run septentrio-sbf:nodered:dev      # local Node-RED on a scratch flow\n```\n\n## Upgrading from 1.x\n\nThe output is now **CMA** rather than `SBFResponse`, and the msg API is entirely different. The node\ntype is **unchanged** (`cma-septentrio-parser`), so existing flows keep their node — but the\nmessages in and out of it must be rewritten.\n\n1.x drove the node with a single `msg.command` string. There is now **one msg key per channel**, so\na message can reconfigure and feed data at once:\n\n| 1.x | 2.0.0 |\n| --- | --- |\n| `command: 'addData'` + a `payload` buffer, then `command: 'getData'` | just send `payload` — the parsed `CMA[]` comes back on `payload` |\n| `command: 'getFirmware'` / `'setFirmware'` | `firmware: { command, payload? }` |\n| `command: 'getMemory'` / `'setMemory'` | `memory: { command, payload? }` |\n| — | `protocol`, `ids`, `definition` and `fake` are new |\n| `SBFResponse` with `frame.header` / `frame.time` / `frame.body` | **CMA**: `payload` is the SBF body, header and time stamp in `metadata` |\n| `name` / `number` / `version` | `metadata.name` / `id` / `metadata.revision` |\n| a bad CRC, a wrong length or an unknown block was **dropped silently** | every one of them is reported — see above |\n| values were raw integers | `value` + `units` are the datasheet's, with the converted value in `metadata` |\n| the sentence date was **wrong by years** (SBF sends milliseconds; the old code passed them to a seconds API, on the GPS scale rather than UTC) | `timestamp` is the receiver's own UTC, checked against its own `ReceiverTime` block |\n| **Node >= 18**, Node-RED >= 3 | **Node >= 22**, Node-RED >= 4 |\n\nSix real bugs in the 1.x library were fixed on the way, including the timestamp above and\n`AttEuler`'s three rate fields being reported on the wrong axes. The library's\n[README](https://www.npmjs.com/package/@coremarine/septentrio-sbf) lists them.\n\n## License\n\nMIT\n","readmeFilename":""}