Shifting IF frequencies to remove spikes

This commit is contained in:
Jan Käberich 2021-12-01 21:11:40 +01:00
parent 55ac50ee84
commit 5e7ab8fb3c
5 changed files with 190 additions and 28 deletions

2
.gitignore vendored
View file

@ -13,3 +13,5 @@
*.toc *.toc
*.pyc *.pyc
Hardware/Renderings Hardware/Renderings
Artefacts/

View file

@ -11,7 +11,7 @@
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/> <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1307432939745961523" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true"> <provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1365419078466866483" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/> <language-scope id="org.eclipse.cdt.core.gcc"/>
@ -33,7 +33,7 @@
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/> <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1307432939745961523" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true"> <provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1365419078466866483" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/> <language-scope id="org.eclipse.cdt.core.gcc"/>

View file

@ -18,8 +18,8 @@ static uint8_t *USBD_Class_GetFSCfgDesc (uint16_t *length);
static uint8_t *USBD_Class_GetDeviceQualifierDescriptor (uint16_t *length); static uint8_t *USBD_Class_GetDeviceQualifierDescriptor (uint16_t *length);
static usbd_recv_callback_t cb; static usbd_recv_callback_t cb;
static uint8_t usb_receive_buffer[1024]; static uint8_t usb_receive_buffer[512];
static uint8_t usb_transmit_fifo[8192]; static uint8_t usb_transmit_fifo[6800];
static uint16_t usb_transmit_read_index = 0; static uint16_t usb_transmit_read_index = 0;
static uint16_t usb_transmit_fifo_level = 0; static uint16_t usb_transmit_fifo_level = 0;
static bool data_transmission_active = false; static bool data_transmission_active = false;

View file

