From a44a00acf058a7c4f779044a6b89133e3aec3779 Mon Sep 17 00:00:00 2001 From: Pezsma <159525557+Pezsma@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:25:52 +0100 Subject: [PATCH] Morse rx further development (#2959) * filters OK, radio configurations set * all modes are working, noise still needs work, especially in FM. A baseband enum is also needed * audio and decoding fixed, clipping and the baseband enum are still missing --- .../external/morse_radio/ui_morse_radio.cpp | 49 ++- .../external/morse_radio/ui_morse_radio.hpp | 15 +- firmware/baseband/proc_morse.cpp | 93 +++-- firmware/baseband/proc_morse.hpp | 15 +- firmware/common/dsp_fir_taps.hpp | 358 ++++++++++++++++++ firmware/common/message.hpp | 1 + 6 files changed, 483 insertions(+), 48 deletions(-) diff --git a/firmware/application/external/morse_radio/ui_morse_radio.cpp b/firmware/application/external/morse_radio/ui_morse_radio.cpp index bb3ba6d30..556970504 100644 --- a/firmware/application/external/morse_radio/ui_morse_radio.cpp +++ b/firmware/application/external/morse_radio/ui_morse_radio.cpp @@ -17,6 +17,7 @@ MorseRadioView::MorseRadioView(ui::NavigationView& nav) &txt_last, &txt_speed, &txt_freq, + &txt_clip, &options_mode, &btn_clear, &console_text, @@ -39,9 +40,9 @@ MorseRadioView::MorseRadioView(ui::NavigationView& nav) receiver_model.enable(); options_mode.on_change = [this](size_t, int32_t value) { - current_mode = (uint8_t)value; + current_mode = static_cast((uint8_t)value); morse_decoder_.resetLearning(); - if (current_mode == 0) { + if (current_mode == ModulationMode::FM) { receiver_model.set_am_configuration(4); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); field_squelch.set_style(Theme::getInstance()->option_active); @@ -49,21 +50,26 @@ MorseRadioView::MorseRadioView(ui::NavigationView& nav) receiver_model.set_squelch_level(field_squelch.value()); audio::set_rate(audio::Rate::Hz_24000); } else { + audio::set_rate(audio::Rate::Hz_12000); receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); - receiver_model.set_am_configuration(current_mode + 7); + if (current_mode == ModulationMode::AM || current_mode == ModulationMode::DSB) + receiver_model.set_am_configuration(7); + else if (current_mode == ModulationMode::USB) + receiver_model.set_am_configuration(9); + else // LSB + receiver_model.set_am_configuration(10); receiver_model.set_squelch_level(0); field_squelch.set_style(Theme::getInstance()->fg_dark); field_squelch.set_focusable(false); - audio::set_rate(audio::Rate::Hz_12000); } - baseband::set_moreserx_config(current_mode); + baseband::set_moreserx_config(static_cast(current_mode)); }; field_squelch.set_value(receiver_model.squelch_level(), false); // will be sent later, no need to send 2x field_squelch.on_change = [this](int32_t v) { - if (current_mode == 0) + if (current_mode == ModulationMode::FM) receiver_model.set_squelch_level(v); }; - options_mode.set_selected_index(current_mode, true); + options_mode.set_selected_index(static_cast(current_mode), true); auto vol = field_volume.value(); // audio volume fix field_volume.set_value(0); @@ -105,9 +111,30 @@ void MorseLogger::init_daily_log(const std::filesystem::path& log_dir) { void MorseLogger::radio_set_log(uint8_t current_mode) { int64_t freq = receiver_model.target_frequency(); + std::string mode_str; + switch (static_cast(current_mode)) { + case MorseRadioView::ModulationMode::AM: + mode_str = "AM"; + break; + case MorseRadioView::ModulationMode::FM: + mode_str = "FM"; + break; + case MorseRadioView::ModulationMode::DSB: + mode_str = "DSB"; + break; + case MorseRadioView::ModulationMode::USB: + mode_str = "USB"; + break; + case MorseRadioView::ModulationMode::LSB: + mode_str = "LSB"; + break; + default: + mode_str = "???"; + break; + } std::string header = "Freq:" + to_string_rounded_freq(freq, 4); header += "MHz, "; - header += "RX MODE:" + std::string(current_mode == 0 ? "CW/FM" : (current_mode == 1 ? "USB" : "LSB")); + header += "RX MODE:" + mode_str; header += "\r\nMessage:"; log_file.write_raw(header); } @@ -152,6 +179,8 @@ MorseRadioView::~MorseRadioView() { void MorseRadioView::focus() { field_frequency.focus(); + txt_clip.set_style(Theme::getInstance()->fg_red); + txt_clip.hidden(true); } void MorseRadioView::check_for_timeout() { @@ -205,6 +234,8 @@ int32_t MorseRadioView::ProcessSignal(int32_t sig_time_us) { void MorseRadioView::on_data(const MorseRXDataMessage* message) { int32_t r; + txt_clip.hidden(!message->clipped); + for (uint8_t i = 0; i <= message->state_cnt; ++i) { r = ProcessSignal(message->state_durations[i]); auto result = morse_decoder_.handleInput((r != 0) ? r : 0); @@ -212,7 +243,7 @@ void MorseRadioView::on_data(const MorseRXDataMessage* message) { last_activity_time = chTimeNow(); // start reset timer on valid input writeCharToConsole(result.text, result.confidence); if (logger && save_log) { - time_stamp = logger->on_packet(result.text, time_stamp, current_mode); + time_stamp = logger->on_packet(result.text, time_stamp, static_cast(current_mode)); } float dah_time = morse_decoder_.getCurrentTimeUnit() * 3.0f; if (dah_time > 0) { diff --git a/firmware/application/external/morse_radio/ui_morse_radio.hpp b/firmware/application/external/morse_radio/ui_morse_radio.hpp index b78845cce..6ce31871d 100644 --- a/firmware/application/external/morse_radio/ui_morse_radio.hpp +++ b/firmware/application/external/morse_radio/ui_morse_radio.hpp @@ -64,6 +64,14 @@ class MorseLogger { class MorseRadioView : public ui::View { public: + enum class ModulationMode : uint8_t { + AM = 0, + FM = 1, + DSB = 2, + USB = 3, + LSB = 4 + }; + MorseRadioView(ui::NavigationView& nav); ~MorseRadioView(); std::string title() const override { @@ -83,12 +91,12 @@ class MorseRadioView : public ui::View { RxRadioState radio_state_{}; std::unique_ptr logger{}; - uint8_t current_mode{0}; // 0=CW/FM, 1=USB, 2=LSB + ModulationMode current_mode = ModulationMode::AM; app_settings::SettingsManager settings_{ "rx_morese_radio", app_settings::Mode::RX, - {{"cwmode"sv, ¤t_mode}}}; + {{"cwmode"sv, reinterpret_cast(¤t_mode)}}}; RxFrequencyField field_frequency{ {UI_POS_X(0), UI_POS_Y(0)}, @@ -115,6 +123,7 @@ class MorseRadioView : public ui::View { ui::Text txt_speed{{UI_POS_X(23), UI_POS_Y(1), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)}, "??"}; ui::Text txt_last{{UI_POS_X(12), UI_POS_Y(4), UI_POS_WIDTH_REMAINING(12), UI_POS_HEIGHT(1)}, ""}; ui::Text txt_freq{{UI_POS_X(21), UI_POS_Y(2), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)}, "??"}; + ui::Text txt_clip{{UI_POS_X(15), UI_POS_Y(3), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)}, "clipping"}; ui::Console console_text{{UI_POS_X(0), UI_POS_Y(5), UI_POS_MAXWIDTH, UI_POS_HEIGHT_REMAINING(7)}}; ui::Labels labels{ {{UI_POS_X(0), UI_POS_Y(1)}, "Squelch:", Theme::getInstance()->fg_light->foreground}, @@ -127,7 +136,7 @@ class MorseRadioView : public ui::View { ui::OptionsField options_mode{ {UI_POS_X(9), UI_POS_Y(2)}, 5, - {{"CW/FM", 0}, {"USB", 1}, {"LSB", 2}}}; + {{"AM", 0}, {"FM", 1}, {"DSB", 2}, {"USB", 3}, {"LSB", 4}}}; Checkbox chk_log{{UI_POS_X(0), UI_POS_Y(2)}, 12, "Log", false}; ui::Button btn_clear{{UI_POS_X(0), UI_POS_Y_BOTTOM(2), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)}, "CLR"}; diff --git a/firmware/baseband/proc_morse.cpp b/firmware/baseband/proc_morse.cpp index 5e08dcc41..2d16dad2a 100644 --- a/firmware/baseband/proc_morse.cpp +++ b/firmware/baseband/proc_morse.cpp @@ -28,26 +28,32 @@ void MorseProcessor::configure(uint8_t mode) { configured = false; - if (mode == 0) { // CW/FM + modulation = static_cast((uint8_t)mode); + + if (modulation == ModulationMode::FM) { // FM decim_0.configure(taps_11k0_decim_0.taps); decim_1.configure(taps_11k0_decim_1.taps); channel_filter.configure(taps_11k0_channel.taps, 2); demod_cw_fm.configure(24000, 5000); - } else { // USB, LSB - decim_0.configure(taps_6k0_decim_0.taps); - decim_1.configure(taps_6k0_decim_1.taps); - - if (mode == 1) // USB - channel_filter.configure(taps_2k8_usb_channel.taps, 4); - else // LSB - channel_filter.configure(taps_2k8_lsb_channel.taps, 4); + } else { + decim_0.configure(taps_4k25_decim_0.taps); + decim_1.configure(taps_4k25_decim_1.taps); + if (modulation == ModulationMode::AM) { // AM + channel_filter.configure(taps_2k0_am_lpf_channel.taps, 4); + } else { // SSB, DSB + if (modulation == ModulationMode::DSB) // DSB + channel_filter.configure(taps_1k5_dsb_lpf.taps, 4); + else if (modulation == ModulationMode::USB) // USB + channel_filter.configure(taps_1k5_USB_channel.taps, 4); + else // LSB + channel_filter.configure(taps_1k5_LSB_channel.taps, 4); + } } - modulation = mode; - if (mode > 0) - audio_output.configure(audio_12k_hpf_300hz_config); - else + if (modulation == ModulationMode::FM) audio_output.configure(iir_config_passthrough, iir_config_passthrough, (float)user_squelch_level / 100.0f); + else + audio_output.configure(audio_12k_hpf_300hz_config); meas_samples_in_period = 0; meas_last_period_len = 0; @@ -70,6 +76,8 @@ void MorseProcessor::configure(uint8_t mode) { squelch_is_open = true; squelch_hold = 0; + dc_average = 0.0f; + current_freq = 700.0f; update_goertzel_coeff(current_freq); @@ -77,10 +85,15 @@ void MorseProcessor::configure(uint8_t mode) { } inline buffer_f32_t MorseProcessor::demodulate(const buffer_c16_t& channel) { - if (modulation > 0) { - // SSB always keeps squelch "technically" open for the demodulator + // AM,SSB,DSB always keeps squelch "technically" open for the demodulator + if (modulation == ModulationMode::AM || modulation == ModulationMode::DSB) { squelch_is_open = true; - return demod_ssb.execute(channel, audio_buffer); + return demod_AM.execute(channel, audio_buffer); + } else { + if (modulation == ModulationMode::USB || modulation == ModulationMode::LSB) { + squelch_is_open = true; + return demod_ssb.execute(channel, audio_buffer); + } } return demod_cw_fm.execute(channel, audio_buffer); } @@ -90,7 +103,7 @@ void MorseProcessor::update_goertzel_coeff(float freq) { if (freq < 300.0f) freq = 300.0f; if (freq > 2300.0f) freq = 2300.0f; - float sample_rate = (modulation == 0) ? 24000.0f : 12000.0f; + float sample_rate = (modulation == ModulationMode::FM) ? 24000.0f : 12000.0f; float omega = 2.0f * M_PI * freq / sample_rate; float omega_sq = omega * omega; float cos_approx = 1.0f - (omega_sq * 0.5f); @@ -100,7 +113,7 @@ void MorseProcessor::update_goertzel_coeff(float freq) { void MorseProcessor::measure_frequency(int32_t sample) { // Noise gate threshold - const int32_t gate_threshold = (modulation == 0) ? 4000 : 2000; + const int32_t gate_threshold = (modulation == ModulationMode::FM) ? 4000 : 2000; if (sample > gate_threshold || sample < -gate_threshold) { if (sample > 0 && !meas_signal_state_high) { @@ -111,7 +124,7 @@ void MorseProcessor::measure_frequency(int32_t sample) { bool period_is_stable = false; if (meas_last_period_len > 0) { int32_t diff = std::abs((int)meas_samples_in_period - (int)meas_last_period_len); - if (diff <= 1) { + if (diff <= 2) { meas_consistency_count++; period_is_stable = true; } else { @@ -121,10 +134,12 @@ void MorseProcessor::measure_frequency(int32_t sample) { meas_last_period_len = meas_samples_in_period; // Wait for AT LEAST 3 STABLE CYCLES - if (period_is_stable && meas_consistency_count > 3) { - float base_rate = (modulation == 0) ? 24000.0f : 12000.0f; + if (period_is_stable && meas_consistency_count > 5) { + float base_rate = (modulation == ModulationMode::FM) ? 24000.0f : 12000.0f; float inst_freq = base_rate / (float)meas_samples_in_period; - + if (modulation == ModulationMode::DSB) { + inst_freq /= 2.0f; + } // Check for overflows if (inst_freq > 250 && inst_freq < 3000) { meas_freq_accumulator += inst_freq; @@ -148,7 +163,7 @@ void MorseProcessor::measure_frequency(int32_t sample) { meas_samples_in_period++; ui_update_timer++; - uint32_t update_limit = (modulation == 0) ? 4800 : 2400; // ~200ms + uint32_t update_limit = (modulation == ModulationMode::FM) ? 4800 : 2400; // ~200ms if (ui_update_timer > update_limit) { if (meas_freq_count > 0) { @@ -202,7 +217,7 @@ void MorseProcessor::process_decoding(int32_t sample) { // Tone Detection bool is_tone = squelch_is_open && (power > current_pwr_threshold) && (power > 150000); - int32_t time_base = modulation == 0 ? 125 : 250; + int32_t time_base = 250; // State Change Logic if (is_tone != was_signaling) { int32_t duration_us = (int32_t)((int64_t)duration_samples * time_base / 3); @@ -254,33 +269,43 @@ void MorseProcessor::execute(const buffer_c8_t& buffer) { if (raw_int_abs > audio_threshold || user_squelch_level == 0) { squelch_is_open = true; - squelch_hold = (modulation == 0) ? 2400 : 1200; + squelch_hold = (modulation == ModulationMode::FM) ? 2400 : 1200; } else { if (squelch_hold > 0) squelch_hold--; - else if (modulation == 0) + else if (modulation == ModulationMode::FM) squelch_is_open = false; } - measure_frequency((int32_t)(raw_audio * 32768.0f)); - float decode_audio = raw_audio; - if (modulation > 0) { - const float gain = 6.0f; + if (modulation != ModulationMode::FM) { + float gain = 16.0f; + if (modulation == ModulationMode::USB || modulation == ModulationMode::LSB) + gain = 5.0f; decode_audio *= gain; // Hard Limiting / Clipping - if (decode_audio > 1.0f) + message.clipped = false; + if (decode_audio > 1.0f) { decode_audio = 1.0f; - else if (decode_audio < -1.0f) + message.clipped = true; + } else if (decode_audio < -1.0f) { decode_audio = -1.0f; - + } audio_buf.p[i] = decode_audio; } + // DC BLOCKING + if (modulation != ModulationMode::FM) { + dc_average = (dc_average * 0.95f) + (decode_audio * 0.05f); + measure_frequency((int32_t)((decode_audio - dc_average) * 32768.0f)); + } else { + measure_frequency((int32_t)(raw_audio * 32768.0f)); + } + process_decoding((int32_t)(decode_audio * 32768.0f)); - if (modulation == 0 && !squelch_is_open) { + if (modulation == ModulationMode::FM && !squelch_is_open) { audio_buf.p[i] = 0.0f; // mute nfm on squelch } } diff --git a/firmware/baseband/proc_morse.hpp b/firmware/baseband/proc_morse.hpp index 14fd2dc6e..d2b02b161 100644 --- a/firmware/baseband/proc_morse.hpp +++ b/firmware/baseband/proc_morse.hpp @@ -60,13 +60,22 @@ class MorseProcessor : public BasebandProcessor { dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; dsp::decimate::FIRAndDecimateComplex channel_filter{}; + dsp::demodulate::AM demod_AM{}; dsp::demodulate::FM demod_cw_fm{}; dsp::demodulate::SSB demod_ssb{}; AudioOutput audio_output{}; bool configured{false}; - uint8_t modulation{0}; // 0=CW/FM, 1=USB, 2=LSB + enum class ModulationMode : uint8_t { + AM = 0, + FM = 1, + DSB = 2, + USB = 3, + LSB = 4 + }; + + ModulationMode modulation = ModulationMode::AM; int32_t user_squelch_level{0}; bool squelch_is_open{true}; int32_t squelch_hold{0}; @@ -79,7 +88,6 @@ class MorseProcessor : public BasebandProcessor { float meas_freq_accumulator{0.0f}; uint32_t meas_freq_count{0}; uint32_t ui_update_timer{0}; - float current_freq{700.0f}; // --- Decoding variables (Goertzel) --- @@ -92,6 +100,9 @@ class MorseProcessor : public BasebandProcessor { int64_t noise_floor{5000}; int32_t startup_delay{20}; + float dc_average = 0.0f; // DC level tracking + int32_t dc_average_int = 0; + MorseRXDataMessage message{}; MorseRXfreqMessage freq_message{}; }; diff --git a/firmware/common/dsp_fir_taps.hpp b/firmware/common/dsp_fir_taps.hpp index c1f5e9787..9602e4f73 100644 --- a/firmware/common/dsp_fir_taps.hpp +++ b/firmware/common/dsp_fir_taps.hpp @@ -1681,4 +1681,362 @@ static constexpr fir_taps_real<24> taps_BTLE_Dual_PHY = { 3}}, }; +static constexpr fir_taps_real<63> taps_2k0_am_lpf_channel = { + .low_frequency_normalized = -2000.0f / 48000.0f, + .high_frequency_normalized = 2000.0f / 48000.0f, + .transition_normalized = 1500.0f / 48000.0f, + .taps = {{ + 26, + 29, + 31, + 33, + 32, + 28, + 18, + 0, + -26, + -61, + -104, + -152, + -202, + -246, + -279, + -292, + -277, + -227, + -136, + 0, + 182, + 409, + 673, + 968, + 1280, + 1595, + 1899, + 2174, + 2407, + 2583, + 2693, + 2731, + 2693, + 2583, + 2407, + 2174, + 1899, + 1595, + 1280, + 968, + 673, + 409, + 182, + 0, + -136, + -227, + -277, + -292, + -279, + -246, + -202, + -152, + -104, + -61, + -26, + 0, + 18, + 28, + 32, + 33, + 31, + 29, + 26, + }}, +}; + +static constexpr fir_taps_real<64> taps_1K5_FM_channel = { + .low_frequency_normalized = 300.0f / 48000.0f, + .high_frequency_normalized = 1800.0f / 48000.0f, + .transition_normalized = 1000.0f / 48000.0f, + .taps = {{ + 24, + 22, + 20, + 15, + 9, + -2, + -18, + -39, + -66, + -98, + -132, + -167, + -198, + -220, + -228, + -217, + -181, + -117, + -19, + 112, + 277, + 474, + 697, + 941, + 1197, + 1454, + 1702, + 1930, + 2128, + 2285, + 2394, + 2451, + 2451, + 2394, + 2285, + 2128, + 1930, + 1702, + 1454, + 1197, + 941, + 697, + 474, + 277, + 112, + -19, + -117, + -181, + -217, + -228, + -220, + -198, + -167, + -132, + -98, + -66, + -39, + -18, + -2, + 9, + 15, + 20, + 22, + 24, + }}, +}; + +static constexpr fir_taps_real<63> taps_1k5_dsb_lpf = { + .low_frequency_normalized = -1500.0f / 48000.0f, + .high_frequency_normalized = 1500.0f / 48000.0f, + .transition_normalized = 1000.0f / 48000.0f, + .taps = {{ + 0, + 0, + -1, + -5, + -10, + -19, + -31, + -46, + -64, + -82, + -99, + -111, + -113, + -100, + -64, + 0, + 99, + 239, + 424, + 655, + 932, + 1251, + 1605, + 1983, + 2372, + 2757, + 3120, + 3447, + 3719, + 3925, + 4053, + 4096, + 4053, + 3925, + 3719, + 3447, + 3120, + 2757, + 2372, + 1983, + 1605, + 1251, + 932, + 655, + 424, + 239, + 99, + 0, + -64, + -100, + -113, + -111, + -99, + -82, + -64, + -46, + -31, + -19, + -10, + -5, + -1, + 0, + 0, + }}, +}; + +// GAIN: 4.0x +static constexpr fir_taps_complex<63> taps_1k5_USB_channel = { + .low_frequency_normalized = 300.0f / 48000.0f, + .high_frequency_normalized = 1800.0f / 48000.0f, + .transition_normalized = 300.0f / 48000.0f, + .taps = {{ + {0, 0}, + {0, 0}, + {2, 0}, + {4, 2}, + {10, 6}, + {17, 14}, + {27, 29}, + {37, 53}, + {45, 89}, + {49, 140}, + {41, 207}, + {17, 290}, + {-31, 389}, + {-109, 499}, + {-227, 614}, + {-387, 725}, + {-595, 819}, + {-850, 884}, + {-1147, 904}, + {-1477, 866}, + {-1827, 757}, + {-2179, 569}, + {-2512, 297}, + {-2804, -55}, + {-3031, -480}, + {-3173, -962}, + {-3213, -1481}, + {-3142, -2011}, + {-2955, -2524}, + {-2658, -2991}, + {-2262, -3386}, + {-1788, -3685}, + {-1258, -3873}, + {-703, -3939}, + {-153, -3884}, + {366, -3713}, + {826, -3440}, + {1208, -3087}, + {1499, -2677}, + {1693, -2236}, + {1789, -1789}, + {1796, -1359}, + {1726, -966}, + {1594, -624}, + {1420, -341}, + {1220, -120}, + {1012, 40}, + {809, 144}, + {622, 202}, + {460, 223}, + {324, 217}, + {217, 193}, + {137, 160}, + {80, 125}, + {42, 91}, + {19, 62}, + {6, 39}, + {0, 22}, + {-1, 11}, + {-1, 5}, + {-1, 1}, + {0, 0}, + {0, 0}, + }}, +}; + +// GAIN: 4.0x +static constexpr fir_taps_complex<63> taps_1k5_LSB_channel = { + .low_frequency_normalized = 300.0f / 48000.0f, + .high_frequency_normalized = 1800.0f / 48000.0f, + .transition_normalized = 300.0f / 48000.0f, + .taps = {{ + {0, 0}, + {0, 0}, + {2, 0}, + {4, -2}, + {10, -6}, + {17, -14}, + {27, -29}, + {37, -53}, + {45, -89}, + {49, -140}, + {41, -207}, + {17, -290}, + {-31, -389}, + {-109, -499}, + {-227, -614}, + {-387, -725}, + {-595, -819}, + {-850, -884}, + {-1147, -904}, + {-1477, -866}, + {-1827, -757}, + {-2179, -569}, + {-2512, -297}, + {-2804, 55}, + {-3031, 480}, + {-3173, 962}, + {-3213, 1481}, + {-3142, 2011}, + {-2955, 2524}, + {-2658, 2991}, + {-2262, 3386}, + {-1788, 3685}, + {-1258, 3873}, + {-703, 3939}, + {-153, 3884}, + {366, 3713}, + {826, 3440}, + {1208, 3087}, + {1499, 2677}, + {1693, 2236}, + {1789, 1789}, + {1796, 1359}, + {1726, 966}, + {1594, 624}, + {1420, 341}, + {1220, 120}, + {1012, -40}, + {809, -144}, + {622, -202}, + {460, -223}, + {324, -217}, + {217, -193}, + {137, -160}, + {80, -125}, + {42, -91}, + {19, -62}, + {6, -39}, + {0, -22}, + {-1, -11}, + {-1, -5}, + {-1, -1}, + {0, 0}, + {0, 0}, + }}, +}; + #endif /*__DSP_FIR_TAPS_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 2abbbe86c..6e10417cd 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -1711,6 +1711,7 @@ class MorseRXDataMessage : public Message { : Message{ID::MorseRXData} {} int32_t state_durations[5] = {0}; // positive: high, negative: low uint8_t state_cnt = 0; + bool clipped = false; const uint8_t maxptr = 4; };