user selectable IF frequencies

This commit is contained in:
Jan Käberich 2022-01-15 16:11:33 +01:00
parent d5f2f7019e
commit 5d8efd4336
12 changed files with 322 additions and 73 deletions

View File

@ -212,6 +212,11 @@ AppWindow::AppWindow(QWidget *parent)
spectrumAnalyzer->setAveragingMode(Averaging::Mode::Mean);
vna->setAveragingMode(Averaging::Mode::Mean);
}
// acquisition frequencies may have changed, update
UpdateAcquisitionFrequencies();
active->initializeDevice();
});
connect(ui->actionAbout, &QAction::triggered, [=](){
@ -306,6 +311,7 @@ bool AppWindow::ConnectToDevice(QString serial)
ui->actionReceiver_Calibration->setEnabled(true);
ui->actionFrequency_Calibration->setEnabled(true);
UpdateAcquisitionFrequencies();
Mode::getActiveMode()->initializeDevice();
UpdateReference();
@ -866,6 +872,20 @@ void AppWindow::UpdateReference()
device->SendPacket(p);
}
void AppWindow::UpdateAcquisitionFrequencies()
{
if(!device) {
return;
}
Protocol::PacketInfo p;
p.type = Protocol::PacketType::AcquisitionFrequencySettings;
auto pref = Preferences::getInstance();
p.acquisitionFrequencySettings.IF1 = pref.Acquisition.IF1;
p.acquisitionFrequencySettings.ADCprescaler = pref.Acquisition.ADCprescaler;
p.acquisitionFrequencySettings.DFTphaseInc = pref.Acquisition.DFTPhaseInc;
device->SendPacket(p);
}
void AppWindow::StartFirmwareUpdateDialog()
{
if(device) {

View File

@ -54,6 +54,7 @@ private slots:
int UpdateDeviceList();
void StartManualControl();
void UpdateReference();
void UpdateAcquisitionFrequencies();
void StartFirmwareUpdateDialog();
void DeviceNeedsUpdate(int reported, int expected);
void SourceCalibrationDialog();

View File

@ -73,6 +73,26 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
connect(ui->AcquisitionUseDFT, &QCheckBox::toggled, [=](bool enabled) {
ui->AcquisitionDFTlimitRBW->setEnabled(enabled);
});
ui->AcquisitionIF1->setUnit("Hz");
ui->AcquisitionIF1->setPrefixes(" kM");
ui->AcquisitionIF1->setPrecision(6);
ui->AcquisitionADCRate->setUnit("Hz");
ui->AcquisitionADCRate->setPrefixes(" kM");
ui->AcquisitionADCRate->setPrecision(6);
ui->AcquisitionIF2->setUnit("Hz");
ui->AcquisitionIF2->setPrefixes(" kM");
ui->AcquisitionIF2->setPrecision(6);
auto updateADCRate = [=]() {
// update ADC rate, see FPGA protocol for calculation
ui->AcquisitionADCRate->setValue(102400000.0 / ui->AcquisitionADCpresc->value());
};
auto updateIF2 = [=]() {
auto ADCrate = ui->AcquisitionADCRate->value();
ui->AcquisitionIF2->setValue(ADCrate * ui->AcquisitionADCphaseInc->value() / 4096);
};
connect(ui->AcquisitionADCpresc, qOverload<int>(&QSpinBox::valueChanged), updateADCRate);
connect(ui->AcquisitionADCpresc, qOverload<int>(&QSpinBox::valueChanged), updateIF2);
connect(ui->AcquisitionADCphaseInc, qOverload<int>(&QSpinBox::valueChanged), updateIF2);
// General page
if(p->TCPoverride) {
@ -136,6 +156,9 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1;
p->Acquisition.IF1 = ui->AcquisitionIF1->value();
p->Acquisition.ADCprescaler = ui->AcquisitionADCpresc->value();
p->Acquisition.DFTPhaseInc = ui->AcquisitionADCphaseInc->value();
p->Graphs.showUnits = ui->GraphsShowUnit->isChecked();
p->Graphs.Color.background = ui->GraphsColorBackground->getColor();
@ -156,6 +179,8 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
});
setInitialGUIState();
updateADCRate();
updateIF2();
connect(ui->AcquisitionUseHarmonic, &QCheckBox::toggled, [=](bool enabled) {
if(enabled) {
@ -208,6 +233,9 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode);
ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT);
ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0);
ui->AcquisitionIF1->setValue(p->Acquisition.IF1);
ui->AcquisitionADCpresc->setValue(p->Acquisition.ADCprescaler);
ui->AcquisitionADCphaseInc->setValue(p->Acquisition.DFTPhaseInc);
ui->GraphsShowUnit->setChecked(p->Graphs.showUnits);
ui->GraphsColorBackground->setColor(p->Graphs.Color.background);

View File

@ -68,6 +68,11 @@ public:
bool useDFTinSAmode;
double RBWLimitForDFT;
bool useMedianAveraging;
// advanced, hardware specific settings
double IF1;
int ADCprescaler;
int DFTPhaseInc;
} Acquisition;
struct {
bool showUnits;
@ -108,7 +113,7 @@ private:
QString name;
QVariant def;
};
const std::array<SettingDescription, 42> descr = {{
const std::array<SettingDescription, 45> descr = {{
{&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true},
{&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false},
{&Startup.DefaultSweep.type, "Startup.DefaultSweep.type", "Frequency"},
@ -138,6 +143,9 @@ private:
{&Acquisition.useDFTinSAmode, "Acquisition.useDFTinSAmode", true},
{&Acquisition.RBWLimitForDFT, "Acquisition.RBWLimitForDFT", 3000.0},
{&Acquisition.useMedianAveraging, "Acquisition.useMedianAveraging", false},
{&Acquisition.IF1, "Acquisition.IF1", 62000000},
{&Acquisition.ADCprescaler, "Acquisition.ADCprescaler", 128},
{&Acquisition.DFTPhaseInc, "Acquisition.DFTPhaseInc", 1280},
{&Graphs.showUnits, "Graphs.showUnits", true},
{&Graphs.Color.background, "Graphs.Color.background", QColor(Qt::black)},
{&Graphs.Color.axis, "Graphs.Color.axis", QColor(Qt::white)},

View File

@ -83,7 +83,7 @@
</size>
</property>
<property name="currentIndex">
<number>3</number>
<number>1</number>
</property>
<widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4">
@ -569,7 +569,7 @@
</layout>
</widget>
<widget class="QWidget" name="Acquisition">
<layout class="QVBoxLayout" name="verticalLayout_5">
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
@ -647,7 +647,9 @@
<property name="title">
<string>Common</string>
</property>
<layout class="QFormLayout" name="formLayout">
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<layout class="QFormLayout" name="formLayout_10">
<item row="0" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
@ -670,6 +672,119 @@
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_15">
<property name="title">
<string>IF frequencies</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label_34">
<property name="text">
<string>This section contains advanced system settings. It is recommended to leave them at default values unless you know what you are doing. Slight changes of the IF frequencies can be used to shift possible spikes to less problematic frequencies. Large changes of these frequencies may severely impact device performance.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>IF 1:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="AcquisitionIF1">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Frequency of the first IF&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>ADC prescaler:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="AcquisitionADCpresc">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ADC prescaler in FPGA. The ADC sample rate is determined by 102.4MHz/prescaler&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>112</number>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>128</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>ADC sample rate:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="AcquisitionADCRate">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Phase increment:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="AcquisitionADCphaseInc">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Phase increment per ADC sample. Together with the ADC sample rate this determines the frequency of the second IF&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>4095</number>
</property>
<property name="value">
<number>1280</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>IF 2:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="SIUnitEdit" name="AcquisitionIF2">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>

View File

@ -222,6 +222,21 @@ inline void App_Process() {
Cal::setFrequencyCal(recv_packet.frequencyCorrection.ppm);
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
break;
case Protocol::PacketType::RequestAcquisitionFrequencySettings:
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
{
Protocol::PacketInfo send;
send.type = Protocol::PacketType::AcquisitionFrequencySettings;
send.acquisitionFrequencySettings.IF1 = HW::getIF1();
send.acquisitionFrequencySettings.ADCprescaler = HW::getADCPrescaler();
send.acquisitionFrequencySettings.DFTphaseInc = HW::getDFTPhaseInc();
Communication::Send(send);
}
break;
case Protocol::PacketType::AcquisitionFrequencySettings:
HW::setAcquisitionFrequencies(recv_packet.acquisitionFrequencySettings);
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
break;
default:
// this packet type is not supported
Communication::SendWithoutPayload(Protocol::PacketType::Nack);

View File

@ -100,6 +100,7 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_
case PacketType::SourceCalPoint:
case PacketType::ReceiverCalPoint: payload_size = sizeof(packet.amplitudePoint); break;
case PacketType::FrequencyCorrection: payload_size = sizeof(packet.frequencyCorrection); break;
case PacketType::AcquisitionFrequencySettings: payload_size = sizeof(packet.acquisitionFrequencySettings); break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
@ -109,6 +110,7 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_
case PacketType::RequestReceiverCal:
case PacketType::SetIdle:
case PacketType::RequestFrequencyCorrection:
case PacketType::RequestAcquisitionFrequencySettings:
// no payload
break;
case PacketType::None:

View File

@ -4,7 +4,7 @@
namespace Protocol {
static constexpr uint16_t Version = 8;
static constexpr uint16_t Version = 9;
#pragma pack(push, 1)
@ -160,6 +160,12 @@ using FrequencyCorrection = struct _frequencycorrection {
float ppm;
};
using AcquisitionFrequencySettings = struct _acquisitionfrequencysettigns {
uint32_t IF1;
uint8_t ADCprescaler;
uint16_t DFTphaseInc;
};
enum class PacketType : uint8_t {
None = 0,
Datapoint = 1,
@ -184,6 +190,8 @@ enum class PacketType : uint8_t {
SetIdle = 20,
RequestFrequencyCorrection = 21,
FrequencyCorrection = 22,
RequestAcquisitionFrequencySettings = 23,
AcquisitionFrequencySettings = 24,
};
using PacketInfo = struct _packetinfo {
@ -201,6 +209,7 @@ using PacketInfo = struct _packetinfo {
SpectrumAnalyzerResult spectrumResult;
AmplitudeCorrectionPoint amplitudePoint;
FrequencyCorrection frequencyCorrection;
AcquisitionFrequencySettings acquisitionFrequencySettings;
};
};

View File

@ -21,6 +21,12 @@ static bool unlevel = false;
static Protocol::ReferenceSettings ref;
static uint32_t lastISR;
static uint32_t IF1 = HW::DefaultIF1;
static uint32_t IF2 = HW::DefaultIF2;
static uint32_t ADCsamplerate = HW::DefaultADCSamplerate;
static uint8_t ADCprescaler = HW::DefaultADCprescaler;
static uint16_t DFTphaseInc = HW::DefaultDFTphaseInc;
using namespace HWHAL;
static void HaltedCallback() {
@ -133,9 +139,9 @@ bool HW::Init() {
}
// Set default ADC samplerate
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::ADCprescaler);
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, ADCprescaler);
// Set phase increment according to
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::DFTphaseInc);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, DFTphaseInc);
Exti::SetCallback(FPGA_INTR_GPIO_Port, FPGA_INTR_Pin, Exti::EdgeType::Rising, Exti::Pull::Down, FPGA_Interrupt);
@ -385,3 +391,33 @@ void HW::Ref::update() {
}
}
}
void HW::setAcquisitionFrequencies(Protocol::AcquisitionFrequencySettings s) {
IF1 = s.IF1;
ADCprescaler = s.ADCprescaler;
DFTphaseInc = s.DFTphaseInc;
float ADCrate = (float) FPGA::Clockrate / ADCprescaler;
IF2 = ADCrate * DFTphaseInc / 4096;
ADCsamplerate = ADCrate;
}
uint32_t HW::getIF1() {
return IF1;
}
uint32_t HW::getIF2() {
return IF2;
}
uint32_t HW::getADCRate() {
return ADCsamplerate;
}
uint8_t HW::getADCPrescaler() {
return ADCprescaler;
}
uint16_t HW::getDFTPhaseInc() {
return DFTphaseInc;
}

View File

@ -35,19 +35,19 @@ static constexpr uint32_t ExtRefOut2Frequency = 10000000;
static constexpr uint32_t SI5351CPLLAlignedFrequency = 832000000;
static constexpr uint32_t SI5351CPLLConstantFrequency = 800000000;
static constexpr uint32_t FPGAClkInFrequency = 16000000;
static constexpr uint32_t ADCSamplerate = 800000;
static constexpr uint32_t IF1 = 62000000;
static constexpr uint32_t IF2 = 250000;
static constexpr uint32_t DefaultADCSamplerate = 800000;
static constexpr uint32_t DefaultIF1 = 62000000;
static constexpr uint32_t DefaultIF2 = 250000;
static constexpr uint32_t LO1_minFreq = 25000000;
static constexpr uint32_t MaxSamples = 130944;
static constexpr uint32_t MinSamples = 16;
static constexpr uint32_t PLLRef = 104000000;
static constexpr uint32_t BandSwitchFrequency = 25000000;
static constexpr uint8_t ADCprescaler = FPGA::Clockrate / ADCSamplerate;
static_assert(ADCprescaler * ADCSamplerate == FPGA::Clockrate, "ADCSamplerate can not be reached exactly");
static constexpr uint16_t DFTphaseInc = 4096 * IF2 / ADCSamplerate;
static_assert(DFTphaseInc * ADCSamplerate == 4096 * IF2, "DFT can not be computed for 2.IF");
static constexpr uint8_t DefaultADCprescaler = FPGA::Clockrate / DefaultADCSamplerate;
static_assert(DefaultADCprescaler * DefaultADCSamplerate == FPGA::Clockrate, "ADCSamplerate can not be reached exactly");
static constexpr uint16_t DefaultDFTphaseInc = 4096 * DefaultIF2 / DefaultADCSamplerate;
static_assert(DefaultDFTphaseInc * DefaultADCSamplerate == 4096 * DefaultIF2, "DFT can not be computed for 2.IF");
static constexpr uint16_t _fpga_div = SI5351CPLLConstantFrequency / FPGAClkInFrequency;
static_assert(_fpga_div * FPGAClkInFrequency == SI5351CPLLConstantFrequency && _fpga_div >= 6 && _fpga_div <= 254 && (_fpga_div & 0x01) == 0, "Unable to generate FPGA clock input frequency");
@ -83,13 +83,13 @@ static constexpr Protocol::DeviceInfo Info = {
.temp_MCU = 0,
.limits_minFreq = 0,
.limits_maxFreq = 6000000000,
.limits_minIFBW = ADCSamplerate / MaxSamples,
.limits_maxIFBW = ADCSamplerate / MinSamples,
.limits_minIFBW = DefaultADCSamplerate / MaxSamples,
.limits_maxIFBW = DefaultADCSamplerate / MinSamples,
.limits_maxPoints = FPGA::MaxPoints,
.limits_cdbm_min = -4000,
.limits_cdbm_max = 0,
.limits_minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples),
.limits_maxRBW = (uint32_t) (ADCSamplerate * 2.23f / MinSamples),
.limits_minRBW = (uint32_t) (DefaultADCSamplerate * 2.23f / MaxSamples),
.limits_maxRBW = (uint32_t) (DefaultADCSamplerate * 2.23f / MinSamples),
.limits_maxAmplitudePoints = Cal::maxPoints,
.limits_maxFreqHarmonic = 18000000000,
};
@ -129,4 +129,12 @@ namespace Ref {
void update();
}
// Acquisition frequency settings
void setAcquisitionFrequencies(Protocol::AcquisitionFrequencySettings s);
uint32_t getIF1();
uint32_t getIF2();
uint32_t getADCRate();
uint8_t getADCPrescaler();
uint16_t getDFTPhaseInc();
}

View File

@ -49,8 +49,8 @@ static void StartNextSample() {
port2Measurement[i] = std::numeric_limits<float>::max();
}
// Use default LO frequencies
LO1freq = freq + HW::IF1;
LO2freq = HW::IF1 - HW::IF2;
LO1freq = freq + HW::getIF1();
LO2freq = HW::getIF1() - HW::getIF2();
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, 112);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, 1120);
negativeDFT = true;
@ -88,17 +88,17 @@ static void StartNextSample() {
}
break;
case 1:
LO2freq = HW::IF1 - HW::IF2;
LO2freq = HW::getIF1() - HW::getIF2();
negativeDFT = false;
// Shift first LO to other side
// depending on the measurement frequency this is not possible or additive mixing has to be used
if(freq >= HW::IF1 + HW::LO1_minFreq) {
if(freq >= HW::getIF1() + HW::LO1_minFreq) {
// frequency is high enough to shift 1.LO below measurement frequency
LO1freq = freq - HW::IF1;
LO1freq = freq - HW::getIF1();
break;
} else if(freq <= HW::IF1 - HW::LO1_minFreq) {
} else if(freq <= HW::getIF1() - HW::LO1_minFreq) {
// frequency is low enough to add 1.LO to measurement frequency
LO1freq = HW::IF1 - freq;
LO1freq = HW::getIF1() - freq;
break;
}
// unable to reach required frequency with 1.LO, skip this signal ID step
@ -106,22 +106,22 @@ static void StartNextSample() {
/* no break */
case 2:
// Shift second LOs to other side
LO1freq = freq + HW::IF1;
LO2freq = HW::IF1 + HW::IF2;
LO1freq = freq + HW::getIF1();
LO2freq = HW::getIF1() + HW::getIF2();
negativeDFT = false;
break;
case 3:
// Shift both LO to other side
LO2freq = HW::IF1 + HW::IF2;
LO2freq = HW::getIF1() + HW::getIF2();
negativeDFT = true;
// depending on the measurement frequency this is not possible or additive mixing has to be used
if(freq >= HW::IF1 + HW::LO1_minFreq) {
if(freq >= HW::getIF1() + HW::LO1_minFreq) {
// frequency is high enough to shift 1.LO below measurement frequency
LO1freq = freq - HW::IF1;
LO1freq = freq - HW::getIF1();
break;
} else if(freq <= HW::IF1 - HW::LO1_minFreq) {
} else if(freq <= HW::getIF1() - HW::LO1_minFreq) {
// frequency is low enough to add 1.LO to measurement frequency
LO1freq = HW::IF1 - freq;
LO1freq = HW::getIF1() - freq;
break;
}
// unable to reach required frequency with 1.LO, skip this signal ID step
@ -130,8 +130,8 @@ static void StartNextSample() {
default:
// Use default frequencies with different ADC samplerate to remove images in final IF
negativeDFT = true;
LO1freq = freq + HW::IF1;
LO2freq = HW::IF1 - HW::IF2;
LO1freq = freq + HW::getIF1();
LO2freq = HW::getIF1() - HW::getIF2();
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, signalIDprescalers[signalIDstep-4]);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, (uint16_t) signalIDprescalers[signalIDstep-4] * 10);
}
@ -147,7 +147,7 @@ static void StartNextSample() {
}
if (s.UseDFT) {
uint32_t spacing = (s.f_stop - s.f_start) / (points - 1);
uint32_t start = HW::IF2;
uint32_t start = HW::getIF2();
if(negativeDFT) {
// needs to look below the start frequency, shift start
start -= spacing * (DFTpoints - 1);
@ -172,13 +172,15 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
s = settings;
HW::SetMode(HW::Mode::SA);
FPGA::SetMode(FPGA::Mode::FPGA);
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::getADCPrescaler());
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::getDFTPhaseInc());
// in almost all cases a full sweep requires more points than the FPGA can handle at a time
// individually start each point and do the sweep in the uC
FPGA::SetNumberOfPoints(1);
// calculate required samples per measurement for requested RBW
// see https://www.tek.com/blog/window-functions-spectrum-analyzers for window factors
constexpr float window_factors[4] = {0.89f, 2.23f, 1.44f, 3.77f};
sampleNum = HW::ADCSamplerate * window_factors[s.WindowType] / s.RBW;
sampleNum = HW::getADCRate() * window_factors[s.WindowType] / s.RBW;
// round up to next multiple of 16
if(sampleNum%16) {
sampleNum += 16 - sampleNum%16;
@ -186,7 +188,7 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
if(sampleNum >= HW::MaxSamples) {
sampleNum = HW::MaxSamples;
}
actualRBW = HW::ADCSamplerate * window_factors[s.WindowType] / sampleNum;
actualRBW = HW::getADCRate() * window_factors[s.WindowType] / sampleNum;
FPGA::SetSamplesPerPoint(sampleNum);
// calculate amount of required points
points = 2 * (s.f_stop - s.f_start) / actualRBW;

View File

@ -44,8 +44,8 @@ static uint16_t IFTableIndexCnt = 0;
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");
static constexpr uint16_t alternativePhaseInc = 4096 * HW::IF2 / alternativeSamplerate;
static_assert(alternativePhaseInc * alternativeSamplerate == 4096 * HW::IF2, "DFT can not be computed for 2.IF when using alternative samplerate");
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");
// Constants for USB buffer overflow prevention
static constexpr uint16_t maxPointsBetweenHalts = 40;
@ -84,6 +84,8 @@ bool VNA::Setup(Protocol::SweepSettings s) {
}
// Abort possible active sweep first
FPGA::SetMode(FPGA::Mode::FPGA);
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::getADCPrescaler());
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::getDFTPhaseInc());
if(settings.points > FPGA::MaxPoints) {
settings.points = FPGA::MaxPoints;
}
@ -92,12 +94,12 @@ bool VNA::Setup(Protocol::SweepSettings s) {
logMultiplier = pow((double) settings.f_stop / settings.f_start, 1.0 / (settings.points-1));
// Configure sweep
FPGA::SetNumberOfPoints(settings.points);
uint32_t samplesPerPoint = (HW::ADCSamplerate / s.if_bandwidth);
uint32_t samplesPerPoint = (HW::getADCRate() / s.if_bandwidth);
// round up to next multiple of 16 (16 samples are spread across 5 IF2 periods)
if(samplesPerPoint%16) {
samplesPerPoint += 16 - samplesPerPoint%16;
}
actualBandwidth = HW::ADCSamplerate / samplesPerPoint;
actualBandwidth = HW::getADCRate() / samplesPerPoint;
// has to be one less than actual number of samples
FPGA::SetSamplesPerPoint(samplesPerPoint);
@ -129,7 +131,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
FPGA::WriteMAX2871Default(Source.GetRegisters());
uint32_t last_LO2 = HW::IF1 - HW::IF2;
uint32_t last_LO2 = HW::getIF1() - HW::getIF2();
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);
@ -180,7 +182,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
// additional halt before first highband point to enable highband source
needs_halt = true;
}
uint64_t LOFreq = freq + HW::IF1;
uint64_t LOFreq = freq + HW::getIF1();
if(harmonic_mixing) {
LOFreq /= LOHarmonic;
}
@ -191,7 +193,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
}
uint32_t actualFirstIF = actualLO1 - actualSourceFreq;
uint32_t actualFinalIF = actualFirstIF - last_LO2;
uint32_t IFdeviation = abs(actualFinalIF - HW::IF2);
uint32_t IFdeviation = abs(actualFinalIF - HW::getIF2());
bool needs_LO2_shift = false;
if(IFdeviation > actualBandwidth / 2) {
needs_LO2_shift = true;
@ -204,7 +206,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
if(IFTableIndexCnt < IFTableNumEntries - 1) {
// Configure LO2 for the changed IF1. This is not necessary right now but it will generate
// the correct clock settings
last_LO2 = actualFirstIF - HW::IF2;
last_LO2 = actualFirstIF - HW::getIF2();
LOG_INFO("Changing 2.LO to %lu at point %lu (%lu%06luHz) to reach correct 2.IF frequency (1.LO: %lu%06luHz, 1.IF: %lu%06luHz)",
last_LO2, i, (uint32_t ) (freq / 1000000),
(uint32_t ) (freq % 1000000), (uint32_t ) (actualLO1 / 1000000),
@ -212,7 +214,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
(uint32_t ) (actualFirstIF % 1000000));
} else {
// last entry in IF table, revert LO2 to default
last_LO2 = HW::IF1 - HW::IF2;
last_LO2 = HW::getIF1() - HW::getIF2();
}
Si5351.SetCLK(SiChannel::RefLO2, last_LO2,
Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
@ -259,10 +261,10 @@ bool VNA::Setup(Protocol::SweepSettings s) {
last_lowband = lowband;
}
// 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::getIF1() - HW::getIF2(), Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
Si5351.ResetPLL(Si5351C::PLL::B);
// Enable mixers/amplifier/PLLs
FPGA::SetWindow(FPGA::Window::None);
FPGA::SetWindow(FPGA::Window::Kaiser);
FPGA::Enable(FPGA::Periphery::Port1Mixer);
FPGA::Enable(FPGA::Periphery::Port2Mixer);
FPGA::Enable(FPGA::Periphery::RefMixer);
@ -399,15 +401,18 @@ void VNA::SweepHalted() {
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
// 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);
if(abs(Util::Alias(LO_mixing, HW::ADCSamplerate) - HW::IF2) <= actualBandwidth * 2) {
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 samplerate
// 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);
@ -420,8 +425,8 @@ void VNA::SweepHalted() {
adcShifted = true;
} else if(adcShifted) {
// reset to default value
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::ADCprescaler);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::DFTphaseInc);
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::getADCPrescaler());
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::getDFTPhaseInc());
adcShifted = false;
}