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); spectrumAnalyzer->setAveragingMode(Averaging::Mode::Mean);
vna->setAveragingMode(Averaging::Mode::Mean); vna->setAveragingMode(Averaging::Mode::Mean);
} }
// acquisition frequencies may have changed, update
UpdateAcquisitionFrequencies();
active->initializeDevice();
}); });
connect(ui->actionAbout, &QAction::triggered, [=](){ connect(ui->actionAbout, &QAction::triggered, [=](){
@ -306,6 +311,7 @@ bool AppWindow::ConnectToDevice(QString serial)
ui->actionReceiver_Calibration->setEnabled(true); ui->actionReceiver_Calibration->setEnabled(true);
ui->actionFrequency_Calibration->setEnabled(true); ui->actionFrequency_Calibration->setEnabled(true);
UpdateAcquisitionFrequencies();
Mode::getActiveMode()->initializeDevice(); Mode::getActiveMode()->initializeDevice();
UpdateReference(); UpdateReference();
@ -866,6 +872,20 @@ void AppWindow::UpdateReference()
device->SendPacket(p); 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() void AppWindow::StartFirmwareUpdateDialog()
{ {
if(device) { if(device) {

View File

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

View File

@ -73,6 +73,26 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
connect(ui->AcquisitionUseDFT, &QCheckBox::toggled, [=](bool enabled) { connect(ui->AcquisitionUseDFT, &QCheckBox::toggled, [=](bool enabled) {
ui->AcquisitionDFTlimitRBW->setEnabled(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 // General page
if(p->TCPoverride) { if(p->TCPoverride) {
@ -136,6 +156,9 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked(); p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value(); p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1; 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.showUnits = ui->GraphsShowUnit->isChecked();
p->Graphs.Color.background = ui->GraphsColorBackground->getColor(); p->Graphs.Color.background = ui->GraphsColorBackground->getColor();
@ -156,6 +179,8 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
}); });
setInitialGUIState(); setInitialGUIState();
updateADCRate();
updateIF2();
connect(ui->AcquisitionUseHarmonic, &QCheckBox::toggled, [=](bool enabled) { connect(ui->AcquisitionUseHarmonic, &QCheckBox::toggled, [=](bool enabled) {
if(enabled) { if(enabled) {
@ -208,6 +233,9 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode); ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode);
ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT); ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT);
ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0); 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->GraphsShowUnit->setChecked(p->Graphs.showUnits);
ui->GraphsColorBackground->setColor(p->Graphs.Color.background); ui->GraphsColorBackground->setColor(p->Graphs.Color.background);

View File

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

View File

@ -83,7 +83,7 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>3</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="Startup"> <widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
@ -569,7 +569,7 @@
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="Acquisition"> <widget class="QWidget" name="Acquisition">
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_15">
<item> <item>
<widget class="QGroupBox" name="groupBox_6"> <widget class="QGroupBox" name="groupBox_6">
<property name="title"> <property name="title">
@ -647,26 +647,141 @@
<property name="title"> <property name="title">
<string>Common</string> <string>Common</string>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QVBoxLayout" name="verticalLayout_14">
<item row="0" column="0"> <item>
<widget class="QLabel" name="label_26"> <layout class="QFormLayout" name="formLayout_10">
<property name="text"> <item row="0" column="0">
<string>Averaging mode:</string> <widget class="QLabel" name="label_26">
</property> <property name="text">
</widget> <string>Averaging mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="AcquisitionAveragingMode">
<item>
<property name="text">
<string>Mean</string>
</property>
</item>
<item>
<property name="text">
<string>Median</string>
</property>
</item>
</widget>
</item>
</layout>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QComboBox" name="AcquisitionAveragingMode"> <widget class="QGroupBox" name="groupBox_15">
<item> <property name="title">
<property name="text"> <string>IF frequencies</string>
<string>Mean</string> </property>
</property> <layout class="QVBoxLayout" name="verticalLayout_5">
</item> <item>
<item> <widget class="QLabel" name="label_34">
<property name="text"> <property name="text">
<string>Median</string> <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>
</item> <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> </widget>
</item> </item>
</layout> </layout>

View File

@ -222,6 +222,21 @@ inline void App_Process() {
Cal::setFrequencyCal(recv_packet.frequencyCorrection.ppm); Cal::setFrequencyCal(recv_packet.frequencyCorrection.ppm);
Communication::SendWithoutPayload(Protocol::PacketType::Ack); Communication::SendWithoutPayload(Protocol::PacketType::Ack);
break; 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: default:
// this packet type is not supported // this packet type is not supported
Communication::SendWithoutPayload(Protocol::PacketType::Nack); 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::SourceCalPoint:
case PacketType::ReceiverCalPoint: payload_size = sizeof(packet.amplitudePoint); break; case PacketType::ReceiverCalPoint: payload_size = sizeof(packet.amplitudePoint); break;
case PacketType::FrequencyCorrection: payload_size = sizeof(packet.frequencyCorrection); break; case PacketType::FrequencyCorrection: payload_size = sizeof(packet.frequencyCorrection); break;
case PacketType::AcquisitionFrequencySettings: payload_size = sizeof(packet.acquisitionFrequencySettings); break;
case PacketType::Ack: case PacketType::Ack:
case PacketType::PerformFirmwareUpdate: case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash: case PacketType::ClearFlash:
@ -109,6 +110,7 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_
case PacketType::RequestReceiverCal: case PacketType::RequestReceiverCal:
case PacketType::SetIdle: case PacketType::SetIdle:
case PacketType::RequestFrequencyCorrection: case PacketType::RequestFrequencyCorrection:
case PacketType::RequestAcquisitionFrequencySettings:
// no payload // no payload
break; break;
case PacketType::None: case PacketType::None:

View File

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

View File

@ -21,6 +21,12 @@ static bool unlevel = false;
static Protocol::ReferenceSettings ref; static Protocol::ReferenceSettings ref;
static uint32_t lastISR; 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; using namespace HWHAL;
static void HaltedCallback() { static void HaltedCallback() {
@ -133,9 +139,9 @@ bool HW::Init() {
} }
// Set default ADC samplerate // Set default ADC samplerate
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::ADCprescaler); FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, ADCprescaler);
// Set phase increment according to // 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); 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 SI5351CPLLAlignedFrequency = 832000000;
static constexpr uint32_t SI5351CPLLConstantFrequency = 800000000; static constexpr uint32_t SI5351CPLLConstantFrequency = 800000000;
static constexpr uint32_t FPGAClkInFrequency = 16000000; static constexpr uint32_t FPGAClkInFrequency = 16000000;
static constexpr uint32_t ADCSamplerate = 800000; static constexpr uint32_t DefaultADCSamplerate = 800000;
static constexpr uint32_t IF1 = 62000000; static constexpr uint32_t DefaultIF1 = 62000000;
static constexpr uint32_t IF2 = 250000; static constexpr uint32_t DefaultIF2 = 250000;
static constexpr uint32_t LO1_minFreq = 25000000; static constexpr uint32_t LO1_minFreq = 25000000;
static constexpr uint32_t MaxSamples = 130944; static constexpr uint32_t MaxSamples = 130944;
static constexpr uint32_t MinSamples = 16; static constexpr uint32_t MinSamples = 16;
static constexpr uint32_t PLLRef = 104000000; static constexpr uint32_t PLLRef = 104000000;
static constexpr uint32_t BandSwitchFrequency = 25000000; static constexpr uint32_t BandSwitchFrequency = 25000000;
static constexpr uint8_t ADCprescaler = FPGA::Clockrate / ADCSamplerate; static constexpr uint8_t DefaultADCprescaler = FPGA::Clockrate / DefaultADCSamplerate;
static_assert(ADCprescaler * ADCSamplerate == FPGA::Clockrate, "ADCSamplerate can not be reached exactly"); static_assert(DefaultADCprescaler * DefaultADCSamplerate == FPGA::Clockrate, "ADCSamplerate can not be reached exactly");
static constexpr uint16_t DFTphaseInc = 4096 * IF2 / ADCSamplerate; static constexpr uint16_t DefaultDFTphaseInc = 4096 * DefaultIF2 / DefaultADCSamplerate;
static_assert(DFTphaseInc * ADCSamplerate == 4096 * IF2, "DFT can not be computed for 2.IF"); static_assert(DefaultDFTphaseInc * DefaultADCSamplerate == 4096 * DefaultIF2, "DFT can not be computed for 2.IF");
static constexpr uint16_t _fpga_div = SI5351CPLLConstantFrequency / FPGAClkInFrequency; 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"); 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, .temp_MCU = 0,
.limits_minFreq = 0, .limits_minFreq = 0,
.limits_maxFreq = 6000000000, .limits_maxFreq = 6000000000,
.limits_minIFBW = ADCSamplerate / MaxSamples, .limits_minIFBW = DefaultADCSamplerate / MaxSamples,
.limits_maxIFBW = ADCSamplerate / MinSamples, .limits_maxIFBW = DefaultADCSamplerate / MinSamples,
.limits_maxPoints = FPGA::MaxPoints, .limits_maxPoints = FPGA::MaxPoints,
.limits_cdbm_min = -4000, .limits_cdbm_min = -4000,
.limits_cdbm_max = 0, .limits_cdbm_max = 0,
.limits_minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples), .limits_minRBW = (uint32_t) (DefaultADCSamplerate * 2.23f / MaxSamples),
.limits_maxRBW = (uint32_t) (ADCSamplerate * 2.23f / MinSamples), .limits_maxRBW = (uint32_t) (DefaultADCSamplerate * 2.23f / MinSamples),
.limits_maxAmplitudePoints = Cal::maxPoints, .limits_maxAmplitudePoints = Cal::maxPoints,
.limits_maxFreqHarmonic = 18000000000, .limits_maxFreqHarmonic = 18000000000,
}; };
@ -129,4 +129,12 @@ namespace Ref {
void update(); 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(); port2Measurement[i] = std::numeric_limits<float>::max();
} }
// Use default LO frequencies // Use default LO frequencies
LO1freq = freq + HW::IF1; LO1freq = freq + HW::getIF1();
LO2freq = HW::IF1 - HW::IF2; LO2freq = HW::getIF1() - HW::getIF2();
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, 112); FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, 112);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, 1120); FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, 1120);
negativeDFT = true; negativeDFT = true;
@ -88,17 +88,17 @@ static void StartNextSample() {
} }
break; break;
case 1: case 1:
LO2freq = HW::IF1 - HW::IF2; LO2freq = HW::getIF1() - HW::getIF2();
negativeDFT = false; negativeDFT = false;
// Shift first LO to other side // Shift first LO to other side
// depending on the measurement frequency this is not possible or additive mixing has to be used // 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 // frequency is high enough to shift 1.LO below measurement frequency
LO1freq = freq - HW::IF1; LO1freq = freq - HW::getIF1();
break; 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 // frequency is low enough to add 1.LO to measurement frequency
LO1freq = HW::IF1 - freq; LO1freq = HW::getIF1() - freq;
break; break;
} }
// unable to reach required frequency with 1.LO, skip this signal ID step // unable to reach required frequency with 1.LO, skip this signal ID step
@ -106,22 +106,22 @@ static void StartNextSample() {
/* no break */ /* no break */
case 2: case 2:
// Shift second LOs to other side // Shift second LOs to other side
LO1freq = freq + HW::IF1; LO1freq = freq + HW::getIF1();
LO2freq = HW::IF1 + HW::IF2; LO2freq = HW::getIF1() + HW::getIF2();
negativeDFT = false; negativeDFT = false;
break; break;
case 3: case 3:
// Shift both LO to other side // Shift both LO to other side
LO2freq = HW::IF1 + HW::IF2; LO2freq = HW::getIF1() + HW::getIF2();
negativeDFT = true; negativeDFT = true;
// depending on the measurement frequency this is not possible or additive mixing has to be used // 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 // frequency is high enough to shift 1.LO below measurement frequency
LO1freq = freq - HW::IF1; LO1freq = freq - HW::getIF1();
break; 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 // frequency is low enough to add 1.LO to measurement frequency
LO1freq = HW::IF1 - freq; LO1freq = HW::getIF1() - freq;
break; break;
} }
// unable to reach required frequency with 1.LO, skip this signal ID step // unable to reach required frequency with 1.LO, skip this signal ID step
@ -130,8 +130,8 @@ static void StartNextSample() {
default: default:
// Use default frequencies with different ADC samplerate to remove images in final IF // Use default frequencies with different ADC samplerate to remove images in final IF
negativeDFT = true; negativeDFT = true;
LO1freq = freq + HW::IF1; LO1freq = freq + HW::getIF1();
LO2freq = HW::IF1 - HW::IF2; LO2freq = HW::getIF1() - HW::getIF2();
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, signalIDprescalers[signalIDstep-4]); FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, signalIDprescalers[signalIDstep-4]);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, (uint16_t) signalIDprescalers[signalIDstep-4] * 10); FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, (uint16_t) signalIDprescalers[signalIDstep-4] * 10);
} }
@ -147,7 +147,7 @@ static void StartNextSample() {
} }
if (s.UseDFT) { if (s.UseDFT) {
uint32_t spacing = (s.f_stop - s.f_start) / (points - 1); uint32_t spacing = (s.f_stop - s.f_start) / (points - 1);
uint32_t start = HW::IF2; uint32_t start = HW::getIF2();
if(negativeDFT) { if(negativeDFT) {
// needs to look below the start frequency, shift start // needs to look below the start frequency, shift start
start -= spacing * (DFTpoints - 1); start -= spacing * (DFTpoints - 1);
@ -172,13 +172,15 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
s = settings; s = settings;
HW::SetMode(HW::Mode::SA); HW::SetMode(HW::Mode::SA);
FPGA::SetMode(FPGA::Mode::FPGA); 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 // 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 // individually start each point and do the sweep in the uC
FPGA::SetNumberOfPoints(1); FPGA::SetNumberOfPoints(1);
// calculate required samples per measurement for requested RBW // calculate required samples per measurement for requested RBW
// see https://www.tek.com/blog/window-functions-spectrum-analyzers for window factors // 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}; 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 // round up to next multiple of 16
if(sampleNum%16) { if(sampleNum%16) {
sampleNum += 16 - sampleNum%16; sampleNum += 16 - sampleNum%16;
@ -186,7 +188,7 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
if(sampleNum >= HW::MaxSamples) { if(sampleNum >= HW::MaxSamples) {
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); FPGA::SetSamplesPerPoint(sampleNum);
// calculate amount of required points // calculate amount of required points
points = 2 * (s.f_stop - s.f_start) / actualRBW; 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 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");
static constexpr uint16_t alternativePhaseInc = 4096 * HW::IF2 / alternativeSamplerate; static constexpr uint16_t alternativePhaseInc = 4096 * HW::DefaultIF2 / alternativeSamplerate;
static_assert(alternativePhaseInc * alternativeSamplerate == 4096 * HW::IF2, "DFT can not be computed for 2.IF when using alternative samplerate"); 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 // Constants for USB buffer overflow prevention
static constexpr uint16_t maxPointsBetweenHalts = 40; static constexpr uint16_t maxPointsBetweenHalts = 40;
@ -84,6 +84,8 @@ bool VNA::Setup(Protocol::SweepSettings s) {
} }
// Abort possible active sweep first // Abort possible active sweep first
FPGA::SetMode(FPGA::Mode::FPGA); FPGA::SetMode(FPGA::Mode::FPGA);
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, HW::getADCPrescaler());
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::getDFTPhaseInc());
if(settings.points > FPGA::MaxPoints) { if(settings.points > FPGA::MaxPoints) {
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)); logMultiplier = pow((double) settings.f_stop / settings.f_start, 1.0 / (settings.points-1));
// Configure sweep // Configure sweep
FPGA::SetNumberOfPoints(settings.points); 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) // round up to next multiple of 16 (16 samples are spread across 5 IF2 periods)
if(samplesPerPoint%16) { if(samplesPerPoint%16) {
samplesPerPoint += 16 - samplesPerPoint%16; samplesPerPoint += 16 - samplesPerPoint%16;
} }
actualBandwidth = HW::ADCSamplerate / samplesPerPoint; actualBandwidth = HW::getADCRate() / samplesPerPoint;
// has to be one less than actual number of samples // has to be one less than actual number of samples
FPGA::SetSamplesPerPoint(samplesPerPoint); FPGA::SetSamplesPerPoint(samplesPerPoint);
@ -129,7 +131,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
FPGA::WriteMAX2871Default(Source.GetRegisters()); 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::Port1LO2, last_LO2, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
Si5351.SetCLK(SiChannel::Port2LO2, 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); 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 // 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::getIF1();
if(harmonic_mixing) { if(harmonic_mixing) {
LOFreq /= LOHarmonic; LOFreq /= LOHarmonic;
} }
@ -191,7 +193,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
} }
uint32_t actualFirstIF = actualLO1 - actualSourceFreq; uint32_t actualFirstIF = actualLO1 - actualSourceFreq;
uint32_t actualFinalIF = actualFirstIF - last_LO2; 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; bool needs_LO2_shift = false;
if(IFdeviation > actualBandwidth / 2) { if(IFdeviation > actualBandwidth / 2) {
needs_LO2_shift = true; needs_LO2_shift = true;
@ -204,7 +206,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
if(IFTableIndexCnt < IFTableNumEntries - 1) { if(IFTableIndexCnt < IFTableNumEntries - 1) {
// Configure LO2 for the changed IF1. This is not necessary right now but it will generate // Configure LO2 for the changed IF1. This is not necessary right now but it will generate
// the correct clock settings // 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)", 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), last_LO2, i, (uint32_t ) (freq / 1000000),
(uint32_t ) (freq % 1000000), (uint32_t ) (actualLO1 / 1000000), (uint32_t ) (freq % 1000000), (uint32_t ) (actualLO1 / 1000000),
@ -212,7 +214,7 @@ bool VNA::Setup(Protocol::SweepSettings s) {
(uint32_t ) (actualFirstIF % 1000000)); (uint32_t ) (actualFirstIF % 1000000));
} else { } else {
// last entry in IF table, revert LO2 to default // 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, Si5351.SetCLK(SiChannel::RefLO2, last_LO2,
Si5351C::PLL::B, Si5351C::DriveStrength::mA2); Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
@ -259,10 +261,10 @@ bool VNA::Setup(Protocol::SweepSettings s) {
last_lowband = lowband; last_lowband = lowband;
} }
// 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::getIF1() - HW::getIF2(), Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
Si5351.ResetPLL(Si5351C::PLL::B); Si5351.ResetPLL(Si5351C::PLL::B);
// Enable mixers/amplifier/PLLs // Enable mixers/amplifier/PLLs
FPGA::SetWindow(FPGA::Window::None); FPGA::SetWindow(FPGA::Window::Kaiser);
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);
@ -399,14 +401,17 @@ 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);
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 // 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
adcShiftRequired = true; if(HW::getIF2() == HW::DefaultIF2) {
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
@ -420,8 +425,8 @@ void VNA::SweepHalted() {
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::getADCPrescaler());
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::DFTphaseInc); FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, HW::getDFTPhaseInc());
adcShifted = false; adcShifted = false;
} }