-
Notifications
You must be signed in to change notification settings - Fork 46
5 Customising a Horus Binary v2 Packet
The Horus Binary v2 format expands the payload ID space from 8 to 16-bits (for 65535 possible payload IDs), and allows for 9 bytes of user-customisable payload data. How these 9 bytes are used is up to the payload designer, but needs to be described in a way that allows the Horus Binary decoder to interpret the bytes, and produce a UKHAS-compliant telemetry sentence suitable for uploading to the Habitat database (and hence displayed on the HabHub tracker).
The Horus Binary v2 packet format (prior to forward-error correction) is as follows:
Byte No. | Data Type | Size (bytes) | Description |
---|---|---|---|
0 | uint16 | 2 | Payload ID (0-65535) |
2 | uint16 | 2 | Sequence Number |
4 | uint8 | 1 | Time-of-day (Hours) |
5 | uint8 | 1 | Time-of-day (Minutes) |
6 | uint8 | 1 | Time-of-day (Seconds) |
7 | float | 4 | Latitude |
11 | float | 4 | Longitude |
15 | uint16 | 2 | Altitude (m) |
17 | uint8 | 1 | Speed (kph) |
18 | uint8 | 1 | Satellites |
19 | int8 | 1 | Temperature (deg C) |
20 | uint8 | 1 | Battery Voltage |
21 | ??? | 9 | Custom data |
30 | uint16 | 2 | CRC16-CCITT Checksum |
Note that while the majority of the packet format is fixed, there are 9 bytes available to the user to include data from other sensors. 9 bytes might not sound like a lot, but if you're conservative with your data types, you can fit quite a lot in here!
The information necessary for interpreting the custom data section is contained with custom_field_list.json. Each payload making use of the custom data area needs to have an entry in this file (matching the callsign allocated in payload_id_list.txt), otherwise the custom data area is ignored when packet parsing is performed.
An example entry in this file is shown below:
"HORUSTEST": {
"struct": "<BbBfH",
"fields": [
["cutdown_battery_voltage", "battery_5v_byte"],
["external_temperature", "none"],
["test_counter", "none"],
["test_float_field", "none"],
["test_int_field", "none"]
]
},
The entry is named with the payload's callsign (in this case HORUSTEST
), which must match the entry in payload_id_list.txt.
The struct
entry tells the decoder how to interpret the 9 bytes, and is a Python struct 'format string'. The text <BbBfH
might look a bit arcane, but is a shorthand way of describing packed binary data types.
The first character indicates the endianness of the multi-byte data types used (e.g. big-endian = most-significant or little-endian = least-significant byte sent first). <
represents little-endian, and >
represents big-endian.
Some common data types that we would expect within high-altitude balloon telemetry are represented by the following characters:
-
B
- Unsigned 8-bit integer (0 - 255) -
b
- Signed 8-bit integer (-128 - 127) -
H
- Unsigned 16-bit integer (0-65535) -
h
- Signed 16-bit integer (-32768 - 32767) -
f
- 32-bit Floating-point number. -
x
- A pad byte - not interpreted.
For the HORUSTEST
example, the string is indicating that the 9 bytes contain:
-
<
- Little-endian ordered data (for multi-byte data types - common on most modern platforms) -
B
- Byte 1 is a unsigned 8-bit integer (0 - 255). -
b
- Byte 2 is a signed 8-bit integer (-128 - 127). -
B
- Byte 3 is a unsigned 8-bit integer. -
f
- Bytes 4-7 are a 32-bit floating point number. -
H
- Bytes 8-9 are a unsigned 16-bit integer (0-65535)
So as an example, if the custom byte area contained the data 0AD87910069E3F162C
, this would be interpreted as values (10, -40, 121, 1.2345600128173828, 11286).
While the struct
entry tells us how to split the bytes into different fields, we would like to provide some information as to what each of the fields are, and potentially do some post-processing of the field data.
For each field that is decoded by the struct
definition, we need to have an entry in the fields
section. Each entry consists of a field name (e.g. 'external_temperature'), and a value with indicates if any additional post-processing is done to the data. The following values are supported:
-
none
- No additional post-processing of the value. The value is converted to a string using standard Python type conversion (e.g.str(value)
) -
battery_5v_byte
- Interpret an unsigned 8-bit integer as a battery voltage, where 0 = 0v, 255 = 5.0V, with linear steps in between.
Additional post-processing functions can be added if necessary - these would need to be added to delegates.py - please let me know what you need!
TODO
TODO