This repository contains a worked example of generating and transmitting Horus Binary v1 and v2 high-altitude balloon telemetry packets on an Arduino-compatible platform, using RadioLib.
This is not a complete high-altitude-balloon tracker codebase, just an example of generating and transmitting Horus Binary packets, for integration into other codebases.
This example uses a Semtech SX1278-compatible radio module (in my case, a HopeRF RFM98W), connected to an Arduino-compatible microcontroller (I used a Seeduino Mega 2560 because it would do 3.3v logic levels). In my case, the module has the usual SPI connections, and the following other pins are used:
- NSS pin: 10
- DIO0 pin: 2
- RESET pin: 9
- DIO1 pin: 3
- Mark Jessop [email protected]
- Arduino IDE (or other compiler that can compile Arduino projects)
- RadioLib Library - https://github.com/jgromes/RadioLib
It should be possible to just open the horusbinary_radiolib.ino file in the Arduino IDE, select your target platform, and compile/flash.
Note that I am using some AVR-specific CRC16 libraries (util/crc16.h
), which will probably need to be replaced for use on other platforms (refer util.ino
).
The Horus Binary packet formats are represented in this codebase as structs located here (v1) and here (v2). The last 9 bytes (before the CRC16 checksum) of the v2 packet format are customisable by the user (refer here for more info).
The two functions build_horus_binary_packet_v1
and build_horus_binary_packet_v2
instantiate a struct, and write data into it. The struct bytes are then written out to a buffer (in this example, rawbuffer
) supplied as a function argument, and the function returns the length of the packet in bytes.
int pkt_len = build_horus_binary_packet_v1(rawbuffer);
These functions are the main bit of code that you would need to modify to insert telemetry data from wherever you are obtaining it (GPS libraries, sensors, etc).
Next, we need to apply the Golay(23,12) forward-error-correction, interleaving and scrambling which protect the telemetry data. This is performed using the horus_l2_encode_packet
function, which is located in horus_l2.cpp
. This function takes a pointer to a destination and source buffer, and the packet length:
int coded_len = horus_l2_encode_tx_packet((unsigned char*)codedbuffer,(unsigned char*)rawbuffer,pkt_len);
At this stage we now have the encoded packet in codedbuffer
, and the length of the encoded packet in coded_len
.
In this example codebase, we are using RadioLib to provide a nice abstracted interface to a range of radio transmitter modules. In my case I'm using a SX127x-compatible module, which is started as follows:
// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
SX1278 radio = new Module(10, 2, 9, 3);
The radio has to be initialised in FSK mode:
int state = radio.beginFSK();
Next, I have provided some functions in FSK4_Mod.ino
which will make use of the radio modules 'transmitDirect' functionality to transmit the 4FSK modulation. We have to initialise these functions using the fsk4_setup function:
state = fsk4_setup(&radio, TX_FREQ, FSK4_SPACING, FSK4_BAUD);
TX_FREQ
is the frequency of the lowest 4FSK tone in MHz, FSK4_BAUD
is the desired baud rate (recommended to be 100 baud, though 50 and 300 baud are supported in the decoders), and FSK4_SPACING
is the spacing between the tones in Hz (should be >=2x the baud rate).
Note that the acheived tone spacing depends on the frequency resolution of the radio module. In the case of the SX127x the resolution is 61 Hz, so while I specify a 270 Hz tone spacing, we end up with an acheived tone spacing of 244 Hz.
Once we have our encoded packet, we can transmit as follows:
fsk4_idle(&radio);
delay(1000);
fsk4_preamble(&radio, 8);
fsk4_write(&radio, codedbuffer, coded_len);
fsk4_idle
turns the transmitter on, and configures it to transmit a carrier on the lowest 4FSK tone. fsk4_preamble
sends a repeated sequence of incrementing 4FSK tones, allowing the decoder to lock-on to the signal. Finally, fsk4_write
transmits the encoded payload, and turns the transmitter off once done.
- Split out Horus-specific code into separate files, for easier integration into other codebases.