RS41ng/src/radio_si4063.c

371 wiersze
11 KiB
C

#include "config.h"
#ifdef DFM17
#include "hal/system.h"
#include "hal/spi.h"
#include "hal/pwm.h"
#include "hal/delay.h"
#include "hal/datatimer.h"
#include "drivers/si4063/si4063.h"
#include "log.h"
#include "radio_si4063.h"
#include "codecs/mfsk/mfsk.h"
#define SI4063_DEVIATION_HZ_RTTY 200.0
#define SI4063_DEVIATION_HZ_APRS 2600.0
#define SI4063_DEVIATION_HZ_CATS 4800.0
#define CW_SYMBOL_RATE_MULTIPLIER 4
// TODO: Add support for multiple APRS baud rates
// This delay is for DFM-17 radiosondes
#define symbol_delay_bell_202_1200bps_us 821
static volatile bool radio_si4063_state_change = false;
static volatile uint32_t radio_si4063_freq = 0;
bool radio_start_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
{
uint16_t frequency_offset;
uint32_t frequency_deviation = 0;
uint32_t data_rate = 0;
si4063_modulation_type modulation_type;
bool use_direct_mode;
bool use_fifo_mode = false;
switch (entry->data_mode) {
case RADIO_DATA_MODE_CW:
case RADIO_DATA_MODE_PIP:
frequency_offset = 1;
modulation_type = SI4063_MODULATION_TYPE_OOK;
use_direct_mode = false;
data_timer_init(entry->symbol_rate * CW_SYMBOL_RATE_MULTIPLIER);
break;
case RADIO_DATA_MODE_RTTY:
frequency_offset = 0;
frequency_deviation = SI4063_DEVIATION_HZ_RTTY;
modulation_type = SI4063_MODULATION_TYPE_CW;
use_direct_mode = false;
break;
case RADIO_DATA_MODE_APRS_1200:
frequency_offset = 0;
frequency_deviation = SI4063_DEVIATION_HZ_APRS;
modulation_type = SI4063_MODULATION_TYPE_FSK;
use_direct_mode = true;
break;
case RADIO_DATA_MODE_HORUS_V1:
case RADIO_DATA_MODE_HORUS_V2: {
fsk_tone *idle_tone = mfsk_get_idle_tone(&entry->fsk_encoder);
frequency_offset = (uint16_t) idle_tone->index + HORUS_FREQUENCY_OFFSET_SI4063;
modulation_type = SI4063_MODULATION_TYPE_CW;
use_direct_mode = false;
data_timer_init(entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder));
break;
}
case RADIO_DATA_MODE_CATS:
frequency_offset = 0;
frequency_deviation = SI4063_DEVIATION_HZ_CATS;
modulation_type = SI4063_MODULATION_TYPE_FIFO_FSK;
use_direct_mode = false;
use_fifo_mode = true;
data_rate = 9600;
break;
default:
return false;
}
si4063_set_tx_frequency(entry->frequency);
si4063_set_tx_power(entry->tx_power);
si4063_set_frequency_offset(frequency_offset);
si4063_set_modulation_type(modulation_type);
si4063_set_frequency_deviation(frequency_deviation);
if (use_fifo_mode) {
si4063_set_data_rate(data_rate);
}
else {
si4063_enable_tx();
}
if (use_direct_mode) {
spi_uninit();
pwm_timer_init(100 * 100); // TODO: Idle tone
pwm_timer_use(true);
pwm_timer_pwm_enable(true);
}
switch (entry->data_mode) {
case RADIO_DATA_MODE_CW:
case RADIO_DATA_MODE_PIP:
spi_uninit();
system_disable_tick();
shared_state->radio_interrupt_transmit_active = true;
break;
case RADIO_DATA_MODE_APRS_1200:
shared_state->radio_manual_transmit_active = true;
break;
case RADIO_DATA_MODE_HORUS_V1:
case RADIO_DATA_MODE_HORUS_V2:
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;
}
return true;
}
static uint32_t radio_next_symbol_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
{
switch (entry->data_mode) {
case RADIO_DATA_MODE_CW:
case RADIO_DATA_MODE_PIP:
return 0;
case RADIO_DATA_MODE_RTTY:
return 0;
case RADIO_DATA_MODE_APRS_1200: {
int8_t next_tone_index = entry->fsk_encoder_api->next_tone(&entry->fsk_encoder);
if (next_tone_index < 0) {
return 0;
}
return shared_state->radio_current_fsk_tones[next_tone_index].frequency_hz_100;
}
default:
return 0;
}
}
bool radio_transmit_symbol_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
{
uint32_t frequency = radio_next_symbol_si4063(entry, shared_state);
if (frequency == 0) {
return false;
}
radio_si4063_freq = frequency;
radio_si4063_state_change = true;
return true;
}
static void radio_handle_main_loop_manual_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
{
fsk_encoder_api *fsk_encoder_api = entry->fsk_encoder_api;
fsk_encoder *fsk_enc = &entry->fsk_encoder;
for (uint8_t i = 0; i < shared_state->radio_current_fsk_tone_count; i++) {
precalculated_pwm_periods[i] = pwm_calculate_period(shared_state->radio_current_fsk_tones[i].frequency_hz_100);
}
system_disable_tick();
switch (entry->data_mode) {
case RADIO_DATA_MODE_APRS_1200: {
int8_t tone_index;
while ((tone_index = fsk_encoder_api->next_tone(fsk_enc)) >= 0) {
pwm_timer_set_frequency(precalculated_pwm_periods[tone_index]);
shared_state->radio_symbol_count_loop++;
delay_us(symbol_delay_bell_202_1200bps_us);
}
radio_si4063_state_change = false;
shared_state->radio_transmission_finished = true;
break;
}
default:
break;
}
system_enable_tick();
}
void radio_handle_fifo_si4063(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);
uint16_t written = si4063_start_tx(data, len);
data += written;
len -= written;
while(len > 0) {
uint16_t written = si4063_refill_buffer(data, len);
data += written;
len -= written;
if(si4063_fifo_underflow()) {
log_info("FIFO underflow - Aborting\n");
shared_state->radio_transmission_finished = true;
return;
}
}
int err = si4063_wait_for_tx_complete(1000);
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_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
{
if (entry->radio_type != RADIO_TYPE_SI4063 || shared_state->radio_interrupt_transmit_active) {
return;
}
if (shared_state->radio_manual_transmit_active) {
radio_handle_main_loop_manual_si4063(entry, shared_state);
return;
}
if (shared_state->radio_fifo_transmit_active) {
radio_handle_fifo_si4063(entry, shared_state);
return;
}
if (radio_si4063_state_change) {
radio_si4063_state_change = false;
pwm_timer_set_frequency(radio_si4063_freq);
shared_state->radio_symbol_count_loop++;
}
}
inline void radio_handle_data_timer_si4063()
{
static int cw_symbol_rate_multiplier = CW_SYMBOL_RATE_MULTIPLIER;
if (radio_current_transmit_entry->radio_type != RADIO_TYPE_SI4063 || !radio_shared_state.radio_interrupt_transmit_active) {
return;
}
switch (radio_current_transmit_entry->data_mode) {
case RADIO_DATA_MODE_CW:
case RADIO_DATA_MODE_PIP: {
cw_symbol_rate_multiplier--;
if (cw_symbol_rate_multiplier > 0) {
break;
}
cw_symbol_rate_multiplier = CW_SYMBOL_RATE_MULTIPLIER;
fsk_encoder_api *fsk_encoder_api = radio_current_transmit_entry->fsk_encoder_api;
fsk_encoder *fsk_enc = &radio_current_transmit_entry->fsk_encoder;
int8_t tone_index;
tone_index = fsk_encoder_api->next_tone(fsk_enc);
if (tone_index < 0) {
si4063_set_direct_mode_pin(false);
log_info("CW TX finished\n");
radio_shared_state.radio_interrupt_transmit_active = false;
radio_shared_state.radio_transmission_finished = true;
system_enable_tick();
break;
}
si4063_set_direct_mode_pin(tone_index == 0 ? false : true);
radio_shared_state.radio_symbol_count_interrupt++;
break;
}
case RADIO_DATA_MODE_HORUS_V1:
case RADIO_DATA_MODE_HORUS_V2: {
fsk_encoder_api *fsk_encoder_api = radio_current_transmit_entry->fsk_encoder_api;
fsk_encoder *fsk_enc = &radio_current_transmit_entry->fsk_encoder;
int8_t tone_index;
tone_index = fsk_encoder_api->next_tone(fsk_enc);
if (tone_index < 0) {
log_info("Horus TX finished\n");
radio_shared_state.radio_interrupt_transmit_active = false;
radio_shared_state.radio_transmission_finished = true;
system_enable_tick();
break;
}
// NOTE: The factor of 23 will produce a tone spacing of about 270 Hz, which is the standard spacing for Horus 4FSK
si4063_set_frequency_offset(tone_index * 23 + HORUS_FREQUENCY_OFFSET_SI4063);
radio_shared_state.radio_symbol_count_interrupt++;
break;
}
default:
break;
}
}
bool radio_stop_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state)
{
bool use_direct_mode = false;
switch (entry->data_mode) {
case RADIO_DATA_MODE_CW:
case RADIO_DATA_MODE_PIP:
data_timer_uninit();
spi_init();
break;
case RADIO_DATA_MODE_RTTY:
case RADIO_DATA_MODE_HORUS_V1:
case RADIO_DATA_MODE_HORUS_V2:
data_timer_uninit();
break;
case RADIO_DATA_MODE_CATS:
break;
case RADIO_DATA_MODE_APRS_1200:
use_direct_mode = true;
break;
default:
break;
}
if (use_direct_mode) {
pwm_timer_pwm_enable(false);
pwm_timer_use(false);
pwm_timer_uninit();
spi_init();
}
si4063_inhibit_tx();
switch (entry->data_mode) {
case RADIO_DATA_MODE_CW:
case RADIO_DATA_MODE_PIP:
system_enable_tick();
break;
case RADIO_DATA_MODE_APRS_1200:
break;
case RADIO_DATA_MODE_HORUS_V1:
case RADIO_DATA_MODE_HORUS_V2:
system_enable_tick();
break;
case RADIO_DATA_MODE_CATS:
break;
default:
break;
}
return true;
}
void radio_init_si4063()
{
}
#endif