kopia lustrzana https://github.com/projecthorus/horusbinary_radiolib
Initial commit
rodzic
f740dc57e5
commit
903a93ab02
|
@ -0,0 +1,104 @@
|
|||
// MFSK Modulation
|
||||
|
||||
#include <RadioLib.h>
|
||||
|
||||
uint32_t fsk4_base = 0, fsk4_baseHz = 0;
|
||||
uint32_t fsk4_shift = 0, fsk4_shiftHz = 0;
|
||||
uint32_t fsk4_bitDuration = 0;
|
||||
uint32_t fsk4_tones[4];
|
||||
uint32_t fsk4_tonesHz[4];
|
||||
|
||||
|
||||
int16_t fsk4_setup(PhysicalLayer* phy, float base, uint32_t shift, uint16_t rate){
|
||||
// save configuration
|
||||
fsk4_baseHz = base;
|
||||
fsk4_shiftHz = shift;
|
||||
|
||||
|
||||
// calculate duration of 1 bit
|
||||
fsk4_bitDuration = (uint32_t)1000000/rate;
|
||||
|
||||
// calculate module carrier frequency resolution
|
||||
uint32_t step = round(phy->getFreqStep());
|
||||
|
||||
// check minimum shift value
|
||||
if(shift < step / 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// round shift to multiples of frequency step size
|
||||
if(shift % step < (step / 2)) {
|
||||
fsk4_shift = shift / step;
|
||||
} else {
|
||||
fsk4_shift = (shift / step) + 1;
|
||||
}
|
||||
|
||||
// Write resultant tones into arrays for quick lookup when modulating.
|
||||
fsk4_tones[0] = 0;
|
||||
fsk4_tones[1] = fsk4_shift;
|
||||
fsk4_tones[2] = fsk4_shift*2;
|
||||
fsk4_tones[3] = fsk4_shift*3;
|
||||
|
||||
|
||||
// calculate 24-bit frequency
|
||||
fsk4_base = (base * 1000000.0) / phy->getFreqStep();
|
||||
|
||||
Serial.println(fsk4_base);
|
||||
|
||||
// configure for direct mode
|
||||
return(phy->startDirect());
|
||||
|
||||
}
|
||||
|
||||
int16_t fsk4_transmitDirect(PhysicalLayer* phy, uint32_t freq) {
|
||||
return(phy->transmitDirect(freq));
|
||||
}
|
||||
|
||||
void fsk4_tone(PhysicalLayer* phy, uint8_t i) {
|
||||
uint32_t start = Module::micros();
|
||||
fsk4_transmitDirect(phy, fsk4_base + fsk4_tones[i]);
|
||||
//delayMicroseconds(fsk4_bitDuration);
|
||||
while(Module::micros() - start < fsk4_bitDuration) {
|
||||
Module::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void fsk4_idle(PhysicalLayer* phy){
|
||||
fsk4_tone(phy, 0);
|
||||
}
|
||||
|
||||
void fsk4_standby(PhysicalLayer* phy){
|
||||
phy->standby();
|
||||
}
|
||||
|
||||
void fsk4_preamble(PhysicalLayer* phy, uint8_t len){
|
||||
int k;
|
||||
for (k=0; k<len; k++){
|
||||
fsk4_writebyte(phy, 0x1B);
|
||||
}
|
||||
}
|
||||
|
||||
size_t fsk4_writebyte(PhysicalLayer* phy, uint8_t b){
|
||||
int k;
|
||||
// Send symbols MSB first.
|
||||
for (k=0;k<4;k++)
|
||||
{
|
||||
// Extract 4FSK symbol (2 bits)
|
||||
uint8_t symbol = (b & 0xC0) >> 6;
|
||||
// Modulate
|
||||
fsk4_tone(phy, symbol);
|
||||
// Shift to next symbol.
|
||||
b = b << 2;
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
void fsk4_write(PhysicalLayer* phy, uint8_t* buff, size_t len){
|
||||
size_t n = 0;
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
n += fsk4_writebyte(phy, buff[i]);
|
||||
}
|
||||
fsk4_standby(phy);
|
||||
return(n);
|
||||
}
|
29
README.md
29
README.md
|
@ -1,2 +1,27 @@
|
|||
# horusbinary_radiolib
|
||||
Horus Binary 4FSK Transmit Example, using RadioLib
|
||||
# Horus Binary 4FSK Transmit Example, using RadioLib
|
||||
|
||||
This repository contains a worked example of generating and transmitting [Horus Binary](https://github.com/projecthorus/horusdemodlib/wiki) v1 and v2 high-altitude balloon telemetry packets on an Arduino-compatible platform, using [RadioLib](https://github.com/jgromes/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
|
||||
|
||||
|
||||
### Contacts
|
||||
* Mark Jessop <vk5qi@rfhead.net>
|
||||
|
||||
## Dependencies
|
||||
* Arduino IDE (or other compiler that can compile Arduino projects)
|
||||
* RadioLib Library - https://github.com/jgromes/RadioLib
|
||||
|
||||
## Compiling this Example
|
||||
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`).
|
||||
|
||||
## TODO List
|
||||
* Split out Horus-specific code into separate files, for easier integration into other codebases.
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,23 @@
|
|||
/*---------------------------------------------------------------------------*\
|
||||
|
||||
FILE........: horus_l2.h
|
||||
AUTHOR......: David Rowe
|
||||
DATE CREATED: Dec 2015
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __HORUS_L2__
|
||||
#define __HORUS_L2__
|
||||
|
||||
int horus_l2_get_num_tx_data_bytes(int num_payload_data_bytes);
|
||||
|
||||
/* returns number of output bytes in output_tx_data */
|
||||
int horus_l2_encode_tx_packet(unsigned char *output_tx_data,
|
||||
unsigned char *input_payload_data,
|
||||
int num_payload_data_bytes);
|
||||
|
||||
void horus_l2_decode_rx_packet(unsigned char *output_payload_data,
|
||||
unsigned char *input_rx_data,
|
||||
int num_payload_data_bytes);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
RadioLib Horus Binary FSK4 Packet Generation & Transmitter Example
|
||||
|
||||
This example sends an example FSK-4 'Horus Binary' message using SX1278's
|
||||
FSK modem. This example uses 'stock' RadioLib - and makes calls into the radio's
|
||||
lower-level functions.
|
||||
|
||||
This signal can be demodulated using a SSB demodulator (SDR or otherwise), and
|
||||
horusdemodlib: https://github.com/projecthorus/horusdemodlib/wiki
|
||||
|
||||
Other modules that can be used for FSK4:
|
||||
(Untested, but work with RTTY to are likely to work here too)
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX128x
|
||||
|
||||
*/
|
||||
|
||||
#include <RadioLib.h>
|
||||
#include "horus_l2.h"
|
||||
|
||||
|
||||
// RADIO SETUP
|
||||
|
||||
#define TX_FREQ 434.200
|
||||
#define FSK4_BAUD 100
|
||||
#define FSK4_SPACING 270 // NOTE: This results in a shift of 244 Hz due to the PLL Resolution of the SX127x
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
// Horus Binary Structures & Variables
|
||||
|
||||
// Horus Binary Packet Structure - Version 1
|
||||
struct HorusBinaryPacketV1
|
||||
{
|
||||
uint8_t PayloadID;
|
||||
uint16_t Counter;
|
||||
uint8_t Hours;
|
||||
uint8_t Minutes;
|
||||
uint8_t Seconds;
|
||||
float Latitude;
|
||||
float Longitude;
|
||||
uint16_t Altitude;
|
||||
uint8_t Speed; // Speed in Knots (1-255 knots)
|
||||
uint8_t Sats;
|
||||
int8_t Temp; // Twos Complement Temp value.
|
||||
uint8_t BattVoltage; // 0 = 0.5v, 255 = 5.0V, linear steps in-between.
|
||||
uint16_t Checksum; // CRC16-CCITT Checksum.
|
||||
} __attribute__ ((packed));
|
||||
|
||||
// Horus v2 Mode 1 (32-byte) Binary Packet
|
||||
struct HorusBinaryPacketV2
|
||||
{
|
||||
uint16_t PayloadID;
|
||||
uint16_t Counter;
|
||||
uint8_t Hours;
|
||||
uint8_t Minutes;
|
||||
uint8_t Seconds;
|
||||
float Latitude;
|
||||
float Longitude;
|
||||
uint16_t Altitude;
|
||||
uint8_t Speed; // Speed in Knots (1-255 knots)
|
||||
uint8_t Sats;
|
||||
int8_t Temp; // Twos Complement Temp value.
|
||||
uint8_t BattVoltage; // 0 = 0.5v, 255 = 2.0V, linear steps in-between.
|
||||
// The following 9 bytes (up to the CRC) are user-customizable. The following just
|
||||
// provides an example of how they could be used.
|
||||
uint8_t dummy1; // unsigned int
|
||||
float dummy2; // Float
|
||||
uint8_t dummy3; // battery voltage test
|
||||
uint8_t dummy4; // divide by 10
|
||||
uint16_t dummy5; // divide by 100
|
||||
uint16_t Checksum; // CRC16-CCITT Checksum.
|
||||
} __attribute__ ((packed));
|
||||
|
||||
// Buffers and counters.
|
||||
char rawbuffer [128]; // Buffer to temporarily store a raw binary packet.
|
||||
char codedbuffer [128]; // Buffer to store an encoded binary packet
|
||||
char debugbuffer[256]; // Buffer to store debug strings
|
||||
uint16_t packet_count = 1; // Packet counter
|
||||
|
||||
|
||||
int build_horus_binary_packet_v1(char *buffer){
|
||||
// Generate a Horus Binary v1 packet, and populate it with data.
|
||||
// The assignments in this function should be replaced with real data
|
||||
|
||||
struct HorusBinaryPacketV1 BinaryPacket;
|
||||
|
||||
BinaryPacket.PayloadID = 0; // 0 = 4FSKTEST
|
||||
BinaryPacket.Counter = packet_count;
|
||||
BinaryPacket.Hours = 12;
|
||||
BinaryPacket.Minutes = 34;
|
||||
BinaryPacket.Seconds = 56;
|
||||
BinaryPacket.Latitude = 0.0;
|
||||
BinaryPacket.Longitude = 0.0;
|
||||
BinaryPacket.Altitude = 0;
|
||||
BinaryPacket.Speed = 0;
|
||||
BinaryPacket.BattVoltage = 0;
|
||||
BinaryPacket.Sats = 0;
|
||||
BinaryPacket.Temp = 0;
|
||||
|
||||
BinaryPacket.Checksum = (uint16_t)crc16((unsigned char*)&BinaryPacket,sizeof(BinaryPacket)-2);
|
||||
|
||||
memcpy(buffer, &BinaryPacket, sizeof(BinaryPacket));
|
||||
|
||||
return sizeof(struct HorusBinaryPacketV1);
|
||||
}
|
||||
|
||||
|
||||
int build_horus_binary_packet_v2(char *buffer){
|
||||
// Generate a Horus Binary v2 packet, and populate it with data.
|
||||
// The assignments in this function should be replaced with real data
|
||||
|
||||
struct HorusBinaryPacketV2 BinaryPacketV2;
|
||||
|
||||
BinaryPacketV2.PayloadID = 256; // 0 = 4FSKTEST-V2
|
||||
BinaryPacketV2.Counter = packet_count;
|
||||
BinaryPacketV2.Hours = 12;
|
||||
BinaryPacketV2.Minutes = 34;
|
||||
BinaryPacketV2.Seconds = 56;
|
||||
BinaryPacketV2.Latitude = 0.0;
|
||||
BinaryPacketV2.Longitude = 0.0;
|
||||
BinaryPacketV2.Altitude = 0;
|
||||
BinaryPacketV2.Speed = 0;
|
||||
BinaryPacketV2.BattVoltage = 0;
|
||||
BinaryPacketV2.Sats = 0;
|
||||
BinaryPacketV2.Temp = 0;
|
||||
// Custom section. This is an example only, and the 9 bytes in this section can be used in other
|
||||
// ways. Refer here for details: https://github.com/projecthorus/horusdemodlib/wiki/5-Customising-a-Horus-Binary-v2-Packet
|
||||
BinaryPacketV2.dummy1 = 1; // uint8
|
||||
BinaryPacketV2.dummy2 = 1.23456; // float32
|
||||
BinaryPacketV2.dummy3 = 100; // uint8 - interpreted as a battery voltage 0-5V
|
||||
BinaryPacketV2.dummy4 = 123; // uint8 - interpreted as a fixed-point value (div/10)
|
||||
BinaryPacketV2.dummy5 = 1234; // uint16 - interpreted as a fixed-point value (div/100)
|
||||
|
||||
BinaryPacketV2.Checksum = (uint16_t)crc16((unsigned char*)&BinaryPacketV2,sizeof(BinaryPacketV2)-2);
|
||||
|
||||
memcpy(buffer, &BinaryPacketV2, sizeof(BinaryPacketV2));
|
||||
|
||||
return sizeof(struct HorusBinaryPacketV2);
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules for FSK4
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Serial.print(F("[FSK4] Initializing ... "));
|
||||
|
||||
// initialize FSK4 transmitter
|
||||
// NOTE: FSK4 frequency shift will be rounded
|
||||
// to the nearest multiple of frequency step size.
|
||||
// The exact value depends on the module:
|
||||
// SX127x/RFM9x - 61 Hz
|
||||
// RF69 - 61 Hz
|
||||
// CC1101 - 397 Hz
|
||||
// SX126x - 1 Hz
|
||||
// nRF24 - 1000000 Hz
|
||||
// Si443x/RFM2x - 156 Hz
|
||||
// SX128x - 198 Hz
|
||||
|
||||
state = fsk4_setup(&radio, TX_FREQ, FSK4_SPACING, FSK4_BAUD);
|
||||
|
||||
if(state == ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// Horus Binary V1
|
||||
Serial.println(F("Generating Horus Binary v1 Packet"));
|
||||
|
||||
// Generate packet
|
||||
int pkt_len = build_horus_binary_packet_v1(rawbuffer);
|
||||
// Debugging
|
||||
Serial.print(F("Uncoded Length (bytes): "));
|
||||
Serial.println(pkt_len);
|
||||
Serial.print("Uncoded: ");
|
||||
PrintHex(rawbuffer, pkt_len, debugbuffer);
|
||||
Serial.println(debugbuffer);
|
||||
|
||||
// Apply Encoding
|
||||
int coded_len = horus_l2_encode_tx_packet((unsigned char*)codedbuffer,(unsigned char*)rawbuffer,pkt_len);
|
||||
// Debugging
|
||||
Serial.print(F("Encoded Length (bytes): "));
|
||||
Serial.println(coded_len);
|
||||
Serial.print("Coded: ");
|
||||
PrintHex(codedbuffer, coded_len, debugbuffer);
|
||||
Serial.println(debugbuffer);
|
||||
|
||||
// Transmit!
|
||||
Serial.println(F("Transmitting Horus Binary v1 Packet"));
|
||||
|
||||
// send out idle condition for 1000 ms
|
||||
fsk4_idle(&radio);
|
||||
delay(1000);
|
||||
fsk4_preamble(&radio, 8);
|
||||
fsk4_write(&radio, codedbuffer, coded_len);
|
||||
|
||||
|
||||
// Horus Binary V2
|
||||
Serial.println(F("Generating Horus Binary v2 Packet"));
|
||||
// Generate packet
|
||||
pkt_len = build_horus_binary_packet_v2(rawbuffer);
|
||||
// Debugging
|
||||
Serial.print(F("Uncoded Length (bytes): "));
|
||||
Serial.println(pkt_len);
|
||||
Serial.print("Uncoded: ");
|
||||
PrintHex(rawbuffer, pkt_len, debugbuffer);
|
||||
Serial.println(debugbuffer);
|
||||
|
||||
// Apply Encoding
|
||||
coded_len = horus_l2_encode_tx_packet((unsigned char*)codedbuffer,(unsigned char*)rawbuffer,pkt_len);
|
||||
// Debugging
|
||||
Serial.print(F("Encoded Length (bytes): "));
|
||||
Serial.println(coded_len);
|
||||
Serial.print("Coded: ");
|
||||
PrintHex(codedbuffer, coded_len, debugbuffer);
|
||||
Serial.println(debugbuffer);
|
||||
|
||||
// Transmit!
|
||||
Serial.println(F("Transmitting Horus Binary v2 Packet"));
|
||||
|
||||
// send out idle condition for 1000 ms
|
||||
fsk4_idle(&radio);
|
||||
delay(1000);
|
||||
fsk4_preamble(&radio, 8);
|
||||
fsk4_write(&radio, codedbuffer, coded_len);
|
||||
|
||||
|
||||
Serial.println(F("done!"));
|
||||
|
||||
delay(1000);
|
||||
|
||||
packet_count++;
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Various utility functions
|
||||
|
||||
#include <util/crc16.h>
|
||||
|
||||
// Fast CRC16 code, using Atmel's optimized libraries!
|
||||
unsigned int crc16(unsigned char *string, unsigned int len) {
|
||||
unsigned int i;
|
||||
unsigned int crc;
|
||||
crc = 0xFFFF; // Standard CCITT seed for CRC16.
|
||||
// Calculate the sum, ignore $ sign's
|
||||
for (i = 0; i < len; i++) {
|
||||
crc = _crc_xmodem_update(crc,(uint8_t)string[i]);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void PrintHex(char *data, uint8_t length, char *tmp){
|
||||
// Print char data as hex
|
||||
byte first ;
|
||||
int j=0;
|
||||
for (uint8_t i=0; i<length; i++)
|
||||
{
|
||||
first = ((uint8_t)data[i] >> 4) | 48;
|
||||
if (first > 57) tmp[j] = first + (byte)39;
|
||||
else tmp[j] = first ;
|
||||
j++;
|
||||
|
||||
first = ((uint8_t)data[i] & 0x0F) | 48;
|
||||
if (first > 57) tmp[j] = first + (byte)39;
|
||||
else tmp[j] = first;
|
||||
j++;
|
||||
}
|
||||
tmp[length*2] = 0;
|
||||
}
|
Ładowanie…
Reference in New Issue