diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index c011c19..4e6fb81 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -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 \ diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.cpp b/Software/PC_Application/Calibration/amplitudecaldialog.cpp index 6cd4182..c1c9c2b 100644 --- a/Software/PC_Application/Calibration/amplitudecaldialog.cpp +++ b/Software/PC_Application/Calibration/amplitudecaldialog.cpp @@ -4,6 +4,7 @@ #include "unit.h" #include #include "ui_addamplitudepointsdialog.h" +#include "ui_automaticamplitudedialog.h" #include 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; diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.h b/Software/PC_Application/Calibration/amplitudecaldialog.h index d28d444..40d77bd 100644 --- a/Software/PC_Application/Calibration/amplitudecaldialog.h +++ b/Software/PC_Application/Calibration/amplitudecaldialog.h @@ -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 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 diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.ui b/Software/PC_Application/Calibration/amplitudecaldialog.ui index baf0099..ed8e848 100644 --- a/Software/PC_Application/Calibration/amplitudecaldialog.ui +++ b/Software/PC_Application/Calibration/amplitudecaldialog.ui @@ -182,6 +182,13 @@ + + + + Automatic measurement + + + diff --git a/Software/PC_Application/Calibration/automaticamplitudedialog.ui b/Software/PC_Application/Calibration/automaticamplitudedialog.ui new file mode 100644 index 0000000..2f63d72 --- /dev/null +++ b/Software/PC_Application/Calibration/automaticamplitudedialog.ui @@ -0,0 +1,114 @@ + + + AutomaticAmplitudeDialog + + + true + + + + 0 + 0 + 575 + 215 + + + + + 0 + 0 + + + + Automatic Measurement + + + true + + + + + + + 0 + 0 + + + + + + + true + + + + + + + 0 + + + + + + + + + Status: + + + + + + + false + + + + + + + + + + + Abort + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Start Measurement + + + + :/icons/play.png:/icons/play.png + + + + + + + + + + + + diff --git a/Software/PC_Application/Calibration/receivercaldialog.cpp b/Software/PC_Application/Calibration/receivercaldialog.cpp index fff53d3..13d1907 100644 --- a/Software/PC_Application/Calibration/receivercaldialog.cpp +++ b/Software/PC_Application/Calibration/receivercaldialog.cpp @@ -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; diff --git a/Software/PC_Application/Calibration/sourcecaldialog.h b/Software/PC_Application/Calibration/sourcecaldialog.h index 15de04d..86e90a3 100644 --- a/Software/PC_Application/Calibration/sourcecaldialog.h +++ b/Software/PC_Application/Calibration/sourcecaldialog.h @@ -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 diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index cc5ed83..0d6d276 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -40,6 +40,7 @@ #include #include #include +#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(&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(&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(); } diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h index fca3e64..bb6c171 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h @@ -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); }; diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h index d79773b..523e712 100644 --- a/Software/PC_Application/Traces/trace.h +++ b/Software/PC_Application/Traces/trace.h @@ -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. diff --git a/Software/PC_Application/Traces/tracemarker.cpp b/Software/PC_Application/Traces/tracemarker.cpp index ff01365..7079158 100644 --- a/Software/PC_Application/Traces/tracemarker.cpp +++ b/Software/PC_Application/Traces/tracemarker.cpp @@ -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: diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp index 8ad534a..4e64a66 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.cpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp @@ -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(); } diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp index adb3f1e..1eed075 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.hpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp @@ -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 { diff --git a/Software/VNA_embedded/Application/Generator.cpp b/Software/VNA_embedded/Application/Generator.cpp index 1c03e64..77173c5 100644 --- a/Software/VNA_embedded/Application/Generator.cpp +++ b/Software/VNA_embedded/Application/Generator.cpp @@ -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); } diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp index 763b832..a211094 100644 --- a/Software/VNA_embedded/Application/Hardware.cpp +++ b/Software/VNA_embedded/Application/Hardware.cpp @@ -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)); diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp index fdf2177..a87ea7a 100644 --- a/Software/VNA_embedded/Application/Hardware.hpp +++ b/Software/VNA_embedded/Application/Hardware.hpp @@ -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 { diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp index cf7f5cc..475a1c8 100644 --- a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp +++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp @@ -32,6 +32,9 @@ static float port1Measurement[FPGA::DFTbins], port2Measurement[FPGA::DFTbins]; static uint8_t signalIDsteps; static std::array 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); diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index cc0b5fe..a847d8f 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -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);