2020-09-17 09:53:52 +02:00
# include <HW_HAL.hpp>
2020-08-24 19:06:50 +02:00
# include <VNA.hpp>
# include "Si5351C.hpp"
# include "max2871.hpp"
# include "main.h"
# include "delay.hpp"
2020-09-11 23:08:30 +02:00
# include "FPGA/FPGA.hpp"
2020-08-24 19:06:50 +02:00
# include <complex>
# include "Exti.hpp"
2020-09-17 09:53:52 +02:00
# include "Hardware.hpp"
# include "Communication.h"
2020-09-26 23:34:31 +02:00
# include "FreeRTOS.h"
# include "task.h"
2020-11-14 23:53:55 +01:00
# include "Util.hpp"
2021-01-09 21:21:47 +01:00
# include "usb.h"
2022-08-07 03:01:22 +02:00
# include "Trigger.hpp"
2022-01-05 16:01:51 +01:00
# include <cmath>
2020-08-24 19:06:50 +02:00
# define LOG_LEVEL LOG_LEVEL_INFO
# define LOG_MODULE "VNA"
# include "Log.h"
static Protocol : : SweepSettings settings ;
static uint16_t pointCnt ;
2022-04-01 23:01:22 +02:00
static uint8_t stageCnt ;
2022-11-20 01:19:42 +01:00
static uint32_t last_LO2 ;
2022-01-05 21:40:51 +01:00
static double logMultiplier , logFrequency ;
2022-08-06 16:22:12 +02:00
static Protocol : : VNADatapoint < 32 > data ;
2020-09-17 09:53:52 +02:00
static bool active = false ;
2022-12-20 20:42:11 +01:00
static bool waitingInStandby = false ;
2021-07-09 19:34:53 +02:00
static Si5351C : : DriveStrength fixedPowerLowband ;
2020-10-30 19:23:34 +01:00
static bool adcShifted ;
static uint32_t actualBandwidth ;
2020-08-24 19:06:50 +02:00
2022-06-20 01:02:09 +02:00
static uint64_t firstPointTime ;
static bool firstPoint ;
static bool zerospan ;
2020-12-18 15:03:01 +01:00
static constexpr uint8_t sourceHarmonic = 5 ;
static constexpr uint8_t LOHarmonic = 3 ;
2020-10-30 19:23:34 +01:00
static constexpr float alternativeSamplerate = 914285.7143f ;
static constexpr uint8_t alternativePrescaler = 102400000UL / alternativeSamplerate ;
static_assert ( alternativePrescaler * alternativeSamplerate = = 102400000UL , " alternative ADCSamplerate can not be reached exactly " ) ;
2022-01-15 16:11:33 +01:00
static constexpr uint16_t alternativePhaseInc = 4096 * HW : : DefaultIF2 / alternativeSamplerate ;
static_assert ( alternativePhaseInc * alternativeSamplerate = = 4096 * HW : : DefaultIF2 , " DFT can not be computed for 2.IF when using alternative samplerate " ) ;
2020-08-24 19:06:50 +02:00
2021-01-09 21:21:47 +01:00
// Constants for USB buffer overflow prevention
static constexpr uint16_t maxPointsBetweenHalts = 40 ;
2025-01-03 14:36:10 +01:00
static constexpr uint16_t full2portDatapointSize = 66 ; // See Protocol::VNADatapoint::
static constexpr uint32_t reservedUSBbuffer = ( maxPointsBetweenHalts + 2 /*additional buffer*/ ) * ( full2portDatapointSize + 8 /*USB packet overhead*/ ) ;
2021-01-09 21:21:47 +01:00
2025-03-03 15:42:14 +01:00
static uint32_t PLLRefFreqs [ ] = { HW : : PLLRef , HW : : PLLRef - 1000000 } ;
static constexpr uint8_t PLLRefFreqsNum = sizeof ( PLLRefFreqs ) / sizeof ( PLLRefFreqs [ 0 ] ) ;
static uint8_t sourceRefIndex , LO1RefIndex ;
2020-09-17 09:53:52 +02:00
using namespace HWHAL ;
2020-09-12 12:17:35 +02:00
2022-01-05 16:01:51 +01:00
static uint64_t getPointFrequency ( uint16_t pointNum ) {
if ( ! settings . logSweep ) {
return settings . f_start + ( settings . f_stop - settings . f_start ) * pointNum / ( settings . points - 1 ) ;
} else {
2022-01-05 21:40:51 +01:00
static uint16_t lastPointNum = 0 ;
if ( pointNum = = 0 ) {
logFrequency = settings . f_start ;
} else if ( pointNum = = lastPointNum ) {
// nothing to do
} else if ( pointNum = = lastPointNum + 1 ) {
logFrequency * = logMultiplier ;
} else {
logFrequency = settings . f_start * pow ( 10.0 , pointNum * log10 ( ( double ) settings . f_stop / settings . f_start ) / ( settings . points - 1 ) ) ;
}
lastPointNum = pointNum ;
return logFrequency ;
2022-01-05 16:01:51 +01:00
}
}
2025-03-03 15:42:14 +01:00
static bool setPLLFrequencies ( uint64_t f ) {
uint64_t sourceFreq = 0 ;
uint64_t LOFreq = 0 ;
2022-11-20 01:19:42 +01:00
if ( f > HW : : Info . limits_maxFreq ) {
2025-03-03 15:42:14 +01:00
sourceFreq = f / sourceHarmonic ;
LOFreq = ( f + HW : : getIF1 ( ) ) / LOHarmonic ;
2022-11-20 01:19:42 +01:00
} else {
if ( f > = HW : : BandSwitchFrequency ) {
2025-03-03 15:42:14 +01:00
sourceFreq = f ;
2022-11-20 01:19:42 +01:00
}
2025-03-03 15:42:14 +01:00
LOFreq = f + HW : : getIF1 ( ) ;
}
2025-03-05 09:34:41 +01:00
if ( sourceFreq > HW : : BandSwitchFrequency ) {
2025-03-03 15:42:14 +01:00
Source . SetFrequency ( sourceFreq ) ;
2022-11-20 01:19:42 +01:00
}
2025-03-03 15:42:14 +01:00
LO1 . SetFrequency ( LOFreq ) ;
bool needsRefSwitch = false ;
if ( settings . suppressPeaks ) {
// Integer spurs can cause a small peak.
2025-03-05 09:34:41 +01:00
if ( sourceFreq > HW : : BandSwitchFrequency ) {
uint32_t sourceDist = Source . DistanceToIntegerSpur ( ) ;
if ( ( sourceDist > 0 ) & & ( sourceDist < 3 * HW : : getIF2 ( ) ) ) {
LOG_DEBUG ( " Source spur at %lu: %lu " , ( uint32_t ) f , sourceDist ) ;
sourceRefIndex = ! sourceRefIndex ;
Source . SetReference ( PLLRefFreqs [ sourceRefIndex ] , false , 1 , false ) ;
Source . SetFrequency ( sourceFreq ) ;
needsRefSwitch = true ;
}
2025-03-03 15:42:14 +01:00
}
2025-03-05 09:34:41 +01:00
uint32_t LODist = LO1 . DistanceToIntegerSpur ( ) ;
2025-03-03 15:42:14 +01:00
if ( ( LODist > 0 ) & & ( LODist < 3 * HW : : getIF2 ( ) ) ) {
2025-03-05 09:34:41 +01:00
LOG_DEBUG ( " LO spur at %lu " , ( uint32_t ) f ) ;
2025-03-03 15:42:14 +01:00
LO1RefIndex = ! LO1RefIndex ;
LO1 . SetReference ( PLLRefFreqs [ LO1RefIndex ] , false , 1 , false ) ;
LO1 . SetFrequency ( LOFreq ) ;
needsRefSwitch = true ;
}
}
return needsRefSwitch ;
2022-11-20 01:19:42 +01:00
}
static bool needs2LOshift ( uint64_t f , uint32_t current2LO , uint32_t IFBW , uint32_t * new2LO ) {
// Check if 2.LO needs to be shifted
uint64_t actualSource , actual1LO ;
actualSource = Source . GetActualFrequency ( ) ;
actual1LO = LO1 . GetActualFrequency ( ) ;
if ( f > HW : : Info . limits_maxFreq ) {
actualSource * = sourceHarmonic ;
actual1LO * = LOHarmonic ;
} else if ( f < HW : : BandSwitchFrequency ) {
// can use the lowband PLL with high frequency resolution, assume perfect frequency match
actualSource = f ;
}
uint32_t actualFirstIF = actual1LO - actualSource ;
uint32_t actualFinalIF = actualFirstIF - current2LO ;
uint32_t IFdeviation = abs ( actualFinalIF - HW : : getIF2 ( ) ) ;
if ( IFdeviation > IFBW / 2 ) {
* new2LO = actualFirstIF - HW : : getIF2 ( ) ;
return true ;
} else {
// no shift required
return false ;
}
}
2020-11-24 18:06:57 +01:00
bool VNA : : Setup ( Protocol : : SweepSettings s ) {
2025-03-03 15:42:14 +01:00
// Abort possible active sweep first
2020-09-26 23:34:31 +02:00
VNA : : Stop ( ) ;
vTaskDelay ( 5 ) ;
2023-07-30 13:08:27 +02:00
data . clear ( ) ;
2020-09-17 09:53:52 +02:00
HW : : SetMode ( HW : : Mode : : VNA ) ;
2025-05-04 13:01:00 +02:00
Trigger : : SetInput ( false ) ;
2025-03-03 15:42:14 +01:00
sourceRefIndex = 0 ;
LO1RefIndex = 0 ;
Source . SetReference ( PLLRefFreqs [ sourceRefIndex ] , false , 1 , false ) ;
LO1 . SetReference ( PLLRefFreqs [ LO1RefIndex ] , false , 1 , false ) ;
2020-09-17 09:53:52 +02:00
FPGA : : SetMode ( FPGA : : Mode : : FPGA ) ;
2022-01-15 16:11:33 +01:00
FPGA : : WriteRegister ( FPGA : : Reg : : ADCPrescaler , HW : : getADCPrescaler ( ) ) ;
FPGA : : WriteRegister ( FPGA : : Reg : : PhaseIncrement , HW : : getDFTPhaseInc ( ) ) ;
2022-01-05 16:01:51 +01:00
if ( settings . points > FPGA : : MaxPoints ) {
settings . points = FPGA : : MaxPoints ;
}
2025-01-03 14:36:10 +01:00
if ( settings . dwell_time > HW : : Info . limits_maxDwellTime ) {
settings . dwell_time = HW : : Info . limits_maxDwellTime ;
}
2022-01-05 21:40:51 +01:00
settings = s ;
// calculate factor between adjacent points for log sweep for faster calculation when sweeping
logMultiplier = pow ( ( double ) settings . f_stop / settings . f_start , 1.0 / ( settings . points - 1 ) ) ;
2020-08-24 19:06:50 +02:00
// Configure sweep
2022-01-05 16:01:51 +01:00
FPGA : : SetNumberOfPoints ( settings . points ) ;
2022-01-15 16:11:33 +01:00
uint32_t samplesPerPoint = ( HW : : getADCRate ( ) / s . if_bandwidth ) ;
2020-09-26 23:34:31 +02:00
// round up to next multiple of 16 (16 samples are spread across 5 IF2 periods)
if ( samplesPerPoint % 16 ) {
samplesPerPoint + = 16 - samplesPerPoint % 16 ;
}
2022-01-15 16:11:33 +01:00
actualBandwidth = HW : : getADCRate ( ) / samplesPerPoint ;
2020-08-24 19:06:50 +02:00
// has to be one less than actual number of samples
FPGA : : SetSamplesPerPoint ( samplesPerPoint ) ;
2025-01-03 14:36:10 +01:00
FPGA : : SetSettlingTime ( s . dwell_time + HW : : getPLLSettlingDelay ( ) ) ;
2025-01-02 19:16:53 +01:00
2021-08-13 21:44:12 +02:00
// reset unlevel flag if it was set from a previous sweep/mode
HW : : SetOutputUnlevel ( false ) ;
2021-07-09 19:34:53 +02:00
// Start with average level
auto cdbm = ( s . cdbm_excitation_start + s . cdbm_excitation_stop ) / 2 ;
// correct for port 1, assumes port 2 is identical
auto centerFreq = ( s . f_start + s . f_stop ) / 2 ;
// force calculation of amplitude setting for PLL, even with lower frequencies
if ( centerFreq < HW : : BandSwitchFrequency ) {
centerFreq = HW : : BandSwitchFrequency ;
2020-08-24 19:06:50 +02:00
}
2021-07-09 19:34:53 +02:00
auto amplitude = HW : : GetAmplitudeSettings ( cdbm , centerFreq , true , false ) ;
2021-08-13 21:44:12 +02:00
if ( amplitude . unlevel ) {
HW : : SetOutputUnlevel ( true ) ;
}
2021-07-09 19:34:53 +02:00
uint8_t fixedAttenuatorHighband = amplitude . attenuator ;
Source . SetPowerOutA ( amplitude . highBandPower , true ) ;
// amplitude calculation for lowband
amplitude = HW : : GetAmplitudeSettings ( cdbm , HW : : BandSwitchFrequency / 2 , true , false ) ;
2021-08-13 21:44:12 +02:00
if ( amplitude . unlevel ) {
HW : : SetOutputUnlevel ( true ) ;
}
2021-07-09 19:34:53 +02:00
uint8_t fixedAttenuatorLowband = amplitude . attenuator ;
fixedPowerLowband = amplitude . lowBandPower ;
2020-09-29 23:03:20 +02:00
FPGA : : WriteMAX2871Default ( Source . GetRegisters ( ) ) ;
2020-08-24 19:06:50 +02:00
2022-11-20 01:19:42 +01:00
last_LO2 = HW : : getIF1 ( ) - HW : : getIF2 ( ) ;
2025-01-02 19:16:53 +01:00
Si5351 . Enable ( SiChannel : : Port1LO2 ) ;
Si5351 . Enable ( SiChannel : : Port2LO2 ) ;
Si5351 . Enable ( SiChannel : : RefLO2 ) ;
Si5351 . SetPLL ( Si5351C : : PLL : : B , last_LO2 * HW : : LO2Multiplier , HW : : Ref : : getSource ( ) ) ;
2020-09-20 10:13:06 +02:00
Si5351 . ResetPLL ( Si5351C : : PLL : : B ) ;
2022-11-17 12:05:52 +01:00
Si5351 . WaitForLock ( Si5351C : : PLL : : B , 10 ) ;
2020-08-24 19:06:50 +02:00
2022-06-20 01:02:09 +02:00
zerospan = ( s . f_start = = s . f_stop ) & & ( s . cdbm_excitation_start = = s . cdbm_excitation_stop ) ;
2020-08-24 19:06:50 +02:00
bool last_lowband = false ;
2021-01-09 21:21:47 +01:00
uint16_t pointsWithoutHalt = 0 ;
2020-08-24 19:06:50 +02:00
// Transfer PLL configuration to FPGA
2022-01-05 16:01:51 +01:00
for ( uint16_t i = 0 ; i < settings . points ; i + + ) {
uint64_t freq = getPointFrequency ( i ) ;
int16_t power = s . cdbm_excitation_start + ( s . cdbm_excitation_stop - s . cdbm_excitation_start ) * i / ( settings . points - 1 ) ;
2021-05-01 18:34:53 +02:00
freq = Cal : : FrequencyCorrectionToDevice ( freq ) ;
2020-12-18 15:03:01 +01:00
2020-08-24 19:06:50 +02:00
bool needs_halt = false ;
bool lowband = false ;
2020-11-18 19:19:29 +01:00
if ( freq < HW : : BandSwitchFrequency ) {
2020-08-24 19:06:50 +02:00
needs_halt = true ;
lowband = true ;
}
2022-11-20 01:19:42 +01:00
if ( i = = 0 ) {
// halt before first point (makes sure that the 2.LO gets configured correctly if it was shifted for the last point in
// the previous sweep
2020-08-24 19:06:50 +02:00
needs_halt = true ;
}
2022-11-20 01:19:42 +01:00
// SetFrequency only manipulates the register content in RAM, no SPI communication is done.
// No mode-switch of FPGA necessary here.
2025-03-03 15:42:14 +01:00
if ( setPLLFrequencies ( freq ) ) {
needs_halt = true ;
}
2025-01-02 19:16:53 +01:00
uint32_t new_LO2 ;
auto needs_shift = needs2LOshift ( freq , last_LO2 , actualBandwidth , & new_LO2 ) ;
if ( needs_shift ) {
if ( s . suppressPeaks ) {
2020-09-20 10:13:06 +02:00
needs_halt = true ;
2025-01-02 19:16:53 +01:00
last_LO2 = new_LO2 ;
} else {
LOG_WARN ( " Point at f=%lu%06lu needs an LO shift but the feature is disabled. This will cause a peak. " , ( uint32_t ) ( freq / 1000000 ) , ( uint32_t ) ( freq % 1000000 ) ) ;
2020-09-20 10:13:06 +02:00
}
}
2022-11-20 01:19:42 +01:00
if ( last_lowband & & ! lowband ) {
// additional halt before first highband point to enable highband source
needs_halt = true ;
2020-09-20 10:13:06 +02:00
}
2021-01-09 21:21:47 +01:00
// halt on regular intervals to prevent USB buffer overflow
if ( ! needs_halt ) {
pointsWithoutHalt + + ;
if ( pointsWithoutHalt > maxPointsBetweenHalts ) {
needs_halt = true ;
2025-01-03 14:36:10 +01:00
pointsWithoutHalt = 0 ;
2021-01-09 21:21:47 +01:00
}
2022-11-20 01:19:42 +01:00
} else {
2021-01-09 21:21:47 +01:00
pointsWithoutHalt = 0 ;
}
2021-07-09 19:34:53 +02:00
uint8_t attenuator = freq > = HW : : BandSwitchFrequency ? fixedAttenuatorHighband : fixedAttenuatorLowband ;
if ( ! s . fixedPowerSetting ) {
// adapt power level throughout the sweep
amplitude = HW : : GetAmplitudeSettings ( power , freq , true , false ) ;
if ( freq > = HW : : BandSwitchFrequency ) {
Source . SetPowerOutA ( amplitude . highBandPower , true ) ;
}
2021-08-13 21:44:12 +02:00
if ( amplitude . unlevel ) {
HW : : SetOutputUnlevel ( true ) ;
}
2021-07-09 19:34:53 +02:00
attenuator = amplitude . attenuator ;
}
2022-03-07 22:51:17 +01:00
// needs halt before first point to allow PLLs to settle
if ( i = = 0 ) {
needs_halt = true ;
}
2020-09-14 11:03:37 +02:00
FPGA : : WriteSweepConfig ( i , lowband , Source . GetRegisters ( ) ,
2025-01-02 19:16:53 +01:00
LO1 . GetRegisters ( ) , attenuator , freq ,
2020-09-14 11:03:37 +02:00
FPGA : : Samples : : SPPRegister , needs_halt ) ;
2020-08-24 19:06:50 +02:00
last_lowband = lowband ;
}
2025-03-03 15:42:14 +01:00
// reset a possibly changed PLL reference index
sourceRefIndex = 0 ;
LO1RefIndex = 0 ;
Source . SetReference ( PLLRefFreqs [ sourceRefIndex ] , false , 1 , false ) ;
LO1 . SetReference ( PLLRefFreqs [ LO1RefIndex ] , false , 1 , false ) ;
2020-09-20 10:13:06 +02:00
// revert clk configuration to previous value (might have been changed in sweep calculation)
2022-01-15 16:11:33 +01:00
Si5351 . SetCLK ( SiChannel : : RefLO2 , HW : : getIF1 ( ) - HW : : getIF2 ( ) , Si5351C : : PLL : : B , Si5351C : : DriveStrength : : mA2 ) ;
2020-09-20 10:13:06 +02:00
Si5351 . ResetPLL ( Si5351C : : PLL : : B ) ;
2022-11-17 12:05:52 +01:00
Si5351 . WaitForLock ( Si5351C : : PLL : : B , 10 ) ;
2020-08-24 19:06:50 +02:00
// Enable mixers/amplifier/PLLs
2022-01-15 16:11:33 +01:00
FPGA : : SetWindow ( FPGA : : Window : : Kaiser ) ;
2020-08-24 19:06:50 +02:00
FPGA : : Enable ( FPGA : : Periphery : : Port1Mixer ) ;
FPGA : : Enable ( FPGA : : Periphery : : Port2Mixer ) ;
FPGA : : Enable ( FPGA : : Periphery : : RefMixer ) ;
FPGA : : Enable ( FPGA : : Periphery : : Amplifier ) ;
FPGA : : Enable ( FPGA : : Periphery : : SourceChip ) ;
FPGA : : Enable ( FPGA : : Periphery : : SourceRF ) ;
FPGA : : Enable ( FPGA : : Periphery : : LO1Chip ) ;
FPGA : : Enable ( FPGA : : Periphery : : LO1RF ) ;
2022-08-08 18:08:40 +02:00
FPGA : : SetupSweep ( s . stages , s . port1Stage , s . port2Stage , s . syncMode ! = 0 , s . syncMaster ) ;
2022-08-07 03:01:22 +02:00
Trigger : : SetMode ( ( Trigger : : Mode ) s . syncMode ) ;
2020-09-17 19:54:03 +02:00
FPGA : : Enable ( FPGA : : Periphery : : PortSwitch ) ;
2020-08-24 19:06:50 +02:00
pointCnt = 0 ;
2022-04-01 23:01:22 +02:00
stageCnt = 0 ;
2020-10-30 19:23:34 +01:00
adcShifted = false ;
2020-09-17 09:53:52 +02:00
active = true ;
2020-11-07 00:50:59 +01:00
// Enable new data and sweep halt interrupt
FPGA : : EnableInterrupt ( FPGA : : Interrupt : : NewData ) ;
FPGA : : EnableInterrupt ( FPGA : : Interrupt : : SweepHalted ) ;
2022-12-17 08:41:59 +01:00
// Start the sweep if not configured for standby
2022-06-20 01:02:09 +02:00
firstPoint = true ;
2022-12-20 20:42:11 +01:00
if ( settings . standby ) {
waitingInStandby = true ;
}
else {
2022-12-17 08:41:59 +01:00
FPGA : : StartSweep ( ) ;
}
2020-08-24 19:06:50 +02:00
return true ;
}
2022-12-17 08:41:59 +01:00
void VNA : : InitiateSweep ( ) {
// Invoked by a host via InitiateSweep packet
2022-12-20 20:42:11 +01:00
if ( waitingInStandby ) {
2022-12-17 08:41:59 +01:00
// make sure that SweepSettings have been configured for standby operation
FPGA : : StartSweep ( ) ;
2022-12-20 20:42:11 +01:00
waitingInStandby = false ;
2022-12-17 08:41:59 +01:00
}
}
bool VNA : : GetStandbyMode ( ) {
return settings . standby ;
}
2022-12-20 20:42:11 +01:00
bool VNA : : IsWaitingInStandby ( ) {
return waitingInStandby ;
}
void VNA : : SetWaitingInStandby ( bool waiting ) {
waitingInStandby = waiting ;
}
2020-09-26 23:34:31 +02:00
static void PassOnData ( ) {
2020-11-24 16:28:57 +01:00
Protocol : : PacketInfo info ;
2022-08-06 16:22:12 +02:00
info . type = Protocol : : PacketType : : VNADatapoint ;
info . VNAdatapoint = & data ;
2025-01-03 14:36:10 +01:00
if ( ! Communication : : Send ( info ) ) {
LOG_ERR ( " Failed to transmit VNADatapoint " ) ;
}
2022-08-06 16:22:12 +02:00
data . clear ( ) ;
2020-09-26 23:34:31 +02:00
}
2020-10-03 21:56:09 +02:00
bool VNA : : MeasurementDone ( const FPGA : : SamplingResult & result ) {
2020-09-17 09:53:52 +02:00
if ( ! active ) {
return false ;
2020-08-24 19:06:50 +02:00
}
2022-04-01 23:01:22 +02:00
if ( result . pointNum ! = pointCnt | | result . stageNum ! = stageCnt ) {
LOG_WARN ( " Indicated point does not match (%u != %u, %d != %d) " , result . pointNum , pointCnt , result . stageNum , stageCnt ) ;
2020-11-04 22:22:02 +01:00
FPGA : : AbortSweep ( ) ;
return false ;
}
2020-09-17 09:53:52 +02:00
// normal sweep mode
2022-08-06 16:22:12 +02:00
data . addValue ( result . P1I , result . P1Q , stageCnt , ( int ) Protocol : : Source : : Port1 ) ;
data . addValue ( result . P2I , result . P2Q , stageCnt , ( int ) Protocol : : Source : : Port2 ) ;
data . addValue ( result . RefI , result . RefQ , stageCnt , ( int ) Protocol : : Source : : Port1 | ( int ) Protocol : : Source : : Port2 | ( int ) Protocol : : Source : : Reference ) ;
2020-09-17 09:53:52 +02:00
data . pointNum = pointCnt ;
2022-06-20 01:02:09 +02:00
if ( zerospan ) {
uint64_t timestamp = HW : : getLastISRTimestamp ( ) ;
if ( firstPoint ) {
data . us = 0 ;
firstPointTime = timestamp ;
firstPoint = false ;
} else {
data . us = timestamp - firstPointTime ;
}
} else {
// non-zero span, set frequency/power
data . frequency = getPointFrequency ( pointCnt ) ;
2022-08-06 16:22:12 +02:00
data . cdBm = settings . cdbm_excitation_start + ( settings . cdbm_excitation_stop - settings . cdbm_excitation_start ) * pointCnt / ( settings . points - 1 ) ;
2020-08-24 19:06:50 +02:00
}
2022-04-01 23:01:22 +02:00
// figure out whether this sweep point is complete
stageCnt + + ;
2022-08-06 16:22:12 +02:00
if ( stageCnt > settings . stages ) {
2022-04-01 23:01:22 +02:00
// point is complete
stageCnt = 0 ;
2020-09-26 23:34:31 +02:00
STM : : DispatchToInterrupt ( PassOnData ) ;
2020-09-17 09:53:52 +02:00
pointCnt + + ;
if ( pointCnt > = settings . points ) {
// reached end of sweep, start again
pointCnt = 0 ;
// request to trigger work function
return true ;
2020-09-12 12:17:35 +02:00
}
}
return false ;
}
2020-09-13 18:01:32 +02:00
2020-09-17 09:53:52 +02:00
void VNA : : Work ( ) {
// end of sweep
2022-08-07 03:01:22 +02:00
if ( Trigger : : GetMode ( ) ! = Trigger : : Mode : : ExtRef ) {
HW : : Ref : : update ( ) ;
}
2025-04-23 16:18:26 +02:00
// Stop the sweep. The source may not have been active for the last phase but the PLL
// must be enabled for reading the temperature. Stopping the sweep implicitly enables
// the source PLL
FPGA : : StopSweep ( ) ;
2020-09-17 09:53:52 +02:00
// Compile info packet
Protocol : : PacketInfo packet ;
2023-02-20 13:08:31 +01:00
packet . type = Protocol : : PacketType : : DeviceStatus ;
2022-12-06 07:29:33 +01:00
if ( HW : : getStatusUpdateFlag ( ) ) {
2023-02-20 13:08:31 +01:00
HW : : getDeviceStatus ( & packet . status , true ) ;
2022-12-06 07:29:33 +01:00
Communication : : Send ( packet ) ;
}
2021-08-13 21:44:12 +02:00
// do not reset unlevel flag here, as it is calculated only once at the setup of the sweep
2022-12-17 08:41:59 +01:00
// Start next sweep if not configured for standby
2022-12-20 20:42:11 +01:00
if ( settings . standby ) {
waitingInStandby = true ;
}
else {
2022-12-17 08:41:59 +01:00
FPGA : : StartSweep ( ) ;
}
2020-09-17 09:53:52 +02:00
}
void VNA : : SweepHalted ( ) {
if ( ! active ) {
return ;
2020-09-15 14:41:24 +02:00
}
2022-08-07 03:01:22 +02:00
// Resuming the halted sweep requires I2C bus operations to the Si5355. When trigger synchronization is enabled
// in the external reference mode, this might collide with the trigger input check. Instead both these actions
// are handled through the STM::DispatchToInterrupt functionality, ensuring that they do not interrupt each other
STM : : DispatchToInterrupt ( [ ] ( ) {
LOG_DEBUG ( " Halted before point %d " , pointCnt ) ;
2022-11-20 01:19:42 +01:00
bool adcShiftRequired = false ;
2022-08-07 03:01:22 +02:00
uint64_t frequency = getPointFrequency ( pointCnt ) ;
2024-02-09 17:20:18 +01:00
frequency = Cal : : FrequencyCorrectionToDevice ( frequency ) ;
2022-08-07 03:01:22 +02:00
int16_t power = settings . cdbm_excitation_start
+ ( settings . cdbm_excitation_stop - settings . cdbm_excitation_start )
* pointCnt / ( settings . points - 1 ) ;
if ( frequency < HW : : BandSwitchFrequency ) {
auto driveStrength = fixedPowerLowband ;
if ( ! settings . fixedPowerSetting ) {
auto amplitude = HW : : GetAmplitudeSettings ( power , frequency , true , false ) ;
// attenuator value has already been set in sweep setup
driveStrength = amplitude . lowBandPower ;
}
2020-10-30 19:23:34 +01:00
2022-08-07 03:01:22 +02:00
// need the Si5351 as Source
2025-08-09 19:10:53 +02:00
bool freqSuccess = Si5351 . SetCLK ( SiChannel : : LowbandSource , frequency , Si5351C : : PLL : : A , driveStrength ) ;
2022-08-07 03:01:22 +02:00
static bool lowbandDisabled = false ;
if ( pointCnt = = 0 ) {
// First point in sweep, switch to correct source
FPGA : : Disable ( FPGA : : Periphery : : SourceRF ) ;
lowbandDisabled = true ;
}
if ( lowbandDisabled & & freqSuccess ) {
// frequency is valid, can enable lowband source now
Si5351 . Enable ( SiChannel : : LowbandSource ) ;
2022-11-17 12:05:52 +01:00
Delay : : ms ( 10 ) ;
2022-08-07 03:01:22 +02:00
lowbandDisabled = false ;
}
2022-01-15 16:11:33 +01:00
2022-08-07 03:01:22 +02:00
// At low frequencies the 1.LO feedthrough mixes with the 2.LO in the second mixer.
// Depending on the stimulus frequency, the resulting mixing product might alias to the 2.IF
// in the ADC which causes a spike. Check for this and shift the ADC sampling frequency if necessary
uint32_t LO_mixing = ( HW : : getIF1 ( ) + frequency ) - ( HW : : getIF1 ( ) - HW : : getIF2 ( ) ) ;
if ( abs ( Util : : Alias ( LO_mixing , HW : : getADCRate ( ) ) - HW : : getIF2 ( ) ) < = actualBandwidth * 2 ) {
// the image is in or near the IF bandwidth and would cause a peak
// Use a slightly different ADC sample rate if possible
if ( HW : : getIF2 ( ) = = HW : : DefaultIF2 ) {
adcShiftRequired = true ;
}
2022-01-15 16:11:33 +01:00
}
2022-08-07 03:01:22 +02:00
} else if ( ! FPGA : : IsEnabled ( FPGA : : Periphery : : SourceRF ) ) {
// first sweep point in highband is also halted, disable lowband source
Si5351 . Disable ( SiChannel : : LowbandSource ) ;
FPGA : : Enable ( FPGA : : Periphery : : SourceRF ) ;
2020-10-30 19:23:34 +01:00
}
2025-03-03 15:42:14 +01:00
if ( pointCnt = = 0 & & settings . suppressPeaks ) {
sourceRefIndex = 0 ;
LO1RefIndex = 0 ;
Source . SetReference ( PLLRefFreqs [ sourceRefIndex ] , false , 1 , false ) ;
LO1 . SetReference ( PLLRefFreqs [ LO1RefIndex ] , false , 1 , false ) ;
// update PLL reference frequencies
Si5351 . SetCLK ( SiChannel : : Source , PLLRefFreqs [ sourceRefIndex ] , Si5351C : : PLL : : A , Si5351C : : DriveStrength : : mA8 ) ;
Si5351 . SetCLK ( SiChannel : : LO1 , PLLRefFreqs [ LO1RefIndex ] , Si5351C : : PLL : : A , Si5351C : : DriveStrength : : mA8 ) ;
last_LO2 = HW : : getIF1 ( ) - HW : : getIF2 ( ) ;
Si5351 . SetPLL ( Si5351C : : PLL : : B , last_LO2 * HW : : LO2Multiplier , HW : : Ref : : getSource ( ) ) ;
Si5351 . ResetPLL ( Si5351C : : PLL : : B ) ;
Si5351 . WaitForLock ( Si5351C : : PLL : : B , 10 ) ;
HAL_Delay ( 2 ) ;
}
2022-11-20 01:19:42 +01:00
if ( settings . suppressPeaks ) {
// does not actually change PLL settings, just calculates the register values and
// is required to determine the need for a 2.LO shift
2025-03-03 15:42:14 +01:00
if ( setPLLFrequencies ( frequency ) ) {
// update PLL reference frequencies
Si5351 . SetCLK ( SiChannel : : Source , PLLRefFreqs [ sourceRefIndex ] , Si5351C : : PLL : : A , Si5351C : : DriveStrength : : mA8 ) ;
Si5351 . SetCLK ( SiChannel : : LO1 , PLLRefFreqs [ LO1RefIndex ] , Si5351C : : PLL : : A , Si5351C : : DriveStrength : : mA8 ) ;
}
2022-11-20 01:19:42 +01:00
if ( needs2LOshift ( frequency , last_LO2 , actualBandwidth , & last_LO2 ) ) {
2025-01-02 19:16:53 +01:00
Si5351 . SetPLL ( Si5351C : : PLL : : B , last_LO2 * HW : : LO2Multiplier , HW : : Ref : : getSource ( ) ) ;
2022-11-20 01:19:42 +01:00
Si5351 . ResetPLL ( Si5351C : : PLL : : B ) ;
Si5351 . WaitForLock ( Si5351C : : PLL : : B , 10 ) ;
// PLL reset causes the 2.LO to turn off briefly and then ramp on back, needs delay before next point
Delay : : us ( 1500 ) ;
}
}
2020-09-17 09:53:52 +02:00
2022-08-07 03:01:22 +02:00
if ( adcShiftRequired ) {
FPGA : : WriteRegister ( FPGA : : Reg : : ADCPrescaler , alternativePrescaler ) ;
FPGA : : WriteRegister ( FPGA : : Reg : : PhaseIncrement , alternativePhaseInc ) ;
adcShifted = true ;
} else if ( adcShifted ) {
// reset to default value
FPGA : : WriteRegister ( FPGA : : Reg : : ADCPrescaler , HW : : getADCPrescaler ( ) ) ;
FPGA : : WriteRegister ( FPGA : : Reg : : PhaseIncrement , HW : : getDFTPhaseInc ( ) ) ;
adcShifted = false ;
}
2020-10-30 19:23:34 +01:00
2022-08-07 03:01:22 +02:00
if ( usb_available_buffer ( ) > = reservedUSBbuffer ) {
// enough space available, can resume immediately
FPGA : : ResumeHaltedSweep ( ) ;
} else {
// USB buffer could potentially overflow before next halted point, wait until more space is available.
2021-09-24 22:21:38 +02:00
uint32_t start = HAL_GetTick ( ) ;
while ( usb_available_buffer ( ) < reservedUSBbuffer ) {
2025-03-13 10:02:36 +01:00
if ( HAL_GetTick ( ) - start > 1000 ) {
2021-09-24 22:21:38 +02:00
// still no buffer space after some time, something more serious must have gone wrong
// -> abort sweep and return to idle
usb_clear_buffer ( ) ;
FPGA : : AbortSweep ( ) ;
HW : : SetIdle ( ) ;
return ;
}
}
2021-01-09 21:21:47 +01:00
FPGA : : ResumeHaltedSweep ( ) ;
2022-08-07 03:01:22 +02:00
}
} ) ;
2020-09-13 18:01:32 +02:00
}
2020-09-14 23:13:32 +02:00
2020-09-17 09:53:52 +02:00
void VNA : : Stop ( ) {
active = false ;
2020-09-14 23:13:32 +02:00
FPGA : : AbortSweep ( ) ;
}
2022-06-26 18:47:34 +02:00
void VNA : : PrintStatus ( ) {
HAL_Delay ( 10 ) ;
LOG_INFO ( " VNA status: " ) ;
HAL_Delay ( 10 ) ;
LOG_INFO ( " Active: %d " , active ) ;
HAL_Delay ( 10 ) ;
LOG_INFO ( " Points: %d/%d " , pointCnt , settings . points ) ;
HAL_Delay ( 10 ) ;
2022-08-06 16:22:12 +02:00
LOG_INFO ( " Stages: %d/%d " , stageCnt , settings . stages ) ;
2022-06-26 18:47:34 +02:00
HAL_Delay ( 10 ) ;
LOG_INFO ( " FPGA status: 0x%04x " , FPGA : : GetStatus ( ) ) ;
}