diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise
index 0860140..80b2140 100644
--- a/FPGA/VNA/VNA.gise
+++ b/FPGA/VNA/VNA.gise
@@ -322,6 +322,8 @@
+
+
diff --git a/Software/VNA_embedded/Application/Drivers/USB/usb.c b/Software/VNA_embedded/Application/Drivers/USB/usb.c
index 80f2d0f..614db23 100644
--- a/Software/VNA_embedded/Application/Drivers/USB/usb.c
+++ b/Software/VNA_embedded/Application/Drivers/USB/usb.c
@@ -19,7 +19,7 @@ static uint8_t *USBD_Class_GetDeviceQualifierDescriptor (uint16_t *length);
static usbd_recv_callback_t cb;
static uint8_t usb_receive_buffer[1024];
-static uint8_t usb_transmit_fifo[6144];
+static uint8_t usb_transmit_fifo[4000];
static uint16_t usb_transmit_read_index = 0;
static uint16_t usb_transmit_fifo_level = 0;
static bool data_transmission_active = false;
diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp
index f28db5c..146413f 100644
--- a/Software/VNA_embedded/Application/VNA.cpp
+++ b/Software/VNA_embedded/Application/VNA.cpp
@@ -54,6 +54,45 @@ using namespace HWHAL;
static constexpr uint8_t alternativePrescalers[] = {112, 113, 114, 115};
+static uint8_t LO2_adjustment[FPGA::MaxPoints * 5 / 2];
+
+static void setLO2Adjustment(unsigned int point, int32_t value) {
+ if(value >= 1L<<20) {
+ value = (1L<<20)-1;
+ } else if(value <-(1L<<20)) {
+ value = -(1L<<20);
+ }
+ uint16_t base = point / 2 * 5;
+ if(point & 0x01) {
+ LO2_adjustment[base+2] = (LO2_adjustment[base+2] & 0x0F) | ((value<<4) & 0xF0);
+ LO2_adjustment[base+3] = (value >> 4) & 0xFF;
+ LO2_adjustment[base+4] = (value >> 12) & 0xFF;
+ } else {
+ LO2_adjustment[base+0] = value & 0xFF;
+ LO2_adjustment[base+1] = (value >> 8) & 0xFF;
+ LO2_adjustment[base+2] = ((value >> 16) & 0x0F) | (LO2_adjustment[base+2] & 0xF0);
+ }
+}
+static int32_t getLO2Adjustment(unsigned int point) {
+ uint16_t base = point / 2 * 5;
+ uint32_t ret = 0;
+ if(point & 0x01) {
+ ret |= (LO2_adjustment[base+2] & 0xF0) >> 4;
+ ret |= (uint16_t) LO2_adjustment[base+3] << 4;
+ ret |= (uint32_t) LO2_adjustment[base+4] << 12;
+ } else {
+ ret |= LO2_adjustment[base+0];
+ ret |= (uint16_t) LO2_adjustment[base+1] << 8;
+ ret |= (uint32_t) (LO2_adjustment[base+2]&0x0F) << 16;
+ }
+ // sign extend
+ constexpr uint32_t m = 1U << (20 - 1);
+ ret = (ret ^ m) - m;
+ return ret;
+}
+
+static uint32_t defaultLO2;
+
static uint64_t getPointFrequency(uint16_t pointNum) {
if(!settings.logSweep) {
return settings.f_start + (settings.f_stop - settings.f_start) * pointNum / (settings.points - 1);
@@ -73,7 +112,7 @@ static uint64_t getPointFrequency(uint16_t pointNum) {
}
}
-static uint32_t closestLOAlias(uint64_t LO1, uint64_t LO2, uint32_t IFBW) {
+static uint32_t closestLOAlias(uint64_t LO1, uint64_t LO2, uint32_t IFBW, uint32_t ADC_Samplerate) {
constexpr uint64_t max_LO_harmonic = 2000000000;
constexpr uint32_t max_ADC_alias = 5000000;
@@ -101,7 +140,7 @@ static uint32_t closestLOAlias(uint64_t LO1, uint64_t LO2, uint32_t IFBW) {
if(mixing > max_ADC_alias) {
continue;
}
- int32_t alias = Util::Alias(mixing, HW::getADCRate());
+ int32_t alias = Util::Alias(mixing, ADC_Samplerate);
uint32_t alias_dist = labs((int32_t) HW::getIF2() - alias);
if(alias_dist < closestAlias) {
closestAlias = alias_dist;
@@ -115,51 +154,44 @@ static uint32_t closestLOAlias(uint64_t LO1, uint64_t LO2, uint32_t IFBW) {
return closestAlias;
}
-static bool noLOAliasing(uint64_t LO1, uint64_t LO2, uint32_t IFBW) {
- constexpr uint64_t max_LO_harmonic = 2000000000;
- constexpr uint32_t max_ADC_alias = 5000000;
-
- for(int64_t lo1 = LO1; lo1 <= (int64_t) max_LO_harmonic; lo1 += LO1) {
- // figure out which 2.LO harmonics we have to check
- uint64_t lo2_min = lo1 - max_ADC_alias;
- uint64_t lo2_max = lo1 + max_ADC_alias;
-
- uint16_t lo2_min_harm = ((lo2_min + LO2 - 1) / LO2);
- uint16_t lo2_max_harm = lo2_max / LO2;
-
- if(lo2_max_harm * LO2 > max_LO_harmonic) {
- lo2_max_harm = max_LO_harmonic / LO2;
- }
-
- if(lo2_min_harm > lo2_max_harm) {
- // no aliasing possible, skip 2.LO loop
- continue;
- }
-
- for(int64_t lo2 = LO2 * lo2_min_harm; lo2 <= (int64_t) LO2 * lo2_max_harm; lo2 += LO2) {
- uint32_t mixing = llabs(lo1 - lo2);
- if(mixing > max_ADC_alias) {
- continue;
- }
- int32_t alias = Util::Alias(mixing, HW::getADCRate());
- if(abs(HW::getIF2() - alias) <= IFBW*3) {
- // we do have LO mixing products aliasing into the 2.IF
- return false;
- }
- }
- }
- // all good, no aliasing
- return true;
-}
-
-static bool setPLLFrequencies(uint64_t f, uint32_t current2LO, uint32_t IFBW, uint32_t *new2LO) {
- const std::array IF_shifts = { 0, IFBW * 2, IFBW * 3,
- IFBW * 5, IFBW * 7, IFBW * 7 / 10, IFBW * 11 / 10, IFBW * 13
- / 10, IFBW * 17 / 10, IFBW * 19 / 10, IFBW, IFBW * 23 / 10,
- IFBW * 29 / 10, IFBW * 31 / 10, IFBW * 37 / 10, IFBW * 41 / 10, IFBW
- * 43 / 10, IFBW * 47 / 10, IFBW * 53 / 10, IFBW * 59 / 10,
- IFBW * 61 / 10 };
+//static bool noLOAliasing(uint64_t LO1, uint64_t LO2, uint32_t IFBW) {
+// constexpr uint64_t max_LO_harmonic = 2000000000;
+// constexpr uint32_t max_ADC_alias = 5000000;
+//
+// for(int64_t lo1 = LO1; lo1 <= (int64_t) max_LO_harmonic; lo1 += LO1) {
+// // figure out which 2.LO harmonics we have to check
+// uint64_t lo2_min = lo1 - max_ADC_alias;
+// uint64_t lo2_max = lo1 + max_ADC_alias;
+//
+// uint16_t lo2_min_harm = ((lo2_min + LO2 - 1) / LO2);
+// uint16_t lo2_max_harm = lo2_max / LO2;
+//
+// if(lo2_max_harm * LO2 > max_LO_harmonic) {
+// lo2_max_harm = max_LO_harmonic / LO2;
+// }
+//
+// if(lo2_min_harm > lo2_max_harm) {
+// // no aliasing possible, skip 2.LO loop
+// continue;
+// }
+//
+// for(int64_t lo2 = LO2 * lo2_min_harm; lo2 <= (int64_t) LO2 * lo2_max_harm; lo2 += LO2) {
+// uint32_t mixing = llabs(lo1 - lo2);
+// if(mixing > max_ADC_alias) {
+// continue;
+// }
+// int32_t alias = Util::Alias(mixing, HW::getADCRate());
+// if(abs(HW::getIF2() - alias) <= IFBW*3) {
+// // we do have LO mixing products aliasing into the 2.IF
+// return false;
+// }
+// }
+// }
+// // all good, no aliasing
+// return true;
+//}
+static void setPLLFrequencies(uint64_t f, uint32_t current2LO, uint32_t IFBW, uint32_t *new2LO) {
uint64_t actualSource;
// set the source, this will never change
if (f > HW::Info.limits_maxFreq) {
@@ -173,6 +205,114 @@ static bool setPLLFrequencies(uint64_t f, uint32_t current2LO, uint32_t IFBW, ui
actualSource = f;
}
+ // set the 1.LO
+ uint64_t actual1LO;
+ if(f > HW::Info.limits_maxFreq) {
+ LO1.SetFrequency((f + HW::getIF1()) / LOHarmonic);
+ actual1LO = LO1.GetActualFrequency() * LOHarmonic;
+ } else {
+ LO1.SetFrequency(f + HW::getIF1());
+ actual1LO = LO1.GetActualFrequency();
+ }
+ // adjust 2.LO if necessary
+ *new2LO = current2LO;
+ uint32_t actualFirstIF = actual1LO - actualSource;
+ uint32_t actualFinalIF = actualFirstIF - *new2LO;
+ uint32_t IFdeviation = abs(actualFinalIF - HW::getIF2());
+ if(IFdeviation > IFBW / 2) {
+ *new2LO = actualFirstIF - HW::getIF2();
+ }
+}
+
+static bool findBestADCRate(uint64_t LO1, uint32_t LO2, uint32_t IFBW, FPGA::ADCSamplerate &bestRate) {
+
+ const std::array ADC_prescalers = { HW::DefaultADCprescaler,
+ alternativePrescalers[0], alternativePrescalers[1],
+ alternativePrescalers[2], alternativePrescalers[3] };
+ const std::array returnArray = {
+ FPGA::ADCSamplerate::Default, FPGA::ADCSamplerate::Alt1,
+ FPGA::ADCSamplerate::Alt2, FPGA::ADCSamplerate::Alt3,
+ FPGA::ADCSamplerate::Alt4 };
+
+ uint8_t maxIndex = ADC_prescalers.size();
+
+ if(!settings.suppressPeaks) {
+ maxIndex = 1;
+ }
+
+ uint8_t bestIndex = 0;
+ uint32_t furthestAliasDistance = 0;
+
+ for(uint8_t i = 0;i IFBW * 3) {
+ // no need to look further, chose this option
+ bestRate = returnArray[i];
+ return true;
+ } else if(closest_alias > furthestAliasDistance) {
+ bestIndex = i;
+ furthestAliasDistance = closest_alias;
+ }
+
+// // check if LO mixing product aliases into the ADC
+// if(noLOAliasing(actual1LO, *new2LO, IFBW)) {
+// // found an IF that can be used without problems
+// if(shift != 0) {
+//// LOG_WARN("Shifting IF for f=%lu, LO1=%lu, LO2= %lu", (uint32_t) f, (uint32_t) actual1LO, *new2LO);
+// }
+// return true;
+// }
+ }
+ // all available IF shifts result in aliasing in the ADC
+// LOG_ERR("Failed to shift IF for f=%lu", (uint32_t) f);
+ // no perfect option, use best shift
+// auto shift = IF_shifts[bestIndex];
+// // set the 1.LO
+// uint64_t actual1LO;
+// if(f > HW::Info.limits_maxFreq) {
+// LO1.SetFrequency((f + HW::getIF1() + shift) / LOHarmonic);
+// actual1LO = LO1.GetActualFrequency() * LOHarmonic;
+// } else {
+// LO1.SetFrequency(f + HW::getIF1() + shift);
+// actual1LO = LO1.GetActualFrequency();
+// }
+// // adjust 2.LO if necessary
+// *new2LO = current2LO;
+// uint32_t actualFirstIF = actual1LO - actualSource;
+// uint32_t actualFinalIF = actualFirstIF - *new2LO;
+// uint32_t IFdeviation = abs(actualFinalIF - HW::getIF2());
+// if(IFdeviation > IFBW / 2) {
+// *new2LO = actualFirstIF - HW::getIF2();
+// }
+ bestRate = returnArray[bestIndex];
+ return false;
+}
+
+static bool shift1IF(uint64_t f, uint32_t current2LO, uint32_t IFBW, uint32_t *new2LO, uint32_t ADC_rate) {
+ const std::array IF_shifts = {0, IFBW * 2, IFBW * 3,
+ IFBW * 5, IFBW * 7, IFBW * 7 / 10, IFBW * 11 / 10, IFBW * 13
+ / 10, IFBW * 17 / 10, IFBW * 19 / 10, IFBW, IFBW * 23 / 10,
+ IFBW * 29 / 10, IFBW * 31 / 10, IFBW * 37 / 10, IFBW * 41 / 10, IFBW
+ * 43 / 10, IFBW * 47 / 10, IFBW * 53 / 10, IFBW * 59 / 10,
+ IFBW * 61 / 10 };
+
+// const std::array IF_shifts = {0, 10, 33, 100, 330, 1000, 3300, 10000};
+
+ uint64_t actualSource;
+ // set the source, this will never change
+ if (f > HW::Info.limits_maxFreq) {
+ actualSource = Source.GetActualFrequency() * sourceHarmonic;
+ } else if (f >= HW::BandSwitchFrequency) {
+ actualSource = Source.GetActualFrequency();
+ } else {
+ // source will be set in sweep halted interrupt
+ actualSource = f;
+ }
+
uint8_t maxIndex = IF_shifts.size();
if(!settings.suppressPeaks) {
maxIndex = 1;
@@ -203,7 +343,7 @@ static bool setPLLFrequencies(uint64_t f, uint32_t current2LO, uint32_t IFBW, ui
// LOG_ERR("Checking F=%lu, SRC=%lu, LO1=%lu", (uint32_t) f, (uint32_t) actualSource, (uint32_t) actual1LO);
- auto closest_alias = closestLOAlias(actual1LO, *new2LO, IFBW);
+ auto closest_alias = closestLOAlias(actual1LO, *new2LO, IFBW, ADC_rate);
if(closest_alias > IFBW * 3) {
// no need to look further, chose this option
return true;
@@ -333,6 +473,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
FPGA::WriteMAX2871Default(Source.GetRegisters());
last_LO2 = HW::getIF1() - HW::getIF2();
+ defaultLO2 = last_LO2;
Si5351.SetCLK(SiChannel::Port1LO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
Si5351.SetCLK(SiChannel::Port2LO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
Si5351.SetCLK(SiChannel::RefLO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
@@ -366,6 +507,22 @@ bool VNA::Setup(Protocol::SweepSettings s) {
// No mode-switch of FPGA necessary here.
uint32_t new2LO;
setPLLFrequencies(freq, last_LO2, actualBandwidth, &new2LO);
+ FPGA::ADCSamplerate ADC_rate;
+ if(!findBestADCRate(LO1.GetActualFrequency(), new2LO, actualBandwidth, ADC_rate)) {
+ // all available ADC rates result in a spike
+ uint8_t presc;
+ switch(ADC_rate) {
+ case FPGA::ADCSamplerate::Default: presc = HW::DefaultADCprescaler; break;
+ case FPGA::ADCSamplerate::Alt1: presc = alternativePrescalers[0]; break;
+ case FPGA::ADCSamplerate::Alt2: presc = alternativePrescalers[1]; break;
+ case FPGA::ADCSamplerate::Alt3: presc = alternativePrescalers[2]; break;
+ case FPGA::ADCSamplerate::Alt4: presc = alternativePrescalers[3]; break;
+ }
+ uint32_t samplerate = FPGA::Clockrate / presc;
+ shift1IF(freq, new2LO, actualBandwidth, &new2LO, samplerate);
+ }
+// LOG_WARN("F %lu 1.LO: %lu 2.LO: %lu ADC: %d", (uint32_t) freq, (uint32_t) LO1.GetActualFrequency(), new2LO, (int) ADC_rate);
+// vTaskDelay(10);
if(new2LO != last_LO2 && s.suppressPeaks) {
last_LO2 = new2LO;
needs_halt = true;
@@ -404,9 +561,10 @@ bool VNA::Setup(Protocol::SweepSettings s) {
needs_halt = true;
}
+ setLO2Adjustment(i, (int32_t) last_LO2 - defaultLO2);
FPGA::WriteSweepConfig(i, lowband, Source.GetRegisters(),
LO1.GetRegisters(), attenuator, freq, FPGA::SettlingTime::us60,
- FPGA::ADCSamplerate::Default, needs_halt);
+ ADC_rate, needs_halt);
last_lowband = lowband;
}
// revert clk configuration to previous value (might have been changed in sweep calculation)
@@ -551,7 +709,7 @@ void VNA::SweepHalted() {
// are handled through the STM::DispatchToInterrupt functionality, ensuring that they do not interrupt each other
STM::DispatchToInterrupt([](){
LOG_DEBUG("Halted before point %d", pointCnt);
- bool adcShiftRequired = false;
+// bool adcShiftRequired = false;
uint64_t frequency = getPointFrequency(pointCnt);
frequency = Cal::FrequencyCorrectionToDevice(frequency);
int16_t power = settings.cdbm_excitation_start
@@ -584,14 +742,14 @@ void VNA::SweepHalted() {
// 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;
- }
- }
+// 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;
+// }
+// }
} else if(!FPGA::IsEnabled(FPGA::Periphery::SourceRF)){
// first sweep point in highband is also halted, disable lowband source
Si5351.Disable(SiChannel::LowbandSource);
@@ -600,8 +758,8 @@ void VNA::SweepHalted() {
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
- uint32_t new2LO;
- setPLLFrequencies(frequency, last_LO2, actualBandwidth, &new2LO);
+ uint32_t new2LO = defaultLO2 + getLO2Adjustment(pointCnt);
+// setPLLFrequencies(frequency, last_LO2, actualBandwidth, &new2LO);
if(new2LO != last_LO2) {
last_LO2 = new2LO;
Si5351.SetCLK(SiChannel::Port1LO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
@@ -618,16 +776,16 @@ void VNA::SweepHalted() {
HAL_Delay(2);
}
- 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;
- }
+// 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;
+// }
if(usb_available_buffer() >= reservedUSBbuffer) {
// enough space available, can resume immediately
diff --git a/Software/VNA_embedded/Src/main.c b/Software/VNA_embedded/Src/main.c
index df77ab7..b0d8e59 100644
--- a/Software/VNA_embedded/Src/main.c
+++ b/Software/VNA_embedded/Src/main.c
@@ -57,7 +57,7 @@ UART_HandleTypeDef huart3;
PCD_HandleTypeDef hpcd_USB_FS;
osThreadId defaultTaskHandle;
-uint32_t defaultTaskBuffer[ 1024 ];
+uint32_t defaultTaskBuffer[ 700 ];
osStaticThreadDef_t defaultTaskControlBlock;
/* USER CODE BEGIN PV */
@@ -148,7 +148,7 @@ int main(void)
/* Create the thread(s) */
/* definition and creation of defaultTask */
- osThreadStaticDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 1024, defaultTaskBuffer, &defaultTaskControlBlock);
+ osThreadStaticDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 700, defaultTaskBuffer, &defaultTaskControlBlock);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* USER CODE BEGIN RTOS_THREADS */