save/load markers in setup
This commit is contained in:
parent
9ad8def2ea
commit
8d382e8b9c
@ -298,6 +298,7 @@ nlohmann::json SpectrumAnalyzer::toJSON()
|
||||
nlohmann::json j;
|
||||
j["traces"] = traceModel.toJSON();
|
||||
j["tiles"] = central->toJSON();
|
||||
j["markers"] = markerModel->toJSON();
|
||||
return j;
|
||||
}
|
||||
|
||||
@ -309,6 +310,9 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
|
||||
if(j.contains("tiles")) {
|
||||
central->fromJSON(j["tiles"]);
|
||||
}
|
||||
if(j.contains("markers")) {
|
||||
markerModel->fromJSON(j["markers"]);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
@ -19,6 +19,13 @@ MarkerWidget::MarkerWidget(TraceMarkerModel &model, QWidget *parent) :
|
||||
connect(&model.getModel(), &TraceModel::traceAdded, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(&model.getModel(), &TraceModel::traceRemoved, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(&model.getModel(), &TraceModel::traceNameChanged, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(&model, &TraceMarkerModel::markerAdded, [=](TraceMarker *m) {
|
||||
connect(m, &TraceMarker::typeChanged, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(m, &TraceMarker::traceChanged, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(m, &TraceMarker::assignedDeltaChanged, this, &MarkerWidget::updatePersistentEditors);
|
||||
updatePersistentEditors();
|
||||
});
|
||||
connect(&model, &TraceMarkerModel::setupLoadComplete, this, &MarkerWidget::updatePersistentEditors);
|
||||
}
|
||||
|
||||
MarkerWidget::~MarkerWidget()
|
||||
@ -46,18 +53,13 @@ void MarkerWidget::on_bDelete_clicked()
|
||||
// can't delete child markers directly
|
||||
return;
|
||||
}
|
||||
model.removeMarker(marker);
|
||||
marker->blockSignals(true);
|
||||
delete marker;
|
||||
}
|
||||
|
||||
void MarkerWidget::on_bAdd_clicked()
|
||||
{
|
||||
auto marker = model.createDefaultMarker();
|
||||
connect(marker, &TraceMarker::typeChanged, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(marker, &TraceMarker::traceChanged, this, &MarkerWidget::updatePersistentEditors);
|
||||
model.addMarker(marker);
|
||||
updatePersistentEditors();
|
||||
}
|
||||
|
||||
void MarkerWidget::updatePersistentEditors()
|
||||
|
@ -127,7 +127,7 @@ QString TraceMarker::readableData()
|
||||
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||
}
|
||||
case Type::Delta:
|
||||
if(!delta && delta->isTimeDomain()) {
|
||||
if(!delta || delta->isTimeDomain()) {
|
||||
return "Invalid delta marker";
|
||||
} else {
|
||||
// calculate difference between markers
|
||||
@ -288,10 +288,21 @@ void TraceMarker::parentTraceDeleted(Trace *t)
|
||||
|
||||
void TraceMarker::traceDataChanged()
|
||||
{
|
||||
// some data of the parent trace changed, check if marker data also changed
|
||||
complex<double> newdata;
|
||||
if(!parentTrace || parentTrace->numSamples() == 0) {
|
||||
// no data, invalidate
|
||||
newdata = numeric_limits<complex<double>>::quiet_NaN();
|
||||
} else {
|
||||
if(position < parentTrace->minX() || position > parentTrace->maxX()) {
|
||||
// this normally should not happen because the position is constrained to the trace X range.
|
||||
// However, when loading a setup, the trace might have been just created and essentially empty
|
||||
newdata = numeric_limits<complex<double>>::quiet_NaN();
|
||||
} else {
|
||||
// some data of the parent trace changed, check if marker data also changed
|
||||
auto sampleType = isTimeDomain() ? Trace::SampleType::TimeImpulse : Trace::SampleType::Frequency;
|
||||
newdata = parentTrace->sample(parentTrace->index(position), sampleType).y;
|
||||
}
|
||||
}
|
||||
if (newdata != data) {
|
||||
data = newdata;
|
||||
update();
|
||||
@ -328,7 +339,7 @@ void TraceMarker::checkDeltaMarker()
|
||||
return;
|
||||
}
|
||||
// Check if type of delta marker is still okay
|
||||
if(delta->isTimeDomain() != isTimeDomain()) {
|
||||
if(!delta || delta->isTimeDomain() != isTimeDomain()) {
|
||||
// not the same domain anymore, adjust delta
|
||||
assignDeltaMarker(bestDeltaCandidate());
|
||||
}
|
||||
@ -418,6 +429,10 @@ TraceMarker *TraceMarker::bestDeltaCandidate()
|
||||
|
||||
void TraceMarker::assignDeltaMarker(TraceMarker *m)
|
||||
{
|
||||
if(type != Type::Delta) {
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
if(delta) {
|
||||
disconnect(delta, &TraceMarker::dataChanged, this, &TraceMarker::update);
|
||||
}
|
||||
@ -427,10 +442,13 @@ void TraceMarker::assignDeltaMarker(TraceMarker *m)
|
||||
connect(delta, &TraceMarker::rawDataChanged, this, &TraceMarker::update);
|
||||
connect(delta, &TraceMarker::domainChanged, this, &TraceMarker::checkDeltaMarker);
|
||||
connect(delta, &TraceMarker::deleted, [=](){
|
||||
delta = nullptr;
|
||||
qDebug() << "assigned delta deleted";
|
||||
assignDeltaMarker(bestDeltaCandidate());
|
||||
update();
|
||||
});
|
||||
}
|
||||
emit assignedDeltaChanged(this);
|
||||
}
|
||||
|
||||
void TraceMarker::deleteHelperMarkers()
|
||||
@ -507,11 +525,104 @@ bool TraceMarker::isVisible()
|
||||
}
|
||||
}
|
||||
|
||||
TraceMarker::Type TraceMarker::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
QString TraceMarker::getSuffix() const
|
||||
{
|
||||
return suffix;
|
||||
}
|
||||
|
||||
nlohmann::json TraceMarker::toJSON()
|
||||
{
|
||||
nlohmann::json j;
|
||||
j["trace"] = parentTrace->toHash();
|
||||
j["type"] = typeToString(type).toStdString();
|
||||
j["number"] = number;
|
||||
j["position"] = position;
|
||||
switch(type) {
|
||||
case Type::Delta:
|
||||
j["delta_marker"] = delta->toHash();
|
||||
break;
|
||||
case Type::PeakTable:
|
||||
j["peak_threshold"] = peakThreshold;
|
||||
break;
|
||||
case Type::Lowpass:
|
||||
case Type::Highpass:
|
||||
case Type::Bandpass:
|
||||
j["cutoff"] = cutoffAmplitude;
|
||||
break;
|
||||
case Type::PhaseNoise:
|
||||
j["offset"] = offset;
|
||||
break;
|
||||
default:
|
||||
// other types have no settings
|
||||
break;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
void TraceMarker::fromJSON(nlohmann::json j)
|
||||
{
|
||||
if(!j.contains("trace")) {
|
||||
throw runtime_error("Marker has no trace assigned");
|
||||
}
|
||||
number = j.value("number", 1);
|
||||
position = j.value("position", 0.0);
|
||||
|
||||
unsigned int hash = j["trace"];
|
||||
// find correct trace
|
||||
bool found = false;
|
||||
for(auto t : model->getModel().getTraces()) {
|
||||
if(t->toHash() == hash) {
|
||||
found = true;
|
||||
assignTrace(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
throw runtime_error("Unable to find trace with hash " + to_string(hash));
|
||||
}
|
||||
auto typeString = QString::fromStdString(j.value("type", "Manual"));
|
||||
for(unsigned int i=0;i<(int) Type::Last;i++) {
|
||||
if(typeToString((Type) i) == typeString) {
|
||||
setType((Type) i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch(type) {
|
||||
case Type::Delta:
|
||||
// can't assign delta marker here, because it might not have been created (if it was below this marker in the table).
|
||||
// Instead it will be correctly assigned in TraceMarkerModel::fromJSON()
|
||||
break;
|
||||
case Type::PeakTable:
|
||||
peakThreshold = j.value("peak_threshold", -40);
|
||||
break;
|
||||
case Type::Lowpass:
|
||||
case Type::Highpass:
|
||||
case Type::Bandpass:
|
||||
cutoffAmplitude = j.value("cutoff", -3.0);
|
||||
break;
|
||||
case Type::PhaseNoise:
|
||||
j.value("offset", 10000);
|
||||
break;
|
||||
default:
|
||||
// other types have no settings
|
||||
break;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
unsigned int TraceMarker::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);
|
||||
}
|
||||
|
||||
const std::vector<TraceMarker *> &TraceMarker::getHelperMarkers() const
|
||||
{
|
||||
return helperMarkers;
|
||||
|
@ -6,10 +6,11 @@
|
||||
#include "trace.h"
|
||||
#include <QComboBox>
|
||||
#include "CustomWidgets/siunitedit.h"
|
||||
#include "savable.h"
|
||||
|
||||
class TraceMarkerModel;
|
||||
|
||||
class TraceMarker : public QObject
|
||||
class TraceMarker : public QObject, public Savable
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
@ -35,7 +36,22 @@ public:
|
||||
bool editingFrequeny;
|
||||
Trace *getTrace() const;
|
||||
|
||||
|
||||
enum class Type {
|
||||
Manual,
|
||||
Maximum,
|
||||
Minimum,
|
||||
Delta,
|
||||
Noise,
|
||||
PeakTable,
|
||||
Lowpass,
|
||||
Highpass,
|
||||
Bandpass,
|
||||
TOI,
|
||||
PhaseNoise,
|
||||
// keep last at end
|
||||
Last,
|
||||
};
|
||||
Type getType() const;
|
||||
QWidget *getTypeEditor(QAbstractItemDelegate *delegate = nullptr);
|
||||
void updateTypeFromEditor(QWidget *c);
|
||||
SIUnitEdit* getSettingsEditor();
|
||||
@ -46,8 +62,19 @@ public:
|
||||
TraceMarker *getParent() const;
|
||||
const std::vector<TraceMarker *>& getHelperMarkers() const;
|
||||
TraceMarker *helperMarker(unsigned int i);
|
||||
void assignDeltaMarker(TraceMarker *m);
|
||||
QString getSuffix() const;
|
||||
|
||||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
// Markers are referenced by pointers throughout this project (e.g. when added to a trace)
|
||||
// When saving the current marker configuration, the pointer is not useful (e.g. for determining
|
||||
// the associated delta marker. Instead a marker hash is saved to identify the correct marker.
|
||||
// The hash should be influenced by every setting the marker can have. It should not depend on
|
||||
// the marker data.
|
||||
unsigned int toHash();
|
||||
|
||||
|
||||
public slots:
|
||||
void setPosition(double freq);
|
||||
signals:
|
||||
@ -55,6 +82,7 @@ signals:
|
||||
void dataChanged(TraceMarker *m);
|
||||
void symbolChanged(TraceMarker *m);
|
||||
void typeChanged(TraceMarker *m);
|
||||
void assignedDeltaChanged(TraceMarker *m);
|
||||
void traceChanged(TraceMarker *m);
|
||||
void beginRemoveHelperMarkers(TraceMarker *m);
|
||||
void endRemoveHelperMarkers(TraceMarker *m);
|
||||
@ -68,20 +96,6 @@ signals:
|
||||
void rawDataChanged();
|
||||
void domainChanged();
|
||||
private:
|
||||
|
||||
enum class Type {
|
||||
Manual,
|
||||
Maximum,
|
||||
Minimum,
|
||||
Delta,
|
||||
Noise,
|
||||
PeakTable,
|
||||
Lowpass,
|
||||
Highpass,
|
||||
Bandpass,
|
||||
TOI,
|
||||
PhaseNoise,
|
||||
};
|
||||
std::set<Type> getSupportedTypes();
|
||||
static QString typeToString(Type t) {
|
||||
switch(t) {
|
||||
@ -101,7 +115,6 @@ private:
|
||||
}
|
||||
void constrainPosition();
|
||||
TraceMarker *bestDeltaCandidate();
|
||||
void assignDeltaMarker(TraceMarker *m);
|
||||
void deleteHelperMarkers();
|
||||
void setType(Type t);
|
||||
double toDecibel();
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "CustomWidgets/siunitedit.h"
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static constexpr int rowHeight = 21;
|
||||
|
||||
TraceMarkerModel::TraceMarkerModel(TraceModel &model, QObject *parent)
|
||||
@ -268,6 +270,57 @@ TraceMarker *TraceMarkerModel::markerFromIndex(const QModelIndex &index) const
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json TraceMarkerModel::toJSON()
|
||||
{
|
||||
nlohmann::json j;
|
||||
for(auto m : markers) {
|
||||
j.push_back(m->toJSON());
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
void TraceMarkerModel::fromJSON(nlohmann::json j)
|
||||
{
|
||||
// remove old markers
|
||||
while(markers.size() > 0) {
|
||||
removeMarker((unsigned int) 0);
|
||||
}
|
||||
for(auto jm : j) {
|
||||
auto m = new TraceMarker(this);
|
||||
try {
|
||||
m->fromJSON(jm);
|
||||
addMarker(m);
|
||||
} catch (const exception &e) {
|
||||
qWarning() << "Failed to creat marker from JSON:" << e.what();
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
// second pass to assign delta markers
|
||||
for(unsigned int i=0;i<markers.size();i++) {
|
||||
if(markers[i]->getType() == TraceMarker::Type::Delta) {
|
||||
if(!j[i].contains("delta_marker")) {
|
||||
qWarning() << "JSON data does not contain assigned delta marker";
|
||||
continue;
|
||||
}
|
||||
unsigned int hash = j[i]["delta_marker"];
|
||||
// attempt to find correct marker
|
||||
unsigned int m_delta = 0;
|
||||
for(;m_delta < markers.size();m_delta++) {
|
||||
auto m = markers[m_delta];
|
||||
if(m->toHash() == hash) {
|
||||
markers[i]->assignDeltaMarker(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(m_delta >= markers.size()) {
|
||||
qWarning() << "Unable to find assigned delta marker:" << hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
// All done loading the markers, trigger update of persistent editors
|
||||
emit setupLoadComplete();
|
||||
}
|
||||
|
||||
QSize MarkerTraceDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
|
||||
{
|
||||
return QSize(0, rowHeight);
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <vector>
|
||||
#include "tracemodel.h"
|
||||
#include <QStyledItemDelegate>
|
||||
#include "savable.h"
|
||||
|
||||
class MarkerTraceDelegate : public QStyledItemDelegate
|
||||
{
|
||||
@ -43,7 +44,7 @@ class MarkerSettingsDelegate : public QStyledItemDelegate
|
||||
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
|
||||
};
|
||||
|
||||
class TraceMarkerModel : public QAbstractItemModel
|
||||
class TraceMarkerModel : public QAbstractItemModel, public Savable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -76,6 +77,9 @@ public:
|
||||
void updateMarkers();
|
||||
TraceMarker *markerFromIndex(const QModelIndex &index) const;
|
||||
|
||||
virtual nlohmann::json toJSON() override;
|
||||
virtual void fromJSON(nlohmann::json j) override;
|
||||
|
||||
public slots:
|
||||
void addMarker(TraceMarker *t);
|
||||
void removeMarker(unsigned int index);
|
||||
@ -84,6 +88,7 @@ public slots:
|
||||
|
||||
signals:
|
||||
void markerAdded(TraceMarker *t);
|
||||
void setupLoadComplete();
|
||||
|
||||
private slots:
|
||||
void markerDataChanged(TraceMarker *m);
|
||||
|
@ -194,6 +194,10 @@ void TraceSmithChart::draw(QPainter &p) {
|
||||
if (limitToSpan && (m->getPosition() < sweep_fmin || m->getPosition() > sweep_fmax)) {
|
||||
continue;
|
||||
}
|
||||
if(m->getPosition() < trace->minX() || m->getPosition() > trace->maxX()) {
|
||||
// marker not in trace range
|
||||
continue;
|
||||
}
|
||||
auto coords = m->getData();
|
||||
coords *= smithCoordMax;
|
||||
auto symbol = m->getSymbol();
|
||||
|
@ -465,6 +465,11 @@ void TraceXYPlot::draw(QPainter &p)
|
||||
xPosition = m->getPosition();
|
||||
// }
|
||||
if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) {
|
||||
// marker not in graph range
|
||||
continue;
|
||||
}
|
||||
if(xPosition < t->minX() || xPosition > t->maxX()) {
|
||||
// marker not in trace range
|
||||
continue;
|
||||
}
|
||||
auto t = m->getTrace();
|
||||
|
@ -448,6 +448,7 @@ nlohmann::json VNA::toJSON()
|
||||
nlohmann::json j;
|
||||
j["traces"] = traceModel.toJSON();
|
||||
j["tiles"] = central->toJSON();
|
||||
j["markers"] = markerModel->toJSON();
|
||||
return j;
|
||||
}
|
||||
|
||||
@ -459,6 +460,9 @@ void VNA::fromJSON(nlohmann::json j)
|
||||
if(j.contains("tiles")) {
|
||||
central->fromJSON(j["tiles"]);
|
||||
}
|
||||
if(j.contains("markers")) {
|
||||
markerModel->fromJSON(j["markers"]);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
Loading…
Reference in New Issue
Block a user