diff --git a/FPGA/VNA/Sampling.vhd b/FPGA/VNA/Sampling.vhd
index eb298b2..c50ef8a 100644
--- a/FPGA/VNA/Sampling.vhd
+++ b/FPGA/VNA/Sampling.vhd
@@ -201,6 +201,7 @@ begin
clk_cnt <= 0;
sample_cnt <= 0;
window_sample_cnt <= 0;
+ window_index <= (others => '0');
phase <= (others => '0');
else
-- when not idle, generate pulses for ADCs
diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise
index 4aa995f..1009a69 100644
--- a/FPGA/VNA/VNA.gise
+++ b/FPGA/VNA/VNA.gise
@@ -266,7 +266,7 @@
-
+
@@ -288,7 +288,7 @@
-
+
@@ -297,7 +297,7 @@
-
+
@@ -311,7 +311,7 @@
-
+
@@ -325,7 +325,7 @@
-
+
@@ -371,7 +371,7 @@
-
+
diff --git a/Software/PC_Application/Application b/Software/PC_Application/Application
index a13fbe4..cb7a605 100755
Binary files a/Software/PC_Application/Application and b/Software/PC_Application/Application differ
diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro
index 4646932..828af30 100644
--- a/Software/PC_Application/Application.pro
+++ b/Software/PC_Application/Application.pro
@@ -15,6 +15,7 @@ HEADERS += \
Device/manualcontroldialog.h \
Generator/generator.h \
Generator/signalgenwidget.h \
+ SpectrumAnalyzer/spectrumanalyzer.h \
Tools/eseries.h \
Tools/impedancematchdialog.h \
Traces/bodeplotaxisdialog.h \
@@ -57,6 +58,7 @@ SOURCES += \
Device/manualcontroldialog.cpp \
Generator/generator.cpp \
Generator/signalgenwidget.cpp \
+ SpectrumAnalyzer/spectrumanalyzer.cpp \
Tools/eseries.cpp \
Tools/impedancematchdialog.cpp \
Traces/bodeplotaxisdialog.cpp \
diff --git a/Software/PC_Application/Device/device.cpp b/Software/PC_Application/Device/device.cpp
index abe079f..2bcca34 100644
--- a/Software/PC_Application/Device/device.cpp
+++ b/Software/PC_Application/Device/device.cpp
@@ -203,6 +203,14 @@ bool Device::Configure(Protocol::SweepSettings settings)
return SendPacket(p);
}
+bool Device::Configure(Protocol::SpectrumAnalyzerSettings settings)
+{
+ Protocol::PacketInfo p;
+ p.type = Protocol::PacketType::SpectrumAnalyzerSettings;
+ p.spectrumSettings = settings;
+ return SendPacket(p);
+}
+
bool Device::SetManual(Protocol::ManualControl manual)
{
Protocol::PacketInfo p;
@@ -364,6 +372,9 @@ void Device::ReceivedData()
case Protocol::PacketType::Status:
emit ManualStatusReceived(packet.status);
break;
+ case Protocol::PacketType::SpectrumAnalyzerResult:
+ emit SpectrumResultReceived(packet.spectrumResult);
+ break;
case Protocol::PacketType::DeviceInfo:
lastInfo = packet.info;
lastInfoValid = true;
diff --git a/Software/PC_Application/Device/device.h b/Software/PC_Application/Device/device.h
index b13c28d..ed64f30 100644
--- a/Software/PC_Application/Device/device.h
+++ b/Software/PC_Application/Device/device.h
@@ -14,6 +14,7 @@
Q_DECLARE_METATYPE(Protocol::Datapoint);
Q_DECLARE_METATYPE(Protocol::ManualStatus);
Q_DECLARE_METATYPE(Protocol::DeviceInfo);
+Q_DECLARE_METATYPE(Protocol::SpectrumAnalyzerResult);
class USBInBuffer : public QObject {
Q_OBJECT;
@@ -57,6 +58,7 @@ public:
~Device();
bool SendPacket(Protocol::PacketInfo packet, std::function cb = nullptr, unsigned int timeout = 10);
bool Configure(Protocol::SweepSettings settings);
+ bool Configure(Protocol::SpectrumAnalyzerSettings settings);
bool SetManual(Protocol::ManualControl manual);
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
bool SendCommandWithoutPayload(Protocol::PacketType type);
@@ -69,6 +71,7 @@ public:
signals:
void DatapointReceived(Protocol::Datapoint);
void ManualStatusReceived(Protocol::ManualStatus);
+ void SpectrumResultReceived(Protocol::SpectrumAnalyzerResult);
void DeviceInfoUpdated();
void ConnectionLost();
void AckReceived();
diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
new file mode 100644
index 0000000..a4a3413
--- /dev/null
+++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
@@ -0,0 +1,371 @@
+#include "spectrumanalyzer.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "unit.h"
+#include "CustomWidgets/toggleswitch.h"
+#include "Device/manualcontroldialog.h"
+#include "Traces/tracemodel.h"
+#include "Traces/tracewidget.h"
+#include "Traces/tracesmithchart.h"
+#include "Traces/tracebodeplot.h"
+#include "Traces/traceimportdialog.h"
+#include "CustomWidgets/tilewidget.h"
+#include "CustomWidgets/siunitedit.h"
+#include
+#include "Traces/markerwidget.h"
+#include "Tools/impedancematchdialog.h"
+#include "Calibration/calibrationtracedialog.h"
+#include "ui_main.h"
+#include "Device/firmwareupdatedialog.h"
+#include "preferences.h"
+#include "Generator/signalgenwidget.h"
+#include
+#include
+#include
+
+SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
+ : Mode(window, "Spectrum Analyzer"),
+ pref(window->getPreferenceRef()),
+ central(new TileWidget(traceModel))
+{
+ averages = 1;
+
+ // Create default traces
+ auto tPort1 = new Trace("Port1", Qt::yellow);
+ tPort1->fromLivedata(Trace::LivedataType::Overwrite, Trace::LiveParameter::Port1);
+ traceModel.addTrace(tPort1);
+ auto tPort2 = new Trace("Port2", Qt::blue);
+ tPort2->fromLivedata(Trace::LivedataType::Overwrite, Trace::LiveParameter::Port2);
+ traceModel.addTrace(tPort2);
+
+ auto tracebode = new TraceBodePlot(traceModel);
+ tracebode->enableTrace(tPort1, true);
+ tracebode->enableTrace(tPort2, true);
+ tracebode->setYAxis(0, TraceBodePlot::YAxisType::Magnitude, false, false, -120,0,10);
+ tracebode->setYAxis(1, TraceBodePlot::YAxisType::Disabled, false, true, 0,0,1);
+
+ central->setPlot(tracebode);
+
+ // Create menu entries and connections
+ // Sweep toolbar
+ auto tb_sweep = new QToolBar("Sweep");
+ auto eStart = new SIUnitEdit("Hz", " kMG", 6);
+ eStart->setFixedWidth(100);
+ eStart->setToolTip("Start frequency");
+ connect(eStart, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetStartFreq);
+ connect(this, &SpectrumAnalyzer::startFreqChanged, eStart, &SIUnitEdit::setValueQuiet);
+ tb_sweep->addWidget(new QLabel("Start:"));
+ tb_sweep->addWidget(eStart);
+
+ auto eCenter = new SIUnitEdit("Hz", " kMG", 6);
+ eCenter->setFixedWidth(100);
+ eCenter->setToolTip("Center frequency");
+ connect(eCenter, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetCenterFreq);
+ connect(this, &SpectrumAnalyzer::centerFreqChanged, eCenter, &SIUnitEdit::setValueQuiet);
+ tb_sweep->addWidget(new QLabel("Center:"));
+ tb_sweep->addWidget(eCenter);
+
+ auto eStop = new SIUnitEdit("Hz", " kMG", 6);
+ eStop->setFixedWidth(100);
+ eStop->setToolTip("Stop frequency");
+ connect(eStop, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetStopFreq);
+ connect(this, &SpectrumAnalyzer::stopFreqChanged, eStop, &SIUnitEdit::setValueQuiet);
+ tb_sweep->addWidget(new QLabel("Stop:"));
+ tb_sweep->addWidget(eStop);
+
+ auto eSpan = new SIUnitEdit("Hz", " kMG", 6);
+ eSpan->setFixedWidth(100);
+ eSpan->setToolTip("Span");
+ connect(eSpan, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetSpan);
+ connect(this, &SpectrumAnalyzer::spanChanged, eSpan, &SIUnitEdit::setValueQuiet);
+ tb_sweep->addWidget(new QLabel("Span:"));
+ tb_sweep->addWidget(eSpan);
+
+ auto bFull = new QPushButton(QIcon::fromTheme("zoom-fit-best"), "");
+ bFull->setToolTip("Full span");
+ connect(bFull, &QPushButton::clicked, this, &SpectrumAnalyzer::SetFullSpan);
+ tb_sweep->addWidget(bFull);
+
+ auto bZoomIn = new QPushButton(QIcon::fromTheme("zoom-in"), "");
+ bZoomIn->setToolTip("Zoom in");
+ connect(bZoomIn, &QPushButton::clicked, this, &SpectrumAnalyzer::SpanZoomIn);
+ tb_sweep->addWidget(bZoomIn);
+
+ auto bZoomOut = new QPushButton(QIcon::fromTheme("zoom-out"), "");
+ bZoomOut->setToolTip("Zoom out");
+ connect(bZoomOut, &QPushButton::clicked, this, &SpectrumAnalyzer::SpanZoomOut);
+ tb_sweep->addWidget(bZoomOut);
+
+ window->addToolBar(tb_sweep);
+ toolbars.insert(tb_sweep);
+
+ // Acquisition toolbar
+ auto tb_acq = new QToolBar("Acquisition");
+
+ auto eBandwidth = new SIUnitEdit("Hz", " k", 3);
+ eBandwidth->setValueQuiet(settings.RBW);
+ eBandwidth->setFixedWidth(70);
+ eBandwidth->setToolTip("RBW");
+ connect(eBandwidth, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetRBW);
+ connect(this, &SpectrumAnalyzer::RBWChanged, eBandwidth, &SIUnitEdit::setValueQuiet);
+ tb_acq->addWidget(new QLabel("RBW:"));
+ tb_acq->addWidget(eBandwidth);
+
+ tb_acq->addWidget(new QLabel("Window:"));
+ auto cbWindowType = new QComboBox();
+ cbWindowType->addItem("None");
+ cbWindowType->addItem("Kaiser");
+ cbWindowType->addItem("Hann");
+ cbWindowType->addItem("Flat Top");
+ cbWindowType->setCurrentIndex(1);
+ connect(cbWindowType, qOverload(&QComboBox::currentIndexChanged), [=](int index) {
+ settings.WindowType = index;
+ SettingsChanged();
+ });
+ tb_acq->addWidget(cbWindowType);
+
+ tb_acq->addWidget(new QLabel("Detector:"));
+ auto cbDetector = new QComboBox();
+ cbDetector->addItem("+Peak");
+ cbDetector->addItem("-Peak");
+ cbDetector->addItem("Sample");
+ cbDetector->addItem("Normal");
+ cbDetector->addItem("Average");
+ cbDetector->setCurrentIndex(0);
+ connect(cbDetector, qOverload(&QComboBox::currentIndexChanged), [=](int index) {
+ settings.Detector = index;
+ SettingsChanged();
+ });
+ tb_acq->addWidget(cbDetector);
+
+ auto cbSignalID = new QCheckBox("Signal ID");
+ connect(cbSignalID, &QCheckBox::toggled, [=](bool enabled) {
+ settings.SignalID = enabled;
+ SettingsChanged();
+ });
+ tb_acq->addWidget(cbSignalID);
+
+ window->addToolBar(tb_acq);
+ toolbars.insert(tb_acq);
+
+
+ markerModel = new TraceMarkerModel(traceModel);
+
+ auto tracesDock = new QDockWidget("Traces");
+ tracesDock->setWidget(new TraceWidget(traceModel, this, true));
+ window->addDockWidget(Qt::LeftDockWidgetArea, tracesDock);
+ docks.insert(tracesDock);
+
+
+ auto markerWidget = new MarkerWidget(*markerModel);
+
+ auto markerDock = new QDockWidget("Marker");
+ markerDock->setWidget(markerWidget);
+ window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
+ docks.insert(markerDock);
+
+ qRegisterMetaType("SpectrumResult");
+
+ // Set initial sweep settings
+ // TODO
+// if(pref.Startup.RememberSweepSettings) {
+// LoadSweepSettings();
+// } else {
+ settings.f_start = pref.Startup.DefaultSweep.start;
+ settings.f_stop = pref.Startup.DefaultSweep.stop;
+ ConstrainAndUpdateFrequencies();
+ SetRBW(10000);
+ settings.WindowType = 1;
+ settings.Detector = 0;
+ settings.pointNum = 1001;
+ settings.SignalID = 0;
+// }
+
+ finalize(central);
+}
+
+void SpectrumAnalyzer::deactivate()
+{
+ StoreSweepSettings();
+ Mode::deactivate();
+}
+
+void SpectrumAnalyzer::initializeDevice()
+{
+ connect(window->getDevice(), &Device::SpectrumResultReceived, this, &SpectrumAnalyzer::NewDatapoint, Qt::UniqueConnection);
+
+ // Configure initial state of device
+ window->getDevice()->Configure(settings);
+}
+
+using namespace std;
+
+void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
+{
+ // TODO level adjustment in device
+ d.port1 /= pow(10.0, 7.5);
+ d.port2 /= pow(10.0, 7.5);
+ d = average.process(d);
+ traceModel.addSAData(d);
+ emit dataChanged();
+}
+
+void SpectrumAnalyzer::SettingsChanged()
+{
+ if(window->getDevice()) {
+ window->getDevice()->Configure(settings);
+ }
+ average.reset();
+ traceModel.clearVNAData();
+ TracePlot::UpdateSpan(settings.f_start, settings.f_stop);
+}
+
+void SpectrumAnalyzer::StartImpedanceMatching()
+{
+ auto dialog = new ImpedanceMatchDialog(*markerModel);
+ dialog->show();
+}
+
+void SpectrumAnalyzer::SetStartFreq(double freq)
+{
+ settings.f_start = freq;
+ if(settings.f_stop < freq) {
+ settings.f_stop = freq;
+ }
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SetStopFreq(double freq)
+{
+ settings.f_stop = freq;
+ if(settings.f_start > freq) {
+ settings.f_start = freq;
+ }
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SetCenterFreq(double freq)
+{
+ auto old_span = settings.f_stop - settings.f_start;
+ if (freq > old_span / 2) {
+ settings.f_start = freq - old_span / 2;
+ settings.f_stop = freq + old_span / 2;
+ } else {
+ settings.f_start = 0;
+ settings.f_stop = 2 * freq;
+ }
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SetSpan(double span)
+{
+ auto old_center = (settings.f_start + settings.f_stop) / 2;
+ if(old_center > span / 2) {
+ settings.f_start = old_center - span / 2;
+ } else {
+ settings.f_start = 0;
+ }
+ settings.f_stop = old_center + span / 2;
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SetFullSpan()
+{
+ settings.f_start = 0;
+ settings.f_stop = 6000000000;
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SpanZoomIn()
+{
+ auto center = (settings.f_start + settings.f_stop) / 2;
+ auto old_span = settings.f_stop - settings.f_start;
+ settings.f_start = center - old_span / 4;
+ settings.f_stop = center + old_span / 4;
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SpanZoomOut()
+{
+ auto center = (settings.f_start + settings.f_stop) / 2;
+ auto old_span = settings.f_stop - settings.f_start;
+ if(center > old_span) {
+ settings.f_start = center - old_span;
+ } else {
+ settings.f_start = 0;
+ }
+ settings.f_stop = center + old_span;
+ ConstrainAndUpdateFrequencies();
+}
+
+void SpectrumAnalyzer::SetRBW(double bandwidth)
+{
+ settings.RBW = bandwidth;
+ emit RBWChanged(settings.RBW);
+ SettingsChanged();
+}
+
+void SpectrumAnalyzer::SetAveraging(unsigned int averages)
+{
+ this->averages = averages;
+ average.setAverages(averages);
+ emit averagingChanged(averages);
+ SettingsChanged();
+}
+
+void SpectrumAnalyzer::ConstrainAndUpdateFrequencies()
+{
+ // TODO central hardware limits
+ if(settings.f_stop > 6000000000) {
+ settings.f_stop = 6000000000;
+ }
+ if(settings.f_start > settings.f_stop) {
+ settings.f_start = settings.f_stop;
+ }
+ 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);
+ SettingsChanged();
+}
+
+void SpectrumAnalyzer::LoadSweepSettings()
+{
+ // TODO
+// QSettings s;
+// settings.f_start = s.value("SweepStart", pref.Startup.DefaultSweep.start).toULongLong();
+// settings.f_stop = s.value("SweepStop", pref.Startup.DefaultSweep.stop).toULongLong();
+// ConstrainAndUpdateFrequencies();
+// SetIFBandwidth(s.value("SweepBandwidth", pref.Startup.DefaultSweep.bandwidth).toUInt());
+// SetPoints(s.value("SweepPoints", pref.Startup.DefaultSweep.points).toInt());
+// SetSourceLevel(s.value("SweepLevel", pref.Startup.DefaultSweep.excitation).toDouble());
+}
+
+void SpectrumAnalyzer::StoreSweepSettings()
+{
+ // TODO
+// QSettings s;
+// s.setValue("SweepStart", static_cast(settings.f_start));
+// s.setValue("SweepStop", static_cast(settings.f_stop));
+// s.setValue("SweepBandwidth", settings.if_bandwidth);
+// s.setValue("SweepPoints", settings.points);
+// s.setValue("SweepLevel", (double) settings.cdbm_excitation / 100.0);
+}
diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h
new file mode 100644
index 0000000..d813bf4
--- /dev/null
+++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h
@@ -0,0 +1,63 @@
+#ifndef SPECTRUMANALYZER_H
+#define SPECTRUMANALYZER_H
+
+#include
+#include
+#include "appwindow.h"
+#include "mode.h"
+#include "CustomWidgets/tilewidget.h"
+
+class SpectrumAnalyzer : public Mode
+{
+ Q_OBJECT
+public:
+ SpectrumAnalyzer(AppWindow *window);
+
+ void deactivate() override;
+ void initializeDevice() override;
+private slots:
+ void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
+ void StartImpedanceMatching();
+ // Sweep control
+ void SetStartFreq(double freq);
+ void SetStopFreq(double freq);
+ void SetCenterFreq(double freq);
+ void SetSpan(double span);
+ void SetFullSpan();
+ void SpanZoomIn();
+ void SpanZoomOut();
+ // Acquisition control
+ void SetRBW(double bandwidth);
+ void SetAveraging(unsigned int averages);
+
+signals:
+
+private:
+ void UpdateStatusPanel();
+ void SettingsChanged();
+ void ConstrainAndUpdateFrequencies();
+ void LoadSweepSettings();
+ void StoreSweepSettings();
+
+ Preferences &pref;
+
+ Protocol::SpectrumAnalyzerSettings settings;
+ unsigned int averages;
+ TraceModel traceModel;
+ TraceMarkerModel *markerModel;
+ Averaging average;
+
+ TileWidget *central;
+
+signals:
+ void dataChanged();
+ void startFreqChanged(double freq);
+ void stopFreqChanged(double freq);
+ void centerFreqChanged(double freq);
+ void spanChanged(double span);
+ void RBWChanged(double RBW);
+
+ void averagingChanged(unsigned int averages);
+};
+
+#endif // VNA_H
diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp
index a1d12fd..ae9b192 100644
--- a/Software/PC_Application/Traces/trace.cpp
+++ b/Software/PC_Application/Traces/trace.cpp
@@ -2,10 +2,11 @@
using namespace std;
-Trace::Trace(QString name, QColor color)
+Trace::Trace(QString name, QColor color, LiveParameter live)
: _name(name),
_color(color),
_liveType(LivedataType::Overwrite),
+ _liveParam(live),
reflection(true),
visible(true),
paused(false),
diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h
index daa9b4a..ef34475 100644
--- a/Software/PC_Application/Traces/trace.h
+++ b/Software/PC_Application/Traces/trace.h
@@ -21,7 +21,16 @@ public:
std::complex S;
};
- Trace(QString name = QString(), QColor color = Qt::darkYellow);
+ enum class LiveParameter {
+ S11,
+ S12,
+ S21,
+ S22,
+ Port1,
+ Port2,
+ };
+
+ Trace(QString name = QString(), QColor color = Qt::darkYellow, LiveParameter live = LiveParameter::S11);
~Trace();
enum class LivedataType {
@@ -29,12 +38,7 @@ public:
MaxHold,
MinHold,
};
- enum class LiveParameter {
- S11,
- S12,
- S21,
- S22,
- };
+
void clear();
void addData(Data d);
diff --git a/Software/PC_Application/Traces/traceeditdialog.cpp b/Software/PC_Application/Traces/traceeditdialog.cpp
index 6c45ceb..4f42a9d 100644
--- a/Software/PC_Application/Traces/traceeditdialog.cpp
+++ b/Software/PC_Application/Traces/traceeditdialog.cpp
@@ -58,11 +58,32 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
case Trace::LivedataType::MinHold: ui->CLiveType->setCurrentIndex(2); break;
}
+ switch(t.liveParameter()) {
+ case Trace::LiveParameter::S11:
+ case Trace::LiveParameter::S12:
+ case Trace::LiveParameter::S21:
+ case Trace::LiveParameter::S22:
+ VNAtrace = true;
+ ui->CLiveParam->addItem("S11");
+ ui->CLiveParam->addItem("S12");
+ ui->CLiveParam->addItem("S21");
+ ui->CLiveParam->addItem("S22");
+ break;
+ case Trace::LiveParameter::Port1:
+ case Trace::LiveParameter::Port2:
+ ui->CLiveParam->addItem("Port 1");
+ ui->CLiveParam->addItem("Port 2");
+ VNAtrace = false;
+ break;
+ }
+
switch(t.liveParameter()) {
case Trace::LiveParameter::S11: ui->CLiveParam->setCurrentIndex(0); break;
case Trace::LiveParameter::S12: ui->CLiveParam->setCurrentIndex(1); break;
case Trace::LiveParameter::S21: ui->CLiveParam->setCurrentIndex(2); break;
case Trace::LiveParameter::S22: ui->CLiveParam->setCurrentIndex(3); break;
+ case Trace::LiveParameter::Port1: ui->CLiveParam->setCurrentIndex(0); break;
+ case Trace::LiveParameter::Port2: ui->CLiveParam->setCurrentIndex(1); break;
}
connect(ui->GSource, qOverload(&QButtonGroup::buttonClicked), updateFileStatus);
@@ -100,11 +121,18 @@ void TraceEditDialog::on_buttonBox_accepted()
case 1: type = Trace::LivedataType::MaxHold; break;
case 2: type = Trace::LivedataType::MinHold; break;
}
- switch(ui->CLiveParam->currentIndex()) {
- case 0: param = Trace::LiveParameter::S11; break;
- case 1: param = Trace::LiveParameter::S12; break;
- case 2: param = Trace::LiveParameter::S21; break;
- case 3: param = Trace::LiveParameter::S22; break;
+ if(VNAtrace) {
+ switch(ui->CLiveParam->currentIndex()) {
+ case 0: param = Trace::LiveParameter::S11; break;
+ case 1: param = Trace::LiveParameter::S12; break;
+ case 2: param = Trace::LiveParameter::S21; break;
+ case 3: param = Trace::LiveParameter::S22; break;
+ }
+ } else {
+ switch(ui->CLiveParam->currentIndex()) {
+ case 0: param = Trace::LiveParameter::Port1; break;
+ case 1: param = Trace::LiveParameter::Port2; break;
+ }
}
trace.fromLivedata(type, param);
}
diff --git a/Software/PC_Application/Traces/traceeditdialog.h b/Software/PC_Application/Traces/traceeditdialog.h
index 60652dd..6149886 100644
--- a/Software/PC_Application/Traces/traceeditdialog.h
+++ b/Software/PC_Application/Traces/traceeditdialog.h
@@ -24,6 +24,7 @@ private:
void setColor(QColor c);
Ui::TraceEditDialog *ui;
Trace &trace;
+ bool VNAtrace;
};
#endif // TRACEEDITDIALOG_H
diff --git a/Software/PC_Application/Traces/traceeditdialog.ui b/Software/PC_Application/Traces/traceeditdialog.ui
index b8aeb47..1f33371 100644
--- a/Software/PC_Application/Traces/traceeditdialog.ui
+++ b/Software/PC_Application/Traces/traceeditdialog.ui
@@ -119,28 +119,7 @@
-
-
-
-
-
- S11
-
-
- -
-
- S12
-
-
- -
-
- S21
-
-
- -
-
- S22
-
-
-
+
diff --git a/Software/PC_Application/Traces/tracemodel.cpp b/Software/PC_Application/Traces/tracemodel.cpp
index af3cb92..c3b235a 100644
--- a/Software/PC_Application/Traces/tracemodel.cpp
+++ b/Software/PC_Application/Traces/tracemodel.cpp
@@ -148,6 +148,27 @@ void TraceModel::addVNAData(Protocol::Datapoint d)
case Trace::LiveParameter::S12: td.S = complex(d.real_S12, d.imag_S12); break;
case Trace::LiveParameter::S21: td.S = complex(d.real_S21, d.imag_S21); break;
case Trace::LiveParameter::S22: td.S = complex(d.real_S22, d.imag_S22); break;
+ default:
+ // not a VNA trace, skip
+ continue;
+ }
+ t->addData(td);
+ }
+ }
+}
+
+void TraceModel::addSAData(Protocol::SpectrumAnalyzerResult d)
+{
+ for(auto t : traces) {
+ if (t->isLive()) {
+ Trace::Data td;
+ td.frequency = d.frequency;
+ switch(t->liveParameter()) {
+ case Trace::LiveParameter::Port1: td.S = complex(d.port1, 0); break;
+ case Trace::LiveParameter::Port2: td.S = complex(d.port2, 0); break;
+ default:
+ // not a SA trace, skip
+ continue;
}
t->addData(td);
}
diff --git a/Software/PC_Application/Traces/tracemodel.h b/Software/PC_Application/Traces/tracemodel.h
index 06c50c7..28bcde9 100644
--- a/Software/PC_Application/Traces/tracemodel.h
+++ b/Software/PC_Application/Traces/tracemodel.h
@@ -33,6 +33,7 @@ signals:
public slots:
void clearVNAData();
void addVNAData(Protocol::Datapoint d);
+ void addSAData(Protocol::SpectrumAnalyzerResult d);
private:
std::vector traces;
diff --git a/Software/PC_Application/Traces/tracewidget.cpp b/Software/PC_Application/Traces/tracewidget.cpp
index b0d6fba..c935fd9 100644
--- a/Software/PC_Application/Traces/tracewidget.cpp
+++ b/Software/PC_Application/Traces/tracewidget.cpp
@@ -7,10 +7,11 @@
#include "traceexportdialog.h"
#include
-TraceWidget::TraceWidget(TraceModel &model, QWidget *parent) :
+TraceWidget::TraceWidget(TraceModel &model, QWidget *parent, bool SA) :
QWidget(parent),
ui(new Ui::TraceWidget),
- model(model)
+ model(model),
+ SA(SA)
{
ui->setupUi(this);
ui->view->setModel(&model);
@@ -27,7 +28,8 @@ TraceWidget::~TraceWidget()
void TraceWidget::on_add_clicked()
{
createCount++;
- auto t = new Trace("Trace #"+QString::number(createCount));
+ auto liveParam = SA ? Trace::LiveParameter::Port1 : Trace::LiveParameter::S11;
+ auto t = new Trace("Trace #"+QString::number(createCount), Qt::darkYellow, liveParam);
t->setColor(QColor::fromHsl((createCount * 50) % 360, 250, 128));
model.addTrace(t);
}
diff --git a/Software/PC_Application/Traces/tracewidget.h b/Software/PC_Application/Traces/tracewidget.h
index 100758e..c34d1fa 100644
--- a/Software/PC_Application/Traces/tracewidget.h
+++ b/Software/PC_Application/Traces/tracewidget.h
@@ -13,7 +13,7 @@ class TraceWidget : public QWidget
Q_OBJECT
public:
- explicit TraceWidget(TraceModel &model, QWidget *parent = nullptr);
+ explicit TraceWidget(TraceModel &model, QWidget *parent = nullptr, bool SA = false);
~TraceWidget();
public slots:
@@ -36,6 +36,7 @@ private:
Ui::TraceWidget *ui;
TraceModel &model;
int createCount;
+ bool SA;
};
#endif // TRACEWIDGET_H
diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp
index b7086bd..9990461 100644
--- a/Software/PC_Application/VNA/vna.cpp
+++ b/Software/PC_Application/VNA/vna.cpp
@@ -85,9 +85,6 @@ VNA::VNA(AppWindow *window)
central->Child2()->Child1()->setPlot(tracebode2);
central->Child2()->Child2()->setPlot(tracesmith2);
- // central widget is constructed, mode can be finalized
- finalize(central);
-
// Create menu entries and connections
auto calMenu = new QMenu("Calibration");
window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), calMenu);
@@ -437,6 +434,16 @@ VNA::VNA(AppWindow *window)
SetIFBandwidth(pref.Startup.DefaultSweep.bandwidth);
SetPoints(pref.Startup.DefaultSweep.points);
}
+
+ // Set ObjectName for toolbars and docks
+ for(auto d : findChildren()) {
+ d->setObjectName(d->windowTitle());
+ }
+ for(auto t : findChildren()) {
+ t->setObjectName(t->windowTitle());
+ }
+
+ finalize(central);
}
void VNA::deactivate()
diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp
index 632846b..1857a3e 100644
--- a/Software/PC_Application/appwindow.cpp
+++ b/Software/PC_Application/appwindow.cpp
@@ -43,6 +43,7 @@
#include
#include "VNA/vna.h"
#include "Generator/generator.h"
+#include "SpectrumAnalyzer/spectrumanalyzer.h"
using namespace std;
@@ -71,6 +72,7 @@ AppWindow::AppWindow(QWidget *parent)
CreateToolbars();
auto logDock = new QDockWidget("Device Log");
logDock->setWidget(&deviceLog);
+ logDock->setObjectName("Log Dock");
addDockWidget(Qt::BottomDockWidgetArea, logDock);
// fill toolbar/dock menu
@@ -88,6 +90,7 @@ AppWindow::AppWindow(QWidget *parent)
setCentralWidget(central);
auto vna = new VNA(this);
new Generator(this);
+ new SpectrumAnalyzer(this);
// auto signalGenWidget = new Signalgenerator;
// modeSGen = new GUIMode(this, "Signal Generator", signalGenWidget);
@@ -122,14 +125,6 @@ AppWindow::AppWindow(QWidget *parent)
restoreGeometry(settings.value("geometry").toByteArray());
}
- // Set ObjectName for toolbars and docks
- for(auto d : findChildren()) {
- d->setObjectName(d->windowTitle());
- }
- for(auto t : findChildren()) {
- t->setObjectName(t->windowTitle());
- }
-
// Set default mode
vna->activate();
@@ -248,6 +243,7 @@ void AppWindow::CreateToolbars()
connect(toolbars.reference.outputEnabled, &QCheckBox::clicked, this, &AppWindow::UpdateReference);
addToolBar(tb_reference);
+ tb_reference->setObjectName("Reference Toolbar");
}
Preferences &AppWindow::getPreferenceRef()
diff --git a/Software/PC_Application/averaging.cpp b/Software/PC_Application/averaging.cpp
index 442e30c..a84180b 100644
--- a/Software/PC_Application/averaging.cpp
+++ b/Software/PC_Application/averaging.cpp
@@ -68,6 +68,40 @@ Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
return d;
}
+Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerResult d)
+{
+ if (d.pointNum == avg.size()) {
+ // add moving average entry
+ deque, 4>> deque;
+ avg.push_back(deque);
+ }
+
+ if (d.pointNum < avg.size()) {
+ // can compute average
+ // get correct queue
+ auto deque = &avg[d.pointNum];
+ // add newest sample to queue
+ array, 4> sample = {d.port1, d.port2, 0, 0};
+ deque->push_back(sample);
+ if(deque->size() > averages) {
+ deque->pop_front();
+ }
+
+ // calculate average
+ complex sum[4];
+ for(auto s : *deque) {
+ sum[0] += s[0];
+ sum[1] += s[1];
+ sum[2] += s[2];
+ sum[3] += s[3];
+ }
+ d.port1 = abs(sum[0] / (double) (deque->size()));
+ d.port2 = abs(sum[1] / (double) (deque->size()));
+ }
+
+ return d;
+}
+
unsigned int Averaging::getLevel()
{
if(avg.size() > 0) {
diff --git a/Software/PC_Application/averaging.h b/Software/PC_Application/averaging.h
index d416d6f..488c00e 100644
--- a/Software/PC_Application/averaging.h
+++ b/Software/PC_Application/averaging.h
@@ -13,6 +13,7 @@ public:
void reset();
void setAverages(unsigned int a);
Protocol::Datapoint process(Protocol::Datapoint d);
+ Protocol::SpectrumAnalyzerResult process(Protocol::SpectrumAnalyzerResult d);
unsigned int getLevel();
private:
std::vector, 4>>> avg;
diff --git a/Software/PC_Application/mode.cpp b/Software/PC_Application/mode.cpp
index 8e3ae7b..977c981 100644
--- a/Software/PC_Application/mode.cpp
+++ b/Software/PC_Application/mode.cpp
@@ -68,7 +68,7 @@ void Mode::activate()
// restore visibility of toolbars and docks
// window->getUi()->menuDocks->clear();
- for(auto d : window->findChildren()) {
+ for(auto d : docks) {
// window->getUi()->menuDocks->addAction(d->toggleViewAction());
bool hidden = settings.value("dock_"+name+"_"+d->windowTitle(), d->isHidden()).toBool();
if(hidden) {
@@ -78,7 +78,7 @@ void Mode::activate()
}
}
// window->getUi()->menuToolbars->clear();
- for(auto t : window->findChildren()) {
+ for(auto t : toolbars) {
// window->getUi()->menuToolbars->addAction(t->toggleViewAction());
bool hidden = settings.value("toolbar_"+name+"_"+t->windowTitle(), t->isHidden()).toBool();
if(hidden) {
@@ -99,10 +99,10 @@ void Mode::deactivate()
{
QSettings settings;
// save dock/toolbar visibility
- for(auto d : window->findChildren()) {
+ for(auto d : docks) {
settings.setValue("dock_"+name+"_"+d->windowTitle(), d->isHidden());
}
- for(auto t : window->findChildren()) {
+ for(auto t : toolbars) {
settings.setValue("toolbar_"+name+"_"+t->windowTitle(), t->isHidden());
}
// settings.setValue("geometry_"+name, window->saveGeometry());
@@ -133,6 +133,13 @@ void Mode::finalize(QWidget *centralWidget)
{
central = centralWidget;
window->getCentral()->addWidget(central);
+ // Set ObjectName for toolbars and docks
+ for(auto d : docks) {
+ d->setObjectName(d->windowTitle()+name);
+ }
+ for(auto t : toolbars) {
+ t->setObjectName(t->windowTitle()+name);
+ }
// hide all mode specific GUI elements
for(auto t : toolbars) {
t->hide();
diff --git a/Software/VNA_embedded/Application/App.cpp b/Software/VNA_embedded/Application/App.cpp
index a555dde..aeb6eab 100644
--- a/Software/VNA_embedded/Application/App.cpp
+++ b/Software/VNA_embedded/Application/App.cpp
@@ -16,6 +16,7 @@
#include "Hardware.hpp"
#include "Manual.hpp"
#include "Generator.hpp"
+#include "SpectrumAnalyzer.hpp"
#define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "App"
@@ -37,6 +38,7 @@ static Flash flash = Flash(&hspi1, FLASH_CS_GPIO_Port, FLASH_CS_Pin);
#define FLAG_USB_PACKET 0x01
#define FLAG_DATAPOINT 0x02
+#define FLAG_WORK_REQUIRED 0x04
static void VNACallback(Protocol::Datapoint res) {
result = res;
@@ -50,6 +52,11 @@ static void USBPacketReceived(Protocol::PacketInfo p) {
xTaskNotifyFromISR(handle, FLAG_USB_PACKET, eSetBits, &woken);
portYIELD_FROM_ISR(woken);
}
+static void HardwareWorkRequired() {
+ BaseType_t woken = false;
+ xTaskNotifyFromISR(handle, FLAG_WORK_REQUIRED, eSetBits, &woken);
+ portYIELD_FROM_ISR(woken);
+}
void App_Start() {
handle = xTaskGetCurrentTaskHandle();
@@ -90,7 +97,7 @@ void App_Start() {
EN_6V_GPIO_Port->BSRR = EN_6V_Pin;
#endif
- if (!HW::Init()) {
+ if (!HW::Init(HardwareWorkRequired)) {
LOG_CRIT("Initialization failed, unable to start");
LED::Error(4);
}
@@ -108,6 +115,9 @@ void App_Start() {
uint32_t notification;
if(xTaskNotifyWait(0x00, UINT32_MAX, ¬ification, 100) == pdPASS) {
// something happened
+ if(notification & FLAG_WORK_REQUIRED) {
+ HW::Work();
+ }
if(notification & FLAG_DATAPOINT) {
Protocol::PacketInfo packet;
packet.type = Protocol::PacketType::Datapoint;
@@ -143,6 +153,12 @@ void App_Start() {
Generator::Setup(packet.generator);
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
break;
+ case Protocol::PacketType::SpectrumAnalyzerSettings:
+ sweepActive = false;
+ LOG_INFO("Updating spectrum analyzer settings");
+ SA::Setup(packet.spectrumSettings);
+ Communication::SendWithoutPayload(Protocol::PacketType::Ack);
+ break;
#ifdef HAS_FLASH
case Protocol::PacketType::ClearFlash:
HW::SetMode(HW::Mode::Idle);
@@ -191,7 +207,7 @@ void App_Start() {
LOG_WARN("Timed out waiting for point, last received point was %d (Status 0x%04x)", result.pointNum, FPGA::GetStatus());
FPGA::AbortSweep();
// restart the current sweep
- HW::Init();
+ HW::Init(HardwareWorkRequired);
HW::Ref::update();
VNA::Setup(settings, VNACallback);
sweepActive = true;
diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp
index 7b53839..cde6a90 100644
--- a/Software/VNA_embedded/Application/Communication/Protocol.cpp
+++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp
@@ -358,6 +358,50 @@ static int16_t EncodeManualControl(Protocol::ManualControl d, uint8_t *buf,
return e.getSize();
}
+static Protocol::SpectrumAnalyzerSettings DecodeSpectrumAnalyzerSettings(uint8_t *buf) {
+ Protocol::SpectrumAnalyzerSettings d;
+ Decoder e(buf);
+ e.get(d.f_start);
+ e.get(d.f_stop);
+ e.get(d.RBW);
+ e.get(d.pointNum);
+ d.WindowType = e.getBits(2);
+ d.SignalID = e.getBits(1);
+ d.Detector = e.getBits(3);
+ return d;
+}
+static int16_t EncodeSpectrumAnalyzerSettings(Protocol::SpectrumAnalyzerSettings d, uint8_t *buf,
+ uint16_t bufSize) {
+ Encoder e(buf, bufSize);
+ e.add(d.f_start);
+ e.add(d.f_stop);
+ e.add(d.RBW);
+ e.add(d.pointNum);
+ e.addBits(d.WindowType, 2);
+ e.addBits(d.SignalID, 1);
+ e.addBits(d.Detector, 3);
+ return e.getSize();
+}
+
+static Protocol::SpectrumAnalyzerResult DecodeSpectrumAnalyzerResult(uint8_t *buf) {
+ Protocol::SpectrumAnalyzerResult d;
+ Decoder e(buf);
+ e.get(d.port1);
+ e.get(d.port2);
+ e.get(d.frequency);
+ e.get(d.pointNum);
+ return d;
+}
+static int16_t EncodeSpectrumAnalyzerResult(Protocol::SpectrumAnalyzerResult d, uint8_t *buf,
+ uint16_t bufSize) {
+ Encoder e(buf, bufSize);
+ e.add(d.port1);
+ e.add(d.port2);
+ e.add(d.frequency);
+ e.add(d.pointNum);
+ return e.getSize();
+}
+
static Protocol::FirmwarePacket DecodeFirmwarePacket(uint8_t *buf) {
Protocol::FirmwarePacket d;
// simple packet format, memcpy is faster than using the decoder
@@ -446,6 +490,12 @@ uint16_t Protocol::DecodeBuffer(uint8_t *buf, uint16_t len, PacketInfo *info) {
case PacketType::Generator:
info->generator = DecodeGeneratorSettings(&data[4]);
break;
+ case PacketType::SpectrumAnalyzerSettings:
+ info->spectrumSettings = DecodeSpectrumAnalyzerSettings(&data[4]);
+ break;
+ case PacketType::SpectrumAnalyzerResult:
+ info->spectrumResult = DecodeSpectrumAnalyzerResult(&data[4]);
+ break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
@@ -486,6 +536,12 @@ uint16_t Protocol::EncodePacket(PacketInfo packet, uint8_t *dest, uint16_t dests
case PacketType::Generator:
payload_size = EncodeGeneratorSettings(packet.generator, &dest[4], destsize - 8);
break;
+ case PacketType::SpectrumAnalyzerSettings:
+ payload_size = EncodeSpectrumAnalyzerSettings(packet.spectrumSettings, &dest[4], destsize - 8);
+ break;
+ case PacketType::SpectrumAnalyzerResult:
+ payload_size = EncodeSpectrumAnalyzerResult(packet.spectrumResult, &dest[4], destsize - 8);
+ break;
case PacketType::Ack:
case PacketType::PerformFirmwareUpdate:
case PacketType::ClearFlash:
diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp
index d5b5a80..dff2420 100644
--- a/Software/VNA_embedded/Application/Communication/Protocol.hpp
+++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp
@@ -102,10 +102,18 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
uint64_t f_start;
uint64_t f_stop;
uint32_t RBW;
+ uint16_t pointNum;
uint8_t WindowType :2;
uint8_t SignalID :1;
+ uint8_t Detector :3;
};
+using SpectrumAnalyzerResult = struct _spectrumAnalyzerResult {
+ float port1;
+ float port2;
+ uint64_t frequency;
+ uint16_t pointNum;
+};
static constexpr uint16_t FirmwareChunkSize = 256;
using FirmwarePacket = struct _firmwarePacket {
@@ -127,6 +135,8 @@ enum class PacketType : uint8_t {
Nack = 10,
Reference = 11,
Generator = 12,
+ SpectrumAnalyzerSettings = 13,
+ SpectrumAnalyzerResult = 14,
};
using PacketInfo = struct _packetinfo {
@@ -138,8 +148,10 @@ using PacketInfo = struct _packetinfo {
GeneratorSettings generator;
DeviceInfo info;
ManualControl manual;
- ManualStatus status;
FirmwarePacket firmware;
+ ManualStatus status;
+ SpectrumAnalyzerSettings spectrumSettings;
+ SpectrumAnalyzerResult spectrumResult;
};
};
diff --git a/Software/VNA_embedded/Application/Drivers/max2871.cpp b/Software/VNA_embedded/Application/Drivers/max2871.cpp
index 8b362c8..fe72405 100644
--- a/Software/VNA_embedded/Application/Drivers/max2871.cpp
+++ b/Software/VNA_embedded/Application/Drivers/max2871.cpp
@@ -4,7 +4,7 @@
#include "delay.hpp"
#include
-#define LOG_LEVEL LOG_LEVEL_WARN
+#define LOG_LEVEL LOG_LEVEL_ERR
#define LOG_MODULE "MAX2871"
#include "Log.h"
@@ -198,7 +198,7 @@ bool MAX2871::SetFrequency(uint64_t f) {
approx.num, approx.denom, abs(rem_f - rem_approx));
}
- uint64_t f_set = (uint64_t) N * f_PFD + (f_PFD * approx.num) / approx.denom;
+ uint64_t f_set = (uint64_t) N * f_PFD + rem_approx;
f_set /= (1UL << div);
// write values to registers
diff --git a/Software/VNA_embedded/Application/Drivers/max2871.hpp b/Software/VNA_embedded/Application/Drivers/max2871.hpp
index 1013e56..4648887 100644
--- a/Software/VNA_embedded/Application/Drivers/max2871.hpp
+++ b/Software/VNA_embedded/Application/Drivers/max2871.hpp
@@ -57,6 +57,9 @@ public:
uint32_t* GetRegisters() {
return regs;
}
+ uint64_t GetActualFrequency() {
+ return outputFrequency;
+ }
private:
static constexpr uint64_t MaxFreq = 6100000000; // 6GHz according to datasheet, but slight overclocking is possible
diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp
index b677eb4..62294c3 100644
--- a/Software/VNA_embedded/Application/Hardware.cpp
+++ b/Software/VNA_embedded/Application/Hardware.cpp
@@ -6,6 +6,7 @@
#include "Exti.hpp"
#include "VNA.hpp"
#include "Manual.hpp"
+#include "SpectrumAnalyzer.hpp"
#define LOG_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE "HW"
@@ -15,8 +16,6 @@ static uint32_t extOutFreq = 0;
static bool extRefInUse = false;
HW::Mode activeMode;
-static constexpr uint32_t IF1 = 60100000;
-static constexpr uint32_t IF2 = 250000;
static Protocol::ReferenceSettings ref;
using namespace HWHAL;
@@ -30,6 +29,9 @@ static void HaltedCallback() {
break;
}
}
+
+static HW::WorkRequest requestWork;
+
static void ReadComplete(FPGA::SamplingResult result) {
bool needs_work = false;
switch(activeMode) {
@@ -39,11 +41,14 @@ static void ReadComplete(FPGA::SamplingResult result) {
case HW::Mode::Manual:
needs_work = Manual::MeasurementDone(result);
break;
+ case HW::Mode::SA:
+ needs_work = SA::MeasurementDone(result);
+ break;
default:
break;
}
- if(needs_work) {
- HAL_NVIC_SetPendingIRQ(COMP4_IRQn);
+ if(needs_work && requestWork) {
+ requestWork();
}
}
@@ -51,9 +56,7 @@ static void FPGA_Interrupt(void*) {
FPGA::InitiateSampleRead(ReadComplete);
}
-/* low priority interrupt to handle additional workload after FPGA interrupt */
-extern "C" {
-void COMP4_IRQHandler(void) {
+void HW::Work() {
switch(activeMode) {
case HW::Mode::VNA:
VNA::Work();
@@ -61,16 +64,17 @@ void COMP4_IRQHandler(void) {
case HW::Mode::Manual:
Manual::Work();
break;
+ case HW::Mode::SA:
+ SA::Work();
+ break;
default:
break;
}
}
-}
-bool HW::Init() {
+bool HW::Init(WorkRequest wr) {
+ requestWork = wr;
LOG_DEBUG("Initializing...");
- HAL_NVIC_SetPriority(COMP4_IRQn, 15, 0);
- HAL_NVIC_EnableIRQ(COMP4_IRQn);
activeMode = Mode::Idle;
@@ -179,10 +183,9 @@ void HW::SetMode(Mode mode) {
default:
break;
}
- if(activeMode == Mode::Manual && mode != Mode::Idle) {
- // do a full initialization when switching from manual mode to anything else than idle
- // (making sure that any changes made in manual mode are reverted)
- HW::Init();
+ if(mode != Mode::Idle && activeMode != Mode::Idle) {
+ // do a full initialization when switching directly between modes
+ HW::Init(requestWork);
}
SetIdle();
activeMode = mode;
diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp
index e5cab3a..0b6dde7 100644
--- a/Software/VNA_embedded/Application/Hardware.hpp
+++ b/Software/VNA_embedded/Application/Hardware.hpp
@@ -5,6 +5,10 @@
namespace HW {
+static constexpr uint32_t ADCSamplerate = 914000;
+static constexpr uint32_t IF1 = 60100000;
+static constexpr uint32_t IF2 = 250000;
+
enum class Mode {
Idle,
Manual,
@@ -12,9 +16,12 @@ enum class Mode {
SA,
};
-bool Init();
+using WorkRequest = void (*)(void);
+
+bool Init(WorkRequest wr);
void SetMode(Mode mode);
void SetIdle();
+void Work();
bool GetTemps(uint8_t *source, uint8_t *lo);
void fillDeviceInfo(Protocol::DeviceInfo *info);
diff --git a/Software/VNA_embedded/Application/Manual.cpp b/Software/VNA_embedded/Application/Manual.cpp
index 6d0890e..b3ca07b 100644
--- a/Software/VNA_embedded/Application/Manual.cpp
+++ b/Software/VNA_embedded/Application/Manual.cpp
@@ -97,6 +97,7 @@ void Manual::Work() {
}
Protocol::PacketInfo p;
p.type = Protocol::PacketType::Status;
+ p.status = status;
uint16_t isr_flags = FPGA::GetStatus();
if (!(isr_flags & 0x0002)) {
p.status.source_locked = 1;
diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
new file mode 100644
index 0000000..8fec47e
--- /dev/null
+++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
@@ -0,0 +1,207 @@
+#include "SpectrumAnalyzer.hpp"
+#include "Hardware.hpp"
+#include "HW_HAL.hpp"
+#include
+#include
+#include "Communication.h"
+
+#define LOG_LEVEL LOG_LEVEL_DEBUG
+#define LOG_MODULE "SA"
+#include "Log.h"
+
+static Protocol::SpectrumAnalyzerSettings s;
+static uint32_t pointCnt;
+static uint32_t points;
+static uint32_t binSize;
+static uint8_t signalIDstep;
+static uint32_t sampleNum;
+static Protocol::PacketInfo p;
+static bool active = false;
+
+static float port1Measurement, port2Measurement;
+
+using namespace HWHAL;
+
+static void StartNextSample() {
+ uint64_t freq = s.f_start + (s.f_stop - s.f_start) * pointCnt / (points - 1);
+ uint64_t LO1freq;
+ uint32_t LO2freq;
+ switch(signalIDstep) {
+ case 0:
+ default:
+ // reset minimum amplitudes in first signal ID step
+ port1Measurement = std::numeric_limits::max();
+ port2Measurement = std::numeric_limits::max();
+ LO1freq = freq + HW::IF1;
+ LO2freq = HW::IF1 - HW::IF2;
+ break;
+ case 1:
+ LO1freq = freq - HW::IF1;
+ LO2freq = HW::IF1 - HW::IF2;
+ break;
+ case 2:
+ LO1freq = freq + HW::IF1;
+ LO2freq = HW::IF1 + HW::IF2;
+ break;
+ case 3:
+ LO1freq = freq - HW::IF1;
+ LO2freq = HW::IF1 + HW::IF2;
+ break;
+ }
+ LO1.SetFrequency(LO1freq);
+ // LO1 is not able to reach all frequencies with the required precision, adjust LO2 to account for deviation
+ int32_t LO1deviation = (int64_t) LO1.GetActualFrequency() - LO1freq;
+ LO2freq += LO1deviation;
+ // Adjust LO2 PLL
+ // Generate second LO with Si5351
+ Si5351.SetCLK(SiChannel::Port1LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
+ Si5351.SetCLK(SiChannel::Port2LO2, LO2freq, Si5351C::PLL::B, Si5351C::DriveStrength::mA2);
+ // 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::StartSweep();
+}
+
+void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) {
+ LOG_DEBUG("Setting up...");
+ s = settings;
+ HW::SetMode(HW::Mode::SA);
+ FPGA::AbortSweep();
+ FPGA::SetMode(FPGA::Mode::FPGA);
+ // 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 amount of required points
+ points = (s.f_stop - s.f_start) / s.RBW;
+ // adjust to integer multiple of requested result points (in order to have the same amount of measurements in each bin)
+ points += s.pointNum - points % s.pointNum;
+ binSize = points / s.pointNum;
+ LOG_DEBUG("%u displayed points, resulting in %lu points and bins of size %u", s.pointNum, points, binSize);
+ // 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;
+ // round up to next multiple of 128
+ sampleNum += 128 - sampleNum%128;
+ FPGA::SetSamplesPerPoint(sampleNum);
+ // set initial state
+ pointCnt = 0;
+ // enable the required hardware resources
+ Si5351.Enable(SiChannel::Port1LO2);
+ Si5351.Enable(SiChannel::Port2LO2);
+ 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::Port1Mixer);
+ FPGA::Enable(FPGA::Periphery::Port2Mixer);
+ active = true;
+ StartNextSample();
+}
+
+bool SA::MeasurementDone(FPGA::SamplingResult result) {
+ if(!active) {
+ return false;
+ }
+ float port1 = abs(std::complex(result.P1I, result.P1Q))/sampleNum;
+ float port2 = abs(std::complex(result.P2I, result.P2Q))/sampleNum;
+ if(port1 < port1Measurement) {
+ port1Measurement = port1;
+ }
+ if(port2 < port2Measurement) {
+ port2Measurement = port2;
+ }
+ // trigger work function
+ return true;
+}
+
+void SA::Work() {
+ if(!active) {
+ return;
+ }
+ if(!s.SignalID || signalIDstep >= 3) {
+ // this measurement point is done, handle result according to detector
+ uint16_t binIndex = pointCnt / binSize;
+ uint32_t pointInBin = pointCnt % binSize;
+ bool lastPointInBin = pointInBin >= binSize - 1;
+ auto det = (Detector) s.Detector;
+ if(det == Detector::Normal) {
+ det = binIndex & 0x01 ? Detector::PosPeak : Detector::NegPeak;
+ }
+ switch(det) {
+ case Detector::PosPeak:
+ if(pointInBin == 0) {
+ p.spectrumResult.port1 = std::numeric_limits::min();
+ p.spectrumResult.port2 = std::numeric_limits::min();
+ }
+ if(port1Measurement > p.spectrumResult.port1) {
+ p.spectrumResult.port1 = port1Measurement;
+ }
+ if(port2Measurement > p.spectrumResult.port2) {
+ p.spectrumResult.port2 = port2Measurement;
+ }
+ break;
+ case Detector::NegPeak:
+ if(pointInBin == 0) {
+ p.spectrumResult.port1 = std::numeric_limits::max();
+ p.spectrumResult.port2 = std::numeric_limits::max();
+ }
+ if(port1Measurement < p.spectrumResult.port1) {
+ p.spectrumResult.port1 = port1Measurement;
+ }
+ if(port2Measurement < p.spectrumResult.port2) {
+ p.spectrumResult.port2 = port2Measurement;
+ }
+ break;
+ case Detector::Sample:
+ if(pointInBin <= binSize / 2) {
+ // still in first half of bin, simply overwrite
+ p.spectrumResult.port1 = port1Measurement;
+ p.spectrumResult.port2 = port2Measurement;
+ }
+ break;
+ case Detector::Average:
+ if(pointInBin == 0) {
+ p.spectrumResult.port1 = 0;
+ p.spectrumResult.port2 = 0;
+ }
+ p.spectrumResult.port1 += port1Measurement;
+ p.spectrumResult.port2 += port2Measurement;
+ if(lastPointInBin) {
+ // calculate average
+ p.spectrumResult.port1 /= binSize;
+ p.spectrumResult.port2 /= binSize;
+ }
+ break;
+ case Detector::Normal:
+ // nothing to do, normal detector handled by PosPeak or NegPeak in each sample
+ break;
+ }
+ if(lastPointInBin) {
+ // Send result to application
+ p.type = Protocol::PacketType::SpectrumAnalyzerResult;
+ // measurements are already up to date, fill remaining fields
+ p.spectrumResult.pointNum = binIndex;
+ p.spectrumResult.frequency = s.f_start + (s.f_stop - s.f_start) * binIndex / (s.pointNum - 1);
+ Communication::Send(p);
+ }
+ // setup for next step
+ signalIDstep = 0;
+ if(pointCnt < points - 1) {
+ pointCnt++;
+ } else {
+ pointCnt = 0;
+ }
+ } else {
+ // more measurements required for signal ID
+ signalIDstep++;
+ }
+ StartNextSample();
+}
+
+void SA::Stop() {
+ active = false;
+ FPGA::AbortSweep();
+}
diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.hpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.hpp
new file mode 100644
index 0000000..9219133
--- /dev/null
+++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "Protocol.hpp"
+#include "FPGA/FPGA.hpp"
+
+namespace SA {
+
+enum class Detector {
+ PosPeak = 0x00,
+ NegPeak = 0x01,
+ Sample = 0x02,
+ Normal = 0x03,
+ Average = 0x04,
+};
+
+void Setup(Protocol::SpectrumAnalyzerSettings settings);
+bool MeasurementDone(FPGA::SamplingResult result);
+void Work();
+void Stop();
+
+}
diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp
index 6690e6a..f9323d2 100644
--- a/Software/VNA_embedded/Application/VNA.cpp
+++ b/Software/VNA_embedded/Application/VNA.cpp
@@ -55,7 +55,7 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) {
uint16_t points = settings.points <= FPGA::MaxPoints ? settings.points : FPGA::MaxPoints;
// Configure sweep
FPGA::SetNumberOfPoints(points);
- uint32_t samplesPerPoint = (1000000 / s.if_bandwidth);
+ uint32_t samplesPerPoint = (HW::ADCSamplerate / s.if_bandwidth);
// round up to next multiple of 128 (128 samples are spread across 35 IF2 periods)
samplesPerPoint = ((uint32_t) ((samplesPerPoint + 127) / 128)) * 128;
// has to be one less than actual number of samples