From b77ba278defc0e9fd1d4df5ad2f2beefd62553ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sat, 4 Jan 2025 16:50:20 +0100 Subject: [PATCH] Improve spectrum analyzer - revert LO2 shift mechanism (restores previous SA speed) - allow tracking generator to reach all(?) frequencies with sufficient accuracy --- .../Device/LibreVNA/librevnadriver.cpp | 26 ------ .../Application/Drivers/Si5351C.cpp | 11 --- .../Application/SpectrumAnalyzer.cpp | 91 ++++++++++++++----- 3 files changed, 68 insertions(+), 60 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp index bc8aef9..1d1ddf6 100644 --- a/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Device/LibreVNA/librevnadriver.cpp @@ -501,32 +501,6 @@ bool LibreVNADriver::setSA(const DeviceDriver::SASettings &s, std::function= 25000000) { - // Check point spacing. - // The highband PLL used as the tracking generator is not able to reach every frequency exactly. This - // could lead to sharp drops in the spectrum at certain frequencies. If the span is wide enough with - // respect to the point number, it is ensured that every displayed point has at least one sample with - // a reachable PLL frequency in it. Display a warning message if this is not the case with the current - // settings. - auto pointSpacing = (p.spectrumSettings.f_stop - p.spectrumSettings.f_start) / (p.spectrumSettings.pointNum - 1); - // The frequency resolution of the PLL is frequency dependent (due to PLL divider). - // This code assumes some knowledge of the actual hardware and probably should be moved - // onto the device at some point - double minSpacing = 25000; - auto stop = p.spectrumSettings.f_stop; - while(stop <= 3000000000) { - minSpacing /= 2; - stop *= 2; - } - if(pointSpacing < minSpacing) { - auto requiredMinSpan = minSpacing * (p.spectrumSettings.pointNum - 1); - auto message = QString() + "Due to PLL limitations, the tracking generator can not reach every frequency exactly. " - "With your current span, this could result in the signal not being detected at some bands. A minimum" - " span of " + Unit::ToString(requiredMinSpan, "Hz", " kMG") + " is recommended at this stop frequency."; - InformationBox::ShowMessage("Warning", message, "TrackingGeneratorSpanTooSmallWarning"); - } - } - return SendPacket(p, [=](TransmissionResult r){ if(cb) { cb(r == TransmissionResult::Ack); diff --git a/Software/VNA_embedded/Application/Drivers/Si5351C.cpp b/Software/VNA_embedded/Application/Drivers/Si5351C.cpp index 9c5f169..f47f874 100644 --- a/Software/VNA_embedded/Application/Drivers/Si5351C.cpp +++ b/Software/VNA_embedded/Application/Drivers/Si5351C.cpp @@ -219,17 +219,6 @@ bool Si5351C::WritePLLConfig(PLLConfig config, PLL pll) { } else { success &=SetBits(Reg::PLLInputSource, mask); } - - // Reset the PLL - mask = pll == PLL::A ? 0x20 : 0x80; - //success &=SetBits(Reg::PLLReset, mask); - reg = pll == PLL::A ? Reg::MSNA_CONFIG : Reg::MSNB_CONFIG; - for(uint8_t i=0;i<8;i++) { - uint8_t readback; - ReadRegister((Reg)((int)reg + i), &readback); - LOG_DEBUG("PLL readback %d: 0x%02x", i, readback); - } - return success; } diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp index 867d7f9..dc9d083 100644 --- a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp +++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp @@ -44,6 +44,17 @@ static uint64_t firstPointTime; static bool firstPoint; // indicates the first point to be transmitted to the GUI, sets the firstPointTime static bool zerospan; +static uint32_t PLLRefFreqs[] = {HW::PLLRef, HW::PLLRef - 1000000}; +static constexpr uint8_t PLLRefFreqsNum = sizeof(PLLRefFreqs)/sizeof(PLLRefFreqs[0]); +static uint8_t PLLRefIndex; + +static void setPLLReference() { + Si5351.SetCLK(SiChannel::Source, PLLRefFreqs[PLLRefIndex], Si5351C::PLL::A, Si5351C::DriveStrength::mA8); + Si5351.SetCLK(SiChannel::LO1, PLLRefFreqs[PLLRefIndex], Si5351C::PLL::A, Si5351C::DriveStrength::mA8); + Source.SetReference(PLLRefFreqs[PLLRefIndex], false, 1, false); + LO1.SetReference(PLLRefFreqs[PLLRefIndex], false, 1, false); +} + static void StartNextSample() { uint64_t freq = s.f_start + (s.f_stop - s.f_start) * pointCnt / (points - 1); freq = Cal::FrequencyCorrectionToDevice(freq); @@ -65,33 +76,61 @@ static void StartNextSample() { negativeDFT = true; // set tracking generator to default values - trackingFreq = 0; trackingLowband = false; attenuator = 0; // this is the first step in each point, update tracking generator if enabled if (s.trackingGenerator) { + auto oldTrackingFreq = trackingFreq; trackingFreq = freq + s.trackingGeneratorOffset; - if(trackingFreq > 0 && trackingFreq <= (int64_t) HW::Info.limits_maxFreq) { - // tracking frequency is valid, calculate required settings and select band - auto amplitude = HW::GetAmplitudeSettings(s.trackingPower, trackingFreq, s.applySourceCorrection, s.trackingGeneratorPort); - // only set the flag here, it is reset at the beginning of each sweep (this makes sure it is set if any of the points are not reached by the TG) - if(amplitude.unlevel) { - HW::SetOutputUnlevel(true); - } - attenuator = amplitude.attenuator; - if(trackingFreq < HW::BandSwitchFrequency) { - Si5351.SetCLK(SiChannel::LowbandSource, trackingFreq, Si5351C::PLL::B, amplitude.lowBandPower); - FPGA::Disable(FPGA::Periphery::SourceChip); - FPGA::Disable(FPGA::Periphery::SourceRF); - trackingLowband = true; - } else { - Source.SetFrequency(trackingFreq); - Source.SetPowerOutA(amplitude.highBandPower, true); - FPGA::Enable(FPGA::Periphery::SourceChip); - FPGA::Enable(FPGA::Periphery::SourceRF); - trackingLowband = false; - // selected power potentially changed, update default registers - FPGA::WriteMAX2871Default(Source.GetRegisters()); + if(trackingFreq != oldTrackingFreq) { + // need to change the source frequency + oldTrackingFreq = trackingFreq; + if(trackingFreq > 0 && trackingFreq <= (int64_t) HW::Info.limits_maxFreq) { + // tracking frequency is valid, calculate required settings and select band + auto amplitude = HW::GetAmplitudeSettings(s.trackingPower, trackingFreq, s.applySourceCorrection, s.trackingGeneratorPort); + // only set the flag here, it is reset at the beginning of each sweep (this makes sure it is set if any of the points are not reached by the TG) + if(amplitude.unlevel) { + HW::SetOutputUnlevel(true); + } + attenuator = amplitude.attenuator; + if(trackingFreq < HW::BandSwitchFrequency) { + Si5351.SetCLK(SiChannel::LowbandSource, trackingFreq, Si5351C::PLL::B, amplitude.lowBandPower); + FPGA::Disable(FPGA::Periphery::SourceChip); + FPGA::Disable(FPGA::Periphery::SourceRF); + trackingLowband = true; + } else { + // Source PLL can not hit all frequencies with high enough accuracy with a single reference frequency. + // Check available reference frequencies for best match + uint8_t bestIndex = 0; + uint32_t smallestDeviation = UINT32_MAX; + for(uint8_t i=0;i actualRBW / 100) { - Si5351.SetPLL(Si5351C::PLL::B, LO2freq*HW::LO2Multiplier, HW::Ref::getSource()); +// Si5351.SetPLL(Si5351C::PLL::B, LO2freq*HW::LO2Multiplier, HW::Ref::getSource()); + Si5351.SetCLK(SiChannel::Port1LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); + Si5351.SetCLK(SiChannel::Port2LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); lastLO2 = LO2freq; } if (s.UseDFT) { @@ -228,6 +269,10 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) { // set initial state pointCnt = 0; signalIDstep = 0; + trackingFreq = 0; + PLLRefIndex = 0; + setPLLReference(); + // enable the required hardware resources Si5351.Enable(SiChannel::Port1LO2); Si5351.Enable(SiChannel::Port2LO2);