proof-of-concept spectrum analyzer mode
This commit is contained in:
parent
76875c2316
commit
38e73365df
@ -201,6 +201,7 @@ begin
|
|||||||
clk_cnt <= 0;
|
clk_cnt <= 0;
|
||||||
sample_cnt <= 0;
|
sample_cnt <= 0;
|
||||||
window_sample_cnt <= 0;
|
window_sample_cnt <= 0;
|
||||||
|
window_index <= (others => '0');
|
||||||
phase <= (others => '0');
|
phase <= (others => '0');
|
||||||
else
|
else
|
||||||
-- when not idle, generate pulses for ADCs
|
-- when not idle, generate pulses for ADCs
|
||||||
|
@ -266,7 +266,7 @@
|
|||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
</transform>
|
</transform>
|
||||||
<transform xil_pn:end_ts="1600270780" xil_pn:in_ck="-1505308035655400832" xil_pn:name="TRANEXT_xstsynthesize_spartan6" xil_pn:prop_ck="3256065936432453276" xil_pn:start_ts="1600270761">
|
<transform xil_pn:end_ts="1600349756" xil_pn:in_ck="-1505308035655400832" xil_pn:name="TRANEXT_xstsynthesize_spartan6" xil_pn:prop_ck="3256065936432453276" xil_pn:start_ts="1600349735">
|
||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="WarningsGenerated"/>
|
<status xil_pn:value="WarningsGenerated"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
@ -288,7 +288,7 @@
|
|||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
</transform>
|
</transform>
|
||||||
<transform xil_pn:end_ts="1600270786" xil_pn:in_ck="490340488621696080" xil_pn:name="TRANEXT_ngdbuild_FPGA" xil_pn:prop_ck="4604875190571501774" xil_pn:start_ts="1600270780">
|
<transform xil_pn:end_ts="1600349762" xil_pn:in_ck="490340488621696080" xil_pn:name="TRANEXT_ngdbuild_FPGA" xil_pn:prop_ck="4604875190571501774" xil_pn:start_ts="1600349756">
|
||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
<outfile xil_pn:name="_ngo"/>
|
<outfile xil_pn:name="_ngo"/>
|
||||||
@ -297,7 +297,7 @@
|
|||||||
<outfile xil_pn:name="top.ngd"/>
|
<outfile xil_pn:name="top.ngd"/>
|
||||||
<outfile xil_pn:name="top_ngdbuild.xrpt"/>
|
<outfile xil_pn:name="top_ngdbuild.xrpt"/>
|
||||||
</transform>
|
</transform>
|
||||||
<transform xil_pn:end_ts="1600270837" xil_pn:in_ck="8512332261572065657" xil_pn:name="TRANEXT_map_spartan6" xil_pn:prop_ck="1448924893915930207" xil_pn:start_ts="1600270786">
|
<transform xil_pn:end_ts="1600349804" xil_pn:in_ck="8512332261572065657" xil_pn:name="TRANEXT_map_spartan6" xil_pn:prop_ck="1448924893915930207" xil_pn:start_ts="1600349762">
|
||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="WarningsGenerated"/>
|
<status xil_pn:value="WarningsGenerated"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
@ -311,7 +311,7 @@
|
|||||||
<outfile xil_pn:name="top_summary.xml"/>
|
<outfile xil_pn:name="top_summary.xml"/>
|
||||||
<outfile xil_pn:name="top_usage.xml"/>
|
<outfile xil_pn:name="top_usage.xml"/>
|
||||||
</transform>
|
</transform>
|
||||||
<transform xil_pn:end_ts="1600270864" xil_pn:in_ck="1117507038335044978" xil_pn:name="TRANEXT_par_spartan6" xil_pn:prop_ck="93661965788626211" xil_pn:start_ts="1600270837">
|
<transform xil_pn:end_ts="1600349833" xil_pn:in_ck="1117507038335044978" xil_pn:name="TRANEXT_par_spartan6" xil_pn:prop_ck="93661965788626211" xil_pn:start_ts="1600349804">
|
||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
<outfile xil_pn:name="_xmsgs/par.xmsgs"/>
|
<outfile xil_pn:name="_xmsgs/par.xmsgs"/>
|
||||||
@ -325,7 +325,7 @@
|
|||||||
<outfile xil_pn:name="top_pad.txt"/>
|
<outfile xil_pn:name="top_pad.txt"/>
|
||||||
<outfile xil_pn:name="top_par.xrpt"/>
|
<outfile xil_pn:name="top_par.xrpt"/>
|
||||||
</transform>
|
</transform>
|
||||||
<transform xil_pn:end_ts="1600270876" xil_pn:in_ck="154288912438" xil_pn:name="TRANEXT_bitFile_spartan6" xil_pn:prop_ck="3274353840855015246" xil_pn:start_ts="1600270864">
|
<transform xil_pn:end_ts="1600349846" xil_pn:in_ck="154288912438" xil_pn:name="TRANEXT_bitFile_spartan6" xil_pn:prop_ck="3274353840855015246" xil_pn:start_ts="1600349833">
|
||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="WarningsGenerated"/>
|
<status xil_pn:value="WarningsGenerated"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
@ -371,7 +371,7 @@
|
|||||||
<status xil_pn:value="OutputChanged"/>
|
<status xil_pn:value="OutputChanged"/>
|
||||||
<status xil_pn:value="OutputRemoved"/>
|
<status xil_pn:value="OutputRemoved"/>
|
||||||
</transform>
|
</transform>
|
||||||
<transform xil_pn:end_ts="1600270864" xil_pn:in_ck="8512326635937592693" xil_pn:name="TRAN_postRouteTrce" xil_pn:prop_ck="445577401284416184" xil_pn:start_ts="1600270857">
|
<transform xil_pn:end_ts="1600349833" xil_pn:in_ck="8512326635937592693" xil_pn:name="TRAN_postRouteTrce" xil_pn:prop_ck="445577401284416184" xil_pn:start_ts="1600349826">
|
||||||
<status xil_pn:value="SuccessfullyRun"/>
|
<status xil_pn:value="SuccessfullyRun"/>
|
||||||
<status xil_pn:value="ReadyToRun"/>
|
<status xil_pn:value="ReadyToRun"/>
|
||||||
<outfile xil_pn:name="_xmsgs/trce.xmsgs"/>
|
<outfile xil_pn:name="_xmsgs/trce.xmsgs"/>
|
||||||
|
Binary file not shown.
@ -15,6 +15,7 @@ HEADERS += \
|
|||||||
Device/manualcontroldialog.h \
|
Device/manualcontroldialog.h \
|
||||||
Generator/generator.h \
|
Generator/generator.h \
|
||||||
Generator/signalgenwidget.h \
|
Generator/signalgenwidget.h \
|
||||||
|
SpectrumAnalyzer/spectrumanalyzer.h \
|
||||||
Tools/eseries.h \
|
Tools/eseries.h \
|
||||||
Tools/impedancematchdialog.h \
|
Tools/impedancematchdialog.h \
|
||||||
Traces/bodeplotaxisdialog.h \
|
Traces/bodeplotaxisdialog.h \
|
||||||
@ -57,6 +58,7 @@ SOURCES += \
|
|||||||
Device/manualcontroldialog.cpp \
|
Device/manualcontroldialog.cpp \
|
||||||
Generator/generator.cpp \
|
Generator/generator.cpp \
|
||||||
Generator/signalgenwidget.cpp \
|
Generator/signalgenwidget.cpp \
|
||||||
|
SpectrumAnalyzer/spectrumanalyzer.cpp \
|
||||||
Tools/eseries.cpp \
|
Tools/eseries.cpp \
|
||||||
Tools/impedancematchdialog.cpp \
|
Tools/impedancematchdialog.cpp \
|
||||||
Traces/bodeplotaxisdialog.cpp \
|
Traces/bodeplotaxisdialog.cpp \
|
||||||
|
@ -203,6 +203,14 @@ bool Device::Configure(Protocol::SweepSettings settings)
|
|||||||
return SendPacket(p);
|
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)
|
bool Device::SetManual(Protocol::ManualControl manual)
|
||||||
{
|
{
|
||||||
Protocol::PacketInfo p;
|
Protocol::PacketInfo p;
|
||||||
@ -364,6 +372,9 @@ void Device::ReceivedData()
|
|||||||
case Protocol::PacketType::Status:
|
case Protocol::PacketType::Status:
|
||||||
emit ManualStatusReceived(packet.status);
|
emit ManualStatusReceived(packet.status);
|
||||||
break;
|
break;
|
||||||
|
case Protocol::PacketType::SpectrumAnalyzerResult:
|
||||||
|
emit SpectrumResultReceived(packet.spectrumResult);
|
||||||
|
break;
|
||||||
case Protocol::PacketType::DeviceInfo:
|
case Protocol::PacketType::DeviceInfo:
|
||||||
lastInfo = packet.info;
|
lastInfo = packet.info;
|
||||||
lastInfoValid = true;
|
lastInfoValid = true;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
Q_DECLARE_METATYPE(Protocol::Datapoint);
|
Q_DECLARE_METATYPE(Protocol::Datapoint);
|
||||||
Q_DECLARE_METATYPE(Protocol::ManualStatus);
|
Q_DECLARE_METATYPE(Protocol::ManualStatus);
|
||||||
Q_DECLARE_METATYPE(Protocol::DeviceInfo);
|
Q_DECLARE_METATYPE(Protocol::DeviceInfo);
|
||||||
|
Q_DECLARE_METATYPE(Protocol::SpectrumAnalyzerResult);
|
||||||
|
|
||||||
class USBInBuffer : public QObject {
|
class USBInBuffer : public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
@ -57,6 +58,7 @@ public:
|
|||||||
~Device();
|
~Device();
|
||||||
bool SendPacket(Protocol::PacketInfo packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 10);
|
bool SendPacket(Protocol::PacketInfo packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 10);
|
||||||
bool Configure(Protocol::SweepSettings settings);
|
bool Configure(Protocol::SweepSettings settings);
|
||||||
|
bool Configure(Protocol::SpectrumAnalyzerSettings settings);
|
||||||
bool SetManual(Protocol::ManualControl manual);
|
bool SetManual(Protocol::ManualControl manual);
|
||||||
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
|
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
|
||||||
bool SendCommandWithoutPayload(Protocol::PacketType type);
|
bool SendCommandWithoutPayload(Protocol::PacketType type);
|
||||||
@ -69,6 +71,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void DatapointReceived(Protocol::Datapoint);
|
void DatapointReceived(Protocol::Datapoint);
|
||||||
void ManualStatusReceived(Protocol::ManualStatus);
|
void ManualStatusReceived(Protocol::ManualStatus);
|
||||||
|
void SpectrumResultReceived(Protocol::SpectrumAnalyzerResult);
|
||||||
void DeviceInfoUpdated();
|
void DeviceInfoUpdated();
|
||||||
void ConnectionLost();
|
void ConnectionLost();
|
||||||
void AckReceived();
|
void AckReceived();
|
||||||
|
371
Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
Normal file
371
Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
#include "spectrumanalyzer.h"
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <math.h>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QActionGroup>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFile>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <QDateTime>
|
||||||
|
#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 <QDockWidget>
|
||||||
|
#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 <QDesktopWidget>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QActionGroup>
|
||||||
|
|
||||||
|
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<int>(&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<int>(&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<Protocol::SpectrumAnalyzerResult>("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<unsigned long long>(settings.f_start));
|
||||||
|
// s.setValue("SweepStop", static_cast<unsigned long long>(settings.f_stop));
|
||||||
|
// s.setValue("SweepBandwidth", settings.if_bandwidth);
|
||||||
|
// s.setValue("SweepPoints", settings.points);
|
||||||
|
// s.setValue("SweepLevel", (double) settings.cdbm_excitation / 100.0);
|
||||||
|
}
|
63
Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h
Normal file
63
Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef SPECTRUMANALYZER_H
|
||||||
|
#define SPECTRUMANALYZER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QWidget>
|
||||||
|
#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
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Trace::Trace(QString name, QColor color)
|
Trace::Trace(QString name, QColor color, LiveParameter live)
|
||||||
: _name(name),
|
: _name(name),
|
||||||
_color(color),
|
_color(color),
|
||||||
_liveType(LivedataType::Overwrite),
|
_liveType(LivedataType::Overwrite),
|
||||||
|
_liveParam(live),
|
||||||
reflection(true),
|
reflection(true),
|
||||||
visible(true),
|
visible(true),
|
||||||
paused(false),
|
paused(false),
|
||||||
|
@ -21,7 +21,16 @@ public:
|
|||||||
std::complex<double> S;
|
std::complex<double> 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();
|
~Trace();
|
||||||
|
|
||||||
enum class LivedataType {
|
enum class LivedataType {
|
||||||
@ -29,12 +38,7 @@ public:
|
|||||||
MaxHold,
|
MaxHold,
|
||||||
MinHold,
|
MinHold,
|
||||||
};
|
};
|
||||||
enum class LiveParameter {
|
|
||||||
S11,
|
|
||||||
S12,
|
|
||||||
S21,
|
|
||||||
S22,
|
|
||||||
};
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
void addData(Data d);
|
void addData(Data d);
|
||||||
|
@ -58,11 +58,32 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
|
|||||||
case Trace::LivedataType::MinHold: ui->CLiveType->setCurrentIndex(2); break;
|
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()) {
|
switch(t.liveParameter()) {
|
||||||
case Trace::LiveParameter::S11: ui->CLiveParam->setCurrentIndex(0); break;
|
case Trace::LiveParameter::S11: ui->CLiveParam->setCurrentIndex(0); break;
|
||||||
case Trace::LiveParameter::S12: ui->CLiveParam->setCurrentIndex(1); break;
|
case Trace::LiveParameter::S12: ui->CLiveParam->setCurrentIndex(1); break;
|
||||||
case Trace::LiveParameter::S21: ui->CLiveParam->setCurrentIndex(2); break;
|
case Trace::LiveParameter::S21: ui->CLiveParam->setCurrentIndex(2); break;
|
||||||
case Trace::LiveParameter::S22: ui->CLiveParam->setCurrentIndex(3); 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<int>(&QButtonGroup::buttonClicked), updateFileStatus);
|
connect(ui->GSource, qOverload<int>(&QButtonGroup::buttonClicked), updateFileStatus);
|
||||||
@ -100,11 +121,18 @@ void TraceEditDialog::on_buttonBox_accepted()
|
|||||||
case 1: type = Trace::LivedataType::MaxHold; break;
|
case 1: type = Trace::LivedataType::MaxHold; break;
|
||||||
case 2: type = Trace::LivedataType::MinHold; break;
|
case 2: type = Trace::LivedataType::MinHold; break;
|
||||||
}
|
}
|
||||||
switch(ui->CLiveParam->currentIndex()) {
|
if(VNAtrace) {
|
||||||
case 0: param = Trace::LiveParameter::S11; break;
|
switch(ui->CLiveParam->currentIndex()) {
|
||||||
case 1: param = Trace::LiveParameter::S12; break;
|
case 0: param = Trace::LiveParameter::S11; break;
|
||||||
case 2: param = Trace::LiveParameter::S21; break;
|
case 1: param = Trace::LiveParameter::S12; break;
|
||||||
case 3: param = Trace::LiveParameter::S22; 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);
|
trace.fromLivedata(type, param);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ private:
|
|||||||
void setColor(QColor c);
|
void setColor(QColor c);
|
||||||
Ui::TraceEditDialog *ui;
|
Ui::TraceEditDialog *ui;
|
||||||
Trace &trace;
|
Trace &trace;
|
||||||
|
bool VNAtrace;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRACEEDITDIALOG_H
|
#endif // TRACEEDITDIALOG_H
|
||||||
|
@ -119,28 +119,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QComboBox" name="CLiveParam">
|
<widget class="QComboBox" name="CLiveParam"/>
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>S11</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>S12</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>S21</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>S22</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -148,6 +148,27 @@ void TraceModel::addVNAData(Protocol::Datapoint d)
|
|||||||
case Trace::LiveParameter::S12: td.S = complex<double>(d.real_S12, d.imag_S12); break;
|
case Trace::LiveParameter::S12: td.S = complex<double>(d.real_S12, d.imag_S12); break;
|
||||||
case Trace::LiveParameter::S21: td.S = complex<double>(d.real_S21, d.imag_S21); break;
|
case Trace::LiveParameter::S21: td.S = complex<double>(d.real_S21, d.imag_S21); break;
|
||||||
case Trace::LiveParameter::S22: td.S = complex<double>(d.real_S22, d.imag_S22); break;
|
case Trace::LiveParameter::S22: td.S = complex<double>(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<double>(d.port1, 0); break;
|
||||||
|
case Trace::LiveParameter::Port2: td.S = complex<double>(d.port2, 0); break;
|
||||||
|
default:
|
||||||
|
// not a SA trace, skip
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
t->addData(td);
|
t->addData(td);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void clearVNAData();
|
void clearVNAData();
|
||||||
void addVNAData(Protocol::Datapoint d);
|
void addVNAData(Protocol::Datapoint d);
|
||||||
|
void addSAData(Protocol::SpectrumAnalyzerResult d);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Trace*> traces;
|
std::vector<Trace*> traces;
|
||||||
|
@ -7,10 +7,11 @@
|
|||||||
#include "traceexportdialog.h"
|
#include "traceexportdialog.h"
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
||||||
TraceWidget::TraceWidget(TraceModel &model, QWidget *parent) :
|
TraceWidget::TraceWidget(TraceModel &model, QWidget *parent, bool SA) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::TraceWidget),
|
ui(new Ui::TraceWidget),
|
||||||
model(model)
|
model(model),
|
||||||
|
SA(SA)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->view->setModel(&model);
|
ui->view->setModel(&model);
|
||||||
@ -27,7 +28,8 @@ TraceWidget::~TraceWidget()
|
|||||||
void TraceWidget::on_add_clicked()
|
void TraceWidget::on_add_clicked()
|
||||||
{
|
{
|
||||||
createCount++;
|
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));
|
t->setColor(QColor::fromHsl((createCount * 50) % 360, 250, 128));
|
||||||
model.addTrace(t);
|
model.addTrace(t);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class TraceWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TraceWidget(TraceModel &model, QWidget *parent = nullptr);
|
explicit TraceWidget(TraceModel &model, QWidget *parent = nullptr, bool SA = false);
|
||||||
~TraceWidget();
|
~TraceWidget();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@ -36,6 +36,7 @@ private:
|
|||||||
Ui::TraceWidget *ui;
|
Ui::TraceWidget *ui;
|
||||||
TraceModel &model;
|
TraceModel &model;
|
||||||
int createCount;
|
int createCount;
|
||||||
|
bool SA;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRACEWIDGET_H
|
#endif // TRACEWIDGET_H
|
||||||
|
@ -85,9 +85,6 @@ VNA::VNA(AppWindow *window)
|
|||||||
central->Child2()->Child1()->setPlot(tracebode2);
|
central->Child2()->Child1()->setPlot(tracebode2);
|
||||||
central->Child2()->Child2()->setPlot(tracesmith2);
|
central->Child2()->Child2()->setPlot(tracesmith2);
|
||||||
|
|
||||||
// central widget is constructed, mode can be finalized
|
|
||||||
finalize(central);
|
|
||||||
|
|
||||||
// Create menu entries and connections
|
// Create menu entries and connections
|
||||||
auto calMenu = new QMenu("Calibration");
|
auto calMenu = new QMenu("Calibration");
|
||||||
window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), calMenu);
|
window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), calMenu);
|
||||||
@ -437,6 +434,16 @@ VNA::VNA(AppWindow *window)
|
|||||||
SetIFBandwidth(pref.Startup.DefaultSweep.bandwidth);
|
SetIFBandwidth(pref.Startup.DefaultSweep.bandwidth);
|
||||||
SetPoints(pref.Startup.DefaultSweep.points);
|
SetPoints(pref.Startup.DefaultSweep.points);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set ObjectName for toolbars and docks
|
||||||
|
for(auto d : findChildren<QDockWidget*>()) {
|
||||||
|
d->setObjectName(d->windowTitle());
|
||||||
|
}
|
||||||
|
for(auto t : findChildren<QToolBar*>()) {
|
||||||
|
t->setObjectName(t->windowTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(central);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNA::deactivate()
|
void VNA::deactivate()
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include <mode.h>
|
#include <mode.h>
|
||||||
#include "VNA/vna.h"
|
#include "VNA/vna.h"
|
||||||
#include "Generator/generator.h"
|
#include "Generator/generator.h"
|
||||||
|
#include "SpectrumAnalyzer/spectrumanalyzer.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -71,6 +72,7 @@ AppWindow::AppWindow(QWidget *parent)
|
|||||||
CreateToolbars();
|
CreateToolbars();
|
||||||
auto logDock = new QDockWidget("Device Log");
|
auto logDock = new QDockWidget("Device Log");
|
||||||
logDock->setWidget(&deviceLog);
|
logDock->setWidget(&deviceLog);
|
||||||
|
logDock->setObjectName("Log Dock");
|
||||||
addDockWidget(Qt::BottomDockWidgetArea, logDock);
|
addDockWidget(Qt::BottomDockWidgetArea, logDock);
|
||||||
|
|
||||||
// fill toolbar/dock menu
|
// fill toolbar/dock menu
|
||||||
@ -88,6 +90,7 @@ AppWindow::AppWindow(QWidget *parent)
|
|||||||
setCentralWidget(central);
|
setCentralWidget(central);
|
||||||
auto vna = new VNA(this);
|
auto vna = new VNA(this);
|
||||||
new Generator(this);
|
new Generator(this);
|
||||||
|
new SpectrumAnalyzer(this);
|
||||||
// auto signalGenWidget = new Signalgenerator;
|
// auto signalGenWidget = new Signalgenerator;
|
||||||
// modeSGen = new GUIMode(this, "Signal Generator", signalGenWidget);
|
// modeSGen = new GUIMode(this, "Signal Generator", signalGenWidget);
|
||||||
|
|
||||||
@ -122,14 +125,6 @@ AppWindow::AppWindow(QWidget *parent)
|
|||||||
restoreGeometry(settings.value("geometry").toByteArray());
|
restoreGeometry(settings.value("geometry").toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set ObjectName for toolbars and docks
|
|
||||||
for(auto d : findChildren<QDockWidget*>()) {
|
|
||||||
d->setObjectName(d->windowTitle());
|
|
||||||
}
|
|
||||||
for(auto t : findChildren<QToolBar*>()) {
|
|
||||||
t->setObjectName(t->windowTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default mode
|
// Set default mode
|
||||||
vna->activate();
|
vna->activate();
|
||||||
|
|
||||||
@ -248,6 +243,7 @@ void AppWindow::CreateToolbars()
|
|||||||
connect(toolbars.reference.outputEnabled, &QCheckBox::clicked, this, &AppWindow::UpdateReference);
|
connect(toolbars.reference.outputEnabled, &QCheckBox::clicked, this, &AppWindow::UpdateReference);
|
||||||
|
|
||||||
addToolBar(tb_reference);
|
addToolBar(tb_reference);
|
||||||
|
tb_reference->setObjectName("Reference Toolbar");
|
||||||
}
|
}
|
||||||
|
|
||||||
Preferences &AppWindow::getPreferenceRef()
|
Preferences &AppWindow::getPreferenceRef()
|
||||||
|
@ -68,6 +68,40 @@ Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerResult d)
|
||||||
|
{
|
||||||
|
if (d.pointNum == avg.size()) {
|
||||||
|
// add moving average entry
|
||||||
|
deque<array<complex<double>, 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<complex<double>, 4> sample = {d.port1, d.port2, 0, 0};
|
||||||
|
deque->push_back(sample);
|
||||||
|
if(deque->size() > averages) {
|
||||||
|
deque->pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate average
|
||||||
|
complex<double> 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()
|
unsigned int Averaging::getLevel()
|
||||||
{
|
{
|
||||||
if(avg.size() > 0) {
|
if(avg.size() > 0) {
|
||||||
|
@ -13,6 +13,7 @@ public:
|
|||||||
void reset();
|
void reset();
|
||||||
void setAverages(unsigned int a);
|
void setAverages(unsigned int a);
|
||||||
Protocol::Datapoint process(Protocol::Datapoint d);
|
Protocol::Datapoint process(Protocol::Datapoint d);
|
||||||
|
Protocol::SpectrumAnalyzerResult process(Protocol::SpectrumAnalyzerResult d);
|
||||||
unsigned int getLevel();
|
unsigned int getLevel();
|
||||||
private:
|
private:
|
||||||
std::vector<std::deque<std::array<std::complex<double>, 4>>> avg;
|
std::vector<std::deque<std::array<std::complex<double>, 4>>> avg;
|
||||||
|
@ -68,7 +68,7 @@ void Mode::activate()
|
|||||||
|
|
||||||
// restore visibility of toolbars and docks
|
// restore visibility of toolbars and docks
|
||||||
// window->getUi()->menuDocks->clear();
|
// window->getUi()->menuDocks->clear();
|
||||||
for(auto d : window->findChildren<QDockWidget*>()) {
|
for(auto d : docks) {
|
||||||
// window->getUi()->menuDocks->addAction(d->toggleViewAction());
|
// window->getUi()->menuDocks->addAction(d->toggleViewAction());
|
||||||
bool hidden = settings.value("dock_"+name+"_"+d->windowTitle(), d->isHidden()).toBool();
|
bool hidden = settings.value("dock_"+name+"_"+d->windowTitle(), d->isHidden()).toBool();
|
||||||
if(hidden) {
|
if(hidden) {
|
||||||
@ -78,7 +78,7 @@ void Mode::activate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// window->getUi()->menuToolbars->clear();
|
// window->getUi()->menuToolbars->clear();
|
||||||
for(auto t : window->findChildren<QToolBar*>()) {
|
for(auto t : toolbars) {
|
||||||
// window->getUi()->menuToolbars->addAction(t->toggleViewAction());
|
// window->getUi()->menuToolbars->addAction(t->toggleViewAction());
|
||||||
bool hidden = settings.value("toolbar_"+name+"_"+t->windowTitle(), t->isHidden()).toBool();
|
bool hidden = settings.value("toolbar_"+name+"_"+t->windowTitle(), t->isHidden()).toBool();
|
||||||
if(hidden) {
|
if(hidden) {
|
||||||
@ -99,10 +99,10 @@ void Mode::deactivate()
|
|||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
// save dock/toolbar visibility
|
// save dock/toolbar visibility
|
||||||
for(auto d : window->findChildren<QDockWidget*>()) {
|
for(auto d : docks) {
|
||||||
settings.setValue("dock_"+name+"_"+d->windowTitle(), d->isHidden());
|
settings.setValue("dock_"+name+"_"+d->windowTitle(), d->isHidden());
|
||||||
}
|
}
|
||||||
for(auto t : window->findChildren<QToolBar*>()) {
|
for(auto t : toolbars) {
|
||||||
settings.setValue("toolbar_"+name+"_"+t->windowTitle(), t->isHidden());
|
settings.setValue("toolbar_"+name+"_"+t->windowTitle(), t->isHidden());
|
||||||
}
|
}
|
||||||
// settings.setValue("geometry_"+name, window->saveGeometry());
|
// settings.setValue("geometry_"+name, window->saveGeometry());
|
||||||
@ -133,6 +133,13 @@ void Mode::finalize(QWidget *centralWidget)
|
|||||||
{
|
{
|
||||||
central = centralWidget;
|
central = centralWidget;
|
||||||
window->getCentral()->addWidget(central);
|
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
|
// hide all mode specific GUI elements
|
||||||
for(auto t : toolbars) {
|
for(auto t : toolbars) {
|
||||||
t->hide();
|
t->hide();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "Hardware.hpp"
|
#include "Hardware.hpp"
|
||||||
#include "Manual.hpp"
|
#include "Manual.hpp"
|
||||||
#include "Generator.hpp"
|
#include "Generator.hpp"
|
||||||
|
#include "SpectrumAnalyzer.hpp"
|
||||||
|
|
||||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||||
#define LOG_MODULE "App"
|
#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_USB_PACKET 0x01
|
||||||
#define FLAG_DATAPOINT 0x02
|
#define FLAG_DATAPOINT 0x02
|
||||||
|
#define FLAG_WORK_REQUIRED 0x04
|
||||||
|
|
||||||
static void VNACallback(Protocol::Datapoint res) {
|
static void VNACallback(Protocol::Datapoint res) {
|
||||||
result = res;
|
result = res;
|
||||||
@ -50,6 +52,11 @@ static void USBPacketReceived(Protocol::PacketInfo p) {
|
|||||||
xTaskNotifyFromISR(handle, FLAG_USB_PACKET, eSetBits, &woken);
|
xTaskNotifyFromISR(handle, FLAG_USB_PACKET, eSetBits, &woken);
|
||||||
portYIELD_FROM_ISR(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() {
|
void App_Start() {
|
||||||
handle = xTaskGetCurrentTaskHandle();
|
handle = xTaskGetCurrentTaskHandle();
|
||||||
@ -90,7 +97,7 @@ void App_Start() {
|
|||||||
EN_6V_GPIO_Port->BSRR = EN_6V_Pin;
|
EN_6V_GPIO_Port->BSRR = EN_6V_Pin;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!HW::Init()) {
|
if (!HW::Init(HardwareWorkRequired)) {
|
||||||
LOG_CRIT("Initialization failed, unable to start");
|
LOG_CRIT("Initialization failed, unable to start");
|
||||||
LED::Error(4);
|
LED::Error(4);
|
||||||
}
|
}
|
||||||
@ -108,6 +115,9 @@ void App_Start() {
|
|||||||
uint32_t notification;
|
uint32_t notification;
|
||||||
if(xTaskNotifyWait(0x00, UINT32_MAX, ¬ification, 100) == pdPASS) {
|
if(xTaskNotifyWait(0x00, UINT32_MAX, ¬ification, 100) == pdPASS) {
|
||||||
// something happened
|
// something happened
|
||||||
|
if(notification & FLAG_WORK_REQUIRED) {
|
||||||
|
HW::Work();
|
||||||
|
}
|
||||||
if(notification & FLAG_DATAPOINT) {
|
if(notification & FLAG_DATAPOINT) {
|
||||||
Protocol::PacketInfo packet;
|
Protocol::PacketInfo packet;
|
||||||
packet.type = Protocol::PacketType::Datapoint;
|
packet.type = Protocol::PacketType::Datapoint;
|
||||||
@ -143,6 +153,12 @@ void App_Start() {
|
|||||||
Generator::Setup(packet.generator);
|
Generator::Setup(packet.generator);
|
||||||
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
|
Communication::SendWithoutPayload(Protocol::PacketType::Ack);
|
||||||
break;
|
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
|
#ifdef HAS_FLASH
|
||||||
case Protocol::PacketType::ClearFlash:
|
case Protocol::PacketType::ClearFlash:
|
||||||
HW::SetMode(HW::Mode::Idle);
|
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());
|
LOG_WARN("Timed out waiting for point, last received point was %d (Status 0x%04x)", result.pointNum, FPGA::GetStatus());
|
||||||
FPGA::AbortSweep();
|
FPGA::AbortSweep();
|
||||||
// restart the current sweep
|
// restart the current sweep
|
||||||
HW::Init();
|
HW::Init(HardwareWorkRequired);
|
||||||
HW::Ref::update();
|
HW::Ref::update();
|
||||||
VNA::Setup(settings, VNACallback);
|
VNA::Setup(settings, VNACallback);
|
||||||
sweepActive = true;
|
sweepActive = true;
|
||||||
|
@ -358,6 +358,50 @@ static int16_t EncodeManualControl(Protocol::ManualControl d, uint8_t *buf,
|
|||||||
return e.getSize();
|
return e.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Protocol::SpectrumAnalyzerSettings DecodeSpectrumAnalyzerSettings(uint8_t *buf) {
|
||||||
|
Protocol::SpectrumAnalyzerSettings d;
|
||||||
|
Decoder e(buf);
|
||||||
|
e.get<uint64_t>(d.f_start);
|
||||||
|
e.get<uint64_t>(d.f_stop);
|
||||||
|
e.get<uint32_t>(d.RBW);
|
||||||
|
e.get<uint16_t>(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<uint64_t>(d.f_start);
|
||||||
|
e.add<uint64_t>(d.f_stop);
|
||||||
|
e.add<uint32_t>(d.RBW);
|
||||||
|
e.add<uint16_t>(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<float>(d.port1);
|
||||||
|
e.get<float>(d.port2);
|
||||||
|
e.get<uint64_t>(d.frequency);
|
||||||
|
e.get<uint16_t>(d.pointNum);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
static int16_t EncodeSpectrumAnalyzerResult(Protocol::SpectrumAnalyzerResult d, uint8_t *buf,
|
||||||
|
uint16_t bufSize) {
|
||||||
|
Encoder e(buf, bufSize);
|
||||||
|
e.add<float>(d.port1);
|
||||||
|
e.add<float>(d.port2);
|
||||||
|
e.add<uint64_t>(d.frequency);
|
||||||
|
e.add<uint16_t>(d.pointNum);
|
||||||
|
return e.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
static Protocol::FirmwarePacket DecodeFirmwarePacket(uint8_t *buf) {
|
static Protocol::FirmwarePacket DecodeFirmwarePacket(uint8_t *buf) {
|
||||||
Protocol::FirmwarePacket d;
|
Protocol::FirmwarePacket d;
|
||||||
// simple packet format, memcpy is faster than using the decoder
|
// 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:
|
case PacketType::Generator:
|
||||||
info->generator = DecodeGeneratorSettings(&data[4]);
|
info->generator = DecodeGeneratorSettings(&data[4]);
|
||||||
break;
|
break;
|
||||||
|
case PacketType::SpectrumAnalyzerSettings:
|
||||||
|
info->spectrumSettings = DecodeSpectrumAnalyzerSettings(&data[4]);
|
||||||
|
break;
|
||||||
|
case PacketType::SpectrumAnalyzerResult:
|
||||||
|
info->spectrumResult = DecodeSpectrumAnalyzerResult(&data[4]);
|
||||||
|
break;
|
||||||
case PacketType::Ack:
|
case PacketType::Ack:
|
||||||
case PacketType::PerformFirmwareUpdate:
|
case PacketType::PerformFirmwareUpdate:
|
||||||
case PacketType::ClearFlash:
|
case PacketType::ClearFlash:
|
||||||
@ -486,6 +536,12 @@ uint16_t Protocol::EncodePacket(PacketInfo packet, uint8_t *dest, uint16_t dests
|
|||||||
case PacketType::Generator:
|
case PacketType::Generator:
|
||||||
payload_size = EncodeGeneratorSettings(packet.generator, &dest[4], destsize - 8);
|
payload_size = EncodeGeneratorSettings(packet.generator, &dest[4], destsize - 8);
|
||||||
break;
|
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::Ack:
|
||||||
case PacketType::PerformFirmwareUpdate:
|
case PacketType::PerformFirmwareUpdate:
|
||||||
case PacketType::ClearFlash:
|
case PacketType::ClearFlash:
|
||||||
|
@ -102,10 +102,18 @@ using SpectrumAnalyzerSettings = struct _spectrumAnalyzerSettings {
|
|||||||
uint64_t f_start;
|
uint64_t f_start;
|
||||||
uint64_t f_stop;
|
uint64_t f_stop;
|
||||||
uint32_t RBW;
|
uint32_t RBW;
|
||||||
|
uint16_t pointNum;
|
||||||
uint8_t WindowType :2;
|
uint8_t WindowType :2;
|
||||||
uint8_t SignalID :1;
|
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;
|
static constexpr uint16_t FirmwareChunkSize = 256;
|
||||||
using FirmwarePacket = struct _firmwarePacket {
|
using FirmwarePacket = struct _firmwarePacket {
|
||||||
@ -127,6 +135,8 @@ enum class PacketType : uint8_t {
|
|||||||
Nack = 10,
|
Nack = 10,
|
||||||
Reference = 11,
|
Reference = 11,
|
||||||
Generator = 12,
|
Generator = 12,
|
||||||
|
SpectrumAnalyzerSettings = 13,
|
||||||
|
SpectrumAnalyzerResult = 14,
|
||||||
};
|
};
|
||||||
|
|
||||||
using PacketInfo = struct _packetinfo {
|
using PacketInfo = struct _packetinfo {
|
||||||
@ -138,8 +148,10 @@ using PacketInfo = struct _packetinfo {
|
|||||||
GeneratorSettings generator;
|
GeneratorSettings generator;
|
||||||
DeviceInfo info;
|
DeviceInfo info;
|
||||||
ManualControl manual;
|
ManualControl manual;
|
||||||
ManualStatus status;
|
|
||||||
FirmwarePacket firmware;
|
FirmwarePacket firmware;
|
||||||
|
ManualStatus status;
|
||||||
|
SpectrumAnalyzerSettings spectrumSettings;
|
||||||
|
SpectrumAnalyzerResult spectrumResult;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "delay.hpp"
|
#include "delay.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#define LOG_LEVEL LOG_LEVEL_WARN
|
#define LOG_LEVEL LOG_LEVEL_ERR
|
||||||
#define LOG_MODULE "MAX2871"
|
#define LOG_MODULE "MAX2871"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ bool MAX2871::SetFrequency(uint64_t f) {
|
|||||||
approx.num, approx.denom, abs(rem_f - rem_approx));
|
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);
|
f_set /= (1UL << div);
|
||||||
|
|
||||||
// write values to registers
|
// write values to registers
|
||||||
|
@ -57,6 +57,9 @@ public:
|
|||||||
uint32_t* GetRegisters() {
|
uint32_t* GetRegisters() {
|
||||||
return regs;
|
return regs;
|
||||||
}
|
}
|
||||||
|
uint64_t GetActualFrequency() {
|
||||||
|
return outputFrequency;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
static constexpr uint64_t MaxFreq = 6100000000; // 6GHz according to datasheet, but slight overclocking is possible
|
static constexpr uint64_t MaxFreq = 6100000000; // 6GHz according to datasheet, but slight overclocking is possible
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "Exti.hpp"
|
#include "Exti.hpp"
|
||||||
#include "VNA.hpp"
|
#include "VNA.hpp"
|
||||||
#include "Manual.hpp"
|
#include "Manual.hpp"
|
||||||
|
#include "SpectrumAnalyzer.hpp"
|
||||||
|
|
||||||
#define LOG_LEVEL LOG_LEVEL_INFO
|
#define LOG_LEVEL LOG_LEVEL_INFO
|
||||||
#define LOG_MODULE "HW"
|
#define LOG_MODULE "HW"
|
||||||
@ -15,8 +16,6 @@ static uint32_t extOutFreq = 0;
|
|||||||
static bool extRefInUse = false;
|
static bool extRefInUse = false;
|
||||||
HW::Mode activeMode;
|
HW::Mode activeMode;
|
||||||
|
|
||||||
static constexpr uint32_t IF1 = 60100000;
|
|
||||||
static constexpr uint32_t IF2 = 250000;
|
|
||||||
static Protocol::ReferenceSettings ref;
|
static Protocol::ReferenceSettings ref;
|
||||||
|
|
||||||
using namespace HWHAL;
|
using namespace HWHAL;
|
||||||
@ -30,6 +29,9 @@ static void HaltedCallback() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HW::WorkRequest requestWork;
|
||||||
|
|
||||||
static void ReadComplete(FPGA::SamplingResult result) {
|
static void ReadComplete(FPGA::SamplingResult result) {
|
||||||
bool needs_work = false;
|
bool needs_work = false;
|
||||||
switch(activeMode) {
|
switch(activeMode) {
|
||||||
@ -39,11 +41,14 @@ static void ReadComplete(FPGA::SamplingResult result) {
|
|||||||
case HW::Mode::Manual:
|
case HW::Mode::Manual:
|
||||||
needs_work = Manual::MeasurementDone(result);
|
needs_work = Manual::MeasurementDone(result);
|
||||||
break;
|
break;
|
||||||
|
case HW::Mode::SA:
|
||||||
|
needs_work = SA::MeasurementDone(result);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(needs_work) {
|
if(needs_work && requestWork) {
|
||||||
HAL_NVIC_SetPendingIRQ(COMP4_IRQn);
|
requestWork();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,9 +56,7 @@ static void FPGA_Interrupt(void*) {
|
|||||||
FPGA::InitiateSampleRead(ReadComplete);
|
FPGA::InitiateSampleRead(ReadComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* low priority interrupt to handle additional workload after FPGA interrupt */
|
void HW::Work() {
|
||||||
extern "C" {
|
|
||||||
void COMP4_IRQHandler(void) {
|
|
||||||
switch(activeMode) {
|
switch(activeMode) {
|
||||||
case HW::Mode::VNA:
|
case HW::Mode::VNA:
|
||||||
VNA::Work();
|
VNA::Work();
|
||||||
@ -61,16 +64,17 @@ void COMP4_IRQHandler(void) {
|
|||||||
case HW::Mode::Manual:
|
case HW::Mode::Manual:
|
||||||
Manual::Work();
|
Manual::Work();
|
||||||
break;
|
break;
|
||||||
|
case HW::Mode::SA:
|
||||||
|
SA::Work();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool HW::Init() {
|
bool HW::Init(WorkRequest wr) {
|
||||||
|
requestWork = wr;
|
||||||
LOG_DEBUG("Initializing...");
|
LOG_DEBUG("Initializing...");
|
||||||
HAL_NVIC_SetPriority(COMP4_IRQn, 15, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(COMP4_IRQn);
|
|
||||||
|
|
||||||
activeMode = Mode::Idle;
|
activeMode = Mode::Idle;
|
||||||
|
|
||||||
@ -179,10 +183,9 @@ void HW::SetMode(Mode mode) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(activeMode == Mode::Manual && mode != Mode::Idle) {
|
if(mode != Mode::Idle && activeMode != Mode::Idle) {
|
||||||
// do a full initialization when switching from manual mode to anything else than idle
|
// do a full initialization when switching directly between modes
|
||||||
// (making sure that any changes made in manual mode are reverted)
|
HW::Init(requestWork);
|
||||||
HW::Init();
|
|
||||||
}
|
}
|
||||||
SetIdle();
|
SetIdle();
|
||||||
activeMode = mode;
|
activeMode = mode;
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
namespace HW {
|
namespace HW {
|
||||||
|
|
||||||
|
static constexpr uint32_t ADCSamplerate = 914000;
|
||||||
|
static constexpr uint32_t IF1 = 60100000;
|
||||||
|
static constexpr uint32_t IF2 = 250000;
|
||||||
|
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
Idle,
|
Idle,
|
||||||
Manual,
|
Manual,
|
||||||
@ -12,9 +16,12 @@ enum class Mode {
|
|||||||
SA,
|
SA,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Init();
|
using WorkRequest = void (*)(void);
|
||||||
|
|
||||||
|
bool Init(WorkRequest wr);
|
||||||
void SetMode(Mode mode);
|
void SetMode(Mode mode);
|
||||||
void SetIdle();
|
void SetIdle();
|
||||||
|
void Work();
|
||||||
|
|
||||||
bool GetTemps(uint8_t *source, uint8_t *lo);
|
bool GetTemps(uint8_t *source, uint8_t *lo);
|
||||||
void fillDeviceInfo(Protocol::DeviceInfo *info);
|
void fillDeviceInfo(Protocol::DeviceInfo *info);
|
||||||
|
@ -97,6 +97,7 @@ void Manual::Work() {
|
|||||||
}
|
}
|
||||||
Protocol::PacketInfo p;
|
Protocol::PacketInfo p;
|
||||||
p.type = Protocol::PacketType::Status;
|
p.type = Protocol::PacketType::Status;
|
||||||
|
p.status = status;
|
||||||
uint16_t isr_flags = FPGA::GetStatus();
|
uint16_t isr_flags = FPGA::GetStatus();
|
||||||
if (!(isr_flags & 0x0002)) {
|
if (!(isr_flags & 0x0002)) {
|
||||||
p.status.source_locked = 1;
|
p.status.source_locked = 1;
|
||||||
|
207
Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
Normal file
207
Software/VNA_embedded/Application/SpectrumAnalyzer.cpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#include "SpectrumAnalyzer.hpp"
|
||||||
|
#include "Hardware.hpp"
|
||||||
|
#include "HW_HAL.hpp"
|
||||||
|
#include <complex.h>
|
||||||
|
#include <limits>
|
||||||
|
#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<float>::max();
|
||||||
|
port2Measurement = std::numeric_limits<float>::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<float>(result.P1I, result.P1Q))/sampleNum;
|
||||||
|
float port2 = abs(std::complex<float>(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<float>::min();
|
||||||
|
p.spectrumResult.port2 = std::numeric_limits<float>::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<float>::max();
|
||||||
|
p.spectrumResult.port2 = std::numeric_limits<float>::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();
|
||||||
|
}
|
21
Software/VNA_embedded/Application/SpectrumAnalyzer.hpp
Normal file
21
Software/VNA_embedded/Application/SpectrumAnalyzer.hpp
Normal file
@ -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();
|
||||||
|
|
||||||
|
}
|
@ -55,7 +55,7 @@ bool VNA::Setup(Protocol::SweepSettings s, SweepCallback cb) {
|
|||||||
uint16_t points = settings.points <= FPGA::MaxPoints ? settings.points : FPGA::MaxPoints;
|
uint16_t points = settings.points <= FPGA::MaxPoints ? settings.points : FPGA::MaxPoints;
|
||||||
// Configure sweep
|
// Configure sweep
|
||||||
FPGA::SetNumberOfPoints(points);
|
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)
|
// round up to next multiple of 128 (128 samples are spread across 35 IF2 periods)
|
||||||
samplesPerPoint = ((uint32_t) ((samplesPerPoint + 127) / 128)) * 128;
|
samplesPerPoint = ((uint32_t) ((samplesPerPoint + 127) / 128)) * 128;
|
||||||
// has to be one less than actual number of samples
|
// has to be one less than actual number of samples
|
||||||
|
Loading…
Reference in New Issue
Block a user