Optional harmonic mixing

This commit is contained in:
Jan Käberich 2020-12-18 15:03:01 +01:00
parent d6ef96914a
commit 49e0b901fd
8 changed files with 76 additions and 14 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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)},

View File

@ -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>

View File

@ -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();
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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);