Tracking generator with offset + incomplete automatic source/receiver calibration

This commit is contained in:
Jan Käberich 2020-11-18 19:19:29 +01:00
parent 026fffd588
commit 5b771e2a86
18 changed files with 517 additions and 51 deletions

View file

@ -110,6 +110,7 @@ QT += widgets
FORMS += \
Calibration/addamplitudepointsdialog.ui \
Calibration/amplitudecaldialog.ui \
Calibration/automaticamplitudedialog.ui \
Calibration/calibrationtracedialog.ui \
Calibration/calkitdialog.ui \
CustomWidgets/tilewidget.ui \

View file

@ -4,6 +4,7 @@
#include "unit.h"
#include <QDebug>
#include "ui_addamplitudepointsdialog.h"
#include "ui_automaticamplitudedialog.h"
#include <QMessageBox>
using namespace std;
@ -24,7 +25,6 @@ AmplitudeCalDialog::AmplitudeCalDialog(Device *dev, QWidget *parent) :
ui->view->setColumnWidth(AmplitudeModel::ColIndexCorrectionFactors, 150);
ui->view->setColumnWidth(AmplitudeModel::ColIndexPort1, 150);
ui->view->setColumnWidth(AmplitudeModel::ColIndexPort2, 150);
connect(dev, &Device::AmplitudeCorrectionPointReceived, this, &AmplitudeCalDialog::ReceivedPoint);
connect(ui->load, &QPushButton::clicked, [=](){
if(ConfirmActionIfEdited()) {
LoadFromDevice();
@ -59,6 +59,7 @@ AmplitudeCalDialog::AmplitudeCalDialog(Device *dev, QWidget *parent) :
mode = CalibrationMode::OnlyPort2;
model.dataChanged(model.index(0, model.ColIndexPort1), model.index(points.size(), model.ColIndexPort2));
});
connect(ui->automatic, &QPushButton::clicked, this, &AmplitudeCalDialog::AutomaticMeasurementDialog);
}
AmplitudeCalDialog::~AmplitudeCalDialog()
@ -108,7 +109,6 @@ void AmplitudeCalDialog::setAmplitude(double amplitude, unsigned int point, bool
void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p)
{
qDebug() << "Received a point";
CorrectionPoint c;
c.frequency = p.freq * 10.0;
c.correctionPort1 = p.port1;
@ -120,13 +120,20 @@ void AmplitudeCalDialog::ReceivedPoint(Protocol::AmplitudeCorrectionPoint p)
points.push_back(c);
model.endInsertRows();
emit pointsUpdated();
if(p.pointNum == p.totalPoints - 1) {
// this was the last point
disconnect(dev, &Device::AmplitudeCorrectionPointReceived, this, nullptr);
ui->load->setEnabled(true);
}
}
void AmplitudeCalDialog::LoadFromDevice()
{
ui->load->setEnabled(false);
dev->SetIdle();
RemoveAllPoints();
qDebug() << "Asking for amplitude calibration";
connect(dev, &Device::AmplitudeCorrectionPointReceived, this, &AmplitudeCalDialog::ReceivedPoint);
dev->SendCommandWithoutPayload(requestCommand());
edited = false;
ui->save->setEnabled(false);
@ -239,6 +246,119 @@ void AmplitudeCalDialog::AddPointDialog()
// aborted, nothing to do
delete d;
});
dev->SendCommandWithoutPayload(requestCommand());
d->show();
}
void AmplitudeCalDialog::AutomaticMeasurementDialog()
{
bool isSourceCal = pointType() == Protocol::PacketType::SourceCalPoint;
const QString ownCal = isSourceCal ? "Source" : "Receiver";
const QString otherCal = isSourceCal ? "Receiver" : "Source";
// compile info text
QString info = "It is possible to determine the " + ownCal + " Calibration if a valid " + otherCal
+ " Calibration is already present. To do so, connect a short cable with as little loss as possible"
+ " between the ports. For each point in the " + otherCal + " Calibration, the device will take"
+ " a measurement and determine the correction factors for the " + ownCal + " Calibration.";
auto d = new QDialog(this);
auto ui = new Ui::AutomaticAmplitudeDialog();
ui->setupUi(d);
ui->explanation->setText(info);
ui->status->setText("Gathering information about "+otherCal+" Calibration...");
automatic.points.clear();
connect(d, &QDialog::rejected, ui->abort, &QPushButton::click);
connect(ui->abort, &QPushButton::clicked, [=](){
// aborted, clean up
delete d;
});
dev->SetIdle();
auto receiveConn = connect(dev, &Device::AmplitudeCorrectionPointReceived, this, [this, ui, otherCal](Protocol::AmplitudeCorrectionPoint p) {
CorrectionPoint c;
c.frequency = p.freq * 10.0;
c.correctionPort1 = p.port1;
c.correctionPort2 = p.port2;
c.port1set = true;
c.port2set = true;
automatic.points.push_back(c);
ui->progress->setValue(100 * (p.pointNum+1) / p.totalPoints);
if(p.pointNum == p.totalPoints - 1) {
// this was the last point, indicate ready for measurement
ui->progress->setValue(0);
ui->status->setText(otherCal + " Calibration contains " +QString::number(p.totalPoints)+" points, ready to start measurement");
ui->start->setEnabled(true);
disconnect(dev, &Device::AmplitudeCorrectionPointReceived, this, nullptr);
}
});
// request points of otherCal
// switch between source/receiver calibration
auto request = isSourceCal ? Protocol::PacketType::RequestReceiverCal : Protocol::PacketType::RequestSourceCal;
dev->SendCommandWithoutPayload(request);
connect(ui->start, &QPushButton::clicked, [=](){
// remove any exising points in own calibration and copy points from other calibration
RemoveAllPoints();
for(auto p : automatic.points) {
AddPoint(p.frequency);
}
// intialize measurement state machine
automatic.resultConnection = connect(dev, &Device::SpectrumResultReceived, [=](Protocol::SpectrumAnalyzerResult res) {
if(res.pointNum != 1) {
// ignore first and last point, only use the middle one
return;
}
if(automatic.settlingCount > 0) {
automatic.settlingCount--;
return;
}
// Grab correct measurement
double measurement;
if(isSourceCal) {
// For a source calibration we need the measurement of the other port (measuring the ouput power of the port to calibrate)
measurement = automatic.measuringPort2 ? res.port1 : res.port2;
} else {
// For a receiver calibration we need the measurement of the port to calibrate
measurement = automatic.measuringPort2 ? res.port2 : res.port1;
}
// convert to dbm
double reported_dbm = 20*log10(measurement);
if(isSourceCal) {
// receiver cal already done, the measurement result is accurate and can be used to determine actual output power
setAmplitude(reported_dbm, automatic.measuringCount, automatic.measuringPort2);
} else {
// source cal already done, the output power is accurate while the measurement might be off
setAmplitude(excitationAmplitude, automatic.measuringCount, automatic.measuringPort2);
}
// advance state machine
// switch ports
automatic.measuringPort2 = !automatic.measuringPort2;
if(!automatic.measuringPort2) {
// now measuring port1, this means we have to advance to the next point
automatic.measuringCount++;
if(automatic.measuringCount >= points.size()) {
// all done, disconnect this lambda and close dialog
disconnect(automatic.resultConnection);
delete d;
dev->SetIdle();
} else {
// update progress bar
ui->progress->setValue(100 * automatic.measuringCount / points.size());
// Start next measurement
SetupNextAutomaticPoint(isSourceCal);
}
}
});
automatic.measuringPort2 = false;
automatic.measuringCount = 0;
SetupNextAutomaticPoint(isSourceCal);
});
d->show();
}
@ -271,6 +391,39 @@ void AmplitudeCalDialog::UpdateSaveButton()
ui->save->setEnabled(enable);
}
void AmplitudeCalDialog::SetupNextAutomaticPoint(bool isSourceCal)
{
auto point = automatic.points[automatic.measuringCount];
Protocol::PacketInfo p = {};
p.type = Protocol::PacketType::SpectrumAnalyzerSettings;
p.spectrumSettings.RBW = 10000;
p.spectrumSettings.UseDFT = 0;
// setup 3 points centered around the measurement frequency (zero span not supported yet)
p.spectrumSettings.f_stop = point.frequency + 1.0;
p.spectrumSettings.f_start = point.frequency - 1.0;
p.spectrumSettings.pointNum = 3;
p.spectrumSettings.Detector = 0;
p.spectrumSettings.SignalID = 1;
p.spectrumSettings.WindowType = 3;
p.spectrumSettings.trackingGenerator = 1;
p.spectrumSettings.trackingPower = excitationAmplitude * 100.0;
p.spectrumSettings.trackingGeneratorOffset = 0;
// For a source cal, the tracking generator has to be enabled at the measurement port (calibrating the output power)
// For a receiver cal, the tracking generator has to be enabled at the other port (calibrating received power)
p.spectrumSettings.trackingGeneratorPort = isSourceCal ? automatic.measuringPort2 : !automatic.measuringPort2;
if(isSourceCal) {
// Calibrating the source which means the receiver is already calibrated -> use receiver corrections but no source corrections
p.spectrumSettings.applyReceiverCorrection = 1;
p.spectrumSettings.applySourceCorrection = 0;
} else {
// the other way around
p.spectrumSettings.applyReceiverCorrection = 0;
p.spectrumSettings.applySourceCorrection = 1;
}
automatic.settlingCount = 30;
dev->SendPacket(p);
}
AmplitudeCalDialog::CalibrationMode AmplitudeCalDialog::getMode() const
{
return mode;

View file

@ -74,10 +74,13 @@ protected slots:
void RemoveAllPoints();
void AddPoint(double frequency);
void AddPointDialog();
void AutomaticMeasurementDialog();
signals:
void pointsUpdated();
void newPointCreated(CorrectionPoint& p);
protected:
static constexpr double excitationAmplitude = -20.0;
bool ConfirmActionIfEdited();
void UpdateSaveButton();
virtual Protocol::PacketType requestCommand() = 0;
@ -95,6 +98,15 @@ protected:
AmplitudeModel model;
bool edited;
CalibrationMode mode;
void SetupNextAutomaticPoint(bool isSourceCal);
struct {
std::vector<CorrectionPoint> points;
bool measuringPort2; // true if port2 is being calibrated
unsigned int measuringCount; // number of calibration point
unsigned int settlingCount; // number of measurements still to ignore before taking measurement
QMetaObject::Connection resultConnection;
} automatic;
};
#endif // SOURCECALDIALOG_H

