Save/load trace and graph setup
This commit is contained in:
parent
b91f431473
commit
9ad8def2ea
@ -5,7 +5,6 @@ HEADERS += \
|
|||||||
Calibration/calibrationtracedialog.h \
|
Calibration/calibrationtracedialog.h \
|
||||||
Calibration/calkit.h \
|
Calibration/calkit.h \
|
||||||
Calibration/calkitdialog.h \
|
Calibration/calkitdialog.h \
|
||||||
Calibration/json.hpp \
|
|
||||||
Calibration/measurementmodel.h \
|
Calibration/measurementmodel.h \
|
||||||
Calibration/receivercaldialog.h \
|
Calibration/receivercaldialog.h \
|
||||||
Calibration/sourcecaldialog.h \
|
Calibration/sourcecaldialog.h \
|
||||||
@ -51,8 +50,10 @@ HEADERS += \
|
|||||||
VNA/vna.h \
|
VNA/vna.h \
|
||||||
appwindow.h \
|
appwindow.h \
|
||||||
averaging.h \
|
averaging.h \
|
||||||
|
json.hpp \
|
||||||
mode.h \
|
mode.h \
|
||||||
preferences.h \
|
preferences.h \
|
||||||
|
savable.h \
|
||||||
touchstone.h \
|
touchstone.h \
|
||||||
unit.h
|
unit.h
|
||||||
|
|
||||||
|
@ -28,6 +28,73 @@ TileWidget::~TileWidget()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileWidget::clear()
|
||||||
|
{
|
||||||
|
if(hasContent) {
|
||||||
|
delete content;
|
||||||
|
hasContent = false;
|
||||||
|
}
|
||||||
|
if(isSplit) {
|
||||||
|
delete child1;
|
||||||
|
delete child2;
|
||||||
|
isSplit = false;
|
||||||
|
delete splitter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json TileWidget::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["split"] = isSplit;
|
||||||
|
if(isSplit) {
|
||||||
|
j["orientation"] = splitter->orientation() == Qt::Horizontal ? "horizontal" : "vertical";
|
||||||
|
j["sizes"] = splitter->sizes();
|
||||||
|
j["tile1"] = child1->toJSON();
|
||||||
|
j["tile2"] = child2->toJSON();
|
||||||
|
}
|
||||||
|
if(hasContent) {
|
||||||
|
std::string plotname;
|
||||||
|
switch(content->getType()) {
|
||||||
|
case TracePlot::Type::SmithChart:
|
||||||
|
plotname = "smithchart";
|
||||||
|
break;
|
||||||
|
case TracePlot::Type::XYPlot:
|
||||||
|
plotname = "XY-plot";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j["plot"] = plotname;
|
||||||
|
j["plotsettings"] = content->toJSON();
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileWidget::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
// delete all childs before parsing json
|
||||||
|
clear();
|
||||||
|
bool split = j.value("split", false);
|
||||||
|
if(split) {
|
||||||
|
if(j["orientation"] == "horizontal") {
|
||||||
|
splitHorizontally();
|
||||||
|
} else {
|
||||||
|
splitVertically();
|
||||||
|
}
|
||||||
|
splitter->setSizes(j["sizes"]);
|
||||||
|
child1->fromJSON(j["tile1"]);
|
||||||
|
child2->fromJSON(j["tile2"]);
|
||||||
|
} else if(j.contains("plot")) {
|
||||||
|
// has a plot enabled
|
||||||
|
auto plotname = j["plot"];
|
||||||
|
if(plotname == "smithchart") {
|
||||||
|
content = new TraceSmithChart(model);
|
||||||
|
} else {
|
||||||
|
content = new TraceXYPlot(model);
|
||||||
|
}
|
||||||
|
setContent(content);
|
||||||
|
content->fromJSON(j["plotsettings"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TileWidget::splitVertically()
|
void TileWidget::splitVertically()
|
||||||
{
|
{
|
||||||
if(isSplit) {
|
if(isSplit) {
|
||||||
|
@ -5,12 +5,13 @@
|
|||||||
#include "Traces/traceplot.h"
|
#include "Traces/traceplot.h"
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include "Traces/tracemodel.h"
|
#include "Traces/tracemodel.h"
|
||||||
|
#include "savable.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class TileWidget;
|
class TileWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TileWidget : public QWidget
|
class TileWidget : public QWidget, public Savable
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -20,6 +21,12 @@ public:
|
|||||||
|
|
||||||
TileWidget *Child1() { return child1; };
|
TileWidget *Child1() { return child1; };
|
||||||
TileWidget *Child2() { return child2; };
|
TileWidget *Child2() { return child2; };
|
||||||
|
|
||||||
|
// closes all plots/childs, leaving only the tilewidget at the top
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
public slots:
|
public slots:
|
||||||
void splitVertically();
|
void splitVertically();
|
||||||
void splitHorizontally();
|
void splitHorizontally();
|
||||||
|
@ -10,6 +10,10 @@ public:
|
|||||||
Generator(AppWindow *window);
|
Generator(AppWindow *window);
|
||||||
void deactivate() override;
|
void deactivate() override;
|
||||||
void initializeDevice() override;
|
void initializeDevice() override;
|
||||||
|
|
||||||
|
// Nothing to do for now
|
||||||
|
virtual nlohmann::json toJSON() override {return nlohmann::json();};
|
||||||
|
virtual void fromJSON(nlohmann::json j) override {Q_UNUSED(j)};
|
||||||
private slots:
|
private slots:
|
||||||
void updateDevice();
|
void updateDevice();
|
||||||
private:
|
private:
|
||||||
|
@ -293,6 +293,24 @@ void SpectrumAnalyzer::initializeDevice()
|
|||||||
window->getDevice()->Configure(settings);
|
window->getDevice()->Configure(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json SpectrumAnalyzer::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["traces"] = traceModel.toJSON();
|
||||||
|
j["tiles"] = central->toJSON();
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpectrumAnalyzer::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
if(j.contains("traces")) {
|
||||||
|
traceModel.fromJSON(j["traces"]);
|
||||||
|
}
|
||||||
|
if(j.contains("tiles")) {
|
||||||
|
central->fromJSON(j["tiles"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
|
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
|
||||||
|
@ -17,6 +17,11 @@ public:
|
|||||||
|
|
||||||
void deactivate() override;
|
void deactivate() override;
|
||||||
void initializeDevice() override;
|
void initializeDevice() override;
|
||||||
|
|
||||||
|
// Only save/load user changeable stuff, no need to save the widgets/mode name etc.
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
|
void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
|
||||||
void StartImpedanceMatching();
|
void StartImpedanceMatching();
|
||||||
|
0
Software/PC_Application/TraceSetup
Normal file
0
Software/PC_Application/TraceSetup
Normal file
@ -55,6 +55,20 @@ QWidget *MedianFilter::createExplanationWidget()
|
|||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json MedianFilter::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["kernel"] = kernelSize;
|
||||||
|
j["order"] = order;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MedianFilter::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
kernelSize = j.value("kernel", 3);
|
||||||
|
order = j.value("order", Order::AbsoluteValue);
|
||||||
|
}
|
||||||
|
|
||||||
void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) {
|
void MedianFilter::inputSamplesChanged(unsigned int begin, unsigned int end) {
|
||||||
if(data.size() != input->rData().size()) {
|
if(data.size() != input->rData().size()) {
|
||||||
data.resize(input->rData().size());
|
data.resize(input->rData().size());
|
||||||
|
@ -17,6 +17,10 @@ public:
|
|||||||
|
|
||||||
static QWidget *createExplanationWidget();
|
static QWidget *createExplanationWidget();
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
Type getType() override {return Type::MedianFilter;};
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// a single value of the input data has changed, index determines which sample has changed
|
// a single value of the input data has changed, index determines which sample has changed
|
||||||
virtual void inputSamplesChanged(unsigned int begin, unsigned int end) override;
|
virtual void inputSamplesChanged(unsigned int begin, unsigned int end) override;
|
||||||
|
@ -111,6 +111,44 @@ QWidget *TDR::createExplanationWidget()
|
|||||||
return new QLabel("Test");
|
return new QLabel("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json TDR::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["bandpass_mode"] = mode == Mode::Bandpass;
|
||||||
|
j["window"] = window.toJSON();
|
||||||
|
if(mode == Mode::Lowpass) {
|
||||||
|
j["step_response"] = stepResponse;
|
||||||
|
if(stepResponse) {
|
||||||
|
j["automatic_DC"] = automaticDC;
|
||||||
|
if(!automaticDC) {
|
||||||
|
j["manual_DC_real"] = manualDC.real();
|
||||||
|
j["manual_DC_imag"] = manualDC.imag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TDR::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
if(j.value("bandpass_mode", true)) {
|
||||||
|
mode = Mode::Bandpass;
|
||||||
|
} else {
|
||||||
|
mode = Mode::Lowpass;
|
||||||
|
if(j.value("step_response", true)) {
|
||||||
|
stepResponse = true;
|
||||||
|
if(j.value("automatic_DC", true)) {
|
||||||
|
automaticDC = true;
|
||||||
|
} else {
|
||||||
|
automaticDC = false;
|
||||||
|
manualDC = complex<double>(j.value("manual_DC_real", 1.0), j.value("manual_DC_imag", 0.0));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stepResponse = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TDR::inputSamplesChanged(unsigned int begin, unsigned int end)
|
void TDR::inputSamplesChanged(unsigned int begin, unsigned int end)
|
||||||
{
|
{
|
||||||
Q_UNUSED(begin);
|
Q_UNUSED(begin);
|
||||||
|
@ -17,6 +17,10 @@ public:
|
|||||||
|
|
||||||
static QWidget* createExplanationWidget();
|
static QWidget* createExplanationWidget();
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
Type getType() override {return Type::TDR;};
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void inputSamplesChanged(unsigned int begin, unsigned int end) override;
|
void inputSamplesChanged(unsigned int begin, unsigned int end) override;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
|
#include "savable.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* How to implement a new type of math operation:
|
* How to implement a new type of math operation:
|
||||||
@ -34,6 +35,8 @@
|
|||||||
* Provide a hint by passing a short description string
|
* Provide a hint by passing a short description string
|
||||||
* error(): something went wrong (called with wrong type of data, mathematical error, ...).
|
* error(): something went wrong (called with wrong type of data, mathematical error, ...).
|
||||||
* Provide a hint by passing a short description string
|
* Provide a hint by passing a short description string
|
||||||
|
* e. getType(): return the type of the operation
|
||||||
|
* f. toJSON() and fromJSON(). Save/load all internal parameters
|
||||||
* 3. Add a new type to the Type enum for your operation
|
* 3. Add a new type to the Type enum for your operation
|
||||||
* 4. Extend the createMath(Type type) factory function to create an instance of your operation
|
* 4. Extend the createMath(Type type) factory function to create an instance of your operation
|
||||||
* 5. Add a static function "createExplanationWidget" which returns a QWidget explaining what your operation does.
|
* 5. Add a static function "createExplanationWidget" which returns a QWidget explaining what your operation does.
|
||||||
@ -43,7 +46,7 @@
|
|||||||
|
|
||||||
class Trace;
|
class Trace;
|
||||||
|
|
||||||
class TraceMath : public QObject {
|
class TraceMath : public QObject, public Savable {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TraceMath();
|
TraceMath();
|
||||||
@ -98,6 +101,7 @@ public:
|
|||||||
std::vector<Data>& rData() { return data;};
|
std::vector<Data>& rData() { return data;};
|
||||||
Status getStatus() const;
|
Status getStatus() const;
|
||||||
QString getStatusDescription() const;
|
QString getStatusDescription() const;
|
||||||
|
virtual Type getType() = 0;
|
||||||
|
|
||||||
// returns the trace this math operation is attached to
|
// returns the trace this math operation is attached to
|
||||||
Trace* root();
|
Trace* root();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QFormLayout>
|
#include <QFormLayout>
|
||||||
#include "CustomWidgets/siunitedit.h"
|
#include "CustomWidgets/siunitedit.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
QString WindowFunction::typeToName(WindowFunction::Type type)
|
QString WindowFunction::typeToName(WindowFunction::Type type)
|
||||||
{
|
{
|
||||||
@ -108,6 +109,45 @@ QString WindowFunction::getDescription()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json WindowFunction::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["type"] = typeToName(type).toStdString();
|
||||||
|
// add additional parameter if type has one
|
||||||
|
switch(type) {
|
||||||
|
case Type::Gaussian:
|
||||||
|
j["sigma"] = gaussian_sigma;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowFunction::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
qDebug() << "Setting window function from json";
|
||||||
|
QString typeName = QString::fromStdString(j["type"]);
|
||||||
|
unsigned int i=0;
|
||||||
|
for(;i<(int) Type::Last;i++) {
|
||||||
|
if(typeToName((Type) i) == typeName) {
|
||||||
|
type = Type(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i>=(int) Type::Last) {
|
||||||
|
qWarning() << "Invalid window type specified, defaulting to hamming";
|
||||||
|
type = Type::Hamming;
|
||||||
|
}
|
||||||
|
switch(type) {
|
||||||
|
case Type::Gaussian:
|
||||||
|
gaussian_sigma = j.value("sigma", 0.4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double WindowFunction::getFactor(unsigned int n, unsigned int N)
|
double WindowFunction::getFactor(unsigned int n, unsigned int N)
|
||||||
{
|
{
|
||||||
// all formulas from https://en.wikipedia.org/wiki/Window_function
|
// all formulas from https://en.wikipedia.org/wiki/Window_function
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "savable.h"
|
||||||
|
|
||||||
class WindowFunction : public QObject
|
class WindowFunction : public QObject, public Savable
|
||||||
{
|
{
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
public:
|
public:
|
||||||
@ -31,6 +32,9 @@ public:
|
|||||||
Type getType() const;
|
Type getType() const;
|
||||||
QString getDescription();
|
QString getDescription();
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void changed();
|
void changed();
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include "fftcomplex.h"
|
#include "fftcomplex.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -193,6 +195,112 @@ double Trace::distanceToTime(double distance)
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json Trace::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
if(isCalibration()) {
|
||||||
|
// calibration traces can't be saved
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
j["name"] = _name.toStdString();
|
||||||
|
j["color"] = _color.name().toStdString();
|
||||||
|
j["visible"] = visible;
|
||||||
|
if(isLive()) {
|
||||||
|
j["type"] = "Live";
|
||||||
|
j["parameter"] = _liveParam;
|
||||||
|
j["livetype"] = _liveType;
|
||||||
|
j["paused"] = paused;
|
||||||
|
} else if(isTouchstone()) {
|
||||||
|
j["type"] = "Touchstone";
|
||||||
|
j["filename"] = touchstoneFilename.toStdString();
|
||||||
|
j["parameter"] = touchstoneParameter;
|
||||||
|
}
|
||||||
|
j["reflection"] = reflection;
|
||||||
|
// TODO how to save assigned markers?
|
||||||
|
nlohmann::json mathList;
|
||||||
|
for(auto m : mathOps) {
|
||||||
|
if(m.math->getType() == Type::Last) {
|
||||||
|
// this is an invalid type reserved for the trace itself, skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nlohmann::json jm;
|
||||||
|
auto info = TraceMath::getInfo(m.math->getType());
|
||||||
|
jm["operation"] = info.name.toStdString();
|
||||||
|
jm["enabled"] = m.enabled;
|
||||||
|
jm["settings"] = m.math->toJSON();
|
||||||
|
mathList.push_back(jm);
|
||||||
|
}
|
||||||
|
j["math"] = mathList;
|
||||||
|
j["math_enabled"] = mathEnabled();
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
touchstone = false;
|
||||||
|
calibration = false;
|
||||||
|
_name = QString::fromStdString(j.value("name", "Missing name"));
|
||||||
|
_color = QColor(QString::fromStdString(j.value("color", "yellow")));
|
||||||
|
visible = j.value("visible", true);
|
||||||
|
auto type = QString::fromStdString(j.value("type", "Live"));
|
||||||
|
if(type == "Live") {
|
||||||
|
_liveParam = j.value("parameter", LiveParameter::S11);
|
||||||
|
_liveType = j.value("livetype", LivedataType::Overwrite);
|
||||||
|
paused = j.value("paused", false);
|
||||||
|
} else if(type == "Touchstone") {
|
||||||
|
std::string filename = j.value("filename", "");
|
||||||
|
touchstoneParameter = j.value("parameter", 0);
|
||||||
|
try {
|
||||||
|
Touchstone t = Touchstone::fromFile(filename);
|
||||||
|
fillFromTouchstone(t, touchstoneParameter, QString::fromStdString(filename));
|
||||||
|
} catch (const exception &e) {
|
||||||
|
std::string what = e.what();
|
||||||
|
throw runtime_error("Failed to create touchstone:" + what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reflection = j.value("reflection", false);
|
||||||
|
for(auto jm : j["math"]) {
|
||||||
|
QString operation = QString::fromStdString(jm.value("operation", ""));
|
||||||
|
if(operation.isEmpty()) {
|
||||||
|
qWarning() << "Skipping empty math operation";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// attempt to find the type of operation
|
||||||
|
TraceMath::Type type = Type::Last;
|
||||||
|
for(unsigned int i=0;i<(int) Type::Last;i++) {
|
||||||
|
auto info = TraceMath::getInfo((Type) i);
|
||||||
|
if(info.name == operation) {
|
||||||
|
// found the correct operation
|
||||||
|
type = (Type) i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(type == Type::Last) {
|
||||||
|
// unable to find this operation
|
||||||
|
qWarning() << "Unable to create math operation:" << operation;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
qDebug() << "Creating math operation of type:" << operation;
|
||||||
|
auto op = TraceMath::createMath(type);
|
||||||
|
MathInfo info;
|
||||||
|
info.enabled = jm.value("enabled", true);
|
||||||
|
info.math = op;
|
||||||
|
op->assignInput(lastMath);
|
||||||
|
mathOps.push_back(info);
|
||||||
|
updateLastMath(mathOps.rbegin());
|
||||||
|
}
|
||||||
|
enableMath(j.value("math_enabled", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Trace::toHash()
|
||||||
|
{
|
||||||
|
// taking the easy way: create the json string and hash it (already contains all necessary information)
|
||||||
|
// This is slower than it could be, but this function is only used when loading setups, so this isn't a big problem
|
||||||
|
std::string json_string = toJSON().dump();
|
||||||
|
return hash<std::string>{}(json_string);
|
||||||
|
}
|
||||||
|
|
||||||
void Trace::updateLastMath(vector<MathInfo>::reverse_iterator start)
|
void Trace::updateLastMath(vector<MathInfo>::reverse_iterator start)
|
||||||
{
|
{
|
||||||
TraceMath *newLast = nullptr;
|
TraceMath *newLast = nullptr;
|
||||||
|
@ -108,6 +108,17 @@ public:
|
|||||||
double timeToDistance(double time);
|
double timeToDistance(double time);
|
||||||
double distanceToTime(double distance);
|
double distanceToTime(double distance);
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
|
||||||
|
Type getType() override {return Type::Last;}; // can return invalid type, this will never be called
|
||||||
|
|
||||||
|
// Traces are referenced by pointers throughout this project (e.g. when added to a graph)
|
||||||
|
// When saving the current graph configuration, the pointer is not useful. Instead a trace
|
||||||
|
// hash is saved to identify the correct trace. The hash should be influenced by every setting
|
||||||
|
// the trace can have (and its math function). It should not depend on the acquired trace samples
|
||||||
|
unsigned int toHash();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setTouchstoneParameter(int value);
|
void setTouchstoneParameter(int value);
|
||||||
void setTouchstoneFilename(const QString &value);
|
void setTouchstoneFilename(const QString &value);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "tracemodel.h"
|
#include "tracemodel.h"
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -164,6 +165,32 @@ bool TraceModel::PortExcitationRequired(int port)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json TraceModel::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
for(auto t : traces) {
|
||||||
|
j.push_back(t->toJSON());
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TraceModel::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
// clear old traces
|
||||||
|
while(traces.size()) {
|
||||||
|
removeTrace(0);
|
||||||
|
}
|
||||||
|
for(auto jt : j) {
|
||||||
|
auto trace = new Trace();
|
||||||
|
try {
|
||||||
|
trace->fromJSON(jt);
|
||||||
|
addTrace(trace);
|
||||||
|
} catch (const exception &e) {
|
||||||
|
qWarning() << "Failed to create trace:" << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TraceModel::clearVNAData()
|
void TraceModel::clearVNAData()
|
||||||
{
|
{
|
||||||
for(auto t : traces) {
|
for(auto t : traces) {
|
||||||
|
@ -5,8 +5,9 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Device/device.h"
|
#include "Device/device.h"
|
||||||
|
#include "savable.h"
|
||||||
|
|
||||||
class TraceModel : public QAbstractTableModel
|
class TraceModel : public QAbstractTableModel, public Savable
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@ -36,6 +37,9 @@ public:
|
|||||||
|
|
||||||
bool PortExcitationRequired(int port);
|
bool PortExcitationRequired(int port);
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void SpanChanged(double fmin, double fmax);
|
void SpanChanged(double fmin, double fmax);
|
||||||
void traceAdded(Trace *t);
|
void traceAdded(Trace *t);
|
||||||
|
@ -7,17 +7,24 @@
|
|||||||
#include <QContextMenuEvent>
|
#include <QContextMenuEvent>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include "savable.h"
|
||||||
|
|
||||||
class TracePlot : public QWidget
|
class TracePlot : public QWidget, public Savable
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
enum class Type {
|
||||||
|
SmithChart,
|
||||||
|
XYPlot,
|
||||||
|
};
|
||||||
|
|
||||||
TracePlot(TraceModel &model, QWidget *parent = nullptr);
|
TracePlot(TraceModel &model, QWidget *parent = nullptr);
|
||||||
~TracePlot();
|
~TracePlot();
|
||||||
|
|
||||||
virtual void enableTrace(Trace *t, bool enabled);
|
virtual void enableTrace(Trace *t, bool enabled);
|
||||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
virtual void updateSpan(double min, double max);
|
virtual void updateSpan(double min, double max);
|
||||||
|
virtual Type getType() = 0;
|
||||||
|
|
||||||
static std::set<TracePlot *> getPlots();
|
static std::set<TracePlot *> getPlots();
|
||||||
|
|
||||||
|
@ -13,15 +13,43 @@ using namespace std;
|
|||||||
TraceSmithChart::TraceSmithChart(TraceModel &model, QWidget *parent)
|
TraceSmithChart::TraceSmithChart(TraceModel &model, QWidget *parent)
|
||||||
: TracePlot(model, parent)
|
: TracePlot(model, parent)
|
||||||
{
|
{
|
||||||
chartLinesPen = QPen(palette().windowText(), 0.75);
|
|
||||||
thinPen = QPen(palette().windowText(), 0.25);
|
|
||||||
textPen = QPen(palette().windowText(), 0.25);
|
|
||||||
pointDataPen = QPen(QColor("red"), 4.0, Qt::SolidLine, Qt::RoundCap);
|
|
||||||
lineDataPen = QPen(QColor("blue"), 1.0);
|
|
||||||
limitToSpan = true;
|
limitToSpan = true;
|
||||||
initializeTraceInfo();
|
initializeTraceInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json TraceSmithChart::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["limit_to_span"] = limitToSpan;
|
||||||
|
nlohmann::json jtraces;
|
||||||
|
for(auto t : traces) {
|
||||||
|
if(t.second) {
|
||||||
|
jtraces.push_back(t.first->toHash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j["traces"] = jtraces;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TraceSmithChart::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
limitToSpan = j.value("limit_to_span", true);
|
||||||
|
for(unsigned int hash : j["traces"]) {
|
||||||
|
// attempt to find the traces with this hash
|
||||||
|
bool found = false;
|
||||||
|
for(auto t : model.getTraces()) {
|
||||||
|
if(t->toHash() == hash) {
|
||||||
|
enableTrace(t, true);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
qWarning() << "Unable to find trace with hash" << hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TraceSmithChart::axisSetupDialog()
|
void TraceSmithChart::axisSetupDialog()
|
||||||
{
|
{
|
||||||
auto dialog = new QDialog();
|
auto dialog = new QDialog();
|
||||||
|
@ -11,6 +11,11 @@ class TraceSmithChart : public TracePlot
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TraceSmithChart(TraceModel &model, QWidget *parent = 0);
|
TraceSmithChart(TraceModel &model, QWidget *parent = 0);
|
||||||
|
|
||||||
|
virtual Type getType() override { return Type::SmithChart;};
|
||||||
|
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
public slots:
|
public slots:
|
||||||
void axisSetupDialog();
|
void axisSetupDialog();
|
||||||
protected:
|
protected:
|
||||||
@ -29,18 +34,7 @@ protected:
|
|||||||
virtual void draw(QPainter& painter) override;
|
virtual void draw(QPainter& painter) override;
|
||||||
virtual void traceDropped(Trace *t, QPoint position) override;
|
virtual void traceDropped(Trace *t, QPoint position) override;
|
||||||
QString mouseText(QPoint pos) override;
|
QString mouseText(QPoint pos) override;
|
||||||
QPen textPen;
|
|
||||||
QPen chartLinesPen;
|
|
||||||
QPen thinPen;
|
|
||||||
QPen pointDataPen;
|
|
||||||
QPen lineDataPen;
|
|
||||||
bool limitToSpan;
|
bool limitToSpan;
|
||||||
|
|
||||||
/// Path for the thin arcs
|
|
||||||
QPainterPath thinArcsPath;
|
|
||||||
/// Path for the thick arcs
|
|
||||||
QPainterPath thickArcsPath;
|
|
||||||
|
|
||||||
QTransform transform;
|
QTransform transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,6 +112,76 @@ void TraceXYPlot::replot()
|
|||||||
TracePlot::replot();
|
TracePlot::replot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json TraceXYPlot::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
nlohmann::json jX;
|
||||||
|
jX["type"] = XAxis.type;
|
||||||
|
jX["mode"] = XAxis.mode;
|
||||||
|
jX["log"] = XAxis.log;
|
||||||
|
jX["min"] = XAxis.rangeMin;
|
||||||
|
jX["max"] = XAxis.rangeMax;
|
||||||
|
jX["div"] = XAxis.rangeDiv;
|
||||||
|
j["XAxis"] = jX;
|
||||||
|
for(unsigned int i=0;i<2;i++) {
|
||||||
|
nlohmann::json jY;
|
||||||
|
jY["type"] = YAxis[i].type;
|
||||||
|
jY["log"] = YAxis[i].log;
|
||||||
|
jY["autorange"] = YAxis[i].autorange;
|
||||||
|
jY["min"] = YAxis[i].rangeMin;
|
||||||
|
jY["max"] = YAxis[i].rangeMax;
|
||||||
|
jY["div"] = YAxis[i].rangeDiv;
|
||||||
|
nlohmann::json jtraces;
|
||||||
|
for(auto t : tracesAxis[i]) {
|
||||||
|
jtraces.push_back(t->toHash());
|
||||||
|
}
|
||||||
|
jY["traces"] = jtraces;
|
||||||
|
|
||||||
|
if(i==0) {
|
||||||
|
j["YPrimary"] = jY;
|
||||||
|
} else {
|
||||||
|
j["YSecondary"] = jY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TraceXYPlot::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
auto jX = j["XAxis"];
|
||||||
|
auto xtype = jX.value("type", XAxisType::Frequency);
|
||||||
|
auto xmode = jX.value("mode", XAxisMode::UseSpan);
|
||||||
|
// auto xlog = jX.value("log", false);
|
||||||
|
auto xmin = jX.value("min", 0);
|
||||||
|
auto xmax = jX.value("max", 6000000000);
|
||||||
|
auto xdiv = jX.value("div", 600000000);
|
||||||
|
setXAxis(xtype, xmode, xmin, xmax, xdiv);
|
||||||
|
nlohmann::json jY[2] = {j["YPrimary"], j["YSecondary"]};
|
||||||
|
for(unsigned int i=0;i<2;i++) {
|
||||||
|
auto ytype = jY[i].value("type", YAxisType::Disabled);
|
||||||
|
auto yauto = jY[i].value("autorange", true);
|
||||||
|
auto ylog = jY[i].value("log", false);
|
||||||
|
auto ymin = jY[i].value("min", -120);
|
||||||
|
auto ymax = jY[i].value("max", 20);
|
||||||
|
auto ydiv = jY[i].value("div", 10);
|
||||||
|
setYAxis(i, ytype, ylog, yauto, ymin, ymax, ydiv);
|
||||||
|
for(unsigned int hash : jY[i]["traces"]) {
|
||||||
|
// attempt to find the traces with this hash
|
||||||
|
bool found = false;
|
||||||
|
for(auto t : model.getTraces()) {
|
||||||
|
if(t->toHash() == hash) {
|
||||||
|
enableTraceAxis(t, i, true);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
qWarning() << "Unable to find trace with hash" << hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool TraceXYPlot::isTDRtype(TraceXYPlot::YAxisType type)
|
bool TraceXYPlot::isTDRtype(TraceXYPlot::YAxisType type)
|
||||||
{
|
{
|
||||||
switch(type) {
|
switch(type) {
|
||||||
|
@ -41,6 +41,10 @@ public:
|
|||||||
void updateSpan(double min, double max) override;
|
void updateSpan(double min, double max) override;
|
||||||
void replot() override;
|
void replot() override;
|
||||||
|
|
||||||
|
virtual Type getType() override { return Type::XYPlot;};
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
|
|
||||||
bool isTDRtype(YAxisType type);
|
bool isTDRtype(YAxisType type);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -443,6 +443,24 @@ void VNA::deviceDisconnected()
|
|||||||
defaultCalMenu->setEnabled(false);
|
defaultCalMenu->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json VNA::toJSON()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["traces"] = traceModel.toJSON();
|
||||||
|
j["tiles"] = central->toJSON();
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNA::fromJSON(nlohmann::json j)
|
||||||
|
{
|
||||||
|
if(j.contains("traces")) {
|
||||||
|
traceModel.fromJSON(j["traces"]);
|
||||||
|
}
|
||||||
|
if(j.contains("tiles")) {
|
||||||
|
central->fromJSON(j["tiles"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void VNA::NewDatapoint(Protocol::Datapoint d)
|
void VNA::NewDatapoint(Protocol::Datapoint d)
|
||||||
|
@ -19,6 +19,10 @@ public:
|
|||||||
void deactivate() override;
|
void deactivate() override;
|
||||||
void initializeDevice() override;
|
void initializeDevice() override;
|
||||||
void deviceDisconnected() override;
|
void deviceDisconnected() override;
|
||||||
|
|
||||||
|
// Only save/load user changeable stuff, no need to save the widgets/mode name etc.
|
||||||
|
virtual nlohmann::json toJSON() override;
|
||||||
|
virtual void fromJSON(nlohmann::json j) override;
|
||||||
private slots:
|
private slots:
|
||||||
void NewDatapoint(Protocol::Datapoint d);
|
void NewDatapoint(Protocol::Datapoint d);
|
||||||
void StartImpedanceMatching();
|
void StartImpedanceMatching();
|
||||||
|
@ -93,14 +93,46 @@ AppWindow::AppWindow(QWidget *parent)
|
|||||||
// Create GUI modes
|
// Create GUI modes
|
||||||
central = new QStackedWidget;
|
central = new QStackedWidget;
|
||||||
setCentralWidget(central);
|
setCentralWidget(central);
|
||||||
auto vna = new VNA(this);
|
vna = new VNA(this);
|
||||||
new Generator(this);
|
generator = new Generator(this);
|
||||||
new SpectrumAnalyzer(this);
|
spectrumAnalyzer = new SpectrumAnalyzer(this);
|
||||||
|
|
||||||
// UI connections
|
// UI connections
|
||||||
connect(ui->actionUpdate_Device_List, &QAction::triggered, this, &AppWindow::UpdateDeviceList);
|
connect(ui->actionUpdate_Device_List, &QAction::triggered, this, &AppWindow::UpdateDeviceList);
|
||||||
connect(ui->actionDisconnect, &QAction::triggered, this, &AppWindow::DisconnectDevice);
|
connect(ui->actionDisconnect, &QAction::triggered, this, &AppWindow::DisconnectDevice);
|
||||||
connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close);
|
connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close);
|
||||||
|
connect(ui->actionSave_setup, &QAction::triggered, [=](){
|
||||||
|
auto filename = QFileDialog::getSaveFileName(nullptr, "Save setup data", "", "Setup files (*.setup)", nullptr, QFileDialog::DontUseNativeDialog);
|
||||||
|
if(filename.isEmpty()) {
|
||||||
|
// aborted selection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!filename.endsWith(".setup")) {
|
||||||
|
filename.append(".setup");
|
||||||
|
}
|
||||||
|
ofstream file;
|
||||||
|
file.open(filename.toStdString());
|
||||||
|
file << setw(4) << SaveSetup() << endl;
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
connect(ui->actionLoad_setup, &QAction::triggered, [=](){
|
||||||
|
auto filename = QFileDialog::getOpenFileName(nullptr, "Load setup data", "", "Setup files (*.setup)", nullptr, QFileDialog::DontUseNativeDialog);
|
||||||
|
if(filename.isEmpty()) {
|
||||||
|
// aborted selection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ifstream file;
|
||||||
|
file.open(filename.toStdString());
|
||||||
|
if(!file.is_open()) {
|
||||||
|
qWarning() << "Unable to open file:" << filename;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nlohmann::json j;
|
||||||
|
file >> j;
|
||||||
|
file.close();
|
||||||
|
LoadSetup(j);
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->actionManual_Control, &QAction::triggered, this, &AppWindow::StartManualControl);
|
connect(ui->actionManual_Control, &QAction::triggered, this, &AppWindow::StartManualControl);
|
||||||
connect(ui->actionFirmware_Update, &QAction::triggered, this, &AppWindow::StartFirmwareUpdateDialog);
|
connect(ui->actionFirmware_Update, &QAction::triggered, this, &AppWindow::StartFirmwareUpdateDialog);
|
||||||
connect(ui->actionSource_Calibration, &QAction::triggered, this, &AppWindow::SourceCalibrationDialog);
|
connect(ui->actionSource_Calibration, &QAction::triggered, this, &AppWindow::SourceCalibrationDialog);
|
||||||
@ -373,6 +405,22 @@ void AppWindow::ReceiverCalibrationDialog()
|
|||||||
d->exec();
|
d->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json AppWindow::SaveSetup()
|
||||||
|
{
|
||||||
|
nlohmann::json j;
|
||||||
|
j["VNA"] = vna->toJSON();
|
||||||
|
j["Generator"] = generator->toJSON();
|
||||||
|
j["SpectrumAnalyzer"] = spectrumAnalyzer->toJSON();
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppWindow::LoadSetup(nlohmann::json j)
|
||||||
|
{
|
||||||
|
vna->fromJSON(j["VNA"]);
|
||||||
|
generator->fromJSON(j["Generator"]);
|
||||||
|
spectrumAnalyzer->fromJSON(j["SpectrumAnalyzer"]);
|
||||||
|
}
|
||||||
|
|
||||||
Device *AppWindow::getDevice() const
|
Device *AppWindow::getDevice() const
|
||||||
{
|
{
|
||||||
return device;
|
return device;
|
||||||
|
@ -23,6 +23,10 @@ namespace Ui {
|
|||||||
class MainWindow;
|
class MainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VNA;
|
||||||
|
class Generator;
|
||||||
|
class SpectrumAnalyzer;
|
||||||
|
|
||||||
class AppWindow : public QMainWindow
|
class AppWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -46,6 +50,8 @@ private slots:
|
|||||||
void DeviceNeedsUpdate(int reported, int expected);
|
void DeviceNeedsUpdate(int reported, int expected);
|
||||||
void SourceCalibrationDialog();
|
void SourceCalibrationDialog();
|
||||||
void ReceiverCalibrationDialog();
|
void ReceiverCalibrationDialog();
|
||||||
|
nlohmann::json SaveSetup();
|
||||||
|
void LoadSetup(nlohmann::json j);
|
||||||
private:
|
private:
|
||||||
void DeviceConnectionLost();
|
void DeviceConnectionLost();
|
||||||
void CreateToolbars();
|
void CreateToolbars();
|
||||||
@ -66,6 +72,11 @@ private:
|
|||||||
QString deviceSerial;
|
QString deviceSerial;
|
||||||
QActionGroup *deviceActionGroup;
|
QActionGroup *deviceActionGroup;
|
||||||
|
|
||||||
|
// Modes
|
||||||
|
VNA *vna;
|
||||||
|
Generator *generator;
|
||||||
|
SpectrumAnalyzer *spectrumAnalyzer;
|
||||||
|
|
||||||
// Status bar widgets
|
// Status bar widgets
|
||||||
QLabel lConnectionStatus;
|
QLabel lConnectionStatus;
|
||||||
QLabel lDeviceInfo;
|
QLabel lDeviceInfo;
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>File</string>
|
<string>File</string>
|
||||||
</property>
|
</property>
|
||||||
|
<addaction name="actionSave_setup"/>
|
||||||
|
<addaction name="actionLoad_setup"/>
|
||||||
<addaction name="actionQuit"/>
|
<addaction name="actionQuit"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuDevice">
|
<widget class="QMenu" name="menuDevice">
|
||||||
@ -170,6 +172,24 @@
|
|||||||
<string>Receiver Calibration</string>
|
<string>Receiver Calibration</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionSave_setup">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="document-save" resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/save.png</normaloff>:/icons/save.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save setup</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionLoad_setup">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="document-open" resource="icons.qrc">
|
||||||
|
<normaloff>:/icons/open.png</normaloff>:/icons/open.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Load setup</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="icons.qrc"/>
|
<include location="icons.qrc"/>
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "appwindow.h"
|
#include "appwindow.h"
|
||||||
|
#include "savable.h"
|
||||||
|
|
||||||
class Mode : public QObject
|
class Mode : public QObject, public Savable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Mode(AppWindow *window, QString name);
|
Mode(AppWindow *window, QString name);
|
||||||
|
12
Software/PC_Application/savable.h
Normal file
12
Software/PC_Application/savable.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef SAVABLE_H
|
||||||
|
#define SAVABLE_H
|
||||||
|
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
class Savable {
|
||||||
|
public:
|
||||||
|
virtual nlohmann::json toJSON() = 0;
|
||||||
|
virtual void fromJSON(nlohmann::json j) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAVABLE_H
|
@ -126,7 +126,7 @@ Touchstone Touchstone::fromFile(string filename)
|
|||||||
file.open(filename);
|
file.open(filename);
|
||||||
|
|
||||||
if(!file.is_open()) {
|
if(!file.is_open()) {
|
||||||
throw runtime_error("Unable to open file");
|
throw runtime_error("Unable to open file:" + filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract number of ports from filename
|
// extract number of ports from filename
|
||||||
|
Loading…
Reference in New Issue
Block a user