Save/load trace and graph setup
This commit is contained in:
parent
b91f431473
commit
9ad8def2ea
@ -5,7 +5,6 @@ HEADERS += \
|
||||
Calibration/calibrationtracedialog.h \
|
||||
Calibration/calkit.h \
|
||||
Calibration/calkitdialog.h \
|
||||
Calibration/json.hpp \
|
||||
Calibration/measurementmodel.h \
|
||||
Calibration/receivercaldialog.h \
|
||||
Calibration/sourcecaldialog.h \
|
||||
@ -51,8 +50,10 @@ HEADERS += \
|
||||
VNA/vna.h \
|
||||
appwindow.h \
|
||||
averaging.h \
|
||||
json.hpp \
|
||||
mode.h \
|
||||
preferences.h \
|
||||
savable.h \
|
||||
touchstone.h \
|
||||
unit.h
|
||||
|
||||
|
@ -679,7 +679,7 @@ bool Calibration::openFromFile(QString filename)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
qDebug() << "Attempting to open calibration from file" << filename;
|
||||
qDebug() << "Attempting to open calibration from file" << filename;
|
||||
|
||||
// reset all data before loading new calibration
|
||||
clearMeasurements();
|
||||
|
@ -28,6 +28,73 @@ TileWidget::~TileWidget()
|
||||
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()
|
||||
{
|
||||
if(isSplit) {
|
||||
|
@ -5,12 +5,13 @@
|
||||
#include "Traces/traceplot.h"
|
||||
#include <QSplitter>
|
||||
#include "Traces/tracemodel.h"
|
||||
#include "savable.h"
|
||||
|
||||
namespace Ui {
|
||||
class TileWidget;
|
||||
}
|
||||
|
||||
class TileWidget : public QWidget
|
||||
class TileWidget : public QWidget, public Savable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -20,6 +21,12 @@ public:
|
||||
|
||||
TileWidget *Child1() { return child1; };
|
||||
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:
|
||||
void splitVertically();
|
||||
void splitHorizontally();
|
||||
|
@ -10,6 +10,10 @@ public:
|
||||
Generator(AppWindow *window);
|
||||
void deactivate() 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:
|
||||
void updateDevice();
|
||||
private:
|
||||
|
@ -293,6 +293,24 @@ void SpectrumAnalyzer::initializeDevice()
|
||||
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;
|
||||
|
||||
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
|
||||
|
@ -17,6 +17,11 @@ public:
|
||||
|
||||
void deactivate() 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:
|
||||
void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if(data.size() != input->rData().size()) {
|
||||
data.resize(input->rData().size());
|
||||
|
@ -17,6 +17,10 @@ public:
|
||||
|
||||
static QWidget *createExplanationWidget();
|
||||
|
||||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
Type getType() override {return Type::MedianFilter;};
|
||||
|
||||
public slots:
|
||||
// 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;
|
||||
|
@ -111,6 +111,44 @@ QWidget *TDR::createExplanationWidget()
|
||||
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)
|
||||
{
|
||||
Q_UNUSED(begin);
|
||||
|
@ -17,6 +17,10 @@ public:
|
||||
|
||||
static QWidget* createExplanationWidget();
|
||||
|
||||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
Type getType() override {return Type::TDR;};
|
||||
|
||||
public slots:
|
||||
void inputSamplesChanged(unsigned int begin, unsigned int end) override;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QObject>
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
#include "savable.h"
|
||||
|
||||
/*
|
||||
* How to implement a new type of math operation:
|
||||
@ -34,6 +35,8 @@
|
||||
* Provide a hint by passing a short description string
|
||||
* error(): something went wrong (called with wrong type of data, mathematical error, ...).
|
||||
* 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
|
||||
* 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.
|
||||
@ -43,7 +46,7 @@
|
||||
|
||||
class Trace;
|
||||
|
||||
class TraceMath : public QObject {
|
||||
class TraceMath : public QObject, public Savable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TraceMath();
|
||||
@ -98,6 +101,7 @@ public:
|
||||
std::vector<Data>& rData() { return data;};
|
||||
Status getStatus() const;
|
||||
QString getStatusDescription() const;
|
||||
virtual Type getType() = 0;
|
||||
|
||||
// returns the trace this math operation is attached to
|
||||
Trace* root();
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QLabel>
|
||||
#include <QFormLayout>
|
||||
#include "CustomWidgets/siunitedit.h"
|
||||
#include <QDebug>
|
||||
|
||||
QString WindowFunction::typeToName(WindowFunction::Type type)
|
||||
{
|
||||
@ -108,6 +109,45 @@ QString WindowFunction::getDescription()
|
||||
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)
|
||||
{
|
||||
// all formulas from https://en.wikipedia.org/wiki/Window_function
|
||||
|
@ -4,8 +4,9 @@
|
||||
#include <QWidget>
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
#include "savable.h"
|
||||
|
||||
class WindowFunction : public QObject
|
||||
class WindowFunction : public QObject, public Savable
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
@ -31,6 +32,9 @@ public:
|
||||
Type getType() const;
|
||||
QString getDescription();
|
||||
|
||||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "trace.h"
|
||||
#include <math.h>
|
||||
#include "fftcomplex.h"
|
||||
#include <QDebug>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -193,6 +195,112 @@ double Trace::distanceToTime(double distance)
|
||||
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)
|
||||
{
|
||||
TraceMath *newLast = nullptr;
|
||||
|
@ -108,6 +108,17 @@ public:
|
||||
double timeToDistance(double time);
|
||||
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:
|
||||
void setTouchstoneParameter(int value);
|
||||
void setTouchstoneFilename(const QString &value);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "tracemodel.h"
|
||||
#include <QIcon>
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -164,6 +165,32 @@ bool TraceModel::PortExcitationRequired(int port)
|
||||
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()
|
||||
{
|
||||
for(auto t : traces) {
|
||||
|
@ -5,8 +5,9 @@
|
||||
#include "trace.h"
|
||||
#include <vector>
|
||||
#include "Device/device.h"
|
||||
#include "savable.h"
|
||||
|
||||
class TraceModel : public QAbstractTableModel
|
||||
class TraceModel : public QAbstractTableModel, public Savable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -36,6 +37,9 @@ public:
|
||||
|
||||
bool PortExcitationRequired(int port);
|
||||
|
||||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
|
||||
signals:
|
||||
void SpanChanged(double fmin, double fmax);
|
||||
void traceAdded(Trace *t);
|
||||
|
@ -7,17 +7,24 @@
|
||||
#include <QContextMenuEvent>
|
||||
#include <QTime>
|
||||
#include <QLabel>
|
||||
#include "savable.h"
|
||||
|
||||
class TracePlot : public QWidget
|
||||
class TracePlot : public QWidget, public Savable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Type {
|
||||
SmithChart,
|
||||
XYPlot,
|
||||
};
|
||||
|
||||
TracePlot(TraceModel &model, QWidget *parent = nullptr);
|
||||
~TracePlot();
|
||||
|
||||
virtual void enableTrace(Trace *t, bool enabled);
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
virtual void updateSpan(double min, double max);
|
||||
virtual Type getType() = 0;
|
||||
|
||||
static std::set<TracePlot *> getPlots();
|
||||
|
||||
|
@ -13,15 +13,43 @@ using namespace std;
|
||||
TraceSmithChart::TraceSmithChart(TraceModel &model, QWidget *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;
|
||||
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()
|
||||
{
|
||||
auto dialog = new QDialog();
|
||||
|
@ -11,6 +11,11 @@ class TraceSmithChart : public TracePlot
|
||||
Q_OBJECT
|
||||
public:
|
||||
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:
|
||||
void axisSetupDialog();
|
||||
protected:
|
||||
@ -29,18 +34,7 @@ protected:
|
||||
virtual void draw(QPainter& painter) override;
|
||||
virtual void traceDropped(Trace *t, QPoint position) override;
|
||||
QString mouseText(QPoint pos) override;
|
||||
QPen textPen;
|
||||
QPen chartLinesPen;
|
||||
QPen thinPen;
|
||||
QPen pointDataPen;
|
||||
QPen lineDataPen;
|
||||
bool limitToSpan;
|
||||
|
||||
/// Path for the thin arcs
|
||||
QPainterPath thinArcsPath;
|
||||
/// Path for the thick arcs
|
||||
QPainterPath thickArcsPath;
|
||||
|
||||
QTransform transform;
|
||||
};
|
||||
|
||||
|
@ -112,6 +112,76 @@ void TraceXYPlot::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)
|
||||
{
|
||||
switch(type) {
|
||||
|
@ -41,6 +41,10 @@ public:
|
||||
void updateSpan(double min, double max) 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);
|
||||
|
||||
public slots:
|
||||
|
@ -443,6 +443,24 @@ void VNA::deviceDisconnected()
|
||||
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;
|
||||
|
||||
void VNA::NewDatapoint(Protocol::Datapoint d)
|
||||
|
@ -19,6 +19,10 @@ public:
|
||||
void deactivate() override;
|
||||
void initializeDevice() 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:
|
||||
void NewDatapoint(Protocol::Datapoint d);
|
||||
void StartImpedanceMatching();
|
||||
|
@ -93,14 +93,46 @@ AppWindow::AppWindow(QWidget *parent)
|
||||
// Create GUI modes
|
||||
central = new QStackedWidget;
|
||||
setCentralWidget(central);
|
||||
auto vna = new VNA(this);
|
||||
new Generator(this);
|
||||
new SpectrumAnalyzer(this);
|
||||
vna = new VNA(this);
|
||||
generator = new Generator(this);
|
||||
spectrumAnalyzer = new SpectrumAnalyzer(this);
|
||||
|
||||
// UI connections
|
||||
connect(ui->actionUpdate_Device_List, &QAction::triggered, this, &AppWindow::UpdateDeviceList);
|
||||
connect(ui->actionDisconnect, &QAction::triggered, this, &AppWindow::DisconnectDevice);
|
||||
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->actionFirmware_Update, &QAction::triggered, this, &AppWindow::StartFirmwareUpdateDialog);
|
||||
connect(ui->actionSource_Calibration, &QAction::triggered, this, &AppWindow::SourceCalibrationDialog);
|
||||
@ -373,6 +405,22 @@ void AppWindow::ReceiverCalibrationDialog()
|
||||
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
|
||||
{
|
||||
return device;
|
||||
|
@ -23,6 +23,10 @@ namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class VNA;
|
||||
class Generator;
|
||||
class SpectrumAnalyzer;
|
||||
|
||||
class AppWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -46,6 +50,8 @@ private slots:
|
||||
void DeviceNeedsUpdate(int reported, int expected);
|
||||
void SourceCalibrationDialog();
|
||||
void ReceiverCalibrationDialog();
|
||||
nlohmann::json SaveSetup();
|
||||
void LoadSetup(nlohmann::json j);
|
||||
private:
|
||||
void DeviceConnectionLost();
|
||||
void CreateToolbars();
|
||||
@ -66,6 +72,11 @@ private:
|
||||
QString deviceSerial;
|
||||
QActionGroup *deviceActionGroup;
|
||||
|
||||
// Modes
|
||||
VNA *vna;
|
||||
Generator *generator;
|
||||
SpectrumAnalyzer *spectrumAnalyzer;
|
||||
|
||||
// Status bar widgets
|
||||
QLabel lConnectionStatus;
|
||||
QLabel lDeviceInfo;
|
||||
|
@ -27,6 +27,8 @@
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionSave_setup"/>
|
||||
<addaction name="actionLoad_setup"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDevice">
|
||||
@ -170,6 +172,24 @@
|
||||
<string>Receiver Calibration</string>
|
||||
</property>
|
||||
</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>
|
||||
<resources>
|
||||
<include location="icons.qrc"/>
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include <QDockWidget>
|
||||
#include <set>
|
||||
#include "appwindow.h"
|
||||
#include "savable.h"
|
||||
|
||||
class Mode : public QObject
|
||||
class Mode : public QObject, public Savable
|
||||
{
|
||||
public:
|
||||
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);
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user