Porównaj commity

...

3 Commity

Autor SHA1 Wiadomość Data
Stephen D 23ef429eaa bug fixes 2024-03-22 16:50:37 -03:00
Mikael Nousiainen bcacf17098
Add comment about CATS packet parity array size 2024-03-22 10:06:40 +02:00
Stephen D 6283d5461f address feedback 2024-03-21 21:21:49 -03:00
14 zmienionych plików z 165 dodań i 108 usunięć

Wyświetl plik

@ -148,7 +148,17 @@ On an external Si5351 clock generator connected to the external I²C bus of the
* The Horus 4FSK v1 and v2 modes have significantly [improved performance compared to APRS or RTTY](https://github.com/projecthorus/horusdemodlib/wiki).
* Use [horus-gui](https://github.com/projecthorus/horus-gui) software to receive the 4FSK mode and to submit packets to [Habhub](http://habhub.org/) high-altitude balloon tracking platform.
* See [horus-gui installation and usage instructions](https://github.com/projecthorus/horusdemodlib/wiki/1.1-Horus-GUI-Reception-Guide-(Windows-Linux-OSX)) and [horusdemodlib](https://github.com/projecthorus/horusdemodlib) library that is responsible for demodulating the signal.
* In order to use Horus 4FSK mode on a flight, you will need to request a new Horus 4FSK payload ID in GitHub according to the instructions at: https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it
* In order to use Horus 4FSK mode on a flight, you will need to request a new Horus 4FSK payload ID in GitHub according to the instructions at: https://github.com/projecthorus/horusdemodlib/wiki#how-do-i-transmit-it
#### Notes about CATS (DFM-17 only)
* CATS is a [modern packet radio standard](https://cats.radio/) designed for communication and telemetry. Due to its increased efficiency over APRS, it allows for fast beacon times (1 Hz or more) without congesting the network.
* To receive CATS, you can either use an [I-Gate board](https://www.tindie.com/products/hamcats/cats-i-gate-board/) on a Raspberry Pi, or just a standard RTL-SDR dongle.
* See [here](https://gitlab.scd31.com/cats/igate) for the I-Gate board software.
* See [here](https://gitlab.scd31.com/cats/sdr-igate) for the SDR software.
* In either case, CATS packets that are received get forwarded to FELINET, and relayed to APRS-IS. This means your CATS packets will show up on [aprs.fi](https://aprs.fi)
* If you're relying on APRS gating, be sure to set an SSID below 100 or the APRS network may reject it.
* For more information, be sure to check [the standard](https://gitlab.scd31.com/cats/cats-standard/-/blob/master/standard.pdf).
### External sensors (RS41 only)
@ -607,6 +617,8 @@ rtl_fm -f 432500000 -M fm -s 250k -r 48000 -g 22 - | ./aprs -
* Various authors with smaller contributions from GitHub pull requests
* Original codebase: DF8OE and other authors of the [RS41HUP](https://github.com/df8oe/RS41HUP) project
* Horus 4FSK code adapted from [darksidelemm fork of RS41HUP](https://github.com/darksidelemm/RS41HUP) project
* CATS code adapted from [CATS reference implementation](https://gitlab.scd31.com/cats/ham-cats/)
* LDPC encoder adapted from [libCATS](https://github.com/CamK06/libCATS)
# Additional documentation

Wyświetl plik

@ -11,34 +11,41 @@
cats_ldpc_code_t *cats_ldpc_pick_code(int len)
{
if(128 <= len)
if(len >= 128) {
return &tm2048;
else if(32 <= len)
}
else if(len >= 32) {
return &tc512;
else if(16 <= len)
}
else if(len >= 16) {
return &tc256;
else
}
else {
return &tc128;
}
}
// For more information on this, see the CATS standard
// https://gitlab.scd31.com/cats/cats-standard
// Section 5.2
// returns # of bytes written to parity_out
size_t cats_ldpc_encode_chunk(uint8_t *data, cats_ldpc_code_t *code, uint8_t *parity_out)
{
// Code parameters
int k = code->k;
int r = code->n - code->k;
int b = code->circulant_size;
int data_length_bits = code->data_length_bits;
int parity_length_bits = code->code_length_bits - code->data_length_bits;
int circ_size = code->circulant_size;
const uint64_t* gc = code->matrix;
int rowLen = r/64;
int row_len = parity_length_bits / 64;
memset(parity_out, 0x00, (code->k)/8);
memset(parity_out, 0x00, parity_length_bits / 8);
for (int offset = 0; offset < b; offset++) {
for (int crow = 0; crow < k/b; crow++) {
int bit = crow*b + offset;
if(GET_BIT(data[bit/8], bit%8)) {
for (int idx = 0; idx < rowLen; idx++) {
uint64_t circ = gc[(crow*rowLen)+idx];
for (int offset = 0; offset < circ_size; offset++) {
for (int crow = 0; crow < data_length_bits / circ_size; crow++) {
int bit = crow * circ_size + offset;
if(GET_BIT(data[bit / 8], bit % 8)) {
for (int idx = 0; idx < row_len; idx++) {
uint64_t circ = gc[(crow * row_len) + idx];
for(int j = 0; j < 8; j++) {
parity_out[idx*8 + j] ^= (uint8_t)(circ >> ((7 - j) * 8));
}
@ -46,10 +53,10 @@ size_t cats_ldpc_encode_chunk(uint8_t *data, cats_ldpc_code_t *code, uint8_t *pa
}
}
for (int block = 0; block < r/b; block++) {
uint8_t* parityblock = &parity_out[block*b/8];
for (int block = 0; block < parity_length_bits / circ_size; block++) {
uint8_t* parityblock = &parity_out[block * circ_size / 8];
uint8_t carry = parityblock[0] >> 7;
for (int x = (b/8)-1; x >= 0; x--) {
for (int x = (circ_size / 8)-1; x >= 0; x--) {
uint8_t c = parityblock[x] >> 7;
parityblock[x] = (parityblock[x] << 1) | carry;
carry = c;
@ -57,7 +64,7 @@ size_t cats_ldpc_encode_chunk(uint8_t *data, cats_ldpc_code_t *code, uint8_t *pa
}
}
return k / 8;
return parity_length_bits / 8;
}
size_t cats_ldpc_encode(uint8_t *data, size_t len)
@ -66,17 +73,19 @@ size_t cats_ldpc_encode(uint8_t *data, size_t len)
// This means we can only encode up to 511 bytes
assert(len < 512);
// A 128 byte array is needed to support CATS packets up to 511 bytes.
uint8_t parity[128];
// Split data into blocks and encode each block
int i = 0;
while(i < len) {
cats_ldpc_code_t *code = cats_ldpc_pick_code(len - i);
int k = code->k;
int data_length_bits = code->data_length_bits;
int data_length = data_length_bits / 8;
uint8_t chunk[code->n / 8];
memset(chunk, 0xAA, k/8);
memcpy(chunk, data + i, (len-i < k/8) ? (len-i) : (k/8));
uint8_t chunk[code->code_length_bits / 8];
memset(chunk, 0xAA, data_length);
memcpy(chunk, data + i, (len-i < data_length) ? (len - i) : data_length);
size_t parity_len = cats_ldpc_encode_chunk(chunk, code, parity);
memcpy(data + len + i, parity, parity_len); // Parity

Wyświetl plik

@ -4,8 +4,8 @@
#include "ldpc_matrices.h"
cats_ldpc_code_t tc128 = {
.n = 128,
.k = 64,
.code_length_bits = 128,
.data_length_bits = 64,
.punctured_bits = 0,
.bf_working_len = 128 + 0,
.circulant_size = 128/8,
@ -14,8 +14,8 @@ cats_ldpc_code_t tc128 = {
};
cats_ldpc_code_t tc256 = {
.n = 256,
.k = 128,
.code_length_bits = 256,
.data_length_bits = 128,
.punctured_bits = 0,
.bf_working_len = 256 + 0,
.circulant_size = 256/8,
@ -24,8 +24,8 @@ cats_ldpc_code_t tc256 = {
};
cats_ldpc_code_t tc512 = {
.n = 512,
.k = 256,
.code_length_bits = 512,
.data_length_bits = 256,
.punctured_bits = 0,
.bf_working_len = 512 + 0,
.circulant_size = 512/8,
@ -34,8 +34,8 @@ cats_ldpc_code_t tc512 = {
};
cats_ldpc_code_t tm2048 = {
.n = 2048,
.k = 1024,
.code_length_bits = 2048,
.data_length_bits = 1024,
.punctured_bits = 512,
.bf_working_len = 2048 + 512,
.circulant_size = 512/4,

Wyświetl plik

@ -2,10 +2,10 @@
#define __CATS_LDPC_MATRICES_H
typedef struct cats_ldpc_code {
// Code length (data+parity)
int n;
// Code length in bits (data+parity)
int code_length_bits;
// Data length in bits
int k;
int data_length_bits;
int punctured_bits;
int bf_working_len;
size_t circulant_size;

Wyświetl plik

@ -3,6 +3,8 @@
#include "whisker.h"
// Hardware IDs defined here: https://cats.radio/pages/hardware-ids.html
// It's just a way to tell where CATS packets come from. It doesn't affect anything beyond this
#ifdef RS41
#define HARDWARE_ID 0x7d00
#endif
@ -10,6 +12,24 @@
#define HARDWARE_ID 0x7d01
#endif
#define CATS_IDENT_TYPE 0x00
#define CATS_GPS_TYPE 0x02
#define CATS_COMMENT_TYPE 0x03
#define CATS_NODE_INFO_TYPE 0x09
#define CATS_NODE_INFO_HARDWARE_ID_PRESENT 1
#define CATS_NODE_INFO_SOFTWARE_ID_PRESENT 2
#define CATS_NODE_INFO_ANTENNA_GAIN_PRESENT 16
#define CATS_NODE_INFO_TX_POWER_PRESENT 32
#define CATS_NODE_INFO_VOLTAGE_PRESENT 64
#define CATS_NODE_INFO_TX_TEMP_PRESENT 128
// corresponds to (21 / 4 = 5.25) dBi antenna gain
#define CATS_ANTENNA_GAIN 21
// Software IDs are basically just a version number.
// Can increment this whenever something big changes, and then
// you can track what version of your software is being used on CATS
#define SOFTWARE_ID 0x00
static inline void cats_push_f16(cats_packet *p, float val)
@ -64,7 +84,7 @@ static inline void cats_push_u32(cats_packet *p, uint32_t val)
void cats_append_identification_whisker(cats_packet *packet, char *callsign, uint8_t ssid, uint16_t icon)
{
packet->data[packet->len++] = 0x00; // type = identidication
packet->data[packet->len++] = CATS_IDENT_TYPE;
size_t callsign_len = strlen(callsign);
packet->data[packet->len++] = callsign_len + 3; // len
packet->data[packet->len++] = icon & 0xFF; // icon
@ -76,7 +96,7 @@ void cats_append_identification_whisker(cats_packet *packet, char *callsign, uin
void cats_append_gps_whisker(cats_packet *packet, gps_data gps)
{
packet->data[packet->len++] = 0x02; // type = gps
packet->data[packet->len++] = CATS_GPS_TYPE;
packet->data[packet->len++] = 14; // len
int32_t lat_converted = gps.latitude_degrees_1000000 * (1LL<<31) / 90LL / 10000000LL;
int32_t lon_converted = gps.longitude_degrees_1000000 * (1LL<<31) / 180LL / 10000000LL;
@ -90,7 +110,7 @@ void cats_append_gps_whisker(cats_packet *packet, gps_data gps)
void cats_append_comment_whisker(cats_packet *packet, char *comment)
{
packet->data[packet->len++] = 0x03; // type = comment
packet->data[packet->len++] = CATS_COMMENT_TYPE;
size_t comment_len = strlen(comment);
assert(comment_len <= 255);
packet->data[packet->len++] = comment_len;
@ -106,16 +126,22 @@ void cats_append_node_info_whisker(cats_packet *packet, telemetry_data *data)
bool has_altitude = GPS_HAS_FIX(data->gps);
float altitude = data->gps.altitude_mm / 1000.0;
d[(*l)++] = 0x09; // type = node info
d[(*l)++] = CATS_NODE_INFO_TYPE;
d[(*l)++] = has_altitude ? 14 : 10; // len
// bitmask
d[(*l)++] = 0;
d[(*l)++] = (CATS_IS_BALLOON << 2) | (has_altitude << 1);
d[(*l)++] = 0b11110011;
d[(*l)++] =
CATS_NODE_INFO_HARDWARE_ID_PRESENT |
CATS_NODE_INFO_SOFTWARE_ID_PRESENT |
CATS_NODE_INFO_ANTENNA_GAIN_PRESENT |
CATS_NODE_INFO_TX_POWER_PRESENT |
CATS_NODE_INFO_VOLTAGE_PRESENT |
CATS_NODE_INFO_TX_TEMP_PRESENT;
cats_push_u16(packet, HARDWARE_ID);
d[(*l)++] = SOFTWARE_ID;
d[(*l)++] = 21; // vertical antenna -> ~5.25 dBi gain
d[(*l)++] = CATS_ANTENNA_GAIN; // vertical antenna -> ~5.25 dBi gain
d[(*l)++] = CATS_REPORTED_TX_POWER_DBM * 4.0; // TX power
d[(*l)++] = data->battery_voltage_millivolts / 100; // voltage
d[(*l)++] = data->internal_temperature_celsius_100 / 100; // transmitter temperature

Wyświetl plik

@ -1,46 +0,0 @@
#include <stdlib.h>
#include "fifo.h"
typedef struct _fifo_encoder {
uint8_t *data;
uint16_t len;
} fifo_encoder;
void fifo_encoder_new(fsk_encoder *encoder)
{
encoder->priv = malloc(sizeof(fifo_encoder));
}
void fifo_encoder_destroy(fsk_encoder *encoder)
{
if (encoder->priv != NULL) {
free(encoder->priv);
encoder->priv = NULL;
}
}
void fifo_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data)
{
fifo_encoder *fifo = (fifo_encoder *) encoder->priv;
fifo->data = data;
fifo->len = data_length;
}
uint8_t *fifo_encoder_get_data(fsk_encoder *encoder)
{
fifo_encoder *fifo = (fifo_encoder *) encoder->priv;
return fifo->data;
}
uint16_t fifo_encoder_get_data_len(fsk_encoder *encoder)
{
fifo_encoder *fifo = (fifo_encoder *) encoder->priv;
return fifo->len;
}
fsk_encoder_api fifo_fsk_encoder_api = {
.set_data = fifo_encoder_set_data,
.get_data = fifo_encoder_get_data,
.get_data_len = fifo_encoder_get_data_len,
};

Wyświetl plik

@ -1,13 +0,0 @@
#ifndef __FIFO_H
#define __FIFO_H
#include <stdint.h>
#include "codecs/fsk/fsk.h"
void fifo_encoder_new(fsk_encoder *encoder);
void fifo_encoder_destroy(fsk_encoder *encoder);
extern fsk_encoder_api fifo_fsk_encoder_api;
#endif

Wyświetl plik

@ -0,0 +1,46 @@
#include <stdlib.h>
#include "raw.h"
typedef struct _raw_encoder {
uint8_t *data;
uint16_t len;
} raw_encoder;
void raw_encoder_new(fsk_encoder *encoder)
{
encoder->priv = malloc(sizeof(raw_encoder));
}
void raw_encoder_destroy(fsk_encoder *encoder)
{
if (encoder->priv != NULL) {
free(encoder->priv);
encoder->priv = NULL;
}
}
void raw_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data)
{
raw_encoder *raw = (raw_encoder *) encoder->priv;
raw->data = data;
raw->len = data_length;
}
uint8_t *raw_encoder_get_data(fsk_encoder *encoder)
{
raw_encoder *raw = (raw_encoder *) encoder->priv;
return raw->data;
}
uint16_t raw_encoder_get_data_len(fsk_encoder *encoder)
{
raw_encoder *raw = (raw_encoder *) encoder->priv;
return raw->len;
}
fsk_encoder_api raw_fsk_encoder_api = {
.set_data = raw_encoder_set_data,
.get_data = raw_encoder_get_data,
.get_data_len = raw_encoder_get_data_len,
};

Wyświetl plik

@ -0,0 +1,13 @@
#ifndef __RAW_H
#define __RAW_H
#include <stdint.h>
#include "codecs/fsk/fsk.h"
void raw_encoder_new(fsk_encoder *encoder);
void raw_encoder_destroy(fsk_encoder *encoder);
extern fsk_encoder_api raw_fsk_encoder_api;
#endif

Wyświetl plik

@ -341,9 +341,15 @@
/**
* CATS mode settings
*/
// CATS is a new digital mode, vaguely meant to be a better APRS (but also much more powerful)
// While it offers a number of advantages, probably the best one for balloons is the rapid beacon rate.
// The protocol is meant to allow for a much higher channel capacity, so beaconing every second is totally fine.
// For more information, see here: https://cats.radio/
#define CATS_CALLSIGN CALLSIGN
#define CATS_SSID 29 // 0 - 255
#define CATS_ICON 13 // Balloon. See the CATS standard for more options
// Balloon. See the CATS standard for more options
// https://gitlab.scd31.com/cats/cats-standard/-/blob/master/standard.pdf
#define CATS_ICON 13
#define CATS_COMMENT "I am a radiosonde. Hear me meow!"
#define CATS_REPORTED_TX_POWER_DBM 20
// You probably want this to be true

Wyświetl plik

@ -227,6 +227,9 @@ void si4063_disable_tx()
// If less than len, remaining bytes will need to be used to top up the buffer
uint16_t si4063_start_tx(uint8_t *data, int len)
{
// Clear fifo underflow interrupt
si4063_fifo_underflow();
// Clear TX FIFO
uint8_t fifo_clear_data[] = {1};
si4063_send_command(SI4063_COMMAND_FIFO_INFO, 1, fifo_clear_data);
@ -336,6 +339,7 @@ static int si4063_get_band(const uint32_t frequency_hz)
return 0;
}
// Also clears status
bool si4063_fifo_underflow()
{
uint8_t data[] = {0xFF, 0xFF, ~0x20}; // Clear underflow status

Wyświetl plik

@ -1,7 +1,7 @@
#ifndef __DELAY_H
#define __DELAY_H
#include <stm32f10x.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {

Wyświetl plik

@ -10,7 +10,7 @@
#include "codecs/morse/morse.h"
#include "codecs/bell/bell.h"
#include "codecs/mfsk/mfsk.h"
#include "codecs/fifo/fifo.h"
#include "codecs/raw/raw.h"
#include "codecs/jtencode/jtencode.h"
#include "drivers/ubxg6010/ubxg6010.h"
#include "radio_internal.h"
@ -304,13 +304,13 @@ radio_transmit_entry radio_transmit_schedule[] = {
.enabled = RADIO_SI4063_TX_CATS,
.radio_type = RADIO_TYPE_SI4063,
.data_mode = RADIO_DATA_MODE_CATS,
.transmit_count = RADIO_SI4063_TX_CATS,
.transmit_count = RADIO_SI4063_TX_CATS_COUNT,
.time_sync_seconds = CATS_TIME_SYNC_SECONDS,
.time_sync_seconds_offset = CATS_TIME_SYNC_OFFSET_SECONDS,
.frequency = RADIO_SI4063_TX_FREQUENCY_CATS,
.tx_power = RADIO_SI4063_TX_POWER,
.payload_encoder = &radio_cats_payload_encoder,
.fsk_encoder_api = &fifo_fsk_encoder_api,
.fsk_encoder_api = &raw_fsk_encoder_api,
},
#endif
#endif
@ -639,7 +639,7 @@ static bool radio_start_transmit(radio_transmit_entry *entry)
case RADIO_DATA_MODE_CATS:
enable_gps_during_transmit = true;
fifo_encoder_new(&entry->fsk_encoder);
raw_encoder_new(&entry->fsk_encoder);
entry->fsk_encoder_api->set_data(&entry->fsk_encoder, radio_current_payload_length, radio_current_payload);
break;
case RADIO_DATA_MODE_WSPR:
@ -778,7 +778,7 @@ static bool radio_stop_transmit(radio_transmit_entry *entry)
mfsk_encoder_destroy(&entry->fsk_encoder);
break;
case RADIO_DATA_MODE_CATS:
fifo_encoder_destroy(&entry->fsk_encoder);
raw_encoder_destroy(&entry->fsk_encoder);
break;
case RADIO_DATA_MODE_WSPR:
case RADIO_DATA_MODE_FT8:

Wyświetl plik

@ -207,7 +207,7 @@ void radio_handle_fifo_si4063(radio_transmit_entry *entry, radio_module_state *s
len -= written;
if(si4063_fifo_underflow()) {
log_info("FIFO underflow - Aborting");
log_info("FIFO underflow - Aborting\n");
shared_state->radio_transmission_finished = true;
return;