user selectable IF frequencies
This commit is contained in:
parent
d5f2f7019e
commit
5d8efd4336
@ -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) {
|
||||
|
@ -54,6 +54,7 @@ private slots:
|
||||
int UpdateDeviceList();
|
||||
void StartManualControl();
|
||||
void UpdateReference();
|
||||
void UpdateAcquisitionFrequencies();
|
||||
void StartFirmwareUpdateDialog();
|
||||
void DeviceNeedsUpdate(int reported, int expected);
|
||||
void SourceCalibrationDialog();
|
||||
|
@ -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);
|
||||
|
@ -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)},
|
||||
|
@ -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><html><head/><body><p>Frequency of the first IF</p></body></html></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><html><head/><body><p>ADC prescaler in FPGA. The ADC sample rate is determined by 102.4MHz/prescaler</p></body></html></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><html><head/><body><p>Phase increment per ADC sample. Together with the ADC sample rate this determines the frequency of the second IF</p></body></html></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>
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user