Imrpoved sound quality in analog_audio_app, and added Nyquist protection option to BBW filter for praline. (#3054)

This commit is contained in:
stafur
2026-02-28 17:36:01 -05:00
committed by GitHub
parent b013e68b17
commit 3a54b112ec
7 changed files with 225 additions and 74 deletions
+36 -48
View File
@@ -247,21 +247,20 @@ constexpr ClockControls si5351c_clock_control_common{{
constexpr ClockControls si5351a_clock_control_common{{
#ifdef PRALINE
// CLK0: MAX5864 (ADC)
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK1: SCT_CLK - iCE40 FPGA timing clock
{ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK2: LPC43xx MCU
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK3: CLKOUT (optional) SMA Port P1
{ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK4: PRALINE MAX2831 reference (40 MHz) - INVERTED per hackrf_usb, 4mA, Integer mode
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK5: PRALINE RFFC5072 reference (40 MHz) - INVERTED, 6mA, Integer mode
// This matches HackRF One OG configuration for RFFC5072
{ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK6: SMA Port P2
{ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
// CLK0: MAX5864 (ADC) - 4mA, Inverted (Standard for Praline sync)
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_On},
// CLK1: SCT_CLK (iCE40 FPGA) - 6mA, Inverted (Fixes 30-60Hz Drumming)
{ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On},
// CLK2: LPC43xx MCU - 4mA, Normal (Must be Integer for MCU stability)
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On},
// CLK3: CLKOUT SMA Port P1 - 8mA, Normal
{ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On},
// CLK4: MAX2831 reference (40 MHz) - Inverted (Required for mixer lock)
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On},
// CLK5: RFFC5072 reference (40 MHz) - Inverted (Required for mixer lock)
{ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On},
// CLK6: SMA Port P2 - 8mA, Normal
{ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_On},
#else
{ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
{ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off},
@@ -274,7 +273,6 @@ constexpr ClockControls si5351a_clock_control_common{{
// CLK6: Not used
{ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
#endif
// CLK7: Not used
{ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off},
@@ -469,7 +467,9 @@ void ClockManager::init_clock_generator() {
si5351a_clock_control_common[6].ms_src(ref_pll),
si5351a_clock_control_common[7].ms_src(ref_pll),
}};
clock_generator.set_clock_control(si5351_clock_control);
// clock_generator.set_clock_control(si5351_clock_control);
// Use single-byte writes instead of multi-byte
clock_generator.set_clock_control_single_byte(si5351_clock_control);
#else
if (hackrf_r9) {
const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal)
@@ -686,53 +686,41 @@ void ClockManager::disable_if_clocks() {
void ClockManager::set_sampling_frequency(const uint32_t frequency) {
#ifdef PRALINE
/* PRALINE: CLK0=AFE_CLK runs at sample rate (VCO/divider/2)
* CLK1=SCT_CLK runs at 2x sample rate (VCO/divider/1)
* Reference: hackrf_core.c sample_rate_frac_set() lines 580-582
*/
/* 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; // Store frequency for StatusViews
/*
* PRALINE sample rate strategy from GSG hackrf_usb radio.c:
*
* 1. Run ADC at the highest rate possible (up to 40 MHz)
* PRALINE sample rate strategy:
* 1. Maximize AFE rate to push Nyquist above MAX2831's 11.6 MHz LPF minimum
* 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
* 3. Ensure AFE rate is achievable by Si5351 (clean division from 800 MHz VCO)
*/
// 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
constexpr uint32_t MAX_AFE_RATE = 40000000; // Use 40 MHz per GSG reference
constexpr uint8_t MAX_N = 5;
// 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;
_base_band_frequency = frequency;
while ((afe_rate_x2 <= MAX_AFE_RATE) && (n < MAX_N)) {
afe_rate_x2 <<= 1;
uint8_t n = 0;
uint32_t afe_rate = frequency;
// Find the largest n where AFE rate stays within limit
// Start at n=0 and work up
while (n < MAX_N) {
uint32_t next_rate = afe_rate << 1;
if (next_rate > MAX_AFE_RATE) break;
afe_rate = next_rate;
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();
// Configure Si5351 clocks
clock_generator.set_ms_frequency(0, afe_rate * 4, si5351_vco_f, 2); // CLK0: AFE_CLK
clock_generator.set_ms_frequency(1, afe_rate * 4, si5351_vco_f, 1); // CLK1: SCT_CLK
// CLK0: AFE_CLK (with r_div=1 for ÷2)
// CLK1: SCT_CLK (with r_div=0 for ÷1, runs at 2× AFE for FPGA timing)
clock_generator.set_ms_frequency(0, afe_rate * 2, si5351_vco_f, 1);
clock_generator.set_ms_frequency(1, afe_rate * 2, si5351_vco_f, 0);
#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