JS Binary Decoder
I have recently been dealing with parsing binary data packets from various sources, and have published two NPM libraries from it - binary-decoder and sbd-direct-ip. How's how they came to be.
I am currently working on two projects that are dealing with structured binary messages: one is decoding messages coming off of Tait Communicationstait digital radios; the other is sending and receiving Iridium short burst data (SBD) messages. Both are structured binary data that can contain various formats.
In the case of the Tait radios, these come off the serial port of the radio as ASCII Hex encoded MAP27 (the mobile access protocol for MPT-1327 radios) messages ended with a carriage return (CR) character (0x0d
) (spaces added for readability).
a4 04 05 a0 00 00 0x0d
b2 06 9e ff 0x0d
b2 07 9e ff 0x0d
b2 06 9e ff 0x0d
c5 04 05 a0 00 00 0x0d
b5 41 55 00 00 0x0d
0b 04 05 a0 00 01 0x0d - Radio 1 transmitting
0b 00 00 00 00 00 0x0d - Radio 1 ends transmission
My project for this data is to poll the radios for their GPS location and to log calls as they come in into an incident management system I am working on for my urban search and rescue team. The data will be read from radios in our communications caravan, decoded and inserted into an ArangoDB database. As the web app is written in SvelteKit and NodeJS, I thought I would continue the trend and write the serial listener and decoder in NodeJS as well.
The Iridium SBD project involves sending data from a microcontroller connected to an Iridium 9602 and receive them using DirectIP (where the SBD messages are forwarded to server over a TCP connection). The data will be a data format split over multiple SBD messages with packet header to ensure they are processed in the correct order.
I had a look around for binary decoding libraries, but couldn't find one that quite did everything I wanted, so I started creating a schema definition for the messages I would be receiving
const standardMAP27Structure = [
{ // First byte is the message type
type: 'uint8',
id: 'messageType',
enums: [
{
value: 0x81,
label: 'SST'
}
]
},
{ // Choose the rest of the bytes structure based on the message type
type: 'map',
key: 'messageType',
structures: {
0x81: [ // SST Message structure
{
bytes: 2,
type: 'bits',
parts: [
{
bits: 1
},
{
bits: 7,
id: 'PFIX1'
}
]
},
{
// TODO First 3 bits are fixed at 0 0 0
type: 'bytes',
bytes: 2,
id: 'IDENT1'
},
{
type: 'bits',
parts: [
{
bits: 4,
id: 'ADESC'
},
{
bits: 4,
id: 'LENGTH'
}
]
},
{
type: 'variable',
length: 'LENGTH',
id: 'address'
},
{
type: 'bits',
This array notion allowed me to specify structures that changed based on values within the binary data. Once I had created the code (the binary-decoder library) to decode the binary based on the structure arrays, it gave the decoded data in a object.
{
decodedMessage: {
messageType: { value: 129, label: 'SST' },
PFIX1: 4,
IDENT1: 1448,
ADESC: 0,
LENGTH: 0,
CODING: { value: 3, label: 'NMEA' },
NLB: 7,
encrypted: 0,
northSouth: 0,
EastWest: 1,
Quality: 1,
speed: 0,
heading: 0,
latDegrees: 41,
latMinutes: 7,
latFraction: 5263,
longDegrees: 175,
longMinutes: 4,
longFraction: 6439,
utcHours: 9,
utcMinutes: 24,
utcSeconds: 24
},
nextByte: 24
}
I was able to easily create a structure for the DirectIP messages as well.
/* @type {import('binary-decoder').MessageFormat} */
export default [
{
type: 'uint8',
id: 'protocolVersion'
},
{
type: 'uint16',
id: 'messageLength'
},
{
type: 'repeat',
id: 'data',
key: 'elementId',
asObject: 'elementId',
structure: [
{
type: 'uint8',
id: 'elementId',
enum: [
{
value: 1,
label: 'MO Header IEI',
key: 'moHeader'
},
{
value: 2,
label: 'MO Payload IEI',
key: 'moPayload'
},
{
value: 3,
label: 'MO Location Information IEI',
key: 'moLocation'
},
{
value: 0x41,
label: 'MT Header IEI',
key: 'mtHeader'
},
{
value: 0x42,
label: 'MT Payload IEI',
key: 'mtPayload'
},
{
value: 0x44,
label: 'MT Confirmation Message IEI',
key: 'mtConfirmation'
}
]
},
...
(the full structure is in sbd.js)
I then wrapped it in a NodeJS Net Server, made it "emit" message
events whenever it received a new message and hey presto a DirectIP receiver server.
The MAP27 protocol is a little complex than the DirectIP messages, so I am still working on implementing it, but watch this space.