Optional harmonic mixing
This commit is contained in:
parent
d6ef96914a
commit
49e0b901fd
@ -817,8 +817,15 @@ void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
|
||||
|
||||
void VNA::ConstrainAndUpdateFrequencies()
|
||||
{
|
||||
if(settings.f_stop > Device::Info().limits_maxFreq) {
|
||||
settings.f_stop = Device::Info().limits_maxFreq;
|
||||
auto pref = Preferences::getInstance();
|
||||
double maxFreq;
|
||||
if(pref.Acquisition.harmonicMixing) {
|
||||
maxFreq = Device::Info().limits_maxFreqHarmonic;
|
||||
} else {
|
||||
maxFreq = Device::Info().limits_maxFreq;
|
||||
}
|
||||
if(settings.f_stop > maxFreq) {
|
||||
settings.f_stop = maxFreq;
|
||||
}
|
||||
if(settings.f_start > settings.f_stop) {
|
||||
settings.f_start = settings.f_stop;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <map>
|
||||
#include <QDebug>
|
||||
#include "CustomWidgets/informationbox.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -115,6 +116,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
|
||||
p->Startup.SA.signalID = ui->StartupSASignalID->isChecked();
|
||||
p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked();
|
||||
p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked();
|
||||
p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked();
|
||||
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
|
||||
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
|
||||
p->General.graphColors.background = ui->GeneralGraphBackground->getColor();
|
||||
@ -124,6 +126,16 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
|
||||
});
|
||||
|
||||
setInitialGUIState();
|
||||
|
||||
connect(ui->AcquisitionUseHarmonic, &QCheckBox::toggled, [=](bool enabled) {
|
||||
if(enabled) {
|
||||
InformationBox::ShowMessage("Harmonic Mixing", "When harmonic mixing is enabled, the frequency range of the VNA is extended up to 18GHz "
|
||||
"by using higher harmonics of the source signal as well as the 1.LO. The fundamental frequency is still present "
|
||||
"in the output signal and might disturb the measurement if the DUT is not linear. Performance above 6GHz is not "
|
||||
"specified and generally not very good. However, this mode might be useful, if the signal of interest is just above "
|
||||
"6GHz. Performance below 6GHz is not affected by this setting");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PreferencesDialog::~PreferencesDialog()
|
||||
@ -157,6 +169,7 @@ void PreferencesDialog::setInitialGUIState()
|
||||
|
||||
ui->AcquisitionAlwaysExciteBoth->setChecked(p->Acquisition.alwaysExciteBothPorts);
|
||||
ui->AcquisitionSuppressPeaks->setChecked(p->Acquisition.suppressPeaks);
|
||||
ui->AcquisitionUseHarmonic->setChecked(p->Acquisition.harmonicMixing);
|
||||
ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode);
|
||||
ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT);
|
||||
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
struct {
|
||||
bool alwaysExciteBothPorts;
|
||||
bool suppressPeaks;
|
||||
bool harmonicMixing;
|
||||
bool useDFTinSAmode;
|
||||
double RBWLimitForDFT;
|
||||
} Acquisition;
|
||||
@ -62,7 +63,7 @@ private:
|
||||
QString name;
|
||||
QVariant def;
|
||||
};
|
||||
const std::array<SettingDescription, 24> descr = {{
|
||||
const std::array<SettingDescription, 25> descr = {{
|
||||
{&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true},
|
||||
{&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false},
|
||||
{&Startup.DefaultSweep.start, "Startup.DefaultSweep.start", 1000000.0},
|
||||
@ -82,6 +83,7 @@ private:
|
||||
{&Startup.SA.signalID, "Startup.SA.signalID", true},
|
||||
{&Acquisition.alwaysExciteBothPorts, "Acquisition.alwaysExciteBothPorts", true},
|
||||
{&Acquisition.suppressPeaks, "Acquisition.suppressPeaks", true},
|
||||
{&Acquisition.harmonicMixing, "Acquisition.harmonicMixing", false},
|
||||
{&Acquisition.useDFTinSAmode, "Acquisition.useDFTinSAmode", true},
|
||||
{&Acquisition.RBWLimitForDFT, "Acquisition.RBWLimitForDFT", 3000.0},
|
||||
{&General.graphColors.background, "General.graphColors.background", QColor(Qt::black)},
|
||||
|
@ -479,6 +479,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="AcquisitionUseHarmonic">
|
||||
<property name="text">
|
||||
<string>Use harmonic mixing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -265,6 +265,7 @@ static Protocol::DeviceInfo DecodeDeviceInfo(uint8_t *buf) {
|
||||
e.get(d.limits_minRBW);
|
||||
e.get(d.limits_maxRBW);
|
||||
e.get(d.limits_maxAmplitudePoints);
|
||||
e.get(d.limits_maxFreqHarmonic);
|
||||
return d;
|
||||
}
|
||||
static int16_t EncodeDeviceInfo(Protocol::DeviceInfo d, uint8_t *buf,
|
||||
@ -295,6 +296,7 @@ static int16_t EncodeDeviceInfo(Protocol::DeviceInfo d, uint8_t *buf,
|
||||
e.add(d.limits_minRBW);
|
||||
e.add(d.limits_maxRBW);
|
||||
e.add(d.limits_maxAmplitudePoints);
|
||||
e.add(d.limits_maxFreqHarmonic);
|
||||
return e.getSize();
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
namespace Protocol {
|
||||
|
||||
static constexpr uint16_t Version = 3;
|
||||
static constexpr uint16_t Version = 4;
|
||||
|
||||
// When changing/adding/removing variables from these structs also adjust the decode/encode functions in Protocol.cpp
|
||||
|
||||
@ -66,6 +66,7 @@ using DeviceInfo = struct _deviceInfo {
|
||||
uint32_t limits_minRBW;
|
||||
uint32_t limits_maxRBW;
|
||||
uint8_t limits_maxAmplitudePoints;
|
||||
uint64_t limits_maxFreqHarmonic;
|
||||
};
|
||||
|
||||
using ManualStatus = struct _manualstatus {
|
||||
|
@ -73,6 +73,7 @@ static constexpr Protocol::DeviceInfo Info = {
|
||||
.limits_minRBW = (uint32_t) (ADCSamplerate * 2.23f / MaxSamples),
|
||||
.limits_maxRBW = (uint32_t) (ADCSamplerate * 2.23f / MinSamples),
|
||||
.limits_maxAmplitudePoints = AmplitudeCal::maxPoints,
|
||||
.limits_maxFreqHarmonic = 18000000000,
|
||||
};
|
||||
|
||||
enum class Mode {
|
||||
|
@ -26,6 +26,9 @@ static bool sourceHighPower;
|
||||
static bool adcShifted;
|
||||
static uint32_t actualBandwidth;
|
||||
|
||||
static constexpr uint8_t sourceHarmonic = 5;
|
||||
static constexpr uint8_t LOHarmonic = 3;
|
||||
|
||||
using IFTableEntry = struct {
|
||||
uint16_t pointCnt;
|
||||
uint8_t clkconfig[8];
|
||||
@ -105,7 +108,13 @@ bool VNA::Setup(Protocol::SweepSettings s) {
|
||||
|
||||
// Transfer PLL configuration to FPGA
|
||||
for (uint16_t i = 0; i < points; i++) {
|
||||
bool harmonic_mixing = false;
|
||||
uint64_t freq = s.f_start + (s.f_stop - s.f_start) * i / (points - 1);
|
||||
|
||||
if(freq > 6000000000ULL) {
|
||||
harmonic_mixing = true;
|
||||
}
|
||||
|
||||
// SetFrequency only manipulates the register content in RAM, no SPI communication is done.
|
||||
// No mode-switch of FPGA necessary here.
|
||||
|
||||
@ -117,15 +126,30 @@ bool VNA::Setup(Protocol::SweepSettings s) {
|
||||
lowband = true;
|
||||
actualSourceFreq = freq;
|
||||
} else {
|
||||
Source.SetFrequency(freq);
|
||||
uint64_t srcFreq = freq;
|
||||
if(harmonic_mixing) {
|
||||
srcFreq /= sourceHarmonic;
|
||||
}
|
||||
Source.SetFrequency(srcFreq);
|
||||
actualSourceFreq = Source.GetActualFrequency();
|
||||
if(harmonic_mixing) {
|
||||
actualSourceFreq *= sourceHarmonic;
|
||||
}
|
||||
}
|
||||
if (last_lowband && !lowband) {
|
||||
// additional halt before first highband point to enable highband source
|
||||
needs_halt = true;
|
||||
}
|
||||
LO1.SetFrequency(freq + HW::IF1);
|
||||
uint32_t actualFirstIF = LO1.GetActualFrequency() - actualSourceFreq;
|
||||
uint64_t LOFreq = freq + HW::IF1;
|
||||
if(harmonic_mixing) {
|
||||
LOFreq /= LOHarmonic;
|
||||
}
|
||||
LO1.SetFrequency(LOFreq);
|
||||
uint64_t actualLO1 = LO1.GetActualFrequency();
|
||||
if(harmonic_mixing) {
|
||||
actualLO1 *= LOHarmonic;
|
||||
}
|
||||
uint32_t actualFirstIF = actualLO1 - actualSourceFreq;
|
||||
uint32_t actualFinalIF = actualFirstIF - last_LO2;
|
||||
uint32_t IFdeviation = abs(actualFinalIF - HW::IF2);
|
||||
bool needs_LO2_shift = false;
|
||||
@ -137,12 +161,17 @@ bool VNA::Setup(Protocol::SweepSettings s) {
|
||||
// still room in table
|
||||
needs_halt = true;
|
||||
IFTable[IFTableIndexCnt].pointCnt = i;
|
||||
// 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;
|
||||
LOG_INFO("Changing 2.LO to %lu at point %lu (%lu%06luHz) to reach correct 2.IF frequency",
|
||||
last_LO2, i, (uint32_t ) (freq / 1000000),
|
||||
(uint32_t ) (freq % 1000000));
|
||||
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;
|
||||
LOG_INFO("Changing 2.LO to %lu at point %lu (%lu%06luHz) to reach correct 2.IF frequency",
|
||||
last_LO2, i, (uint32_t ) (freq / 1000000),
|
||||
(uint32_t ) (freq % 1000000));
|
||||
} else {
|
||||
// last entry in IF table, revert LO2 to default
|
||||
last_LO2 = HW::IF1 - HW::IF2;
|
||||
}
|
||||
Si5351.SetCLK(SiChannel::RefLO2, last_LO2,
|
||||
Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
|
||||
// store calculated clock configuration for later change
|
||||
@ -271,7 +300,7 @@ void VNA::SweepHalted() {
|
||||
}
|
||||
LOG_DEBUG("Halted before point %d", pointCnt);
|
||||
// Check if IF table has entry at this point
|
||||
if (IFTable[IFTableIndexCnt].pointCnt == pointCnt) {
|
||||
if (IFTableIndexCnt < IFTableNumEntries && IFTable[IFTableIndexCnt].pointCnt == pointCnt) {
|
||||
Si5351.WriteRawCLKConfig(SiChannel::Port1LO2, IFTable[IFTableIndexCnt].clkconfig);
|
||||
Si5351.WriteRawCLKConfig(SiChannel::Port2LO2, IFTable[IFTableIndexCnt].clkconfig);
|
||||
Si5351.WriteRawCLKConfig(SiChannel::RefLO2, IFTable[IFTableIndexCnt].clkconfig);
|
||||
|
Loading…
Reference in New Issue
Block a user