diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-04-12 23:30:51 +0200 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-04-12 23:30:51 +0200 |
commit | 74f34510c6f51b5510a80431369fe61c384c72c1 (patch) | |
tree | c5a4d412ebf216447e23f423634a9ed9e805eaf3 /src | |
parent | 66f0e1af5b1f79ccb66909ce57f7d73ea7194a53 (diff) | |
download | glutte-o-matic-74f34510c6f51b5510a80431369fe61c384c72c1.tar.gz glutte-o-matic-74f34510c6f51b5510a80431369fe61c384c72c1.tar.bz2 glutte-o-matic-74f34510c6f51b5510a80431369fe61c384c72c1.zip |
Implement DTMF sequence decoding and FSM activation of tone detector (untested)
Diffstat (limited to 'src')
-rw-r--r-- | src/common/includes/Audio/audio_in.h | 3 | ||||
-rw-r--r-- | src/common/includes/Audio/tone.h | 10 | ||||
-rw-r--r-- | src/common/includes/Core/fsm.h | 2 | ||||
-rw-r--r-- | src/common/src/Audio/tone.c | 134 | ||||
-rw-r--r-- | src/common/src/Core/common.c | 2 | ||||
-rw-r--r-- | src/common/src/Core/fsm.c | 30 | ||||
-rw-r--r-- | src/common/src/Core/main.c | 11 | ||||
-rw-r--r-- | src/glutt-o-logique/audio_in.c | 17 | ||||
-rw-r--r-- | src/simulator/src/Audio/audio_in.c | 19 |
9 files changed, 190 insertions, 38 deletions
diff --git a/src/common/includes/Audio/audio_in.h b/src/common/includes/Audio/audio_in.h index a543a69..40482d9 100644 --- a/src/common/includes/Audio/audio_in.h +++ b/src/common/includes/Audio/audio_in.h @@ -32,6 +32,9 @@ void audio_in_initialize(void); +/* Enable or disable the audio input */ +void audio_in_enable(int enable); + // After calling this function, *buffer will point to new audio data. // Returns the number of times filling the internal buffer failed. int32_t audio_in_get_buffer(int16_t **buffer /*of length AUDIO_IN_BUF_LEN*/ ); diff --git a/src/common/includes/Audio/tone.h b/src/common/includes/Audio/tone.h index b8a7f72..a4692df 100644 --- a/src/common/includes/Audio/tone.h +++ b/src/common/includes/Audio/tone.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2018 Matthias P. Braendli, Maximilien Cuony + * Copyright (c) 2019 Matthias P. Braendli, Maximilien Cuony * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,10 +42,14 @@ struct tone_detector { void tone_init(void); +void tone_detector_enable(int enable); + /* Return 1 when 1750 detected, 0 otherwise */ int tone_1750_status(void); -/* Update 1750 tone detection status */ -void tone_detect_1750(const int16_t *samples, int len); +int tone_fax_status(void); + +/* Update all tone detection status */ +void tone_detect_push_samples(const int16_t *samples, int len); #endif diff --git a/src/common/includes/Core/fsm.h b/src/common/includes/Core/fsm.h index f8a800c..91bb231 100644 --- a/src/common/includes/Core/fsm.h +++ b/src/common/includes/Core/fsm.h @@ -105,6 +105,8 @@ struct fsm_output_signals_t { int cw_psk31_trigger; // Set to true to trigger a CW or PSK31 transmission. // PSK31 is sent if cw_dit_duration is 0 + /* Tone detector */ + int require_tone_detector; // Enables audio input and detector for DTMF and 1750 }; // Initialise local structures diff --git a/src/common/src/Audio/tone.c b/src/common/src/Audio/tone.c index e498cb7..ebfa5ee 100644 --- a/src/common/src/Audio/tone.c +++ b/src/common/src/Audio/tone.c @@ -53,6 +53,16 @@ #define DET_ROW_STAR 3 #define DET_1750 4 #define NUM_DETECTORS 5 + +// Incomplete because not all frequencies decoded +enum dtmf_code { + DTMF_NONE = 0, + DTMF_0, + DTMF_7, + DTMF_8, + DTMF_STAR, +}; + static struct tone_detector detectors[NUM_DETECTORS]; // Apply an IIR filter with alpha = 5/16 to smooth out variations. @@ -60,6 +70,81 @@ static struct tone_detector detectors[NUM_DETECTORS]; static int32_t normalised_results[NUM_DETECTORS]; static int tone_1750_detected = 0; +static int detectors_enabled = 0; + +static enum dtmf_code dtmf_last_seen = 0; +static uint64_t dtmf_last_seen_at = 0; + +// Store the sequence of dtmf codes in this FIFO. If no DTMF code gets +// decoded in the interval, a NONE gets inserted into the sequence +#define NUM_DTMF_SEQ 3 +#define DTMF_MAX_TONE_INTERVAL 1500 +static enum dtmf_code dtmf_sequence[NUM_DTMF_SEQ]; + +static inline void push_dtmf_code(enum dtmf_code code) +{ + for (int i = 0; i < NUM_DTMF_SEQ-1; i++) { + dtmf_sequence[i] = dtmf_sequence[i+1]; + } + dtmf_sequence[NUM_DTMF_SEQ-1] = code; +} + +const int thresh = 400; // TODO: Does that depend on TONE_N? + +static void analyse_dtmf() +{ + // Bits 0 to 9 are numbers, bit 10 to 13 letters, bit 14 is star, 15 is hash + const uint16_t pattern = + ((normalised_results[DET_COL_1] > thresh && + normalised_results[DET_ROW_7] > thresh) ? (1 << 7) : 0) + + ((normalised_results[DET_COL_2] > thresh && + normalised_results[DET_ROW_7] > thresh) ? (1 << 8) : 0) + + ((normalised_results[DET_COL_1] > thresh && + normalised_results[DET_ROW_STAR] > thresh) ? (1 << 14) : 0) + + ((normalised_results[DET_COL_2] > thresh && + normalised_results[DET_ROW_STAR] > thresh) ? (1 << 0) : 0); + + // Match patterns exactly to exclude multiple simultaneous DTMF codes. + if (pattern == (1 << 0)) { + if (dtmf_last_seen != DTMF_0) { + push_dtmf_code(DTMF_0); + } + + dtmf_last_seen = DTMF_0; + dtmf_last_seen_at = timestamp_now(); + } + else if (pattern == (1 << 7)) { + if (dtmf_last_seen != DTMF_7) { + push_dtmf_code(DTMF_7); + } + + dtmf_last_seen = DTMF_7; + dtmf_last_seen_at = timestamp_now(); + } + else if (pattern == (1 << 8)) { + if (dtmf_last_seen != DTMF_8) { + push_dtmf_code(DTMF_8); + } + + dtmf_last_seen = DTMF_8; + dtmf_last_seen_at = timestamp_now(); + } + else if (pattern == (1 << 14)) { + if (dtmf_last_seen != DTMF_STAR) { + push_dtmf_code(DTMF_STAR); + } + + dtmf_last_seen = DTMF_STAR; + dtmf_last_seen_at = timestamp_now(); + } + else if (dtmf_last_seen_at + DTMF_MAX_TONE_INTERVAL > timestamp_now()) { + // Flush out all codes + push_dtmf_code(DTMF_NONE); + + dtmf_last_seen = DTMF_NONE; + dtmf_last_seen_at = timestamp_now(); + } +} int tone_1750_status() @@ -67,7 +152,14 @@ int tone_1750_status() return tone_1750_detected; } -static void init_tone(struct tone_detector* detector, int freq) { +int tone_fax_status() +{ + return dtmf_sequence[0] == DTMF_0 && + dtmf_sequence[1] == DTMF_7 && + dtmf_sequence[2] == DTMF_STAR; +} + +static inline void init_detector(struct tone_detector* detector, int freq) { detector->coef = 2.0f * arm_cos_f32(2.0f * FLOAT_PI * freq / AUDIO_IN_RATE); detector->Q1 = 0; detector->Q2 = 0; @@ -75,11 +167,32 @@ static void init_tone(struct tone_detector* detector, int freq) { } void tone_init() { - init_tone(&detectors[DET_COL_1], 1209); - init_tone(&detectors[DET_COL_2], 1336); - init_tone(&detectors[DET_ROW_7], 852); - init_tone(&detectors[DET_ROW_STAR], 941); - init_tone(&detectors[DET_1750], 1750); + for (int i = 0; i < NUM_DTMF_SEQ; i++) { + dtmf_sequence[i] = DTMF_NONE; + } + + init_detector(&detectors[DET_COL_1], 1209); + init_detector(&detectors[DET_COL_2], 1336); + init_detector(&detectors[DET_ROW_7], 852); + init_detector(&detectors[DET_ROW_STAR], 941); + init_detector(&detectors[DET_1750], 1750); +} + +void tone_detector_enable(int enable) +{ + if (enable && !detectors_enabled) { + for (int det = 0; det < NUM_DETECTORS; det++) { + detectors[det].Q1 = 0; + detectors[det].Q2 = 0; + detectors[det].num_samples_analysed = 0; + } + audio_in_enable(1); + detectors_enabled = 1; + } + else if (!enable && detectors_enabled) { + audio_in_enable(0); + detectors_enabled = 0; + } } /* Analyse a sample. Returns -1 if more samples needed, 0 if no tone detected, @@ -110,7 +223,7 @@ static inline float analyse_sample(int16_t sample, struct tone_detector *detecto } } -void tone_detect_1750(const int16_t *samples, int len) +void tone_detect_push_samples(const int16_t *samples, int len) { int32_t mean = 0; int32_t min = 0x7fffffff; @@ -153,6 +266,10 @@ void tone_detect_1750(const int16_t *samples, int len) } } + /* Normalise the results so that a relative comparison of + * the different detectors can be done. This is more reliable + * than looking at the detector outputs independently, especially + * in the presence of noise whose amplitude varies. */ float inv_mean = 0; for (int det = 0; det < NUM_DETECTORS; det++) { inv_mean += results[det]; @@ -166,7 +283,8 @@ void tone_detect_1750(const int16_t *samples, int len) >> 4; // divide by 16 } - tone_1750_detected = (normalised_results[DET_1750] > 400); + tone_1750_detected = (normalised_results[DET_1750] > thresh); + analyse_dtmf(); static int printcounter = 0; if (++printcounter == 5) { diff --git a/src/common/src/Core/common.c b/src/common/src/Core/common.c index 7af361f..7e19eaf 100644 --- a/src/common/src/Core/common.c +++ b/src/common/src/Core/common.c @@ -229,7 +229,7 @@ static void common_increase_timestamp(TimerHandle_t __attribute__ ((unused))t) uint64_t timestamp_now(void) { - return common_timestamp; + return common_timestamp; // ms } diff --git a/src/common/src/Core/fsm.c b/src/common/src/Core/fsm.c index 38546b0..c088fe0 100644 --- a/src/common/src/Core/fsm.c +++ b/src/common/src/Core/fsm.c @@ -191,11 +191,11 @@ void fsm_update() { fsm_out.cw_psk31_trigger = 0; fsm_out.cw_dit_duration = 50; fsm_out.msg_frequency = 960; + fsm_out.require_tone_detector = 0; // other output signals keep their value switch (current_state) { case FSM_OISIF: - // Check the length of the last QSO, and reset the SHORT_BEACON counter if needed if (last_qso_start_timestamp != 0) { @@ -212,6 +212,9 @@ void fsm_update() { short_beacon_counter_s++; } + // SQ is debounced inside pio.c (300ms) + fsm_out.require_tone_detector = fsm_in.sq; + if ((fsm_in.sq && fsm_in.det_1750) | (fsm_in.sq && sstv_state == SSTV_FSM_ON)) { next_state = FSM_OPEN1; } @@ -230,6 +233,7 @@ void fsm_update() { /* Do not enable TX_ON here, otherwise we could get stuck transmitting * forever if SQ never goes low. */ + fsm_out.require_tone_detector = 1; if (!fsm_in.sq && !fsm_in.det_1750) { next_state = FSM_OPEN2; } @@ -238,6 +242,7 @@ void fsm_update() { case FSM_OPEN2: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; qso_info.qso_occurred = 0; qso_info.qso_start_time = timestamp_now(); @@ -249,6 +254,7 @@ void fsm_update() { case FSM_LETTRE: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; fsm_out.msg = fsm_select_letter(); if (fsm_out.msg[0] == 'G') { // The letter 'G' is a bit different @@ -264,6 +270,7 @@ void fsm_update() { case FSM_ECOUTE: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; /* Time checks: * We need to check the total TX_ON duration to decide the text to @@ -314,6 +321,7 @@ void fsm_update() { case FSM_ATTENTE: if (fsm_in.sq) { + fsm_out.require_tone_detector = 1; next_state = FSM_ECOUTE; } else if (fsm_current_state_time_s() > 15) { @@ -324,6 +332,7 @@ void fsm_update() { case FSM_QSO: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; qso_info.qso_occurred = 1; // Save the starting timestamp, if there is none @@ -369,6 +378,7 @@ void fsm_update() { case FSM_TEXTE_73: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; fsm_out.msg_frequency = 696; fsm_out.cw_dit_duration = 70; fsm_out.msg = " 73" CW_POSTDELAY; @@ -386,6 +396,7 @@ void fsm_update() { case FSM_TEXTE_HB9G: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; fsm_out.msg_frequency = 696; fsm_out.cw_dit_duration = 70; // No need for CW_PREDELAY, since we are already transmitting @@ -404,6 +415,7 @@ void fsm_update() { case FSM_TEXTE_LONG: fsm_out.tx_on = 1; fsm_out.modulation = 1; + fsm_out.require_tone_detector = 1; fsm_out.msg_frequency = 696; fsm_out.cw_dit_duration = 70; @@ -540,21 +552,17 @@ void fsm_update() { fsm_out.cw_psk31_trigger = 1; if (current_state == FSM_BALISE_COURTE) { - if (fsm_in.sq) { next_state = FSM_BALISE_COURTE_OPEN; - } else { - if (fsm_in.cw_psk31_done) { - next_state = FSM_OISIF; - } } - - } else { //FSM_BALISE_COURTE_OPEN - + else if (fsm_in.cw_psk31_done) { + next_state = FSM_OISIF; + } + } + else { //FSM_BALISE_COURTE_OPEN if (fsm_in.cw_psk31_done) { next_state = FSM_OPEN2; } - } break; @@ -615,7 +623,6 @@ void fsm_balise_update() { next_state = BALISE_FSM_EVEN_HOUR; } break; - default: // Should never happen next_state = BALISE_FSM_EVEN_HOUR; @@ -627,7 +634,6 @@ void fsm_balise_update() { } balise_state = next_state; - } void fsm_sstv_update() { diff --git a/src/common/src/Core/main.c b/src/common/src/Core/main.c index 49ca8e6..44d7e9c 100644 --- a/src/common/src/Core/main.c +++ b/src/common/src/Core/main.c @@ -244,7 +244,8 @@ static void launcher_task(void __attribute__ ((unused))*pvParameters) i = 1; leds_turn_on(LED_GREEN); - } else { + } + else { i = 0; leds_turn_off(LED_GREEN); } @@ -252,6 +253,8 @@ static void launcher_task(void __attribute__ ((unused))*pvParameters) struct fsm_output_signals_t fsm_out; fsm_get_outputs(&fsm_out); + tone_detector_enable(fsm_out.require_tone_detector); + if (fsm_out.tx_on) { int swr_fwd_mv, swr_refl_mv; if (analog_measure_swr(&swr_fwd_mv, &swr_refl_mv)) { @@ -283,7 +286,6 @@ static void launcher_task(void __attribute__ ((unused))*pvParameters) pio_set_qrp(qrp_from_supply); } - } if (timestamp_now() - timestamp_last_audio_callback > 1000) { @@ -291,7 +293,8 @@ static void launcher_task(void __attribute__ ((unused))*pvParameters) send_audio_callback_warning = 1; usart_debug("[HOHO] timestamp_last_audio_callback > 1000\r\n"); } - } else { + } + else { if (send_audio_callback_warning == 1) { send_audio_callback_warning = 0; usart_debug("[HOHO] Fix ? Now timestamp_last_audio_callback < 1000\r\n"); @@ -639,7 +642,7 @@ static void nf_analyse(void __attribute__ ((unused))*pvParameters) usart_debug("Number of input samples lost: %d\r\n", new_num_fails); } num_fails = new_num_fails; - tone_detect_1750(audio_in_buffer, AUDIO_IN_BUF_LEN); + tone_detect_push_samples(audio_in_buffer, AUDIO_IN_BUF_LEN); total_samples_analysed += AUDIO_IN_BUF_LEN; if (++timestamp == 80) { diff --git a/src/glutt-o-logique/audio_in.c b/src/glutt-o-logique/audio_in.c index a820b5f..a3abdce 100644 --- a/src/glutt-o-logique/audio_in.c +++ b/src/glutt-o-logique/audio_in.c @@ -93,7 +93,7 @@ void ADC_IRQHandler() } // Timer6 is used for ADC2 sampling -static void enable_timer6() +static void setup_timer() { /* TIM6 Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); @@ -116,8 +116,6 @@ static void enable_timer6() NVIC_SetPriority(TIM6_DAC_IRQn, 5); TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); - ADC_SoftwareStartConv(ADC2); - TIM_Cmd(TIM6, ENABLE); } @@ -159,13 +157,17 @@ void audio_in_initialize() ADC_Cmd(ADC2, ENABLE); - adc2_values_queue = xQueueCreate(4, sizeof(adc2_current_buffer)); if (adc2_values_queue == 0) { trigger_fault(FAULT_SOURCE_ADC2_QUEUE); } - enable_timer6(); + setup_timer(); +} + +void audio_in_enable(int enable) +{ + TIM_Cmd(TIM6, enable ? ENABLE : DISABLE); } int32_t audio_in_get_buffer(int16_t **buffer /*of length AUDIO_IN_BUF_LEN*/ ) @@ -178,3 +180,8 @@ int32_t audio_in_get_buffer(int16_t **buffer /*of length AUDIO_IN_BUF_LEN*/ ) return adc2_lost_samples; } + +#warning "Test tone detector activation from FSM" +#warning "Test 1750 not triggered by start bip my yaesu makes" +#warning "Test DTMF detector" +#warning "Test if FAX mode gets enabled after 0-7-*" diff --git a/src/simulator/src/Audio/audio_in.c b/src/simulator/src/Audio/audio_in.c index 097eaf0..45e53ba 100644 --- a/src/simulator/src/Audio/audio_in.c +++ b/src/simulator/src/Audio/audio_in.c @@ -36,6 +36,8 @@ pa_simple *s_in = NULL; static int16_t buffers[2][AUDIO_IN_BUF_LEN]; static int current_buffer = 0; +static int enabled = 0; // TODO concurrent access: must be protected by a mutex :-/ + static QueueHandle_t adc2_values_queue; static void audio_buffer_reader(void __attribute__((unused))*args) @@ -43,11 +45,13 @@ static void audio_buffer_reader(void __attribute__((unused))*args) while (1) { pa_simple_read(s_in, buffers[current_buffer], AUDIO_IN_BUF_LEN * sizeof(int16_t), NULL); - int success = xQueueSendToBack( - adc2_values_queue, - ¤t_buffer, - portMAX_DELAY); - assert(success); + if (enabled) { + int success = xQueueSendToBack( + adc2_values_queue, + ¤t_buffer, + portMAX_DELAY); + assert(success); + } current_buffer = (current_buffer + 1) % 2; @@ -83,6 +87,11 @@ void audio_in_initialize() { &task_handle); } +void audio_in_enable(int enable) +{ + enabled = enable; +} + int32_t audio_in_get_buffer(int16_t **buffer /*of length AUDIO_IN_BUF_LEN*/ ) { int last_written_buffer = 0; |