diff --git a/README.md b/README.md index 0c62572..e8b8e4f 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ On an external Si5351 clock generator connected to the external I²C bus of the * 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 -#### Notes about CATS (DFM-17 only) +#### Notes about CATS * 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. diff --git a/src/config.h b/src/config.h index 58fb916..5566d31 100644 --- a/src/config.h +++ b/src/config.h @@ -153,6 +153,8 @@ #define RADIO_SI4032_TX_HORUS_V1_COUNT 1 #define RADIO_SI4032_TX_HORUS_V2 true #define RADIO_SI4032_TX_HORUS_V2_COUNT 6 +#define RADIO_SI4032_TX_CATS false +#define RADIO_SI4032_TX_CATS_COUNT 1 // Continuous transmit mode can be enabled for *either* Horus V1 or V2, but not both. This disables all other transmission modes. // The continuous mode transmits Horus 4FSK preamble between transmissions @@ -167,6 +169,7 @@ // Use a frequency offset to place FSK tones slightly above the defined frequency for SSB reception #define RADIO_SI4032_TX_FREQUENCY_HORUS_V1 432501000 #define RADIO_SI4032_TX_FREQUENCY_HORUS_V2 432501000 +#define RADIO_SI4032_TX_FREQUENCY_CATS 430500000 /** * DFM-17 only: Built-in Si4063 radio chip transmission configuration diff --git a/src/drivers/si4032/si4032.c b/src/drivers/si4032/si4032.c index 313875e..273ce22 100644 --- a/src/drivers/si4032/si4032.c +++ b/src/drivers/si4032/si4032.c @@ -1,11 +1,15 @@ #include #include "hal/spi.h" +#include "hal/hal.h" +#include "hal/delay.h" #include "si4032.h" +#include "log.h" #define SPI_WRITE_FLAG 0x80 #define SI4032_CLOCK 26.0f +#define EXPECTED_SI4032_CLOCK 30 #define GPIO_SI4032_NSEL GPIOC #define GPIO_PIN_SI4032_NSEL GPIO_Pin_13 @@ -45,6 +49,97 @@ void si4032_disable_tx() si4032_write(0x07, 0x40); } +// Returns number of bytes sent from *data +// If less than len, remaining bytes will need to be used to top up the buffer +uint16_t si4032_start_tx(uint8_t *data, int len) +{ + const uint8_t buffer_size = 64; + + // No TX header + // Fixed packet length (don't transmit length) + si4032_write(0x33, 0b00001000); + + // Clear fifo + si4032_write(0x08, 1); + si4032_write(0x08, 0); + + // set almost full threshold to buffer_size + si4032_write(0x7C, buffer_size); + + // set almost empty threshold + si4032_write(0x7D, 16); + + // enable interrupts + si4032_write(0x05, 0b11100100); + + // Clear fifo underflow interrupt, along with other interrupts + si4032_read(0x03); + + // disable packet handler - just transmit whatever's in the FIFO + si4032_write(0x30, 0x00); + + // Set packet length (max 255 bytes) + si4032_write(0x3E, len); + + // Fill our FIFO + int fifo_len = len; + if(fifo_len > buffer_size) { + fifo_len = buffer_size; + } + for(int i = 0; i < fifo_len; i++) { + si4032_write(0x7F, data[i]); + } + + // Start transmitting + si4032_write(0x07, 0x09); + + return fifo_len; +} + +// Add additional bytes to the si4032's FIFO buffer +// Needed for large packets that don't fit in its buffer. +// Keep refilling it while transmitting +// Returns number of bytes taken from *data +// If less than len, you will need to keep calling this +uint16_t si4032_refill_buffer(uint8_t *data, int len, bool *overflow) +{ + uint8_t interrupts; + int i = 0; + + while(i < len){ + do { + si4032_write(0x7F, data[i]); + i++; + delay_us(10); + + interrupts = si4032_read(0x03); + } while ((interrupts & 0x20) && i < len); + + delay_us(500); + } + + return i; +} + +int si4032_wait_for_tx_complete(int timeout_ms) +{ + for(int i = 0; i < timeout_ms; i++) { + uint8_t status = si4032_read(0x03); + + // ipksent is set + if (status & 0x04) { + return HAL_OK; + } + + delay_ms(1); + } + + // clear txon manually + si4032_write(0x07, 0x40); + + return HAL_ERROR_TIMEOUT; +} + void si4032_use_direct_mode(bool use) { if (use) { @@ -67,6 +162,18 @@ void si4032_set_tx_frequency(const float frequency_mhz) si4032_write(0x77, (uint8_t) ((uint16_t) fc & 0xff)); } +void si4032_set_data_rate(const uint32_t rate_bps) +{ + uint32_t rate = (uint64_t) rate_bps * (1 << 21) * EXPECTED_SI4032_CLOCK / 1000000 / SI4032_CLOCK; + + log_info("Rate BPS: %lu\n", rate_bps); + log_info("Rate (raw): %lu\n", rate); + + si4032_write(0x6E, rate >> 8); + si4032_write(0x6F, rate & 0xFF); + si4032_write(0x70, 0b00100000); +} + void si4032_set_tx_power(uint8_t power) { si4032_write(0x6D, power & 0x7U); @@ -110,6 +217,10 @@ void si4032_set_modulation_type(si4032_modulation_type type) // Direct Async Mode with FSK modulation value = 0b00010010; break; + case SI4032_MODULATION_TYPE_FIFO_FSK: + // FIFO with FSK modulation + value = 0b00100010; + break; default: return; } @@ -194,5 +305,8 @@ void si4032_init() si4032_set_frequency_offset(0); si4032_set_frequency_deviation(5); // Was: 5 for APRS in RS41HUP? + // enable interrupts + si4032_write(0x05, 0b11100100); + si4032_set_modulation_type(SI4032_MODULATION_TYPE_NONE); -} +} \ No newline at end of file diff --git a/src/drivers/si4032/si4032.h b/src/drivers/si4032/si4032.h index ca9de65..408d49a 100644 --- a/src/drivers/si4032/si4032.h +++ b/src/drivers/si4032/si4032.h @@ -8,14 +8,19 @@ typedef enum _si4032_modulation_type { SI4032_MODULATION_TYPE_NONE = 0, SI4032_MODULATION_TYPE_OOK, SI4032_MODULATION_TYPE_FSK, + SI4032_MODULATION_TYPE_FIFO_FSK, } si4032_modulation_type; void si4032_soft_reset(); void si4032_enable_tx(); void si4032_inhibit_tx(); void si4032_disable_tx(); +uint16_t si4032_start_tx(uint8_t *data, int len); +uint16_t si4032_refill_buffer(uint8_t *data, int len, bool *overflow); +int si4032_wait_for_tx_complete(int timeout_ms); void si4032_use_direct_mode(bool use); void si4032_set_tx_frequency(float frequency_mhz); +void si4032_set_data_rate(const uint32_t rate_bps); void si4032_set_tx_power(uint8_t power); void si4032_set_frequency_offset(uint16_t offset); void si4032_set_frequency_offset_small(uint8_t offset); diff --git a/src/radio.c b/src/radio.c index c848e1a..795f457 100644 --- a/src/radio.c +++ b/src/radio.c @@ -164,6 +164,20 @@ radio_transmit_entry radio_transmit_schedule[] = { .fsk_encoder_api = &mfsk_fsk_encoder_api, }, #endif +#if RADIO_SI4032_TX_CATS + { + .enabled = RADIO_SI4032_TX_CATS, + .radio_type = RADIO_TYPE_SI4032, + .data_mode = RADIO_DATA_MODE_CATS, + .transmit_count = RADIO_SI4032_TX_CATS_COUNT, + .time_sync_seconds = CATS_TIME_SYNC_SECONDS, + .time_sync_seconds_offset = CATS_TIME_SYNC_OFFSET_SECONDS, + .frequency = RADIO_SI4032_TX_FREQUENCY_CATS, + .tx_power = RADIO_SI4032_TX_POWER, + .payload_encoder = &radio_cats_payload_encoder, + .fsk_encoder_api = &raw_fsk_encoder_api, + }, +#endif #endif #endif diff --git a/src/radio_si4032.c b/src/radio_si4032.c index b8f2f93..1c2266c 100644 --- a/src/radio_si4032.c +++ b/src/radio_si4032.c @@ -31,6 +31,7 @@ static bool si4032_use_dma = false; // TODO: Add support for multiple APRS baud rates // This delay is for RS41 radiosondes #define symbol_delay_bell_202_1200bps_us 823 +#define SI4032_DEVIATION_HZ_625_CATS 8 // 4800 / 625 static volatile bool radio_si4032_state_change = false; static volatile uint32_t radio_si4032_freq = 0; @@ -41,8 +42,11 @@ uint16_t radio_si4032_fill_pwm_buffer(uint16_t offset, uint16_t length, uint16_t bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state *shared_state) { uint16_t frequency_offset; + uint32_t frequency_deviation = 5; + uint32_t data_rate = 0; si4032_modulation_type modulation_type; bool use_direct_mode; + bool use_fifo_mode = false; switch (entry->data_mode) { case RADIO_DATA_MODE_CW: @@ -80,6 +84,14 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state data_timer_init(entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder)); break; } + case RADIO_DATA_MODE_CATS: + frequency_offset = 0; + frequency_deviation = SI4032_DEVIATION_HZ_625_CATS; + modulation_type = SI4032_MODULATION_TYPE_FIFO_FSK; + use_direct_mode = false; + use_fifo_mode = true; + data_rate = 9600; + break; default: return false; } @@ -88,8 +100,14 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state si4032_set_tx_power(entry->tx_power); si4032_set_frequency_offset(frequency_offset); si4032_set_modulation_type(modulation_type); + si4032_set_frequency_deviation(frequency_deviation); - si4032_enable_tx(); + if(use_fifo_mode) { + si4032_set_data_rate(data_rate); + } + else { + si4032_enable_tx(); + } if (use_direct_mode) { spi_uninit(); @@ -122,6 +140,9 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state system_disable_tick(); shared_state->radio_interrupt_transmit_active = true; break; + case RADIO_DATA_MODE_CATS: + shared_state->radio_fifo_transmit_active = true; + break; default: break; } @@ -196,6 +217,32 @@ static void radio_handle_main_loop_manual_si4032(radio_transmit_entry *entry, ra system_enable_tick(); } +void radio_handle_fifo_si4032(radio_transmit_entry *entry, radio_module_state *shared_state) { + log_debug("Start FIFO TX\n"); + fsk_encoder_api *fsk_encoder_api = entry->fsk_encoder_api; + fsk_encoder *fsk_enc = &entry->fsk_encoder; + + uint8_t *data = fsk_encoder_api->get_data(fsk_enc); + uint16_t len = fsk_encoder_api->get_data_len(fsk_enc); + + bool overflow = false; + + uint16_t written = si4032_start_tx(data, len); + data += written; + len -= written; + + si4032_refill_buffer(data, len, &overflow); + + int err = si4032_wait_for_tx_complete(500); + if(err != HAL_OK) { + log_info("Error waiting for tx complete: %d\n", err); + } + + log_debug("Finished FIFO TX\n"); + + shared_state->radio_transmission_finished = true; +} + void radio_handle_main_loop_si4032(radio_transmit_entry *entry, radio_module_state *shared_state) { if (entry->radio_type != RADIO_TYPE_SI4032 || shared_state->radio_interrupt_transmit_active) { @@ -207,6 +254,11 @@ void radio_handle_main_loop_si4032(radio_transmit_entry *entry, radio_module_sta return; } + if (shared_state->radio_fifo_transmit_active) { + radio_handle_fifo_si4032(entry, shared_state); + return; + } + if (radio_si4032_state_change) { radio_si4032_state_change = false; pwm_timer_set_frequency(radio_si4032_freq);