diff --git a/legacy-code/morse.c b/legacy-code/morse.c deleted file mode 100644 index 89f9f62..0000000 --- a/legacy-code/morse.c +++ /dev/null @@ -1,151 +0,0 @@ -// Morse Code Playback Functions -// Mark Jessop 2018-04 -// OK1TE 2018-11 -// -// Based on code from https://github.com/Paradoxis/Arduino-morse-code-translator/blob/master/main.ino -// -#include "morse.h" -#include "config.h" - -// All morse delays -#define MORSE_DELAY (1200 / MORSE_WPM) -#define MORSE_DELAY_DOT (MORSE_DELAY * 1) -#define MORSE_DELAY_DASH (MORSE_DELAY * 3) -#define MORSE_DELAY_SPACE (MORSE_DELAY * 7) - -// All morse characters -const char MORSE_DOT = '.'; -const char MORSE_DASH = '-'; - -// Letters -const char* const MORSE_LETTERS[] = { - ".-", // A - "-...", // B - "-.-.", // C - "-..", // D - ".", // E - "..-.", // F - "--.", // G - "....", // H - "..", // I - ".---", // J - "-.-", // K - ".-..", // L - "--", // M - "-.", // N - "---", // O - ".--.", // P - "--.-", // Q - ".-.", // R - "...", // S - "-", // T - "..-", // U - "...-", // V - ".--", // W - "-..-", // X - "-.--", // Y - "--.." // Z -}; - -// Numerals. -const char* const MORSE_NUMBERS[] = { - "-----", // 0 - ".----", // 1 - "..---", // 2 - "...--", // 3 - "....-", // 4 - ".....", // 5 - "-....", // 6 - "--...", // 7 - "---..", // 8 - "----." // 9 -}; - -// Symbols -const char Morse_Slash[] = "-..-."; -const char Morse_Equal[] = "-...-"; -const char Morse_FullStop[] = ".-.-.-"; -const char Morse_Comma[] = "--..--"; -const char Morse_QuestionMark[] = "..--.."; -const char Morse_Plus[] = ".-.-."; -const char Morse_AtSign[] = ".--.-."; - -// Send a single character -void sendDotOrDash (char unit) { - //radio_enable_tx(); - - // Unit is a dot (500 ms) - if (unit == MORSE_DOT) { - //_delay_ms(MORSE_DELAY_DOT); - } - - // Unit is a dash (1500 ms) - else if (unit == MORSE_DASH) { - //_delay_ms(MORSE_DELAY_DASH); - } - - // Inter-element gap - //radio_inhibit_tx(); - //_delay_ms(MORSE_DELAY); -} - -void sendMorseSequence (const char* sequence) { - // Counter - int i = 0; - - // Loop through every character until an 'end-of-line' (null) character is found - while (sequence[i] != 0) { - sendDotOrDash(sequence[i]); - i++; - } - - // Delay between every letter - //_delay_ms(MORSE_DELAY * 3); -} - -void sendMorse(const char* message){ - int i = 0; - while (message[i] != 0){ - const char current = message[i]; - - // Lower case letters - if (current >= 'a' && current <= 'z') { - sendMorseSequence(MORSE_LETTERS[current - 'a']); - } - - // Capital case letters - else if (current >= 'A' && current <= 'Z') { - sendMorseSequence(MORSE_LETTERS[current - 'A']); - } - - // Numbers - else if (current >= '0' && current <= '9') { - sendMorseSequence(MORSE_NUMBERS[current - '0']); - } - - else switch (current) { - case '/': sendMorseSequence(Morse_Slash); - break; - case '=': sendMorseSequence(Morse_Equal); - break; - case '.': sendMorseSequence(Morse_FullStop); - break; - case ',': sendMorseSequence(Morse_Comma); - break; - case '?': sendMorseSequence(Morse_QuestionMark); - break; - case '+': sendMorseSequence(Morse_Plus); - break; - case '@': sendMorseSequence(Morse_AtSign); - break; - default: - break; - // Treat all other characters as a space. - //_delay_ms(MORSE_DELAY_SPACE); - } - - i++; - } - - //radio_disable_tx(); -} diff --git a/legacy-code/morse.h b/legacy-code/morse.h deleted file mode 100644 index c81784a..0000000 --- a/legacy-code/morse.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Morse Code Playback Functions -// Mark Jessop 2018-04 -// OK1TE 2018-10 -// -#ifndef __MORSE_H -#define __MORSE_H - -void sendDotOrDash (char unit); -void sendMorseSequence (const char* sequence); -void sendMorse(const char* message); - -#endif //__MORSE_H \ No newline at end of file diff --git a/src/codecs/morse/morse.c b/src/codecs/morse/morse.c new file mode 100644 index 0000000..20b8e23 --- /dev/null +++ b/src/codecs/morse/morse.c @@ -0,0 +1,265 @@ +#include +#include +#include + +#include "morse.h" + +// Morse code definitions adapted from code by Mark Jessop VK5QI and OK1TE, also based on +// https://github.com/Paradoxis/Arduino-morse-code-translator/blob/master/main.ino + +#define MORSE_UNITS_DOT 1 +#define MORSE_UNITS_DASH 3 +#define MORSE_UNITS_GAP 3 +#define MORSE_UNITS_SPACE 7 + +const char MORSE_DOT = '.'; +const char MORSE_DASH = '-'; +const char MORSE_SPACE = ' '; + +const char *const morse_letters[] = { + ".-", // A + "-...", // B + "-.-.", // C + "-..", // D + ".", // E + "..-.", // F + "--.", // G + "....", // H + "..", // I + ".---", // J + "-.-", // K + ".-..", // L + "--", // M + "-.", // N + "---", // O + ".--.", // P + "--.-", // Q + ".-.", // R + "...", // S + "-", // T + "..-", // U + "...-", // V + ".--", // W + "-..-", // X + "-.--", // Y + "--.." // Z +}; + +const char *const morse_numbers[] = { + "-----", // 0 + ".----", // 1 + "..---", // 2 + "...--", // 3 + "....-", // 4 + ".....", // 5 + "-....", // 6 + "--...", // 7 + "---..", // 8 + "----." // 9 +}; + +const char morse_stroke[] = "-..-."; +const char morse_equal[] = "-...-"; +const char morse_full_stop[] = ".-.-.-"; +const char morse_comma[] = "--..--"; +const char morse_question_mark[] = "..--.."; +const char morse_plus[] = ".-.-."; +const char morse_at_sign[] = ".--.-."; +const char morse_space[] = " "; + +static const char *morse_get_sequence(char c) +{ + if (c >= 'A' && c <= 'Z') { // Uppercase letters + return morse_letters[c - 'A']; + } else if (c >= 'a' && c <= 'z') { // Lowercase letters + return morse_letters[c - 'a']; + } else if (c >= '0' && c <= '9') { // Numbers + return morse_numbers[c - '0']; + } + + switch (c) { + case '/': + return morse_stroke; + case '=': + return morse_equal; + case '.': + return morse_full_stop; + case ',': + return morse_comma; + case '?': + return morse_question_mark; + case '+': + return morse_plus; + case '@': + return morse_at_sign; + default: + // Treat all other characters as a space + return morse_space; + } +} + +typedef struct _morse_encoder { + uint32_t symbol_rate; + + uint16_t data_length; + uint8_t *data; + + uint16_t current_byte_index; + + const char *current_sequence; + uint8_t current_sequence_index; + + bool tone_active; + uint8_t units_left; + + bool start; + + fsk_tone tones[2]; +} morse_encoder; + +void morse_encoder_new(fsk_encoder *encoder, uint32_t symbol_rate) +{ + encoder->priv = malloc(sizeof(morse_encoder)); + memset(encoder->priv, 0, sizeof(morse_encoder)); + + morse_encoder *morse = (morse_encoder *) encoder->priv; + morse->symbol_rate = symbol_rate; + + for (int i = 0; i < 2; i++) { + morse->tones[i].index = (int8_t) i; + morse->tones[i].frequency_hz_100 = i; + } +} + +void morse_encoder_destroy(fsk_encoder *encoder) +{ + if (encoder->priv != NULL) { + free(encoder->priv); + encoder->priv = NULL; + } +} + +void morse_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data) +{ + morse_encoder *morse = (morse_encoder *) encoder->priv; + + morse->data = data; + morse->data_length = data_length; + + morse->current_byte_index = 0; + morse->current_sequence = morse_get_sequence((char) data[0]); + morse->current_sequence_index = 0; + morse->tone_active = false; + morse->units_left = 0; + morse->start = true; +} + +void morse_encoder_get_tones(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones) +{ + morse_encoder *morse = (morse_encoder *) encoder->priv; + + *tone_count = 2; + *tones = morse->tones; +} + +uint32_t morse_encoder_get_tone_spacing(fsk_encoder *encoder) +{ + return 0; +} + +uint32_t morse_encoder_get_symbol_rate(fsk_encoder *encoder) +{ + morse_encoder *morse = (morse_encoder *) encoder->priv; + return morse->symbol_rate; +} + +uint32_t morse_encoder_get_symbol_delay(fsk_encoder *encoder) +{ + return 0; +} + +int8_t morse_encoder_next_tone(fsk_encoder *encoder) +{ + morse_encoder *morse = (morse_encoder *) encoder->priv; + + if (morse->current_byte_index >= morse->data_length) { + return -1; + } + + if (!morse->start && morse->units_left == 0) { + char next_element = morse->current_sequence[morse->current_sequence_index + 1]; + if (morse->tone_active) { + if (next_element == 0) { + bool char_gap = true; + + if (morse->current_byte_index + 1 < morse->data_length) { + const char *next_byte_sequence = morse_get_sequence((char) morse->data[morse->current_byte_index + 1]); + if (next_byte_sequence[0] == MORSE_SPACE) { + char_gap = false; + } + } else { + char_gap = false; + } + + if (char_gap) { + // Char gap + morse->tone_active = false; + morse->units_left = MORSE_UNITS_GAP - 1; + return 0; + } + } else { + // Unit gap + morse->tone_active = false; + return 0; + } + } + + morse->current_sequence_index++; + + if (next_element == 0) { + morse->current_byte_index++; + + if (morse->current_byte_index >= morse->data_length) { + return -1; + } + + morse->current_sequence = morse_get_sequence((char) morse->data[morse->current_byte_index]); + morse->current_sequence_index = 0; + + morse->tone_active = false; + } + } + + morse->start = false; + + char element = morse->current_sequence[morse->current_sequence_index]; + + if (morse->units_left > 0) { + morse->units_left--; + return morse->tone_active ? 1 : 0; + } + + if (element == MORSE_DOT) { + morse->units_left = MORSE_UNITS_DOT; + morse->tone_active = true; + } else if (element == MORSE_DASH) { + morse->units_left = MORSE_UNITS_DASH; + morse->tone_active = true; + } else { + morse->units_left = MORSE_UNITS_SPACE; + morse->tone_active = false; + } + + morse->units_left--; + + return morse->tone_active ? 1 : 0; +} + +fsk_encoder_api morse_fsk_encoder_api = { + .get_tones = morse_encoder_get_tones, + .get_tone_spacing = morse_encoder_get_tone_spacing, + .get_symbol_rate = morse_encoder_get_symbol_rate, + .get_symbol_delay = morse_encoder_get_symbol_delay, + .set_data = morse_encoder_set_data, + .next_tone = morse_encoder_next_tone, +}; diff --git a/src/codecs/morse/morse.h b/src/codecs/morse/morse.h new file mode 100644 index 0000000..b7ee967 --- /dev/null +++ b/src/codecs/morse/morse.h @@ -0,0 +1,18 @@ +#ifndef __MORSE_H +#define __MORSE_H + +#include + +#include "codecs/fsk/fsk.h" + +void morse_encoder_new(fsk_encoder *encoder, uint32_t symbol_rate); +void morse_encoder_destroy(fsk_encoder *encoder); +void morse_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data); +void morse_encoder_get_tones(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones); +uint32_t morse_encoder_get_symbol_rate(fsk_encoder *encoder); +uint32_t morse_encoder_get_symbol_delay(fsk_encoder *encoder); +int8_t morse_encoder_next_tone(fsk_encoder *encoder); + +extern fsk_encoder_api morse_fsk_encoder_api; + +#endif diff --git a/src/config.c b/src/config.c index 6648079..87be0f4 100644 --- a/src/config.c +++ b/src/config.c @@ -46,6 +46,16 @@ bool si5351_enabled = RADIO_SI5351_ENABLE; volatile bool system_initialized = false; +/** + * CW mode messages. + * Maximum length: 64 characters. + */ +char *cw_message_templates[] = { + "$cs", + "$loc6", + NULL +}; + /** * APRS mode comment messages. * Maximum length: depends on the packet contents, but keeping this under 100 characters is usually safe. @@ -64,8 +74,8 @@ char *aprs_comment_templates[] = { * Maximum length: 130 characters. */ char *fsq_comment_templates[] = { - " $lat $lon, $alt m, $cl m/s, $gs km/h, $he deg - " FSQ_COMMENT, - " $loc12, $teC $hu% $prmb $hh:$mm:$ss @ $tow ms - " FSQ_COMMENT, +// " $lat $lon, $alt m, $cl m/s, $gs km/h, $he deg - " FSQ_COMMENT, +// " $loc12, $teC $hu% $prmb $hh:$mm:$ss @ $tow ms - " FSQ_COMMENT, NULL }; @@ -74,10 +84,10 @@ char *fsq_comment_templates[] = { * Maximum length: 13 characters allowed by the protocols. */ char *ftjt_message_templates[] = { - "$cs $loc4", - "$loc12", - "$altm $cl", - "$bvmV $tiC", - "$hu% $prmb", +// "$cs $loc4", +// "$loc12", +// "$altm $cl", +// "$bvmV $tiC", +// "$hu% $prmb", NULL }; diff --git a/src/config.h b/src/config.h index cb4518c..5eab464 100644 --- a/src/config.h +++ b/src/config.h @@ -42,14 +42,12 @@ #define RADIO_SI4032_TX_POWER 7 // Which modes to transmit using the built-in Si4032 transmitter chip -#define RADIO_SI4032_TX_CW false -#define RADIO_SI4032_TX_RTTY false +#define RADIO_SI4032_TX_CW true #define RADIO_SI4032_TX_APRS true #define RADIO_SI4032_TX_HORUS_V1 true // Transmit frequencies for the Si4032 transmitter modes -#define RADIO_SI4032_TX_FREQUENCY_CW 432060000 -#define RADIO_SI4032_TX_FREQUENCY_RTTY 432060000 +#define RADIO_SI4032_TX_FREQUENCY_CW 432500000 #define RADIO_SI4032_TX_FREQUENCY_APRS_1200 432500000 // Use a frequency offset to place FSK tones slightly above the defined frequency for SSB reception #define RADIO_SI4032_TX_FREQUENCY_HORUS_V1 432501000 @@ -132,10 +130,16 @@ #define HORUS_V1_TIME_SYNC_OFFSET_SECONDS 0 /** - * TODO: CW settings (once implemented) + * CW settings */ -#define CW_LOCATOR_PAIR_COUNT 4 // max. 6 (12 characters WWL) +// CW speed in WPM, range 5 - 40 +#define CW_SPEED_WPM 20 + +// Schedule transmission every N seconds, counting from beginning of an hour (based on GPS time). Set to zero to disable time sync. +#define CW_TIME_SYNC_SECONDS 1 +// Delay transmission for an N second offset after the scheduled time. +#define CW_TIME_SYNC_OFFSET_SECONDS 0 /** * WSPR settings diff --git a/src/config_internal.h b/src/config_internal.h index b4cc6b2..8b123bb 100644 --- a/src/config_internal.h +++ b/src/config_internal.h @@ -5,6 +5,9 @@ #define RADIO_SYMBOL_DATA_MAX_LENGTH 512 #define RADIO_PAYLOAD_MESSAGE_MAX_LENGTH 128 +// PARIS: 50 dot durations, 20 WPM -> 60ms per unit +#define MORSE_WPM_TO_SYMBOL_RATE(wpm) (1000 / (60 * 20 / wpm)) + #include extern bool leds_enabled; @@ -13,6 +16,7 @@ extern bool si5351_enabled; extern volatile bool system_initialized; +extern char *cw_message_templates[]; extern char *aprs_comment_templates[]; extern char *fsq_comment_templates[]; extern char *ftjt_message_templates[]; diff --git a/src/drivers/si4032/si4032.c b/src/drivers/si4032/si4032.c index 0e69ea3..313875e 100644 --- a/src/drivers/si4032/si4032.c +++ b/src/drivers/si4032/si4032.c @@ -7,22 +7,20 @@ #define SI4032_CLOCK 26.0f -#define GPIO_SI_4032_CS GPIOC -#define GPIO_PIN_SI4032_CS GPIO_Pin_13 +#define GPIO_SI4032_NSEL GPIOC +#define GPIO_PIN_SI4032_NSEL GPIO_Pin_13 -// TODO: For CW support: -// TODO: static const uint16_t radioSDIpin = GPIO_Pin_15; // @ GPIOB! -// TODO: Add methods to init SDI pin GPIO and to set SDI pin state -> Verify it can be used for OOK (CW), NSEL (CS) must be high -// TODO: Call uninitialization of PWM timer after use so that SDI pin is free to use +#define GPIO_SI4032_SDI GPIOB +#define GPIO_PIN_SI4032_SDI GPIO_Pin_15 static inline uint8_t si4032_write(uint8_t reg, uint8_t value) { - return spi_send_and_receive(GPIO_SI_4032_CS, GPIO_PIN_SI4032_CS, ((reg | SPI_WRITE_FLAG) << 8U) | value); + return spi_send_and_receive(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL, ((reg | SPI_WRITE_FLAG) << 8U) | value); } static inline uint8_t si4032_read(uint8_t reg) { - return spi_send_and_receive(GPIO_SI_4032_CS, GPIO_PIN_SI4032_CS, (reg << 8U) | 0xFFU); + return spi_send_and_receive(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL, (reg << 8U) | 0xFFU); } void si4032_soft_reset() @@ -50,9 +48,9 @@ void si4032_disable_tx() void si4032_use_direct_mode(bool use) { if (use) { - GPIO_SetBits(GPIO_SI_4032_CS, GPIO_PIN_SI4032_CS); + GPIO_SetBits(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL); } else { - GPIO_ResetBits(GPIO_SI_4032_CS, GPIO_PIN_SI4032_CS); + GPIO_ResetBits(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL); } } @@ -138,15 +136,49 @@ int32_t si4032_read_temperature_celsius_100() return temperature; } +static void si4032_set_nsel_pin(bool high) +{ + if (high) { + GPIO_SetBits(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL); + } else { + GPIO_ResetBits(GPIO_SI4032_NSEL, GPIO_PIN_SI4032_NSEL); + } +} + +void si4032_set_sdi_pin(bool high) +{ + if (high) { + GPIO_SetBits(GPIO_SI4032_SDI, GPIO_PIN_SI4032_SDI); + } else { + GPIO_ResetBits(GPIO_SI4032_SDI, GPIO_PIN_SI4032_SDI); + } +} + +void si4032_use_sdi_pin(bool use) +{ + GPIO_InitTypeDef gpio_init; + + si4032_set_nsel_pin(true); + + gpio_init.GPIO_Pin = GPIO_PIN_SI4032_SDI; + gpio_init.GPIO_Mode = use ? GPIO_Mode_Out_PP : GPIO_Mode_AF_PP; + gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIO_SI4032_SDI, &gpio_init); + + si4032_set_sdi_pin(false); +} + void si4032_init() { GPIO_InitTypeDef gpio_init; // Si4032 chip select pin - gpio_init.GPIO_Pin = GPIO_PIN_SI4032_CS; + gpio_init.GPIO_Pin = GPIO_PIN_SI4032_NSEL; gpio_init.GPIO_Mode = GPIO_Mode_Out_PP; gpio_init.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIO_SI_4032_CS, &gpio_init); + GPIO_Init(GPIO_SI4032_NSEL, &gpio_init); + + si4032_set_nsel_pin(true); si4032_soft_reset(); si4032_set_tx_power(0); diff --git a/src/drivers/si4032/si4032.h b/src/drivers/si4032/si4032.h index 07b0efb..f26b054 100644 --- a/src/drivers/si4032/si4032.h +++ b/src/drivers/si4032/si4032.h @@ -22,6 +22,8 @@ void si4032_set_frequency_offset_small(uint8_t offset); void si4032_set_frequency_deviation(uint8_t deviation); void si4032_set_modulation_type(si4032_modulation_type type); int32_t si4032_read_temperature_celsius_100(); +void si4032_set_sdi_pin(bool high); +void si4032_use_sdi_pin(bool use); void si4032_init(); #endif diff --git a/src/radio.c b/src/radio.c index bc0be68..b1f749a 100644 --- a/src/radio.c +++ b/src/radio.c @@ -6,6 +6,7 @@ #include "hal/system.h" #include "hal/delay.h" #include "hal/usart_gps.h" +#include "codecs/morse/morse.h" #include "codecs/bell/bell.h" #include "codecs/mfsk/mfsk.h" #include "codecs/jtencode/jtencode.h" @@ -13,6 +14,7 @@ #include "radio_internal.h" #include "radio_si4032.h" #include "radio_si5351.h" +#include "radio_payload_cw.h" #include "radio_payload_aprs.h" #include "radio_payload_horus_v1.h" #include "radio_payload_wspr.h" @@ -20,6 +22,18 @@ #include "radio_payload_fsq.h" radio_transmit_entry radio_transmit_schedule[] = { + { + .enabled = RADIO_SI4032_TX_CW, + .radio_type = RADIO_TYPE_SI4032, + .data_mode = RADIO_DATA_MODE_CW, + .time_sync_seconds = CW_TIME_SYNC_SECONDS, + .time_sync_seconds_offset = CW_TIME_SYNC_OFFSET_SECONDS, + .frequency = RADIO_SI4032_TX_FREQUENCY_CW, + .tx_power = RADIO_SI4032_TX_POWER, + .symbol_rate = MORSE_WPM_TO_SYMBOL_RATE(CW_SPEED_WPM), + .payload_encoder = &radio_cw_payload_encoder, + .fsk_encoder_api = &morse_fsk_encoder_api, + }, { .enabled = RADIO_SI4032_TX_APRS, .radio_type = RADIO_TYPE_SI4032, @@ -233,6 +247,11 @@ static bool radio_start_transmit(radio_transmit_entry *entry) switch (entry->data_mode) { case RADIO_DATA_MODE_CW: + morse_encoder_new(&entry->fsk_encoder, entry->symbol_rate); + radio_shared_state.radio_current_symbol_rate = entry->fsk_encoder_api->get_symbol_rate(&entry->fsk_encoder); + entry->fsk_encoder_api->get_tones(&entry->fsk_encoder, &radio_shared_state.radio_current_fsk_tone_count, + &radio_shared_state.radio_current_fsk_tones); + entry->fsk_encoder_api->set_data(&entry->fsk_encoder, radio_current_payload_length, radio_current_payload); break; case RADIO_DATA_MODE_RTTY: break; @@ -354,6 +373,7 @@ static bool radio_stop_transmit(radio_transmit_entry *entry) switch (entry->data_mode) { case RADIO_DATA_MODE_CW: + morse_encoder_destroy(&entry->fsk_encoder); break; case RADIO_DATA_MODE_RTTY: break; @@ -616,6 +636,9 @@ void radio_init() for (uint8_t i = 0; i < radio_transmit_entry_count; i++) { radio_transmit_entry *entry = &radio_transmit_schedule[i]; switch (entry->data_mode) { + case RADIO_DATA_MODE_CW: + entry->messages = cw_message_templates; + break; case RADIO_DATA_MODE_APRS_1200: entry->messages = aprs_comment_templates; break; diff --git a/src/radio_payload_cw.c b/src/radio_payload_cw.c new file mode 100644 index 0000000..ce1201b --- /dev/null +++ b/src/radio_payload_cw.c @@ -0,0 +1,12 @@ +#include +#include +#include "radio_payload_cw.h" + +uint16_t radio_cw_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) +{ + return snprintf((char *) payload, length, "%s", message); +} + +payload_encoder radio_cw_payload_encoder = { + .encode = radio_cw_encode, +}; diff --git a/src/radio_payload_cw.h b/src/radio_payload_cw.h new file mode 100644 index 0000000..700364c --- /dev/null +++ b/src/radio_payload_cw.h @@ -0,0 +1,8 @@ +#ifndef __RADIO_PAYLOAD_CW_H +#define __RADIO_PAYLOAD_CW_H + +#include "payload.h" + +extern payload_encoder radio_cw_payload_encoder; + +#endif diff --git a/src/radio_si4032.c b/src/radio_si4032.c index 870b042..41f7d6b 100644 --- a/src/radio_si4032.c +++ b/src/radio_si4032.c @@ -12,6 +12,8 @@ #include "radio_si4032.h" #include "codecs/mfsk/mfsk.h" +#define CW_SYMBOL_RATE_MULTIPLIER 4 + /** * I have attempted to implement Bell 202 frequency generation using hardware DMA and PWM, but have failed to generate * correct symbol rate that other APRS equipment are able to decode. I have tried to decode the DMA-based modulation with @@ -46,6 +48,8 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state frequency_offset = 1; modulation_type = SI4032_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; @@ -83,12 +87,19 @@ bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state if (use_direct_mode) { spi_uninit(); + pwm_timer_init(100 * 100); // TODO: Idle tone pwm_timer_use(true); pwm_timer_pwm_enable(true); si4032_use_direct_mode(true); } switch (entry->data_mode) { + case RADIO_DATA_MODE_CW: + spi_uninit(); + system_disable_tick(); + si4032_use_sdi_pin(true); + shared_state->radio_interrupt_transmit_active = true; + break; case RADIO_DATA_MODE_APRS_1200: if (si4032_use_dma) { shared_state->radio_dma_transfer_active = true; @@ -196,11 +207,40 @@ void radio_handle_main_loop_si4032(radio_transmit_entry *entry, radio_module_sta inline void radio_handle_data_timer_si4032() { + static int cw_symbol_rate_multiplier = CW_SYMBOL_RATE_MULTIPLIER; + if (radio_current_transmit_entry->radio_type != RADIO_TYPE_SI4032 || !radio_shared_state.radio_interrupt_transmit_active) { return; } switch (radio_current_transmit_entry->data_mode) { + case RADIO_DATA_MODE_CW: { + 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) { + si4032_set_sdi_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; + } + + si4032_set_sdi_pin(tone_index == 0 ? false : true); + + radio_shared_state.radio_symbol_count_interrupt++; + break; + } case RADIO_DATA_MODE_HORUS_V1: { fsk_encoder_api *fsk_encoder_api = radio_current_transmit_entry->fsk_encoder_api; fsk_encoder *fsk_enc = &radio_current_transmit_entry->fsk_encoder; @@ -216,6 +256,7 @@ inline void radio_handle_data_timer_si4032() } si4032_set_frequency_offset_small(tone_index + HORUS_V1_FREQUENCY_OFFSET); + radio_shared_state.radio_symbol_count_interrupt++; break; } @@ -230,9 +271,13 @@ bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state switch (entry->data_mode) { case RADIO_DATA_MODE_CW: + si4032_use_sdi_pin(false); + data_timer_uninit(); + spi_init(); + break; case RADIO_DATA_MODE_RTTY: case RADIO_DATA_MODE_HORUS_V1: - use_direct_mode = false; + data_timer_uninit(); break; case RADIO_DATA_MODE_APRS_1200: use_direct_mode = true; @@ -245,12 +290,16 @@ bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state si4032_use_direct_mode(false); pwm_timer_pwm_enable(false); pwm_timer_use(false); + pwm_timer_uninit(); spi_init(); } si4032_inhibit_tx(); switch (entry->data_mode) { + case RADIO_DATA_MODE_CW: + system_enable_tick(); + break; case RADIO_DATA_MODE_APRS_1200: if (si4032_use_dma) { pwm_data_timer_uninit(); @@ -258,7 +307,6 @@ bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state } break; case RADIO_DATA_MODE_HORUS_V1: - data_timer_uninit(); system_enable_tick(); break; default: @@ -338,8 +386,6 @@ void radio_init_si4032() pwm_handle_dma_transfer_half = radio_si4032_handle_pwm_transfer_half; pwm_handle_dma_transfer_full = radio_si4032_handle_pwm_transfer_full; - pwm_timer_init(100 * 100); - if (si4032_use_dma) { pwm_data_timer_init(); pwm_dma_init(); diff --git a/tests/morse_test.c b/tests/morse_test.c new file mode 100644 index 0000000..62b02e0 --- /dev/null +++ b/tests/morse_test.c @@ -0,0 +1,28 @@ +#include +#include + +#include "codecs/morse/morse.h" +#include "config.h" + +int main(void) +{ + fsk_encoder morse; + + printf("%d\n", MORSE_WPM_TO_SYMBOL_RATE(20)); + printf("%d\n", MORSE_WPM_TO_SYMBOL_RATE(15)); + printf("%d\n", MORSE_WPM_TO_SYMBOL_RATE(10)); + + char *input = "TEST T"; + + morse_encoder_new(&morse, 25); + + morse_encoder_set_data(&morse, strlen(input), (uint8_t *) input); + + int8_t tone_index; + + while ((tone_index = morse_encoder_next_tone(&morse)) >= 0) { + printf("CW: %d\n", tone_index); + } + + morse_encoder_destroy(&morse); +} diff --git a/tests/template_test.c b/tests/template_test.c index 1586ce6..3b8dc2d 100644 --- a/tests/template_test.c +++ b/tests/template_test.c @@ -3,7 +3,7 @@ #include #include "template.h" -int main(void) +int main3(void) { char *source = "DE $cs: $bv $loc6, $hh:$mm:$ss, $tow, Ti$ti Te$te $hu $pr"; char dest[512];