View file

@ -182,6 +182,13 @@
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="automatic">
<property name="text">
<string>Automatic measurement</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View file

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AutomaticAmplitudeDialog</class>
<widget class="QDialog" name="AutomaticAmplitudeDialog">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>215</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Automatic Measurement</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0">
<item>
<widget class="QLabel" name="explanation">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progress">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Status:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="status">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="abort">
<property name="text">
<string>Abort</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="start">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Start Measurement</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -14,10 +14,10 @@ ReceiverCalDialog::ReceiverCalDialog(Device *dev)
});
}
void ReceiverCalDialog::SelectedPoint(double frequency, bool port2)
void ReceiverCalDialog::SelectedPoint(double frequency, bool)
{
if(frequency > 0) {
Protocol::PacketInfo p;
Protocol::PacketInfo p = {};
p.type = Protocol::PacketType::SpectrumAnalyzerSettings;
p.spectrumSettings.RBW = 10000;
p.spectrumSettings.UseDFT = 0;

View file

@ -15,8 +15,6 @@ protected:
void SelectedPoint(double frequency, bool port2) override;
void AmplitudeChanged(CorrectionPoint &point, bool port2) override;
void UpdateAmplitude(CorrectionPoint& point) override;
private:
static constexpr double excitationAmplitude = -20.0;
};
#endif // SOURCECALDIALOG_H

View file

@ -40,6 +40,7 @@
#include <QDesktopWidget>
#include <QApplication>
#include <QActionGroup>
#include "CustomWidgets/informationbox.h"
SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
: Mode(window, "Spectrum Analyzer"),
@ -176,6 +177,49 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
window->addToolBar(tb_acq);
toolbars.insert(tb_acq);
// Tracking generator toolbar
auto tb_trackgen = new QToolBar("Tracking Generator");
auto cbTrackGenEnable = new QCheckBox("Tracking Generator");
connect(cbTrackGenEnable, &QCheckBox::toggled, [=](bool enabled) {
settings.trackingGenerator = enabled;
SettingsChanged();
});
tb_trackgen->addWidget(cbTrackGenEnable);
auto cbTrackGenPort = new QComboBox();
cbTrackGenPort->addItem("Port 1");
cbTrackGenPort->addItem("Port 2");
cbTrackGenPort->setCurrentIndex(0);
connect(cbTrackGenPort, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
settings.trackingGeneratorPort = index;
if(settings.trackingGenerator) {
SettingsChanged();
}
});
tb_trackgen->addWidget(cbTrackGenPort);
auto dbm = new QDoubleSpinBox();
dbm->setFixedWidth(95);
dbm->setRange(-100.0, 100.0);
dbm->setSingleStep(0.25);
dbm->setSuffix("dbm");
dbm->setToolTip("Level");
dbm->setKeyboardTracking(false);
connect(dbm, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &SpectrumAnalyzer::SetTGLevel);
connect(this, &SpectrumAnalyzer::TGLevelChanged, dbm, &QDoubleSpinBox::setValue);
tb_trackgen->addWidget(new QLabel("Level:"));
tb_trackgen->addWidget(dbm);
auto tgOffset = new SIUnitEdit("Hz", " kMG", 6);
tgOffset->setFixedWidth(100);
tgOffset->setToolTip("Tracking generator offset");
connect(tgOffset, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetTGOffset);
connect(this, &SpectrumAnalyzer::TGOffsetChanged, tgOffset, &SIUnitEdit::setValueQuiet);
tb_trackgen->addWidget(new QLabel("Offset:"));
tb_trackgen->addWidget(tgOffset);
window->addToolBar(tb_trackgen);
toolbars.insert(tb_trackgen);
markerModel = new TraceMarkerModel(traceModel);
@ -192,6 +236,11 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
docks.insert(markerDock);
// Set initial TG settings
SetTGLevel(-20.0);
SetTGOffset(0);
// Set initial sweep settings
auto pref = Preferences::getInstance();
if(pref.Startup.RememberSweepSettings) {
@ -246,9 +295,11 @@ void SpectrumAnalyzer::SettingsChanged()
settings.pointNum = settings.f_stop - settings.f_start + 1;
}
settings.applyReceiverCorrection = 1;
settings.applySourceCorrection = 1;
auto pref = Preferences::getInstance();
if(pref.Acquisition.useDFTinSAmode && settings.RBW <= pref.Acquisition.RBWLimitForDFT) {
if(!settings.trackingGenerator && pref.Acquisition.useDFTinSAmode && settings.RBW <= pref.Acquisition.RBWLimitForDFT) {
// Enable DFT if below RBW threshold and TG is not enabled
settings.UseDFT = 1;
} else {
settings.UseDFT = 0;
@ -372,6 +423,30 @@ void SpectrumAnalyzer::SetAveraging(unsigned int averages)
SettingsChanged();
}
void SpectrumAnalyzer::SetTGLevel(double level)
{
if(level > Device::Info().limits_cdbm_max / 100.0) {
level = Device::Info().limits_cdbm_max / 100.0;
} else if(level < Device::Info().limits_cdbm_min / 100.0) {
level = Device::Info().limits_cdbm_min / 100.0;
}
emit TGLevelChanged(level);
settings.trackingPower = level * 100;
if(settings.trackingGenerator) {
SettingsChanged();
}
}
void SpectrumAnalyzer::SetTGOffset(double offset)
{
settings.trackingGeneratorOffset = offset;
ConstrainAndUpdateFrequencies();
if(settings.trackingGenerator) {
SettingsChanged();
}
}
void SpectrumAnalyzer::UpdateAverageCount()
{
lAverages->setText(QString::number(average.getLevel()) + "/");
@ -388,10 +463,25 @@ void SpectrumAnalyzer::ConstrainAndUpdateFrequencies()
if(settings.f_start < Device::Info().limits_minFreq) {
settings.f_start = Device::Info().limits_minFreq;
}
bool trackingOffset_limited = false;
if(settings.f_stop + settings.trackingGeneratorOffset > Device::Info().limits_maxFreq) {
trackingOffset_limited = true;
settings.trackingGeneratorOffset = Device::Info().limits_maxFreq - settings.f_stop;
}
if(settings.f_start + settings.trackingGeneratorOffset < Device::Info().limits_minFreq) {
trackingOffset_limited = true;
settings.trackingGeneratorOffset = Device::Info().limits_minFreq - settings.f_start;
}
if(trackingOffset_limited) {
InformationBox::ShowMessage("Warning", "The selected tracking generator offset is not reachable for all frequencies with the current span. "
"The tracking generator offset has been constrained according to the selected start and stop frequencies");
}
emit startFreqChanged(settings.f_start);
emit stopFreqChanged(settings.f_stop);
emit spanChanged(settings.f_stop - settings.f_start);
emit centerFreqChanged((settings.f_stop + settings.f_start)/2);
emit TGOffsetChanged(settings.trackingGeneratorOffset);
SettingsChanged();
}

View file

@ -31,8 +31,9 @@ private slots:
// Acquisition control
void SetRBW(double bandwidth);
void SetAveraging(unsigned int averages);
signals:
// TG control
void SetTGLevel(double level);
void SetTGOffset(double offset);
private:
void UpdateAverageCount();
@ -59,6 +60,8 @@ signals:
void centerFreqChanged(double freq);
void spanChanged(double span);
void RBWChanged(double RBW);
void TGOffsetChanged(double offset);
void TGLevelChanged(double level);
void averagingChanged(unsigned int averages);
};

View file

@ -69,8 +69,8 @@ public:
LiveParameter liveParameter() { return _liveParam; }
LivedataType liveType() { return _liveType; }
unsigned int size() { return _data.size(); }
double minFreq() { return _data.front().frequency; };
double maxFreq() { return _data.back().frequency; };
double minFreq() { return size() > 0 ? _data.front().frequency : 0.0; };
double maxFreq() { return size() > 0 ? _data.back().frequency : 0.0; };
double findExtremumFreq(bool max);
/* Searches for peaks in the trace data and returns the peak frequencies in ascending order.
* Up to maxPeaks will be returned, with higher level peaks taking priority over lower level peaks.

View file

@ -85,7 +85,8 @@ QString TraceMarker::readableData()
auto freqDiff = frequency - delta->frequency;
auto valueDiff = data / delta->data;
auto phase = arg(valueDiff);
return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
auto db = 20*log10(abs(valueDiff));
return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
}
break;
case Type::Noise:

View file

@ -407,6 +407,11 @@ static Protocol::SpectrumAnalyzerSettings DecodeSpectrumAnalyzerSettings(uint8_t
d.Detector = e.getBits(3);
d.UseDFT = e.getBits(1);
d.applyReceiverCorrection = e.getBits(1);
d.trackingGenerator = e.getBits(1);
d.applySourceCorrection = e.getBits(1);
d.trackingGeneratorPort = e.getBits(1);
e.get(d.trackingGeneratorOffset);
e.get(d.trackingPower);
return d;
}
static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings d, uint8_t *buf,
@ -421,6 +426,11 @@ static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings
e.addBits(d.Detector, 3);
e.addBits(d.UseDFT, 1);
e.addBits(d.applyReceiverCorrection, 1);
e.addBits(d.trackingGenerator, 1);
e.addBits(d.applySourceCorrection, 1);
e.addBits(d.trackingGeneratorPort, 1);
e.add(d.trackingGeneratorOffset);
e.add(d.trackingPower);
return e.getSize();
}

View file

@ -122,6 +122,11 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
uint8_t Detector :3;
uint8_t UseDFT :1;
uint8_t applyReceiverCorrection :1;
uint8_t trackingGenerator :1;
uint8_t applySourceCorrection :1;
uint8_t trackingGeneratorPort :1; // 0 for port1, 1 for port2
int64_t trackingGeneratorOffset;
int16_t trackingPower;
};
using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {

View file

@ -5,8 +5,6 @@
#include "Si5351C.hpp"
#include "AmplitudeCal.hpp"
static constexpr uint32_t BandSwitchFrequency = 25000000;
void Generator::Setup(Protocol::GeneratorSettings g) {
if(g.activePort == 0) {
// both ports disabled, no need to configure PLLs
@ -45,29 +43,22 @@ void Generator::Setup(Protocol::GeneratorSettings g) {
g.cdbm_level += correction.port2;
}
}
auto amplitude = HW::GetAmplitudeSettings(g.cdbm_level, g.frequency, g.applyAmplitudeCorrection, g.activePort == 2);
// Select correct source
if(g.frequency < BandSwitchFrequency) {
if(g.frequency < HW::BandSwitchFrequency) {
m.SourceLowEN = 1;
m.SourceLowFrequency = g.frequency;
m.SourceHighCE = 0;
m.SourceHighRFEN = 0;
m.SourceHighFrequency = BandSwitchFrequency;
m.SourceHighFrequency = HW::BandSwitchFrequency;
m.SourceHighLowpass = (int) FPGA::LowpassFilter::M947;
m.SourceHighPower = (int) MAX2871::Power::n4dbm;
m.SourceHighband = false;
if(g.cdbm_level <= HW::LowBandMinPower) {
// can use the low power setting
m.SourceLowPower = (int) Si5351C::DriveStrength::mA2;
g.cdbm_level -= HW::LowBandMinPower;
} else {
// needs the high power setting
m.SourceLowPower = (int) Si5351C::DriveStrength::mA8;
g.cdbm_level -= HW::LowBandMaxPower;
}
m.SourceLowPower = (int) amplitude.lowBandPower;
m.SourceHighPower = (int) MAX2871::Power::n4dbm;
} else {
m.SourceLowEN = 0;
m.SourceLowFrequency = BandSwitchFrequency;
m.SourceLowFrequency = HW::BandSwitchFrequency;
m.SourceHighCE = 1;
m.SourceHighRFEN = 1;
m.SourceHighFrequency = g.frequency;
@ -81,26 +72,10 @@ void Generator::Setup(Protocol::GeneratorSettings g) {
m.SourceHighLowpass = (int) FPGA::LowpassFilter::None;
}
m.SourceHighband = true;
if(g.cdbm_level <= HW::HighBandMinPower) {
// can use the low power setting
m.SourceHighPower = (int) MAX2871::Power::n4dbm;
g.cdbm_level -= HW::HighBandMinPower;
} else {
// needs the high power setting
m.SourceHighPower = (int) MAX2871::Power::p5dbm;
g.cdbm_level -= HW::HighBandMaxPower;
}
m.SourceHighPower = (int) amplitude.highBandPower;
m.SourceLowPower = (int) MAX2871::Power::n4dbm;
}
// calculate required attenuation
int16_t attval = -g.cdbm_level / 25;
// TODO set some flag if attenuator limit reached?
if(attval > 127) {
attval = 127;
} else if(attval < 0) {
attval = 0;
}
m.attenuator = attval;
m.attenuator = amplitude.attenuator;
Manual::Setup(m);
}

View file

@ -223,6 +223,52 @@ void HW::SetIdle() {
FPGA::Enable(FPGA::Periphery::PortSwitch, false);
}
HW::AmplitudeSettings HW::GetAmplitudeSettings(int16_t cdbm, uint64_t freq, bool applyCorrections, bool port2) {
if (applyCorrections) {
auto correction = AmplitudeCal::SourceCorrection(freq);
if (port2) {
cdbm += correction.port2;
} else {
cdbm += correction.port1;
}
}
AmplitudeSettings ret;
if(freq < BandSwitchFrequency) {
if(cdbm <= HW::LowBandMinPower) {
// can use the low power setting
ret.lowBandPower = Si5351C::DriveStrength::mA2;
cdbm -= HW::LowBandMinPower;
} else {
// needs the high power setting
ret.lowBandPower = Si5351C::DriveStrength::mA8;
cdbm -= HW::LowBandMaxPower;
}
} else {
if(cdbm <= HW::HighBandMinPower) {
// can use the low power setting
ret.highBandPower = MAX2871::Power::n4dbm;
cdbm -= HW::HighBandMinPower;
} else {
// needs the high power setting
ret.highBandPower = MAX2871::Power::p5dbm;
cdbm -= HW::HighBandMaxPower;
}
}
// calculate required attenuation
int16_t attval = -cdbm / 25;
if(attval > 127) {
attval = 127;
ret.unlevel = true;
} else if(attval < 0) {
attval = 0;
ret.unlevel = true;
} else {
ret.unlevel = false;
}
ret.attenuator = attval;
return ret;
}
void HW::fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy) {
// copy constant default values
memcpy(info, &HW::Info, sizeof(HW::Info));

View file

@ -4,6 +4,8 @@
#include "Protocol.hpp"
#include "FPGA/FPGA.hpp"
#include "AmplitudeCal.hpp"
#include "max2871.hpp"
#include "Si5351C.hpp"
#define USE_DEBUG_PINS
@ -33,6 +35,7 @@ static constexpr uint32_t LO1_minFreq = 25000000;
static constexpr uint32_t MaxSamples = 130944;
static constexpr uint32_t MinSamples = 16;
static constexpr uint32_t PLLRef = 100000000;
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");
@ -84,6 +87,16 @@ void SetMode(Mode mode);
void SetIdle();
void Work();
using AmplitudeSettings = struct _amplitudeSettings {
uint8_t attenuator;
union {
MAX2871::Power highBandPower;
Si5351C::DriveStrength lowBandPower;
};
bool unlevel;
};
AmplitudeSettings GetAmplitudeSettings(int16_t cdbm, uint64_t freq = 0, bool applyCorrections = false, bool port2 = false);
bool GetTemps(uint8_t *source, uint8_t *lo);
void fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy = false);
namespace Ref {

View file

@ -32,6 +32,9 @@ static float port1Measurement[FPGA::DFTbins], port2Measurement[FPGA::DFTbins];
static uint8_t signalIDsteps;
static std::array<uint8_t, 4> signalIDprescalers;
static uint8_t attenuator;
static int64_t trackingFreq;
static bool trackingLowband;
static void StartNextSample() {
uint64_t freq = s.f_start + (s.f_stop - s.f_start) * pointCnt / (points - 1);
@ -50,6 +53,34 @@ static void StartNextSample() {
FPGA::WriteRegister(FPGA::Reg::ADCPrescaler, 112);
FPGA::WriteRegister(FPGA::Reg::PhaseIncrement, 1120);
negativeDFT = true;
// set tracking generator to default values
trackingFreq = 0;
trackingLowband = false;
attenuator = 0;
// this is the first step in each point, update tracking generator if enabled
if (s.trackingGenerator) {
trackingFreq = freq - s.trackingGeneratorOffset;
if(trackingFreq > 0 && trackingFreq <= (int64_t) HW::Info.limits_maxFreq) {
// tracking frequency is valid, calculate required settings and select band
auto amplitude = HW::GetAmplitudeSettings(s.trackingPower, trackingFreq, s.applySourceCorrection, s.trackingGeneratorPort);
attenuator = amplitude.attenuator;
if(trackingFreq < HW::BandSwitchFrequency) {
Si5351.SetCLK(SiChannel::LowbandSource, trackingFreq, Si5351C::PLL::B, amplitude.lowBandPower);
FPGA::Disable(FPGA::Periphery::SourceChip);
FPGA::Disable(FPGA::Periphery::SourceRF);
trackingLowband = true;
} else {
Source.SetFrequency(trackingFreq);
Source.SetPowerOutA(amplitude.highBandPower, true);
FPGA::Enable(FPGA::Periphery::SourceChip);
FPGA::Enable(FPGA::Periphery::SourceRF);
trackingLowband = false;
// selected power potentially changed, update default registers
FPGA::WriteMAX2871Default(Source.GetRegisters());
}
}
}
break;
case 1:
LO2freq = HW::IF1 - HW::IF2;
@ -121,9 +152,9 @@ static void StartNextSample() {
}
// Configure the sampling in the FPGA
FPGA::WriteSweepConfig(0, 0, Source.GetRegisters(), LO1.GetRegisters(), 0,
0, FPGA::SettlingTime::us20, FPGA::Samples::SPPRegister, 0,
FPGA::LowpassFilter::M947);
FPGA::WriteSweepConfig(0, trackingLowband, Source.GetRegisters(), LO1.GetRegisters(), attenuator,
trackingFreq, FPGA::SettlingTime::us20, FPGA::Samples::SPPRegister, 0,
FPGA::LowpassFilter::Auto);
FPGA::StartSweep();
}
@ -162,10 +193,18 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
// enable the required hardware resources
Si5351.Enable(SiChannel::Port1LO2);
Si5351.Enable(SiChannel::Port2LO2);
if (s.trackingGenerator) {
Si5351.Enable(SiChannel::LowbandSource);
} else {
Si5351.Disable(SiChannel::LowbandSource);
}
FPGA::SetWindow((FPGA::Window) s.WindowType);
FPGA::Enable(FPGA::Periphery::LO1Chip);
FPGA::Enable(FPGA::Periphery::LO1RF);
FPGA::Enable(FPGA::Periphery::ExcitePort1);
FPGA::Enable(FPGA::Periphery::ExcitePort1, s.trackingGeneratorPort == 0);
FPGA::Enable(FPGA::Periphery::ExcitePort2, s.trackingGeneratorPort == 1);
FPGA::Enable(FPGA::Periphery::PortSwitch, s.trackingGenerator);
FPGA::Enable(FPGA::Periphery::Amplifier, s.trackingGenerator);
FPGA::Enable(FPGA::Periphery::Port1Mixer);
FPGA::Enable(FPGA::Periphery::Port2Mixer);

View file

@ -36,7 +36,6 @@ static constexpr uint16_t IFTableNumEntries = 500;
static IFTableEntry IFTable[IFTableNumEntries];
static uint16_t IFTableIndexCnt = 0;
static constexpr uint32_t BandSwitchFrequency = 25000000;
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");
@ -115,7 +114,7 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) {
bool needs_halt = false;
uint64_t actualSourceFreq;
bool lowband = false;
if (freq < BandSwitchFrequency) {
if (freq < HW::BandSwitchFrequency) {
needs_halt = true;
lowband = true;
actualSourceFreq = freq;
@ -290,7 +289,7 @@ void VNA::SweepHalted() {
+ (settings.f_stop - settings.f_start) * pointCnt
/ (settings.points - 1);
bool adcShiftRequired = false;
if (frequency < BandSwitchFrequency) {
if (frequency < HW::BandSwitchFrequency) {
// need the Si5351 as Source
Si5351.SetCLK(SiChannel::LowbandSource, frequency, Si5351C::PLL::B,
sourceHighPower ? Si5351C::DriveStrength::mA8 : Si5351C::DriveStrength::mA4);