aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-04-12 23:30:51 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-04-12 23:30:51 +0200
commit74f34510c6f51b5510a80431369fe61c384c72c1 (patch)
treec5a4d412ebf216447e23f423634a9ed9e805eaf3 /src
parent66f0e1af5b1f79ccb66909ce57f7d73ea7194a53 (diff)
downloadglutte-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.h3
-rw-r--r--src/common/includes/Audio/tone.h10
-rw-r--r--src/common/includes/Core/fsm.h2
-rw-r--r--src/common/src/Audio/tone.c134
-rw-r--r--src/common/src/Core/common.c2
-rw-r--r--src/common/src/Core/fsm.c30
-rw-r--r--src/common/src/Core/main.c11
-rw-r--r--src/glutt-o-logique/audio_in.c17
-rw-r--r--src/simulator/src/Audio/audio_in.c19
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,
- &current_buffer,
- portMAX_DELAY);
- assert(success);
+ if (enabled) {
+ int success = xQueueSendToBack(
+ adc2_values_queue,
+ &current_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;