mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-05 22:45:23 +00:00
Refactoring, better code encapsulation for different operating modes
This commit is contained in:
parent
d9d00b8c71
commit
00244022c9
13 changed files with 701 additions and 519 deletions
|
|
@ -1,3 +1,4 @@
|
|||
#include <HW_HAL.hpp>
|
||||
#include <VNA.hpp>
|
||||
#include "Si5351C.hpp"
|
||||
#include "max2871.hpp"
|
||||
|
|
@ -6,7 +7,8 @@
|
|||
#include "FPGA/FPGA.hpp"
|
||||
#include <complex>
|
||||
#include "Exti.hpp"
|
||||
#include "VNA_HAL.hpp"
|
||||
#include "Hardware.hpp"
|
||||
#include "Communication.h"
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||
#define LOG_MODULE "VNA"
|
||||
|
|
@ -17,12 +19,11 @@ static constexpr uint32_t IF1_alternate = 57000000;
|
|||
static constexpr uint32_t IF2 = 250000;
|
||||
|
||||
static VNA::SweepCallback sweepCallback;
|
||||
static VNA::StatusCallback statusCallback;
|
||||
static Protocol::SweepSettings settings;
|
||||
static uint16_t pointCnt;
|
||||
static bool excitingPort1;
|
||||
static Protocol::Datapoint data;
|
||||
static bool manualMode = false;
|
||||
static bool active = false;
|
||||
|
||||
using IFTableEntry = struct {
|
||||
uint16_t pointCnt;
|
||||
|
|
@ -36,208 +37,21 @@ static uint16_t IFTableIndexCnt = 0;
|
|||
|
||||
static constexpr uint32_t BandSwitchFrequency = 25000000;
|
||||
|
||||
static uint32_t extOutFreq = 0;
|
||||
static bool extRefInUse = false;
|
||||
using namespace HWHAL;
|
||||
|
||||
using namespace VNAHAL;
|
||||
|
||||
static void HaltedCallback() {
|
||||
LOG_DEBUG("Halted before point %d", pointCnt);
|
||||
// Check if IF table has entry at this point
|
||||
// if (IFTable[IFTableIndexCnt].pointCnt == pointCnt) {
|
||||
// LOG_DEBUG("Shifting IF to %lu at point %u",
|
||||
// IFTable[IFTableIndexCnt].IF1, pointCnt);
|
||||
// Si5351.WriteRawCLKConfig(1, IFTable[IFTableIndexCnt].clkconfig);
|
||||
// Si5351.WriteRawCLKConfig(4, IFTable[IFTableIndexCnt].clkconfig);
|
||||
// Si5351.WriteRawCLKConfig(5, IFTable[IFTableIndexCnt].clkconfig);
|
||||
// Si5351.ResetPLL(Si5351C::PLL::B);
|
||||
// IFTableIndexCnt++;
|
||||
// }
|
||||
uint64_t frequency = settings.f_start
|
||||
+ (settings.f_stop - settings.f_start) * pointCnt
|
||||
/ (settings.points - 1);
|
||||
if (frequency < BandSwitchFrequency) {
|
||||
// need the Si5351 as Source
|
||||
Si5351.SetCLK(SiChannel::LowbandSource, frequency, Si5351C::PLL::B,
|
||||
Si5351C::DriveStrength::mA2);
|
||||
if (pointCnt == 0) {
|
||||
// First point in sweep, enable CLK
|
||||
Si5351.Enable(SiChannel::LowbandSource);
|
||||
FPGA::Disable(FPGA::Periphery::SourceRF);
|
||||
}
|
||||
} else {
|
||||
// first sweep point in highband is also halted, disable lowband source
|
||||
Si5351.Disable(SiChannel::LowbandSource);
|
||||
FPGA::Enable(FPGA::Periphery::SourceRF);
|
||||
}
|
||||
|
||||
FPGA::ResumeHaltedSweep();
|
||||
}
|
||||
|
||||
static void ReadComplete(FPGA::SamplingResult result) {
|
||||
if(!manualMode) {
|
||||
// normal sweep mode
|
||||
auto port1_raw = std::complex<float>(result.P1I, result.P1Q);
|
||||
auto port2_raw = std::complex<float>(result.P2I, result.P2Q);
|
||||
auto ref = std::complex<float>(result.RefI, result.RefQ);
|
||||
auto port1 = port1_raw / ref;
|
||||
auto port2 = port2_raw / ref;
|
||||
data.pointNum = pointCnt;
|
||||
data.frequency = settings.f_start + (settings.f_stop - settings.f_start) * pointCnt / (settings.points - 1);
|
||||
if(excitingPort1) {
|
||||
data.real_S11 = port1.real();
|
||||
data.imag_S11 = port1.imag();
|
||||
data.real_S21 = port2.real();
|
||||
data.imag_S21 = port2.imag();
|
||||
} else {
|
||||
data.real_S12 = port1.real();
|
||||
data.imag_S12 = port1.imag();
|
||||
data.real_S22 = port2.real();
|
||||
data.imag_S22 = port2.imag();
|
||||
}
|
||||
// figure out whether this sweep point is complete and which port gets excited next
|
||||
bool pointComplete = false;
|
||||
if(settings.excitePort1 == 1 && settings.excitePort2 == 1) {
|
||||
// point is complete when port 2 was active
|
||||
pointComplete = !excitingPort1;
|
||||
// next measurement will be from other port
|
||||
excitingPort1 = !excitingPort1;
|
||||
} else {
|
||||
// only one port active, point is complete after every measurement
|
||||
pointComplete = true;
|
||||
}
|
||||
if(pointComplete) {
|
||||
if (sweepCallback) {
|
||||
sweepCallback(data);
|
||||
}
|
||||
pointCnt++;
|
||||
if (pointCnt >= settings.points) {
|
||||
// reached end of sweep, start again
|
||||
pointCnt = 0;
|
||||
IFTableIndexCnt = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Manual control mode, simply pass on raw result
|
||||
if(statusCallback) {
|
||||
statusCallback(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FPGA_Interrupt(void*) {
|
||||
FPGA::InitiateSampleRead(ReadComplete);
|
||||
}
|
||||
|
||||
bool VNA::Init() {
|
||||
LOG_DEBUG("Initializing...");
|
||||
|
||||
manualMode = false;
|
||||
|
||||
Si5351.Init();
|
||||
|
||||
// Use Si5351 to generate reference frequencies for other PLLs and ADC
|
||||
Si5351.SetPLL(Si5351C::PLL::A, 800000000, Si5351C::PLLSource::XTAL);
|
||||
while(!Si5351.Locked(Si5351C::PLL::A));
|
||||
|
||||
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::XTAL);
|
||||
while(!Si5351.Locked(Si5351C::PLL::B));
|
||||
|
||||
extRefInUse = 0;
|
||||
extOutFreq = 0;
|
||||
Si5351.Disable(SiChannel::ReferenceOut);
|
||||
|
||||
// Both MAX2871 get a 100MHz reference
|
||||
Si5351.SetCLK(SiChannel::Source, 100000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::Source);
|
||||
Si5351.SetCLK(SiChannel::LO1, 100000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::LO1);
|
||||
// 16MHz FPGA clock
|
||||
Si5351.SetCLK(SiChannel::FPGA, 16000000, Si5351C::PLL::A, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::FPGA);
|
||||
|
||||
// Generate second LO with Si5351
|
||||
Si5351.SetCLK(SiChannel::Port1LO2, IF1 - IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::Port1LO2);
|
||||
Si5351.SetCLK(SiChannel::Port2LO2, IF1 - IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::Port2LO2);
|
||||
Si5351.SetCLK(SiChannel::RefLO2, IF1 - IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::RefLO2);
|
||||
|
||||
// PLL reset appears to realign phases of clock signals
|
||||
Si5351.ResetPLL(Si5351C::PLL::B);
|
||||
|
||||
LOG_DEBUG("Si5351 locked");
|
||||
|
||||
// FPGA clock is now present, can initialize
|
||||
if (!FPGA::Init(HaltedCallback)) {
|
||||
LOG_ERR("Aborting due to uninitialized FPGA");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable new data and sweep halt interrupt
|
||||
FPGA::EnableInterrupt(FPGA::Interrupt::NewData);
|
||||
FPGA::EnableInterrupt(FPGA::Interrupt::SweepHalted);
|
||||
|
||||
Exti::SetCallback(FPGA_INTR_GPIO_Port, FPGA_INTR_Pin, Exti::EdgeType::Rising, Exti::Pull::Down, FPGA_Interrupt);
|
||||
|
||||
// Initialize PLLs and build VCO maps
|
||||
// enable source synthesizer
|
||||
FPGA::Enable(FPGA::Periphery::SourceChip);
|
||||
FPGA::SetMode(FPGA::Mode::SourcePLL);
|
||||
Source.Init(100000000, false, 1, false);
|
||||
Source.SetPowerOutA(MAX2871::Power::n4dbm);
|
||||
// output B is not used
|
||||
Source.SetPowerOutB(MAX2871::Power::n4dbm, false);
|
||||
if(!Source.BuildVCOMap()) {
|
||||
LOG_WARN("Source VCO map failed");
|
||||
} else {
|
||||
LOG_INFO("Source VCO map complete");
|
||||
}
|
||||
Source.SetFrequency(1000000000);
|
||||
Source.UpdateFrequency();
|
||||
LOG_DEBUG("Source temp: %u", Source.GetTemp());
|
||||
// disable source synthesizer/enable LO synthesizer
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
FPGA::Disable(FPGA::Periphery::SourceChip);
|
||||
FPGA::Enable(FPGA::Periphery::LO1Chip);
|
||||
FPGA::SetMode(FPGA::Mode::LOPLL);
|
||||
LO1.Init(100000000, false, 1, false);
|
||||
LO1.SetPowerOutA(MAX2871::Power::n4dbm);
|
||||
LO1.SetPowerOutB(MAX2871::Power::n4dbm);
|
||||
if(!LO1.BuildVCOMap()) {
|
||||
LOG_WARN("LO1 VCO map failed");
|
||||
} else {
|
||||
LOG_INFO("LO1 VCO map complete");
|
||||
}
|
||||
LO1.SetFrequency(1000000000 + IF1);
|
||||
LO1.UpdateFrequency();
|
||||
LOG_DEBUG("LO temp: %u", LO1.GetTemp());
|
||||
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
// disable both synthesizers
|
||||
FPGA::Disable(FPGA::Periphery::LO1Chip);
|
||||
FPGA::WriteMAX2871Default(Source.GetRegisters());
|
||||
|
||||
LOG_INFO("Initialized");
|
||||
FPGA::Enable(FPGA::Periphery::ReadyLED);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VNA::ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb) {
|
||||
if (manualMode) {
|
||||
// was used in manual mode last, do full initialization before starting sweep
|
||||
VNA::Init();
|
||||
}
|
||||
bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) {
|
||||
HW::SetMode(HW::Mode::VNA);
|
||||
if(s.excitePort1 == 0 && s.excitePort2 == 0) {
|
||||
// both ports disabled, set to idle
|
||||
SetIdle();
|
||||
// both ports disabled, nothing to do
|
||||
HW::SetIdle();
|
||||
active = false;
|
||||
return false;
|
||||
}
|
||||
sweepCallback = cb;
|
||||
settings = s;
|
||||
// Abort possible active sweep first
|
||||
FPGA::AbortSweep();
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
uint16_t points = settings.points <= FPGA::MaxPoints ? settings.points : FPGA::MaxPoints;
|
||||
// Configure sweep
|
||||
FPGA::SetNumberOfPoints(points);
|
||||
|
|
@ -264,7 +78,7 @@ bool VNA::ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb) {
|
|||
|
||||
// Transfer PLL configuration to FPGA
|
||||
for (uint16_t i = 0; i < points; i++) {
|
||||
uint64_t freq = s.f_start + (s.f_stop - s.f_start) * i / (s.points - 1);
|
||||
uint64_t freq = s.f_start + (s.f_stop - s.f_start) * i / (points - 1);
|
||||
// SetFrequency only manipulates the register content in RAM, no SPI communication is done.
|
||||
// No mode-switch of FPGA necessary here.
|
||||
|
||||
|
|
@ -355,251 +169,116 @@ bool VNA::ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb) {
|
|||
// starting port depends on whether port 1 is active in sweep
|
||||
excitingPort1 = s.excitePort1;
|
||||
IFTableIndexCnt = 0;
|
||||
active = true;
|
||||
// Start the sweep
|
||||
FPGA::StartSweep();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VNA::ConfigureManual(Protocol::ManualControl m, StatusCallback cb) {
|
||||
manualMode = true;
|
||||
statusCallback = cb;
|
||||
FPGA::AbortSweep();
|
||||
// Configure lowband source
|
||||
if (m.SourceLowEN) {
|
||||
Si5351.SetCLK(SiChannel::LowbandSource, m.SourceLowFrequency, Si5351C::PLL::B,
|
||||
(Si5351C::DriveStrength) m.SourceLowPower);
|
||||
Si5351.Enable(SiChannel::LowbandSource);
|
||||
} else {
|
||||
Si5351.Disable(SiChannel::LowbandSource);
|
||||
bool VNA::MeasurementDone(FPGA::SamplingResult result) {
|
||||
if(!active) {
|
||||
return false;
|
||||
}
|
||||
// Configure highband source
|
||||
Source.SetFrequency(m.SourceHighFrequency);
|
||||
Source.SetPowerOutA((MAX2871::Power) m.SourceHighPower);
|
||||
|
||||
// Configure LO1
|
||||
LO1.SetFrequency(m.LO1Frequency);
|
||||
|
||||
// Configure LO2
|
||||
if(m.LO2EN) {
|
||||
// Generate second LO with Si5351
|
||||
Si5351.SetCLK(SiChannel::Port1LO2, m.LO2Frequency, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::Port1LO2);
|
||||
Si5351.SetCLK(SiChannel::Port2LO2, m.LO2Frequency, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::Port2LO2);
|
||||
Si5351.SetCLK(SiChannel::RefLO2, m.LO2Frequency, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
Si5351.Enable(SiChannel::RefLO2);
|
||||
|
||||
// PLL reset appears to realign phases of clock signals
|
||||
Si5351.ResetPLL(Si5351C::PLL::B);
|
||||
// normal sweep mode
|
||||
auto port1_raw = std::complex<float>(result.P1I, result.P1Q);
|
||||
auto port2_raw = std::complex<float>(result.P2I, result.P2Q);
|
||||
auto ref = std::complex<float>(result.RefI, result.RefQ);
|
||||
auto port1 = port1_raw / ref;
|
||||
auto port2 = port2_raw / ref;
|
||||
data.pointNum = pointCnt;
|
||||
data.frequency = settings.f_start + (settings.f_stop - settings.f_start) * pointCnt / (settings.points - 1);
|
||||
if(excitingPort1) {
|
||||
data.real_S11 = port1.real();
|
||||
data.imag_S11 = port1.imag();
|
||||
data.real_S21 = port2.real();
|
||||
data.imag_S21 = port2.imag();
|
||||
} else {
|
||||
Si5351.Disable(SiChannel::Port1LO2);
|
||||
Si5351.Disable(SiChannel::Port2LO2);
|
||||
Si5351.Disable(SiChannel::RefLO2);
|
||||
data.real_S12 = port1.real();
|
||||
data.imag_S12 = port1.imag();
|
||||
data.real_S22 = port2.real();
|
||||
data.imag_S22 = port2.imag();
|
||||
}
|
||||
|
||||
FPGA::WriteMAX2871Default(Source.GetRegisters());
|
||||
|
||||
FPGA::SetNumberOfPoints(1);
|
||||
FPGA::SetSamplesPerPoint(m.Samples);
|
||||
|
||||
// Configure single sweep point
|
||||
FPGA::WriteSweepConfig(0, !m.SourceHighband, Source.GetRegisters(),
|
||||
LO1.GetRegisters(), m.attenuator, 0, FPGA::SettlingTime::us20,
|
||||
FPGA::Samples::SPPRegister, 0,
|
||||
(FPGA::LowpassFilter) m.SourceHighLowpass);
|
||||
|
||||
FPGA::SetWindow((FPGA::Window) m.WindowType);
|
||||
|
||||
// Enable/Disable periphery
|
||||
FPGA::Enable(FPGA::Periphery::SourceChip, m.SourceHighCE);
|
||||
FPGA::Enable(FPGA::Periphery::SourceRF, m.SourceHighRFEN);
|
||||
FPGA::Enable(FPGA::Periphery::LO1Chip, m.LO1CE);
|
||||
FPGA::Enable(FPGA::Periphery::LO1RF, m.LO1RFEN);
|
||||
FPGA::Enable(FPGA::Periphery::Amplifier, m.AmplifierEN);
|
||||
FPGA::Enable(FPGA::Periphery::Port1Mixer, m.Port1EN);
|
||||
FPGA::Enable(FPGA::Periphery::Port2Mixer, m.Port2EN);
|
||||
FPGA::Enable(FPGA::Periphery::RefMixer, m.RefEN);
|
||||
FPGA::Enable(FPGA::Periphery::ExcitePort1, m.PortSwitch == 0);
|
||||
FPGA::Enable(FPGA::Periphery::ExcitePort2, m.PortSwitch == 1);
|
||||
|
||||
FPGA::StartSweep();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VNA::GetTemps(uint8_t *source, uint8_t *lo) {
|
||||
FPGA::SetMode(FPGA::Mode::SourcePLL);
|
||||
*source = Source.GetTemp();
|
||||
FPGA::SetMode(FPGA::Mode::LOPLL);
|
||||
*lo = LO1.GetTemp();
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VNA::fillDeviceInfo(Protocol::DeviceInfo *info) {
|
||||
// read PLL temperatures
|
||||
uint8_t tempSource, tempLO;
|
||||
VNA::GetTemps(&tempSource, &tempLO);
|
||||
LOG_INFO("PLL temperatures: %u/%u", tempSource, tempLO);
|
||||
// Read ADC min/max
|
||||
auto limits = FPGA::GetADCLimits();
|
||||
LOG_INFO("ADC limits: P1: %d/%d P2: %d/%d R: %d/%d",
|
||||
limits.P1min, limits.P1max, limits.P2min, limits.P2max,
|
||||
limits.Rmin, limits.Rmax);
|
||||
#define ADC_LIMIT 30000
|
||||
// Set VNA related member of info struct
|
||||
if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT
|
||||
|| limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT
|
||||
|| limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) {
|
||||
info->ADC_overload = true;
|
||||
// figure out whether this sweep point is complete and which port gets excited next
|
||||
bool pointComplete = false;
|
||||
if(settings.excitePort1 == 1 && settings.excitePort2 == 1) {
|
||||
// point is complete when port 2 was active
|
||||
pointComplete = !excitingPort1;
|
||||
// next measurement will be from other port
|
||||
excitingPort1 = !excitingPort1;
|
||||
} else {
|
||||
info->ADC_overload = false;
|
||||
// only one port active, point is complete after every measurement
|
||||
pointComplete = true;
|
||||
}
|
||||
auto status = FPGA::GetStatus();
|
||||
info->LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1;
|
||||
info->source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1;
|
||||
info->extRefAvailable = Ref::available();
|
||||
info->extRefInUse = extRefInUse;
|
||||
info->temperatures.LO1 = tempLO;
|
||||
info->temperatures.source = tempSource;
|
||||
info->temperatures.MCU = 0;
|
||||
FPGA::ResetADCLimits();
|
||||
}
|
||||
|
||||
bool VNA::Ref::available() {
|
||||
return Si5351.ExtCLKAvailable();
|
||||
}
|
||||
|
||||
bool VNA::Ref::applySettings(Protocol::ReferenceSettings s) {
|
||||
if(extOutFreq != s.ExtRefOuputFreq) {
|
||||
extOutFreq = s.ExtRefOuputFreq;
|
||||
if(extOutFreq == 0) {
|
||||
Si5351.Disable(SiChannel::ReferenceOut);
|
||||
LOG_INFO("External reference output disabled");
|
||||
} else {
|
||||
Si5351.SetCLK(SiChannel::ReferenceOut, extOutFreq, Si5351C::PLL::A);
|
||||
Si5351.Enable(SiChannel::ReferenceOut);
|
||||
LOG_INFO("External reference output set to %luHz", extOutFreq);
|
||||
if(pointComplete) {
|
||||
if (sweepCallback) {
|
||||
sweepCallback(data);
|
||||
}
|
||||
}
|
||||
bool useExternal = s.UseExternalRef;
|
||||
if (s.AutomaticSwitch) {
|
||||
useExternal = Ref::available();
|
||||
}
|
||||
if(useExternal != extRefInUse) {
|
||||
// switch between internal and external reference
|
||||
extRefInUse = useExternal;
|
||||
if(extRefInUse) {
|
||||
if(!Ref::available()) {
|
||||
LOG_WARN("Forced switch to external reference but no signal detected");
|
||||
}
|
||||
Si5351.ConfigureCLKIn(10000000);
|
||||
Si5351.SetPLL(Si5351C::PLL::A, 800000000, Si5351C::PLLSource::CLKIN);
|
||||
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::CLKIN);
|
||||
LOG_INFO("Switched to external reference");
|
||||
FPGA::Enable(FPGA::Periphery::ExtRefLED);
|
||||
} else {
|
||||
Si5351.SetPLL(Si5351C::PLL::A, 800000000, Si5351C::PLLSource::XTAL);
|
||||
Si5351.SetPLL(Si5351C::PLL::B, 800000000, Si5351C::PLLSource::XTAL);
|
||||
LOG_INFO("Switched to internal reference");
|
||||
FPGA::Disable(FPGA::Periphery::ExtRefLED);
|
||||
}
|
||||
}
|
||||
constexpr uint32_t lock_timeout = 10;
|
||||
uint32_t start = HAL_GetTick();
|
||||
while(!Si5351.Locked(Si5351C::PLL::A) || !Si5351.Locked(Si5351C::PLL::A)) {
|
||||
if(HAL_GetTick() - start > lock_timeout) {
|
||||
LOG_ERR("Clock distributor PLLs failed to lock");
|
||||
return false;
|
||||
pointCnt++;
|
||||
if (pointCnt >= settings.points) {
|
||||
// reached end of sweep, start again
|
||||
pointCnt = 0;
|
||||
IFTableIndexCnt = 0;
|
||||
// request to trigger work function
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VNA::ConfigureGenerator(Protocol::GeneratorSettings g) {
|
||||
if(g.activePort == 0) {
|
||||
// both ports disabled, no need to configure PLLs
|
||||
SetIdle();
|
||||
return true;
|
||||
}
|
||||
Protocol::ManualControl m;
|
||||
// LOs not required
|
||||
m.LO1CE = 0;
|
||||
m.LO1Frequency = 1000000000;
|
||||
m.LO1RFEN = 0;
|
||||
m.LO1RFEN = 0;
|
||||
m.LO2EN = 0;
|
||||
m.LO2Frequency = 60000000;
|
||||
m.Port1EN = 0;
|
||||
m.Port2EN = 0;
|
||||
m.RefEN = 0;
|
||||
m.Samples = 131072;
|
||||
m.WindowType = (int) FPGA::Window::None;
|
||||
// Select correct source
|
||||
if(g.frequency < BandSwitchFrequency) {
|
||||
m.SourceLowEN = 1;
|
||||
m.SourceLowFrequency = g.frequency;
|
||||
m.SourceHighCE = 0;
|
||||
m.SourceHighRFEN = 0;
|
||||
m.SourceHighFrequency = BandSwitchFrequency;
|
||||
m.SourceHighLowpass = (int) FPGA::LowpassFilter::M947;
|
||||
m.SourceHighPower = (int) MAX2871::Power::n4dbm;
|
||||
m.SourceHighband = false;
|
||||
} else {
|
||||
m.SourceLowEN = 0;
|
||||
m.SourceLowFrequency = BandSwitchFrequency;
|
||||
m.SourceHighCE = 1;
|
||||
m.SourceHighRFEN = 1;
|
||||
m.SourceHighFrequency = g.frequency;
|
||||
if(g.frequency < 900000000UL) {
|
||||
m.SourceHighLowpass = (int) FPGA::LowpassFilter::M947;
|
||||
} else if(g.frequency < 1800000000UL) {
|
||||
m.SourceHighLowpass = (int) FPGA::LowpassFilter::M1880;
|
||||
} else if(g.frequency < 3500000000UL) {
|
||||
m.SourceHighLowpass = (int) FPGA::LowpassFilter::M3500;
|
||||
} else {
|
||||
m.SourceHighLowpass = (int) FPGA::LowpassFilter::None;
|
||||
}
|
||||
m.SourceHighband = true;
|
||||
}
|
||||
switch(g.activePort) {
|
||||
case 1:
|
||||
m.AmplifierEN = 1;
|
||||
m.PortSwitch = 0;
|
||||
break;
|
||||
case 2:
|
||||
m.AmplifierEN = 1;
|
||||
m.PortSwitch = 1;
|
||||
break;
|
||||
}
|
||||
// Set level (not very accurate)
|
||||
if(g.cdbm_level > -1000) {
|
||||
// use higher source power (approx 0dbm with no attenuation)
|
||||
m.SourceHighPower = (int) MAX2871::Power::p5dbm;
|
||||
m.SourceLowPower = (int) Si5351C::DriveStrength::mA8;
|
||||
} else {
|
||||
// use lower source power (approx -10dbm with no attenuation)
|
||||
m.SourceHighPower = (int) MAX2871::Power::n4dbm;
|
||||
m.SourceLowPower = (int) Si5351C::DriveStrength::mA2;
|
||||
g.cdbm_level += 1000;
|
||||
}
|
||||
// calculate required attenuation
|
||||
uint16_t attval = -g.cdbm_level / 25;
|
||||
if(attval > 127) {
|
||||
attval = 127;
|
||||
}
|
||||
m.attenuator = attval;
|
||||
return ConfigureManual(m, nullptr);
|
||||
void VNA::Work() {
|
||||
// end of sweep
|
||||
HW::Ref::update();
|
||||
// Compile info packet
|
||||
Protocol::PacketInfo packet;
|
||||
packet.type = Protocol::PacketType::DeviceInfo;
|
||||
packet.info.FPGA_configured = 1;
|
||||
packet.info.FW_major = FW_MAJOR;
|
||||
packet.info.FW_minor = FW_MINOR;
|
||||
packet.info.HW_Revision = HW_REVISION;
|
||||
HW::fillDeviceInfo(&packet.info);
|
||||
Communication::Send(packet);
|
||||
FPGA::ResetADCLimits();
|
||||
// Start next sweep
|
||||
FPGA::StartSweep();
|
||||
}
|
||||
|
||||
void VNA::SetIdle() {
|
||||
FPGA::AbortSweep();
|
||||
FPGA::SetMode(FPGA::Mode::FPGA);
|
||||
FPGA::Enable(FPGA::Periphery::SourceChip, false);
|
||||
FPGA::Enable(FPGA::Periphery::SourceRF, false);
|
||||
FPGA::Enable(FPGA::Periphery::LO1Chip, false);
|
||||
FPGA::Enable(FPGA::Periphery::LO1RF, false);
|
||||
FPGA::Enable(FPGA::Periphery::Amplifier, false);
|
||||
FPGA::Enable(FPGA::Periphery::Port1Mixer, false);
|
||||
FPGA::Enable(FPGA::Periphery::Port2Mixer, false);
|
||||
FPGA::Enable(FPGA::Periphery::RefMixer, false);
|
||||
void VNA::SweepHalted() {
|
||||
if(!active) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Halted before point %d", pointCnt);
|
||||
// Check if IF table has entry at this point
|
||||
// if (IFTable[IFTableIndexCnt].pointCnt == pointCnt) {
|
||||
// LOG_DEBUG("Shifting IF to %lu at point %u",
|
||||
// IFTable[IFTableIndexCnt].IF1, pointCnt);
|
||||
// Si5351.WriteRawCLKConfig(1, IFTable[IFTableIndexCnt].clkconfig);
|
||||
// Si5351.WriteRawCLKConfig(4, IFTable[IFTableIndexCnt].clkconfig);
|
||||
// Si5351.WriteRawCLKConfig(5, IFTable[IFTableIndexCnt].clkconfig);
|
||||
// Si5351.ResetPLL(Si5351C::PLL::B);
|
||||
// IFTableIndexCnt++;
|
||||
// }
|
||||
uint64_t frequency = settings.f_start
|
||||
+ (settings.f_stop - settings.f_start) * pointCnt
|
||||
/ (settings.points - 1);
|
||||
if (frequency < BandSwitchFrequency) {
|
||||
// need the Si5351 as Source
|
||||
Si5351.SetCLK(SiChannel::LowbandSource, frequency, Si5351C::PLL::B,
|
||||
Si5351C::DriveStrength::mA2);
|
||||
if (pointCnt == 0) {
|
||||
// First point in sweep, enable CLK
|
||||
Si5351.Enable(SiChannel::LowbandSource);
|
||||
FPGA::Disable(FPGA::Periphery::SourceRF);
|
||||
}
|
||||
} else {
|
||||
// first sweep point in highband is also halted, disable lowband source
|
||||
Si5351.Disable(SiChannel::LowbandSource);
|
||||
FPGA::Enable(FPGA::Periphery::SourceRF);
|
||||
}
|
||||
|
||||
FPGA::ResumeHaltedSweep();
|
||||
}
|
||||
|
||||
void VNA::Stop() {
|
||||
active = false;
|
||||
FPGA::AbortSweep();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue