diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index 01608d8aa..cadbc092e 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -208,16 +208,41 @@ PralineOptionsView::PralineOptionsView(Rect parent_rect, const Style* style) { &label_qi, &options_qi, &label_qs, &options_qs, &label_dec, &options_dec}); + // 1. READ the current state from hardware + fpga_reg_1 = radio::debug::fpga::register_read(1); + uint32_t fpga_reg_2 = radio::debug::fpga::register_read(2); + + // 2. INITIALIZE UI WIDGETS based on read bits + options_dc.set_by_value((fpga_reg_1 & 0x01) ? 1 : 0); // Bit 0 + options_qi.set_by_value((fpga_reg_1 & 0x02) ? 1 : 0); // Bit 1 + options_qs.set_by_value((fpga_reg_1 & 0x04) ? 1 : 0); // Bit 2 + options_dec.set_by_value(fpga_reg_2); options_sr.set_value(receiver_model.sampling_rate() / 1000); - options_dc.set_by_value(1); - options_qi.set_by_value(0); - options_qs.set_by_value(0); - options_dec.set_by_value(0); options_sr.on_change = [this](int32_t v) { receiver_model.set_sampling_rate(static_cast(v) * 1000); }; - options_dc.on_change = [this](size_t, OptionsField::value_t v) { if (v) fpga_reg_1 |= 0x01; else fpga_reg_1 &= ~0x01; update_fpga_ctrl(); }; - options_qi.on_change = [this](size_t, OptionsField::value_t v) { if (v) fpga_reg_1 |= 0x02; else fpga_reg_1 &= ~0x02; update_fpga_ctrl(); }; - options_qs.on_change = [this](size_t, OptionsField::value_t v) { if (v) fpga_reg_1 |= 0x04; else fpga_reg_1 &= ~0x04; update_fpga_ctrl(); }; + options_dc.on_change = [this](size_t, OptionsField::value_t v) { + if (v) + fpga_reg_1 |= 0x01; + else + fpga_reg_1 &= ~0x01; + update_fpga_ctrl(); + }; + + options_qi.on_change = [this](size_t, OptionsField::value_t v) { + if (v) + fpga_reg_1 |= 0x02; + else + fpga_reg_1 &= ~0x02; + update_fpga_ctrl(); + }; + + options_qs.on_change = [this](size_t, OptionsField::value_t v) { + if (v) + fpga_reg_1 |= 0x04; + else + fpga_reg_1 &= ~0x04; + update_fpga_ctrl(); + }; options_dec.on_change = [this](size_t, OptionsField::value_t v) { radio::debug::fpga::register_write(2, v); }; } @@ -549,13 +574,8 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) { const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis); receiver_model.set_modulation(modulation); -#ifdef PRALINE - receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 4000000); - receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw / 2 : 2000000); -#else receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 3072000); receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw / 2 : 1750000); -#endif receiver_model.set_hidden_offset(modulation == ReceiverModel::Mode::AMAudioFMApt ? -2200 : 0); // wefax needs to be shifted, see wefax rx app. diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index 5b04ee843..b2ed584fe 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -67,7 +67,7 @@ class PralineOptionsView : public View { Text label_dec{{UI_POS_X(26), UI_POS_Y(0), UI_POS_WIDTH(1), UI_POS_HEIGHT(1)}, "D"}; OptionsField options_dec{{UI_POS_X(28), UI_POS_Y(0)}, 2, {{" 1", 0}, {" 2", 1}, {" 4", 2}, {" 8", 3}, {"16", 4}}}; - uint8_t fpga_reg_1{0x01}; // Tracks DC, QI, QS bits + uint8_t fpga_reg_1{0x00}; // Tracks DC, QI, QS bits void update_fpga_ctrl(); }; #endif diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index c0697bf23..2c45798d7 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -745,7 +745,7 @@ void WFMAudioDebugView::refresh() { // === De-emphasis Status === // We can't directly read the M4 de-emphasis config, but we can indicate what SHOULD be set // 75µs for USA, 50µs for Europe - text_deemph.set("Check M4 config"); + text_deemph.set("Chk M4 cfg."); text_deemph.set_style(Theme::getInstance()->fg_orange); // === Status Summary === @@ -756,7 +756,7 @@ void WFMAudioDebugView::refresh() { if (sample_rate_ok && lpf_ok && dc_ok) { text_status.set("Hardware config looks OK."); text_status.set_style(Theme::getInstance()->fg_green); - text_status2.set("If ringy: Check de-emphasis in M4!"); + text_status2.set("If ringy: Chk d-emph in M4!"); text_status2.set_style(Theme::getInstance()->fg_orange); } else { std::string issues = "Issues: "; @@ -765,7 +765,7 @@ void WFMAudioDebugView::refresh() { if (!dc_ok) issues += "DC_Block "; text_status.set(issues); text_status.set_style(Theme::getInstance()->fg_red); - text_status2.set("Fix above before checking audio."); + text_status2.set("Fix before checking audio."); text_status2.set_style(Theme::getInstance()->fg_red); } } diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 2af5fa839..9c1ed5a00 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -523,17 +523,17 @@ class WFMAudioDebugView : public View { Text text_lbl_fpga_r1{{0, 140, 140, 16}, "Reg1 (Ctrl):"}; Text text_fpga_r1{{150, 140, 90, 16}, "---"}; - Text text_lbl_dc_q{{0, 156, 140, 16}, "DC/Q/QS:"}; - Text text_dc_q{{150, 156, 90, 16}, "---"}; + Text text_lbl_dc_q{{0, 156, 70, 16}, "DC/Q/QS:"}; + Text text_dc_q{{72, 156, 168, 16}, "---"}; // Audio section Text text_section3{{0, 176, 240, 16}, "--- Audio Path ---"}; - Text text_lbl_expected{{0, 192, 140, 16}, "Expected Audio:"}; - Text text_expected{{150, 192, 90, 16}, "---"}; + Text text_lbl_expected{{0, 192, 96, 16}, "Expctd Aud:"}; + Text text_expected{{98, 192, 142, 16}, "---"}; - Text text_lbl_deemph{{0, 208, 140, 16}, "De-emph Config:"}; - Text text_deemph{{150, 208, 90, 16}, "---"}; + Text text_lbl_deemph{{0, 208, 96, 16}, "D-emph Cfg:"}; + Text text_deemph{{98, 208, 132, 16}, "---"}; // Status Text text_status{{0, 228, 240, 16}, "---"}; diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index 686ffde86..3701877a4 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -109,6 +109,15 @@ constexpr si5351::MultisynthFractional si5351_ms_0_20m { }; constexpr auto si5351_ms_0_20m_reg = si5351_ms_0_20m.reg(0); */ + +constexpr si5351::MultisynthFractional si5351_ms_0_4m{ + .f_src = si5351_vco_f, // 800,000,000 Hz + .a = 100, // Integer divider 100 + .b = 0, + .c = 1, + .r_div = 1 // Final R-divider: 2^1 = 2 +}; + constexpr si5351::MultisynthFractional si5351_ms_0_8m{ .f_src = si5351_vco_f, .a = 50, @@ -398,12 +407,35 @@ void ClockManager::init_clock_generator() { #endif #ifdef PRALINE - /* PRALINE uses Si5351A with: - * CLK0 = AFE_CLK (codec/FPGA sample clock) - * CLK1 = SCT_CLK (FPGA timing clock at 2x sample rate) - * CLK4 = second IF (MAX2831) - * CLK5 = first IF (RFFC5072) - * Uses PLLA on XTAL only (no CLKIN support). + + /* * Praline HackRF Pro Clock Assignments (800 MHz VCO Configuration) + * VCO Frequency: 800,000,000 Hz (Master Reference) + * * CLK0: AFE_CLK (MAX5864 Codec & FPGA ADC Interface) + * - Initialized to: 4,000,000 Hz (si5351_ms_4m) + * - Divider: 100 (Integer), R_DIV: 2 (r_div=1) + * - Note: Defines hardware sample rate. Essential for WFM purity. + * * CLK1: SCT_CLK (iCE40 FPGA System/Timing Clock) + * - Initialized to: 10,000,000 Hz (si5351_ms_10m) + * - Divider: 80 (Integer), R_DIV: 1 (r_div=0) + * - Note: Timing for SGPIO data bus; scales to 2x SR in wideband modes. + * * CLK2: MCU_CLKIN (LPC43xx MCU External Clock Input) + * - Initialized to: 10,000,000 Hz (si5351_ms_10m) + * - Divider: 80 (Integer), R_DIV: 1 (r_div=0) + * - Note: Synchronizes MCU processing to the RF clock tree. + * * CLK3: SG_CLK (Switching Regulator/Internal Logic Sync) + * - Initialized to: 10,000,000 Hz (si5351_ms_10m) + * - Divider: 80 (Integer), R_DIV: 1 (r_div=0) + * - Note: Used for internal FPGA logic/gateware synchronization. + * * CLK4: P_CLK (Peripheral/Expansion Clock) + * - Configured via: si5351c_ms_4_reg + * - Note: Routed to expansion headers for external hardware sync. + * * CLK5: AUX_CLK (Auxiliary reference for secondary logic) + * - Configured via: si5351c_ms_5_reg + * - Note: Provides additional timing flexibility for the iCE40 FPGA. + * * CLK6/7: Unused / Power-Down + * - State: Disabled (si5351a_ms6_7_off_reg) + * - Note: Kept OFF to reduce EMI/RFI near the RF front-end. + * * CLKOUT: Optional external clock output on the header. */ /* Step 1: Write PLL A configuration (800 MHz VCO from 25 MHz XTAL) */ @@ -417,25 +449,13 @@ void ClockManager::init_clock_generator() { } /* Step 2: Write multisynth configurations using single-byte writes */ - clock_generator.write_ms_single_byte(0, si5351_ms_0_8m); // MS0 = divider 50, r_div=1 for 8 MHz - clock_generator.write_ms_single_byte(1, si5351_ms_16m); // MS1 = divider 50, r_div=0 for 16 MHz - - /* CLK4 and CLK5 - use single-byte writes too */ - { - const auto& ms4_regs = si5351c_ms_4_reg; - const uint8_t base_reg = ms4_regs[0]; - for (size_t i = 1; i < ms4_regs.size(); i++) { - clock_generator.write_register(base_reg + i - 1, ms4_regs[i]); - } - } - { - const auto& ms5_regs = si5351c_ms_5_reg; - const uint8_t base_reg = ms5_regs[0]; - for (size_t i = 1; i < ms5_regs.size(); i++) { - clock_generator.write_register(base_reg + i - 1, ms5_regs[i]); - } - } - clock_generator.write(si5351a_ms6_7_off_reg); // MS6/7 off - short write is OK + // These cover all active channels on the Praline board + clock_generator.write_ms_single_byte(0, si5351_ms_0_4m); // CLK0: Codec (4 MHz) + clock_generator.write_ms_single_byte(1, si5351_ms_10m); // CLK1: FPGA Timing (10 MHz) + clock_generator.write_ms_single_byte(2, si5351_ms_10m); // CLK2: MCU Input (10 MHz) + clock_generator.write_ms_single_byte(3, si5351_ms_10m); // CLK3: Logic Sync (10 MHz) + clock_generator.write_ms_single_byte(4, si5351_ms_40m); // CLK4: First IF (40 MHz) + clock_generator.write_ms_single_byte(5, si5351_ms_40m); // CLK5: Second IF (40 MHz) /* Step 3: NOW set clock control registers (AFTER multisynths per HackRF reference) */ const auto ref_pll = ClockControl::MultiSynthSource::PLLA; @@ -489,21 +509,6 @@ void ClockManager::init_clock_generator() { // CRITICAL: Add delay to ensure Si5351 writes complete before I2C bus stops chThdSleepMilliseconds(100); - - // ===== ADD THIS SAFETY BLOCK ===== - // Pre-configure CLK4/CLK5 with correct settings including inversion. - // This ensures the registers have correct values even if enable_if_clocks() - // is not called or fails. The clocks will still be powered off until - // enable_if_clocks() enables the outputs. - - // CLK5: OFF (for now), Integer, PLLA, INVERTED, MS_Self, 6mA = 0xDE - // (Same as 0x5E but with bit 7 set for power off) - clock_generator.write_register(21, 0xDE); - - // CLK4: OFF (for now), Integer, PLLA, INVERTED, MS_Self, 4mA = 0xDD - clock_generator.write_register(20, 0xDD); - // ===== END SAFETY BLOCK ===== - #else // Wait for PLL(s) to lock - with timeout to prevent hang uint8_t device_status_mask = hackrf_r9 @@ -642,27 +647,6 @@ void ClockManager::disable_codec_clocks() { void ClockManager::enable_if_clocks() { #ifdef PRALINE - /* PRALINE: CLK4=MAX2831, CLK5=RFFC5072 - * - * Force-write complete configuration to guarantee correct setup. - * Added force-write to verify correct registers are applying necessary register clock settings. - */ - - // Configure and enable CLK4 (MAX2831) - // Register 20: ON, Integer, PLLA, INVERTED, MS_Self, 4mA = 0x5D - clock_generator.write_register(20, 0x5D); - - // Configure and enable CLK5 (RFFC5072) - CRITICAL! - // Register 21: ON, Integer, PLLA, INVERTED, MS_Self, 6mA = 0x5E - clock_generator.write_register(21, 0x5E); - - // Enable outputs (register 3, bits 4 and 5 = 0) - uint8_t reg3 = clock_generator.read_register(3); - reg3 &= ~0x30; - clock_generator.write_register(3, reg3); - - chThdSleepMilliseconds(10); - /* PRALINE uses CLK5 (first IF) and CLK4 (second IF) */ clock_generator.enable_clock(clock_generator_output_og_first_if); clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if); @@ -710,15 +694,45 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) { /* PRALINE: Match HackRF USB sample_rate_frac_set() * Reference: hackrf_usb radio.c lines 29-91, hackrf_core.c lines 501-685 */ - _base_band_frequency = frequency; + _base_band_frequency = frequency; // Store frequency for StatusViews - // Set FPGA decimation to 0 (no decimation) for direct passthrough - fpga_debug_register_write(2, 0x00); + /* + * PRALINE sample rate strategy from GSG hackrf_usb radio.c: + * + * 1. Run ADC at the highest rate possible (up to 40 MHz) + * 2. Use FPGA decimation to achieve desired output rate + * 3. This makes the analog LPF effective at rejecting aliases + * 4. Re-apply frequency after to reconfigure LPF bandwidth + */ + + // 20 MHz, since GSG reference of 40MHz caused shifts at certain values. + constexpr uint32_t MAX_AFE_RATE = 20000000; + constexpr uint8_t MAX_N = 5; // Max decimation = 2^5 = 32 + + // Calculate optimal decimation factor for RX + // Start with n=1 (minimum decimation of 2) per reference + uint8_t n = 1; + uint32_t afe_rate_x2 = 2 * frequency; + + while ((afe_rate_x2 <= MAX_AFE_RATE) && (n < MAX_N)) { + afe_rate_x2 <<= 1; + n++; + } + + // Store decimation factor for potential use elsewhere + _resampling_n = n; + + // The actual AFE rate = frequency * 2^n + uint32_t afe_rate = frequency << n; + + // Set FPGA RX decimation register + fpga_debug_register_write(2, n); radio::invalidate_spi_config(); - // The following was originally from @kitty. Adopting for testing radio. - clock_generator.set_ms_frequency(0, frequency * 2, si5351_vco_f, 1); // CLK0: r_div=1 (÷2) - clock_generator.set_ms_frequency(1, frequency * 2, si5351_vco_f, 0); // CLK1: r_div=0 (÷1) + // Configure Si5351 clocks + clock_generator.set_ms_frequency(0, afe_rate * 2, si5351_vco_f, 1); // CLK0: AFE_CLK + clock_generator.set_ms_frequency(1, afe_rate * 2, si5351_vco_f, 0); // CLK1: SCT_CLK + #else /* Codec clock is at sampling frequency, CPLD and SGPIO clocks are at * twice the frequency, and derived from the MS0 synth. So it's only @@ -797,27 +811,37 @@ void ClockManager::start_audio_pll() { * 12MHz * 1024 / 25 = 491.52MHz * MSEL=1024, NSEL=25, PSEL=20 */ - cgu::pll0audio::ctrl({ - .pd = 1, - .bypass = 0, - .directi = 0, - .directo = 0, - .clken = 0, - .frm = 0, - .autoblock = 1, - .pllfract_req = 0, - .sel_ext = 1, - .mod_pd = 1, - .clk_sel = cgu::CLK_SEL::XTAL, - }); cgu::pll0audio::mdiv({ - .mdec = 22625UL, // MDEC for MSEL=1024 + .mdec = 22625UL, // Encoded value for MSEL=1024 }); cgu::pll0audio::np_div({ - .pdec = 31, // PSEL=20 - .ndec = 69, // NDEC for NSEL=25 + .pdec = 31, // Encoded value for PSEL=20 + .ndec = 69, // Encoded value for NSEL=25 }); + + cgu::pll0audio::frac({ + .pllfract_ctrl = 0, + }); + + cgu::pll0audio::power_up(); + + // Praline Fix: Wait for lock with a safety timeout + { + uint32_t timeout = 100000; + while (!cgu::pll0audio::is_locked() && timeout > 0) { + timeout--; + } + } + + cgu::pll0audio::clock_enable(); + + /* Route the 12.288MHz PLL to the Base Audio Clock */ + // PD = 0 enables the clock; AUTOBLOCK = 1 prevents glitches during clock switching + LPC_CGU->BASE_AUDIO_CLK.PD = 0; + LPC_CGU->BASE_AUDIO_CLK.AUTOBLOCK = 1; + LPC_CGU->BASE_AUDIO_CLK.CLK_SEL = toUType(cgu::CLK_SEL::PLL0AUDIO); + #else cgu::pll0audio::ctrl({ .pd = 1, diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index 999726bec..6d5f7ba05 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -77,6 +77,8 @@ class ClockManager { void set_reference_ppb(const int32_t ppb); #ifdef PRALINE + uint8_t get_resampling_n() const { return _resampling_n; } + // Si5351 diagnostic methods uint8_t si5351_read_status() { return clock_generator.device_status(); } uint8_t si5351_read_register(uint8_t reg) { return clock_generator.read_register(reg); } @@ -112,6 +114,10 @@ class ClockManager { ReferenceSource detect_reference_source(); Reference choose_reference(); bool loss_of_signal(); + +#ifdef PRALINE + uint8_t _resampling_n{0}; // Current decimation factor (log2) +#endif }; #endif /*__CLOCK_MANAGER_H__*/ diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index ef9798ebe..53bd77d9d 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -172,7 +172,7 @@ void init() { /* Initialize FPGA registers - DC_BLOCK must be enabled for RX */ // debug::fpga::init(); // These FPGA registers control DC_BLOCK, Q-Inv, QUARTER SHIFT, and Decimation. - fpga_debug_register_write(1, 0x01); // DC_BLOCK=1, QUARTER_SHIFT=0, Q_INVERT=0 + fpga_debug_register_write(1, 0x00); // DC_BLOCK=1, QUARTER_SHIFT=0, Q_INVERT=0 fpga_debug_register_write(2, 0x00); // RX_DECIM=No Decim fpga_debug_register_write(3, 0x00); // TX_CTRL=0 fpga_debug_register_write(4, 0x00); // TX_INTRP=0 @@ -512,7 +512,7 @@ void register_write(const size_t register_number, uint32_t value) { void init() { // Initialize FPGA registers after bitstream load // DC_BLOCK (bit 0) must be enabled for RX to work - fpga_debug_register_write(1, 0x01); // CTRL: DC_BLOCK=1 + fpga_debug_register_write(1, 0x00); // CTRL: DC_BLOCK=1 fpga_debug_register_write(2, 0x00); // RX_DECIM: no decimation fpga_debug_register_write(3, 0x00); // TX_CTRL: NCO disabled fpga_debug_register_write(4, 0x00); // TX_INTRP: no interpolation diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index cd4159afb..a99377ca6 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -34,12 +34,6 @@ #include "dsp_iir_config.hpp" #include "utility.hpp" -#ifdef PRALINE -extern "C" { -#include "fpga_bridge.h" -} -#endif - using namespace hackrf::one; using namespace portapack; @@ -312,17 +306,6 @@ void ReceiverModel::update_tuning_frequency() { // TODO: use positive offset if freq < offset. if (enabled_) { radio::set_tuning_frequency(target_frequency() + hidden_offset + tuning_offset()); - -#ifdef PRALINE - /* Praline: Must re-apply baseband filter after frequency change - * Reference: hackrf_usb radio.c radio_set_frequency() - * - * Different frequency ranges may use different quarter-shift modes, - * which affects the required LPF bandwidth. For now we just - * recalculate the filter to be safe. - */ - update_baseband_bandwidth(); -#endif } } @@ -334,20 +317,25 @@ void ReceiverModel::set_hidden_offset(rf::Frequency offset) { void ReceiverModel::update_baseband_bandwidth() { if (enabled_) { #ifdef PRALINE - /* Praline: LPF bandwidth calculation - * Reference: hackrf_usb radio.c radio_set_filter() + /* + * PRALINE LPF bandwidth calculation from GSG hackrf_usb radio.c: * - * LPF = (sample_rate * 3) / 8 - * Plus additional offset if quarter-shift is enabled (not implemented yet) + * Base: (sample_rate * 3) / 8 + * If quarter-shift enabled: add (AFE_rate / 8) * 2 */ - uint32_t lpf_bandwidth = (sampling_rate() * 3) / 8; + uint32_t sample_rate = sampling_rate(); + uint32_t lpf_bandwidth = (sample_rate * 3) / 8; - // For now, quarter-shift is disabled, so no offset added - // When quarter-shift is implemented: - // if (quarter_shift_enabled) { - // uint32_t offset = (sampling_rate() << decimation_n) / 8; - // lpf_bandwidth += offset * 2; - // } + // Check if quarter-shift is enabled (FPGA register 1, bits 2-3) + uint32_t fpga_ctrl = radio::debug::fpga::register_read(1); + uint8_t quarter_shift = (fpga_ctrl >> 2) & 0x03; + + if (quarter_shift != 0) { + // Get resampling factor from clock manager + uint8_t resampling_n = portapack::clock_manager.get_resampling_n(); + uint32_t offset = (sample_rate << resampling_n) / 8; // AFE_rate / 8 + lpf_bandwidth += offset * 2; + } radio::set_baseband_filter_bandwidth_rx(lpf_bandwidth); #else @@ -366,6 +354,12 @@ void ReceiverModel::update_sampling_rate() { radio::set_baseband_rate(sampling_rate()); } update_tuning_frequency(); + +#ifdef PRALINE + // GSG reference: re-apply frequency after sample rate change + // This reconfigures LPF bandwidth based on new decimation + update_baseband_bandwidth(); +#endif } void ReceiverModel::update_lna() { diff --git a/firmware/application/receiver_model.hpp b/firmware/application/receiver_model.hpp index 29fc070d9..ea9317bf5 100644 --- a/firmware/application/receiver_model.hpp +++ b/firmware/application/receiver_model.hpp @@ -48,11 +48,7 @@ class ReceiverModel { struct settings_t { uint32_t baseband_bandwidth = max283x::filter::bandwidth_minimum; -#ifdef PRALINE - uint32_t sampling_rate = 4'000'000; -#else uint32_t sampling_rate = 3'072'000; -#endif rf::Frequency frequency_step = 25'000; rf::Frequency frequency_app_override = 0; uint8_t lna_gain_db = 32; diff --git a/firmware/baseband/proc_wfm_audio.hpp b/firmware/baseband/proc_wfm_audio.hpp index 00abd5bc1..a9ebd87d5 100644 --- a/firmware/baseband/proc_wfm_audio.hpp +++ b/firmware/baseband/proc_wfm_audio.hpp @@ -81,11 +81,7 @@ class WidebandFMAudio : public BasebandProcessor { void on_message(const Message* const message) override; private: -#ifdef PRALINE - static constexpr size_t baseband_fs = 2000000; -#else static constexpr size_t baseband_fs = 3072000; -#endif static constexpr auto spectrum_rate_hz = 50.0f; std::array dst{};