pull/2/head
Mark Jessop 2021-09-12 14:26:48 +09:30
rodzic f740dc57e5
commit 903a93ab02
6 zmienionych plików z 1643 dodań i 2 usunięć

104
FSK4_Mod.ino 100644
Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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.

1184
horus_l2.cpp 100644

Plik diff jest za duży Load Diff

23
horus_l2.h 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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++;
}

34
util.ino 100644
Wyświetl plik

@ -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;
}