@ -281,7 +281,7 @@ HW::AmplitudeSettings HW::GetAmplitudeSettings(int16_t cdbm, uint64_t freq, bool
} }
bool HW::TimedOut() { bool HW::TimedOut() {
constexpr uint32_t timeout = 1000; constexpr uint32_t timeout = 5000;
if(activeMode != Mode::Idle && HAL_GetTick() - lastISR > timeout) { if(activeMode != Mode::Idle && HAL_GetTick() - lastISR > timeout) {
return true; return true;
} else { } else {

View file

@ -35,10 +35,19 @@ using IFTableEntry = struct {
uint8_t clkconfig[8]; uint8_t clkconfig[8];
}; };
using ADCTableEntry = struct {
uint16_t pointCnt;
uint8_t adcPrescaler;
};
static constexpr uint16_t IFTableNumEntries = 500; static constexpr uint16_t IFTableNumEntries = 500;
static IFTableEntry IFTable[IFTableNumEntries]; static IFTableEntry IFTable[IFTableNumEntries];
static uint16_t IFTableIndexCnt = 0; static uint16_t IFTableIndexCnt = 0;
static constexpr uint16_t ADCTableNumEntries = 500;
static ADCTableEntry ADCTable[ADCTableNumEntries];
static uint16_t ADCTableIndexCnt = 0;
static constexpr float alternativeSamplerate = 914285.7143f; static constexpr float alternativeSamplerate = 914285.7143f;
static constexpr uint8_t alternativePrescaler = 102400000UL / alternativeSamplerate; static constexpr uint8_t alternativePrescaler = 102400000UL / alternativeSamplerate;
static_assert(alternativePrescaler * alternativeSamplerate == 102400000UL, "alternative ADCSamplerate can not be reached exactly"); static_assert(alternativePrescaler * alternativeSamplerate == 102400000UL, "alternative ADCSamplerate can not be reached exactly");
@ -111,12 +120,11 @@ bool VNA::Setup(Protocol::SweepSettings s) {
Si5351.ResetPLL(Si5351C::PLL::B); Si5351.ResetPLL(Si5351C::PLL::B);
IFTableIndexCnt = 0; IFTableIndexCnt = 0;
ADCTableIndexCnt = 0;
uint8_t last_ADC_prescaler = HW::ADCprescaler;
bool last_lowband = false; bool last_lowband = false;
// invalidate first entry of IFTable, preventing switing of 2.LO in halted callback
IFTable[0].pointCnt = 0xFFFF;
uint16_t pointsWithoutHalt = 0; uint16_t pointsWithoutHalt = 0;
// Transfer PLL configuration to FPGA // Transfer PLL configuration to FPGA
@ -155,7 +163,92 @@ bool VNA::Setup(Protocol::SweepSettings s) {
// additional halt before first highband point to enable highband source // additional halt before first highband point to enable highband source
needs_halt = true; needs_halt = true;
} }
uint64_t LOFreq = freq + HW::IF1; uint64_t LOFreq = freq + HW::IF1;
// check for collision between IF and point frequency
if(s.suppressPeaks && IFTableIndexCnt < IFTableNumEntries - 1) {
static constexpr int8_t max_multiple = 3;
bool needs_alternate_IF = false;
for(int8_t i=-max_multiple;i<=max_multiple;i++) {
if(i==0) {
continue;
}
for(int8_t j=-max_multiple;j<=max_multiple;j++) {
if(j==0) {
continue;
}
float m_IF, m_freq;
if(i<0) {
m_IF = (float) HW::IF1 / -i;
} else {
m_IF = HW::IF1 * i;
}
if(j<0) {
m_freq = (float) freq / -j;
} else {
m_freq = freq * j;
}
float ratio = m_IF / m_freq;
if(ratio >= 0.97f && ratio <= 1.03f) {
needs_alternate_IF = true;
break;
}
}
if(needs_alternate_IF) {
break;
}
}
if(needs_alternate_IF) {
LOFreq += 2150000;
}
}
// if(s.suppressPeaks && IFTableIndexCnt < IFTableNumEntries - 1) {
// bool ADC_alias;
// static constexpr uint64_t max_LO_shift = 2000000;
// static constexpr uint64_t LO_shift_inc = 71777;
// static constexpr uint32_t max_mixing_freq = 30000000;
// do {
// ADC_alias = false;
// static uint8_t max_harmonic_LO1 = 5;
// static uint8_t max_harmonic_LO2 = 97;
// for (uint64_t harmonic_1LO = 1; harmonic_1LO <= max_harmonic_LO1;
// harmonic_1LO+=2) {
// for (uint64_t harmonic_2LO = 1; harmonic_2LO <= max_harmonic_LO2;
// harmonic_2LO+=2) {
// int64_t harmonic_freq_1LO = harmonic_1LO * LOFreq;
// int64_t harmonic_freq_2LO = harmonic_2LO * (HW::IF1 - HW::IF2);
//
// if (harmonic_freq_2LO > harmonic_freq_1LO + max_mixing_freq) {
// break;
// }
//
// uint64_t mixing_freq = llabs(harmonic_freq_1LO - harmonic_freq_2LO);
// if(mixing_freq > max_mixing_freq) {
// // ADC filter will take care of frequencies this high
// continue;
// }
// if(abs(Util::Alias(mixing_freq, HW::ADCSamplerate) - HW::IF2) <= actualBandwidth * 2) {
// // this frequency point aliases into the final IF frequency, need to shift ADC samplerate
// LOG_INFO("ADC alias with f=%lu%06luHz at point %lu (%lu%06luHz), 1.LO harmonic: %d, 2.LO harmonic: %d",
// (uint32_t ) (mixing_freq / 1000000),
// (uint32_t ) (mixing_freq % 1000000),i, (uint32_t ) (freq / 1000000),
// (uint32_t ) (freq % 1000000), (uint16_t) harmonic_1LO, (uint16_t) harmonic_2LO);
// ADC_alias = true;
// // select different 1.LO
// LOFreq -= LO_shift_inc;
// break;
// }
// }
// if(ADC_alias) {
// // already detected a problem, no need for further loop execution
// break;
// }
// }
// } while(ADC_alias && LOFreq >= freq + HW::IF1 - (max_LO_shift - LO_shift_inc));
// }
if(harmonic_mixing) { if(harmonic_mixing) {
LOFreq /= LOHarmonic; LOFreq /= LOHarmonic;
} }
@ -204,6 +297,56 @@ bool VNA::Setup(Protocol::SweepSettings s) {
IFdeviation, (uint32_t ) (freq / 1000000), (uint32_t ) (freq % 1000000)); IFdeviation, (uint32_t ) (freq / 1000000), (uint32_t ) (freq % 1000000));
} }
// Check if we have an ADC alias problem at this frequency. The 1.LO (and its harmonics) will mix with
// the 2.LO (and its harmonics) in the 2.mixer stage. The mixing product may alias to the final IF in
// the ADCs. This would cause a peak at this frequency in the spectrum
// bool ADC_alias = false;
// static uint8_t max_harmonic = 3;
// for (uint64_t harmonic_1LO = 1; harmonic_1LO <= max_harmonic;
// harmonic_1LO++) {
// for (uint64_t harmonic_2LO = 1; harmonic_2LO <= max_harmonic;
// harmonic_2LO++) {
// uint64_t harmonic_freq_1LO = harmonic_1LO * actualLO1;
// uint64_t harmonic_freq_2LO = harmonic_2LO * last_LO2;
//
// uint64_t mixing_freq = abs(harmonic_freq_1LO - harmonic_freq_2LO);
// if(abs(Util::Alias(mixing_freq, HW::ADCSamplerate) - HW::IF2) <= actualBandwidth * 2) {
// // this frequency point aliases into the final IF frequency, need to shift ADC samplerate
// ADC_alias = true;
// break;
// }
// }
// if(ADC_alias) {
// // already detected a problem, no need for further loop execution
// break;
// }
// }
//
// uint8_t required_prescaler = HW::ADCprescaler;
// if(ADC_alias) {
// required_prescaler = alternativePrescaler;
// }
//
// if(required_prescaler != last_ADC_prescaler) {
// // needs to shift ADC prescaler at this point
// if (ADCTableIndexCnt < ADCTableNumEntries) {
// // still room in table
// needs_halt = true;
// ADCTable[ADCTableIndexCnt].pointCnt = i;
// if(ADCTableIndexCnt < ADCTableNumEntries - 1) {
// ADCTable[ADCTableIndexCnt].adcPrescaler = required_prescaler;
// } else {
// // last entry in ADC table, revert ADC prescaler to default
// ADCTable[ADCTableIndexCnt].adcPrescaler = HW::ADCprescaler;
// }
// last_ADC_prescaler = ADCTable[ADCTableIndexCnt].adcPrescaler;
// LOG_INFO("Changing ADC prescaler to %d at point %lu (%lu%06luHz) to prevent alias.",
// ADCTable[ADCTableIndexCnt].adcPrescaler, i, (uint32_t ) (freq / 1000000),
// (uint32_t ) (freq % 1000000));
// ADCTableIndexCnt++;
// }
// }
// halt on regular intervals to prevent USB buffer overflow // halt on regular intervals to prevent USB buffer overflow
if(!needs_halt) { if(!needs_halt) {
pointsWithoutHalt++; pointsWithoutHalt++;
@ -236,8 +379,13 @@ bool VNA::Setup(Protocol::SweepSettings s) {
// revert clk configuration to previous value (might have been changed in sweep calculation) // revert clk configuration to previous value (might have been changed in sweep calculation)
Si5351.SetCLK(SiChannel::RefLO2, HW::IF1 - HW::IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2); Si5351.SetCLK(SiChannel::RefLO2, HW::IF1 - HW::IF2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
Si5351.ResetPLL(Si5351C::PLL::B); Si5351.ResetPLL(Si5351C::PLL::B);
if (s.f_start >= HW::BandSwitchFrequency) {
// first point already uses the MAX2871 as the source and (probably) does not have the "halt sweep" bit
// set. Give Si5351 some time to ramp up the 2.LO, otherwise the first samples might be invalid
Delay::us(1300);
}
// Enable mixers/amplifier/PLLs // Enable mixers/amplifier/PLLs
FPGA::SetWindow(FPGA::Window::None); FPGA::SetWindow(FPGA::Window::Hann);
FPGA::Enable(FPGA::Periphery::Port1Mixer); FPGA::Enable(FPGA::Periphery::Port1Mixer);
FPGA::Enable(FPGA::Periphery::Port2Mixer); FPGA::Enable(FPGA::Periphery::Port2Mixer);
FPGA::Enable(FPGA::Periphery::RefMixer); FPGA::Enable(FPGA::Periphery::RefMixer);
@ -252,7 +400,13 @@ bool VNA::Setup(Protocol::SweepSettings s) {
pointCnt = 0; pointCnt = 0;
// starting port depends on whether port 1 is active in sweep // starting port depends on whether port 1 is active in sweep
excitingPort1 = s.excitePort1; excitingPort1 = s.excitePort1;
// invalidate first invalid entry of IFTable, preventing switching of 2.LO in halted callback
IFTable[IFTableIndexCnt].pointCnt = 0xFFFF;
ADCTable[ADCTableIndexCnt].pointCnt = 0xFFFF;
// reset table index
IFTableIndexCnt = 0; IFTableIndexCnt = 0;
ADCTableIndexCnt = 0;
adcShifted = false; adcShifted = false;
active = true; active = true;
// Enable new data and sweep halt interrupt // Enable new data and sweep halt interrupt
@ -376,31 +530,37 @@ void VNA::SweepHalted() {
Delay::us(1300); Delay::us(1300);
} }
// At low frequencies the 1.LO feedtrough mixes with the 2.LO in the second mixer. // // 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 // // 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 // // in the ADC which causes a spike. Check for this and shift the ADC sampling frequency if necessary
uint32_t LO_mixing = (HW::IF1 + frequency) - (HW::IF1 - HW::IF2); // uint32_t LO_mixing = (HW::IF1 + frequency) - (HW::IF1 - HW::IF2);
if(abs(Util::Alias(LO_mixing, HW::ADCSamplerate) - HW::IF2) <= actualBandwidth * 2) { // if(abs(Util::Alias(LO_mixing, HW::ADCSamplerate) - HW::IF2) <= actualBandwidth * 2) {
// the image is in or near the IF bandwidth and would cause a peak // // the image is in or near the IF bandwidth and would cause a peak
// Use a slightly different ADC samplerate // // Use a slightly different ADC samplerate
adcShiftRequired = true; // adcShiftRequired = true;
} // }
} else if(!FPGA::IsEnabled(FPGA::Periphery::SourceRF)){ } else if(!FPGA::IsEnabled(FPGA::Periphery::SourceRF)){
// first sweep point in highband is also halted, disable lowband source // first sweep point in highband is also halted, disable lowband source
Si5351.Disable(SiChannel::LowbandSource); Si5351.Disable(SiChannel::LowbandSource);
FPGA::Enable(FPGA::Periphery::SourceRF); FPGA::Enable(FPGA::Periphery::SourceRF);
} }
if(adcShiftRequired) { // if(adcShiftRequired) {
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, alternativePrescaler); // FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, alternativePrescaler);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, alternativePhaseInc); // FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, alternativePhaseInc);
adcShifted = true; // adcShifted = true;
} else if(adcShifted) { // } else if(adcShifted) {
// reset to default value // // reset to default value
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::ADCprescaler); // FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::ADCprescaler);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::DFTphaseInc); // FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::DFTphaseInc);
adcShifted = false; // adcShifted = false;
} // }
// // Check if IF table has entry at this point
// if (ADCTableIndexCnt < ADCTableNumEntries && ADCTable[ADCTableIndexCnt].pointCnt == pointCnt) {
// FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, ADCTable[ADCTableIndexCnt].adcPrescaler);
// FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, ADCTable[ADCTableIndexCnt].adcPrescaler * 10);
// ADCTableIndexCnt++;
// }
if(usb_available_buffer() >= reservedUSBbuffer) { if(usb_available_buffer() >= reservedUSBbuffer) {
// enough space available, can resume immediately // enough space available, can resume immediately