diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index fd6f5bc15..36815079e 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -53,12 +53,13 @@ private: OptionsField options_config { { 3 * 8, 0 * 16 }, - 4, + 5, { - { "DSB ", 0 }, - { "USB ", 0 }, - { "LSB ", 0 }, - { "CW ", 0 }, + { "DSB 9k ", 0 }, + { "DSB 6k ", 0 }, + { "USB+3k ", 0 }, + { "LSB-3k ", 0 }, + { "CW ", 0 }, } }; }; diff --git a/firmware/application/apps/ui_mictx.cpp b/firmware/application/apps/ui_mictx.cpp index f164aec1a..d7a13a7e7 100644 --- a/firmware/application/apps/ui_mictx.cpp +++ b/firmware/application/apps/ui_mictx.cpp @@ -163,7 +163,7 @@ void MicTXView::rxaudio(bool is_on) { baseband::run_image(portapack::spi_flash::image_tag_am_audio); receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); // that AM demodulation engine is common to all Amplitude mod : AM/USB/LSB/DSB (2,3,4,5) if (options_mode.selected_index() < 5) // We will be called here with 2,3,4,5 . We treat here demod. filter 2,3,4; (excluding DSB-C case (5) it is treated more down). - receiver_model.set_am_configuration(options_mode.selected_index() - 2); // selecting proper filter(2,3,4). 2-2=0=>6k-AM(0) , 3-2=1=>+3k-USB(1), 4-2=2=>-3K-LSB(2), + receiver_model.set_am_configuration(options_mode.selected_index() - 1); // selecting proper filter(2,3,4). 2-1=1=>6k-AM(1) , 3-1=2=>+3k-USB(2), 4-1=3=>-3K-LSB(3), } else { // We are in NFM/FM or WFM (NFM BW:8k5 or 11k / FM BW 16k / WFM BW:200k) @@ -423,7 +423,8 @@ MicTXView::MicTXView( set_dirty(); // Refresh display options_tone_key.hidden(1); // we hide that Key-tones & CTCSS input selecction, (no meaning in AM/DSB/SSB). - rxbw.emplace_back(" 6k-AM ", 0); // locked a fixed option , to display it . + rxbw.emplace_back(" DSB1-9k ", 0); // we offer in AM DSB two audio BW 9k / 6k . + rxbw.emplace_back(" DSB2-6k ", 1); field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. field_rxbw.hidden(0); // we show fixed RX AM BW 6Khz @@ -436,7 +437,7 @@ MicTXView::MicTXView( check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now. check_rogerbeep.hidden(1); // hide that roger beep selection. - rxbw.emplace_back(" 3k-USB ", 0); // locked a fixed option , to display it . + rxbw.emplace_back(" USB+3k ", 0); // locked a fixed option , to display it . field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. set_dirty(); // Refresh display @@ -447,7 +448,7 @@ MicTXView::MicTXView( check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now. check_rogerbeep.hidden(1); // hide that roger beep selection. - rxbw.emplace_back(" 3k-LSB ", 0); // locked a fixed option , to display it . + rxbw.emplace_back(" LSB-3k ", 0); // locked a fixed option , to display it . field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. set_dirty(); // Refresh display @@ -457,8 +458,8 @@ MicTXView::MicTXView( rxaudio(rx_enabled); //Update now if we have RX audio on check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection. - rxbw.emplace_back("SSB1:3k-USB", 0); // added dynamically two options (index 0,1) to that DSB-C case to the field_rxbw value. - rxbw.emplace_back("SSB2:3k-LSB", 1); + rxbw.emplace_back("SSB1:USB+3k", 0); // added dynamically two options (index 0,1) to that DSB-C case to the field_rxbw value. + rxbw.emplace_back("SSB2:LSB-3k", 1); field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw. @@ -542,9 +543,13 @@ MicTXView::MicTXView( // In Previous fw versions, that nbfm_configuration(n) was done in any mode (FM/AM/SSB/DSB)...strictly speaking only need it in (NFM/FM) receiver_model.set_nbfm_configuration(v ); // we are in NFM/FM case, we need to select proper NFM/FM RX channel filter , NFM BW 8K5(0), NFM BW 11K(1) , FM BW 16K (2) } - else { // we are not in NFM/FM mode .(we could be in any of the rest : AM /USB/LSB/DSB-C) - if (enable_dsb) { // we are in DSB-SC in TX mode , we will allow both independent RX SSB demodulation (USB / LSB side band) - receiver_model.set_am_configuration(v +1 ); // we are in DSB-C TX mode , we need to select proper SSB filter. 0+1 =>usb(1), 1+1=2 =>lsb(2), + else { // we are not in NFM/FM mode .(we could be in any of the rest : AM /USB/LSB/DSB-SC) + if (enable_am) { // we are in AM TX mode , we will allow both independent RX audio BW : AM 9K (9K00AE3 / AM 6K (6K00AE3). (In AM option v can be 0 (9k) , 1 (6k) + receiver_model.set_am_configuration(v ); // we are in AM TX mode , we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1), + } + + if (enable_dsb) { // we are in DSB-SC in TX mode , we will allow both independent RX SSB demodulation (USB / LSB side band). in that submenu, v is 0 (SSB1 USB) or 1 (SSB2 LSB) + receiver_model.set_am_configuration(v +2 ); // we are in DSB-SC TX mode , we need to select proper SSB filter. 0+2 =>usb(2), 1+2=3 =>lsb(3), } } }; diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index ec7ba1a68..3f7fe12de 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -45,11 +45,11 @@ static void send_message(const Message* const message) { void AMConfig::apply() const { const AMConfigureMessage message { - taps_6k0_decim_0, - taps_6k0_decim_1, - taps_6k0_decim_2, - channel, - modulation, + taps_6k0_decim_0, // common FIR filter taps pre-decim_0 to all 5 x AM mod types.(AM-9K, AM-6K, USB, LSB, CW) + taps_6k0_decim_1, // common FIR filter taps pre-decim_1 to all 5 x AM mod. types. + decim_2, // var decim_2 FIR taps filter , variable values, depending selected AM mod(AM 9k / 6k all rest AM modes) + channel, // var channel FIR taps filter , variable values, depending selected AM mode, each one different (DSB-9K, DSB-6K, USB-3K, LSB-3K,CW) + modulation, // var parameter . audio_12k_hpf_300hz_config }; send_message(&message); diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 5c9b3c955..e28a49e73 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -36,6 +36,7 @@ namespace baseband { struct AMConfig { + const fir_taps_real<32> decim_2; // added to handle two types decim_2 9k, 6k const fir_taps_complex<64> channel; const AMConfigureMessage::Modulation modulation; diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index bef08025e..c62d56d2f 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -38,11 +38,12 @@ using namespace portapack; namespace { -static constexpr std::array am_configs { { - { taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, - { taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB }, - { taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB }, - { taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB }, +static constexpr std::array am_configs { { // we config here all the non COMMON parameters to each AM modulation type in RX. + { taps_9k0_decim_2, taps_9k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, // AM DSB-C BW 9khz (+-4k5) commercial EU bandwidth . + { taps_6k0_decim_2, taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, // AM DSB-C BW 6khz (+-3k0) narrow AM , ham equipments. + { taps_6k0_decim_2, taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB }, // SSB USB BW 2K8 (+ 2K8) + { taps_6k0_decim_2, taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB }, // SSB LSB BW 2K8 (- 2K8) + { taps_6k0_decim_2, taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB }, // SSB USB BW 0K7 (+ 0K7) used to get audio tone from CW Morse, assuming tx shifted +700hz aprox } }; static constexpr std::array nbfm_configs { { diff --git a/firmware/common/dsp_fir_taps.hpp b/firmware/common/dsp_fir_taps.hpp index 5df9f515a..3b9f474ac 100644 --- a/firmware/common/dsp_fir_taps.hpp +++ b/firmware/common/dsp_fir_taps.hpp @@ -245,12 +245,29 @@ constexpr fir_taps_real<32> taps_6k0_decim_2 { 11815, 10413, 7946, 4978, 2134, -83, -1411, -1857, -1640, -1080, -474, -21, 208, 247, 178, 95, } }, +}; + +// IFIR prototype filter fs=48000 ; pass=4500 (cutt off -3dBs) , stop=8000 (<-60dBs), decim=4, fout=12000 +// For Europe AM commercial broadcasting stations in LF/MF/HF, Emissions Designator 9K00A3E Bandwidth: 9.00 kHz (derivated from taps_6k0_decim_2 ) +// Pre-decimate LPF FIR filter design Created with SciPy Python with the "window method", num_taps = 32, cut_off = 5150. sample_rate = 48000 # Hz, +// Created with h = signal.firwin(num_taps, cut_off, nyq=sample_rate/2, window=('chebwin',50)) , achieving good STOP band plot < -60 dB's with some ripple. +// post-scaled h taps to avoid decimals , targeting <= similar int values as previous taps_6k0_dsb_channel peak < 32.767 (2 exp 15) and similar H(f)gain +constexpr fir_taps_real<32> taps_9k0_decim_2 { + .low_frequency_normalized = -4500.0f / 48000.0f, // Negative -cutt off freq -3dB (real achieved data ,in the plot and measurements) + .high_frequency_normalized = 4500.0f / 48000.0f, // Positive +cutt off freq -3dB (idem) + .transition_normalized = 3500.0f / 48000.0f, // 3500 Hz = (8000 Hz - 4500 Hz) (both from plot H(f) curve plot) + .taps = { { + -53, -30, 47, 198, 355, 372, 89, -535, + -1307, -1771, -1353, 370, 3384, 7109, 10535, 12591, + 12591, 10535, 7109, 3384, 370, -1353, -1771, -1307, + -535, 89, 372, 355, 198, 47, -30, -53 + } }, }; // Channel filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000 /* NOTE: Slightly less than 1.0 gain (normalized to 65536) due to max(taps) being * slightly larger than 32767 (33312). - */ +*/ constexpr fir_taps_complex<64> taps_6k0_dsb_channel { .low_frequency_normalized = -3000.0f / 12000.0f, .high_frequency_normalized = 3000.0f / 12000.0f, @@ -275,6 +292,36 @@ constexpr fir_taps_complex<64> taps_6k0_dsb_channel { } }, }; +// Channel filter: fs=12000, pass=4500 (cutt off -3dBs), stop=4940 (<-60dBs), decim=1, fout=12000 (*1) real frec pass / stop , based on plotted H(f) curve) +// For Europe AM commercial broadcasting stations in LF/MF/HF, Emissions Designator 9K00A3E Bandwidth: 9.00 kHz (derivative from taps_6k0_dsb_channel) +// FIR filter design created with SciPy Python using "window method"; selected design parameters: num_taps = 64, cut_off = 4575. sample_rate = 12000 # Hz, +// Created with : h = signal.firwin(num_taps, cut_off, nyq=sample_rate/2, window=('chebwin',50)) , achieving real plot curve (*1) with peak stop band ripple -60dBs. +// post-scaled h taps to avoid decimals , targeting <= similar int values as previous taps_6k0_dsb_channel peak < 32.767 (2 exp 15), (29625) and similar H(f)gain +constexpr fir_taps_complex<64> taps_9k0_dsb_channel { + .low_frequency_normalized = -4500.0f / 12000.0f, // Negative -cutt off freq -3dB (in the H(f) curve plot) + .high_frequency_normalized = 4500.0f / 12000.0f, // Positive +cutt off freq -3dB (in the H(f) curve plot) + .transition_normalized = 440.0f / 12000.0f, // 440Hz = (4940 Hz -4500 Hz) cut-3dB's (both data comes from H(f) curve plot and confirmed by measurements ) + .taps = { { + { 2, 0 }, { -18, 0 }, { 34, 0 }, { -33, 0 }, + { 6, 0 }, { 44, 0 }, { -91, 0 }, { 96, 0 }, + { -35, 0 }, { -80, 0 }, { 193, 0 }, { -223, 0 }, + { 116, 0 }, { 112, 0 }, { -353, 0 }, { 452, 0 }, + { -293, 0 }, { -111, 0 }, { 584, 0 }, { -844, 0 }, + { 653, 0 }, { 22, 0 }, { -921, 0 }, { 1554, 0 }, + { -1422, 0 }, { 301, 0 }, { 1533, 0 }, { -3282, 0 }, + { 3804, 0 }, { -1819, 0 }, { -4605, 0 }, { 29625, 0 }, + { 29625, 0 }, { -4605, 0 }, { -1819, 0 }, { 3804, 0 }, + { -3282, 0 }, { 1533, 0 }, { 301, 0 }, { -1422, 0 }, + { 1554, 0 }, { -921, 0 }, { 22, 0 }, { 653, 0 }, + { -844, 0 }, { 584, 0 }, { -111, 0 }, { -293, 0 }, + { 452, 0 }, { -353, 0 }, { 112, 0 }, { 116, 0 }, + { -223, 0 }, { 193, 0 }, { -80, 0 }, { -35, 0 }, + { 96, 0 }, { -91, 0 }, { 44, 0 }, { 6, 0 }, + { -33, 0 }, { 34, 0 }, { -18, 0 }, { 2, 0 }, + } }, +}; + + // USB AM 2K80J3E emission type /////////////////////////////////////////// // IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000