Basic working support for math operations between traces
This commit is contained in:
parent
97fceb4ff4
commit
cf31fd7908
@ -672,7 +672,7 @@ std::vector<Trace *> Calibration::getErrorTermTraces()
|
|||||||
constexpr bool reflection[12] = {true, true, false, false, true, false, true, true, false, false, true, false};
|
constexpr bool reflection[12] = {true, true, false, false, true, false, true, true, false, false, true, false};
|
||||||
for(int i=0;i<12;i++) {
|
for(int i=0;i<12;i++) {
|
||||||
auto t = new Trace(traceNames[i], Qt::red);
|
auto t = new Trace(traceNames[i], Qt::red);
|
||||||
t->setCalibration(true);
|
t->setCalibration();
|
||||||
t->setReflection(reflection[i]);
|
t->setReflection(reflection[i]);
|
||||||
traces.push_back(t);
|
traces.push_back(t);
|
||||||
}
|
}
|
||||||
@ -728,7 +728,7 @@ std::vector<Trace *> Calibration::getMeasurementTraces()
|
|||||||
}
|
}
|
||||||
for(auto prefix : usedPrefixes) {
|
for(auto prefix : usedPrefixes) {
|
||||||
auto t = new Trace(prefix + " " + info.name);
|
auto t = new Trace(prefix + " " + info.name);
|
||||||
t->setCalibration(true);
|
t->setCalibration();
|
||||||
t->setReflection(prefix == "S11" || prefix == "S22");
|
t->setReflection(prefix == "S11" || prefix == "S22");
|
||||||
for(auto p : m.second.datapoints) {
|
for(auto p : m.second.datapoints) {
|
||||||
Trace::Data d;
|
Trace::Data d;
|
||||||
|
@ -870,7 +870,7 @@ std::set<Marker::Type> Marker::getSupportedTypes()
|
|||||||
supported.insert(Type::Highpass);
|
supported.insert(Type::Highpass);
|
||||||
supported.insert(Type::Bandpass);
|
supported.insert(Type::Bandpass);
|
||||||
}
|
}
|
||||||
if(parentTrace->isLive()) {
|
if(parentTrace->getSource() == Trace::Source::Live) {
|
||||||
switch(parentTrace->liveParameter()) {
|
switch(parentTrace->liveParameter()) {
|
||||||
case Trace::LiveParameter::S11:
|
case Trace::LiveParameter::S11:
|
||||||
case Trace::LiveParameter::S12:
|
case Trace::LiveParameter::S12:
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "Util/util.h"
|
#include "Util/util.h"
|
||||||
#include "Marker/marker.h"
|
#include "Marker/marker.h"
|
||||||
#include "traceaxis.h"
|
#include "traceaxis.h"
|
||||||
|
#include "tracemodel.h"
|
||||||
|
#include "Math/parser/mpParser.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@ -12,19 +14,23 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace mup;
|
||||||
|
|
||||||
Trace::Trace(QString name, QColor color, LiveParameter live)
|
Trace::Trace(QString name, QColor color, LiveParameter live)
|
||||||
: _name(name),
|
: model(nullptr),
|
||||||
|
_name(name),
|
||||||
_color(color),
|
_color(color),
|
||||||
|
source(Source::Live),
|
||||||
|
hash(0),
|
||||||
|
hashSet(false),
|
||||||
|
JSONskipHash(false),
|
||||||
_liveType(LivedataType::Overwrite),
|
_liveType(LivedataType::Overwrite),
|
||||||
_liveParam(live),
|
_liveParam(live),
|
||||||
vFactor(0.66),
|
vFactor(0.66),
|
||||||
reflection(true),
|
reflection(true),
|
||||||
reference_impedance(50.0),
|
|
||||||
visible(true),
|
visible(true),
|
||||||
paused(false),
|
paused(false),
|
||||||
createdFromFile(false),
|
reference_impedance(50.0),
|
||||||
calibration(false),
|
|
||||||
domain(DataType::Frequency),
|
domain(DataType::Frequency),
|
||||||
lastMath(nullptr)
|
lastMath(nullptr)
|
||||||
{
|
{
|
||||||
@ -32,6 +38,10 @@ Trace::Trace(QString name, QColor color, LiveParameter live)
|
|||||||
mathOps.push_back(self);
|
mathOps.push_back(self);
|
||||||
updateLastMath(mathOps.rbegin());
|
updateLastMath(mathOps.rbegin());
|
||||||
|
|
||||||
|
lastMathUpdate = QTime::currentTime();
|
||||||
|
mathCalcTimer.setSingleShot(true);
|
||||||
|
connect(&mathCalcTimer, &QTimer::timeout, this, &Trace::calculateMath);
|
||||||
|
|
||||||
self.enabled = false;
|
self.enabled = false;
|
||||||
dataType = DataType::Frequency;
|
dataType = DataType::Frequency;
|
||||||
connect(this, &Trace::typeChanged, [=](){
|
connect(this, &Trace::typeChanged, [=](){
|
||||||
@ -52,8 +62,8 @@ Trace::~Trace()
|
|||||||
emit deleted(this);
|
emit deleted(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trace::clear() {
|
void Trace::clear(bool force) {
|
||||||
if(paused) {
|
if(paused && !force) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.clear();
|
data.clear();
|
||||||
@ -166,7 +176,8 @@ void Trace::fillFromTouchstone(Touchstone &t, unsigned int parameter)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createdFromFile = true;
|
clearMathSources();
|
||||||
|
source = Source::File;
|
||||||
reference_impedance = t.getReferenceImpedance();
|
reference_impedance = t.getReferenceImpedance();
|
||||||
emit typeChanged(this);
|
emit typeChanged(this);
|
||||||
emit outputSamplesChanged(0, data.size());
|
emit outputSamplesChanged(0, data.size());
|
||||||
@ -241,7 +252,8 @@ QString Trace::fillFromCSV(CSV &csv, unsigned int parameter)
|
|||||||
addData(d, domain);
|
addData(d, domain);
|
||||||
}
|
}
|
||||||
reflection = false;
|
reflection = false;
|
||||||
createdFromFile = true;
|
clearMathSources();
|
||||||
|
source = Source::File;
|
||||||
emit typeChanged(this);
|
emit typeChanged(this);
|
||||||
emit outputSamplesChanged(0, data.size());
|
emit outputSamplesChanged(0, data.size());
|
||||||
return lastTraceName;
|
return lastTraceName;
|
||||||
@ -269,7 +281,8 @@ void Trace::fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, c
|
|||||||
|
|
||||||
void Trace::fromLivedata(Trace::LivedataType type, LiveParameter param)
|
void Trace::fromLivedata(Trace::LivedataType type, LiveParameter param)
|
||||||
{
|
{
|
||||||
createdFromFile = false;
|
clearMathSources();
|
||||||
|
source = Source::Live;
|
||||||
_liveType = type;
|
_liveType = type;
|
||||||
_liveParam = param;
|
_liveParam = param;
|
||||||
if(param == LiveParameter::S11 || param == LiveParameter::S22) {
|
if(param == LiveParameter::S11 || param == LiveParameter::S22) {
|
||||||
@ -280,6 +293,15 @@ void Trace::fromLivedata(Trace::LivedataType type, LiveParameter param)
|
|||||||
emit typeChanged(this);
|
emit typeChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Trace::fromMath()
|
||||||
|
{
|
||||||
|
source = Source::Math;
|
||||||
|
clear();
|
||||||
|
updateMathTracePoints();
|
||||||
|
scheduleMathCalculation(0, data.size());
|
||||||
|
emit typeChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
void Trace::setColor(QColor color) {
|
void Trace::setColor(QColor color) {
|
||||||
if(_color != color) {
|
if(_color != color) {
|
||||||
_color = color;
|
_color = color;
|
||||||
@ -309,6 +331,281 @@ void Trace::markerVisibilityChanged(Marker *m)
|
|||||||
emit visibilityChanged(this);
|
emit visibilityChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString &Trace::getMathFormula() const
|
||||||
|
{
|
||||||
|
return mathFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::setMathFormula(const QString &newMathFormula)
|
||||||
|
{
|
||||||
|
mathFormula = newMathFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::mathFormularValid() const
|
||||||
|
{
|
||||||
|
if(mathFormula.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ParserX parser(pckCOMMON | pckUNIT | pckCOMPLEX);
|
||||||
|
parser.SetExpr(mathFormula.toStdString());
|
||||||
|
auto vars = parser.GetExprVar();
|
||||||
|
for(auto var : vars) {
|
||||||
|
auto varName = QString::fromStdString(var.first);
|
||||||
|
// try to find variable name
|
||||||
|
bool found = false;
|
||||||
|
for(auto ms : mathSourceTraces) {
|
||||||
|
if(ms.second == varName) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const ParserError &e) {
|
||||||
|
// parser error occurred
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// all variables used in the expression are set as math sources
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::resolveMathSourceHashes()
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
for(auto unresolved : mathSourceUnresolvedHashes) {
|
||||||
|
if(!addMathSource(unresolved.first, unresolved.second)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(success) {
|
||||||
|
mathSourceUnresolvedHashes.clear();
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::updateMathTracePoints()
|
||||||
|
{
|
||||||
|
if(!mathSourceTraces.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
double startX = std::numeric_limits<double>::lowest();
|
||||||
|
double stopX = std::numeric_limits<double>::max();
|
||||||
|
double stepSize = std::numeric_limits<double>::max();
|
||||||
|
for(auto t : mathSourceTraces) {
|
||||||
|
if(t.first->minX() > startX) {
|
||||||
|
startX = t.first->minX();
|
||||||
|
}
|
||||||
|
if(t.first->maxX() < stopX) {
|
||||||
|
stopX = t.first->maxX();
|
||||||
|
}
|
||||||
|
double traceStepSize = std::numeric_limits<double>::max();
|
||||||
|
if(t.first->numSamples() > 1) {
|
||||||
|
traceStepSize = (t.first->maxX() - t.first->minX()) / (t.first->numSamples() - 1);
|
||||||
|
}
|
||||||
|
if(traceStepSize < stepSize) {
|
||||||
|
stepSize = traceStepSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsigned int samples = round((stopX - startX) / stepSize + 1);
|
||||||
|
bool fullUpdate = false;
|
||||||
|
if(samples != data.size()) {
|
||||||
|
data.resize(samples);
|
||||||
|
fullUpdate = true;
|
||||||
|
}
|
||||||
|
if(samples > 0 && (startX != data.front().x || stopX != data.back().x)) {
|
||||||
|
fullUpdate = true;
|
||||||
|
}
|
||||||
|
if(fullUpdate) {
|
||||||
|
// steps changed, complete update required
|
||||||
|
mathUpdateBegin = 0;
|
||||||
|
mathUpdateEnd = samples;
|
||||||
|
for(unsigned int i=0;i<samples;i++) {
|
||||||
|
data[i].x = startX + i * stepSize;
|
||||||
|
data[i].y = numeric_limits<complex<double>>::quiet_NaN();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::mathSourceTraceDeleted(Trace *t)
|
||||||
|
{
|
||||||
|
if (mathSourceTraces.count(t)) {
|
||||||
|
removeMathSource(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::scheduleMathCalculation(unsigned int begin, unsigned int end)
|
||||||
|
{
|
||||||
|
if(source != Source::Math) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(begin < mathUpdateBegin) {
|
||||||
|
mathUpdateBegin = begin;
|
||||||
|
}
|
||||||
|
if(end > mathUpdateEnd) {
|
||||||
|
mathUpdateEnd = end;
|
||||||
|
}
|
||||||
|
auto now = QTime::currentTime();
|
||||||
|
if (lastMathUpdate.msecsTo(now) >= MinMathUpdateInterval) {
|
||||||
|
calculateMath();
|
||||||
|
} else {
|
||||||
|
mathCalcTimer.start(MinMathUpdateInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::calculateMath()
|
||||||
|
{
|
||||||
|
lastMathUpdate = QTime::currentTime();
|
||||||
|
if(mathUpdateBegin >= data.size() || mathUpdateEnd >= data.size() + 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(mathFormula.isEmpty()) {
|
||||||
|
error("Expression is empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!isPaused()) {
|
||||||
|
try {
|
||||||
|
ParserX parser(pckCOMMON | pckUNIT | pckCOMPLEX);
|
||||||
|
parser.SetExpr(mathFormula.toStdString());
|
||||||
|
map<Trace*,Value> values;
|
||||||
|
Value x;
|
||||||
|
parser.DefineVar("x", Variable(&x));
|
||||||
|
for(const auto &ts : mathSourceTraces) {
|
||||||
|
values[ts.first] = Value();
|
||||||
|
parser.DefineVar(ts.second.toStdString(), Variable(&values[ts.first]));
|
||||||
|
}
|
||||||
|
for(unsigned int i=mathUpdateBegin;i<mathUpdateEnd;i++) {
|
||||||
|
x = data[i].x;
|
||||||
|
for(auto &val : values) {
|
||||||
|
val.second = val.first->interpolatedSample(data[i].x).y;
|
||||||
|
}
|
||||||
|
Value res = parser.Eval();
|
||||||
|
data[i].y = res.GetComplex();
|
||||||
|
}
|
||||||
|
} catch (const ParserError &e) {
|
||||||
|
error(QString::fromStdString(e.GetMsg()));
|
||||||
|
// parser error occurred
|
||||||
|
for(unsigned int i=mathUpdateBegin;i<mathUpdateEnd;i++) {
|
||||||
|
data[i].y = numeric_limits<complex<double>>::quiet_NaN();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success();
|
||||||
|
emit outputSamplesChanged(mathUpdateBegin, mathUpdateEnd + 1);
|
||||||
|
}
|
||||||
|
mathUpdateBegin = data.size();
|
||||||
|
mathUpdateEnd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::clearMathSources()
|
||||||
|
{
|
||||||
|
while(mathSourceTraces.size() > 0) {
|
||||||
|
removeMathSource(mathSourceTraces.begin()->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::addMathSource(unsigned int hash, QString variableName)
|
||||||
|
{
|
||||||
|
if(!model) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(auto t : model->getTraces()) {
|
||||||
|
if(t->toHash() == hash) {
|
||||||
|
return addMathSource(t, variableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::mathDependsOn(Trace *t, bool onlyDirectDependency)
|
||||||
|
{
|
||||||
|
if(mathSourceTraces.count(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(onlyDirectDependency) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// also check math source traces recursively
|
||||||
|
for(auto m : mathSourceTraces) {
|
||||||
|
if(m.first->mathDependsOn(t)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::canAddAsMathSource(Trace *t)
|
||||||
|
{
|
||||||
|
if(t == this) {
|
||||||
|
// can't add itself
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// check if we would create a loop of math traces depending on each other
|
||||||
|
if(t->mathDependsOn(this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(mathSourceTraces.size() == 0) {
|
||||||
|
// no traces used as source yet, can add anything
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// can only add traces of the same domain
|
||||||
|
if(mathSourceTraces.begin()->first->outputType() == t->outputType()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Trace::addMathSource(Trace *t, QString variableName)
|
||||||
|
{
|
||||||
|
// qDebug() << "Adding trace" << t << "as a math source to" << this << "as variable" << variableName;
|
||||||
|
if(!canAddAsMathSource(t)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mathSourceTraces[t] = variableName;
|
||||||
|
connect(t, &Trace::deleted, this, &Trace::mathSourceTraceDeleted, Qt::UniqueConnection);
|
||||||
|
connect(t, &Trace::dataChanged, this, [=](unsigned int begin, unsigned int end){
|
||||||
|
updateMathTracePoints();
|
||||||
|
auto startX = t->sample(begin).x;
|
||||||
|
auto stopX = t->sample(end).x;
|
||||||
|
scheduleMathCalculation(index(startX), index(stopX));
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::removeMathSource(Trace *t)
|
||||||
|
{
|
||||||
|
// qDebug() << "Removing trace" << t << "as a math source from" << this;
|
||||||
|
mathSourceTraces.erase(t);
|
||||||
|
disconnect(t, &Trace::deleted, this, &Trace::mathSourceTraceDeleted);
|
||||||
|
disconnect(t, &Trace::dataChanged, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Trace::getSourceVariableName(Trace *t)
|
||||||
|
{
|
||||||
|
if(mathSourceTraces.count(t)) {
|
||||||
|
return mathSourceTraces[t];
|
||||||
|
} else {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceModel *Trace::getModel() const
|
||||||
|
{
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Trace::setModel(TraceModel *model)
|
||||||
|
{
|
||||||
|
this->model = model;
|
||||||
|
}
|
||||||
|
|
||||||
double Trace::getReferenceImpedance() const
|
double Trace::getReferenceImpedance() const
|
||||||
{
|
{
|
||||||
return reference_impedance;
|
return reference_impedance;
|
||||||
@ -347,22 +644,41 @@ double Trace::distanceToTime(double distance)
|
|||||||
nlohmann::json Trace::toJSON()
|
nlohmann::json Trace::toJSON()
|
||||||
{
|
{
|
||||||
nlohmann::json j;
|
nlohmann::json j;
|
||||||
if(isCalibration()) {
|
if(!JSONskipHash) {
|
||||||
|
j["hash"] = toHash(true);
|
||||||
|
}
|
||||||
|
if(source == Source::Calibration) {
|
||||||
// calibration traces can't be saved
|
// calibration traces can't be saved
|
||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
j["name"] = _name.toStdString();
|
j["name"] = _name.toStdString();
|
||||||
j["color"] = _color.name().toStdString();
|
j["color"] = _color.name().toStdString();
|
||||||
j["visible"] = visible;
|
j["visible"] = visible;
|
||||||
if(isLive()) {
|
switch(source) {
|
||||||
|
case Source::Live:
|
||||||
j["type"] = "Live";
|
j["type"] = "Live";
|
||||||
j["parameter"] = _liveParam;
|
j["parameter"] = _liveParam;
|
||||||
j["livetype"] = _liveType;
|
j["livetype"] = _liveType;
|
||||||
j["paused"] = paused;
|
j["paused"] = paused;
|
||||||
} else if(isFromFile()) {
|
break;
|
||||||
|
case Source::File:
|
||||||
j["type"] = "File";
|
j["type"] = "File";
|
||||||
j["filename"] = filename.toStdString();
|
j["filename"] = filename.toStdString();
|
||||||
j["parameter"] = fileParameter;
|
j["parameter"] = fileParameter;
|
||||||
|
break;
|
||||||
|
case Source::Math: {
|
||||||
|
j["type"] = "Math";
|
||||||
|
j["expression"] = mathFormula.toStdString();
|
||||||
|
nlohmann::json jsources;
|
||||||
|
for(auto ms : mathSourceTraces) {
|
||||||
|
nlohmann::json jsource;
|
||||||
|
jsource["trace"] = ms.first->toHash();
|
||||||
|
jsource["variable"] = ms.second.toStdString();
|
||||||
|
jsources.push_back(jsource);
|
||||||
|
}
|
||||||
|
j["sources"] = jsources;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
j["velocityFactor"] = vFactor;
|
j["velocityFactor"] = vFactor;
|
||||||
j["reflection"] = reflection;
|
j["reflection"] = reflection;
|
||||||
@ -388,8 +704,13 @@ nlohmann::json Trace::toJSON()
|
|||||||
|
|
||||||
void Trace::fromJSON(nlohmann::json j)
|
void Trace::fromJSON(nlohmann::json j)
|
||||||
{
|
{
|
||||||
createdFromFile = false;
|
source = Source::Live;
|
||||||
calibration = false;
|
if(j.contains("hash")) {
|
||||||
|
hash = j["hash"];
|
||||||
|
hashSet = true;
|
||||||
|
} else {
|
||||||
|
hashSet = false;
|
||||||
|
}
|
||||||
_name = QString::fromStdString(j.value("name", "Missing name"));
|
_name = QString::fromStdString(j.value("name", "Missing name"));
|
||||||
_color = QColor(QString::fromStdString(j.value("color", "yellow")));
|
_color = QColor(QString::fromStdString(j.value("color", "yellow")));
|
||||||
visible = j.value("visible", true);
|
visible = j.value("visible", true);
|
||||||
@ -414,6 +735,19 @@ void Trace::fromJSON(nlohmann::json j)
|
|||||||
std::string what = e.what();
|
std::string what = e.what();
|
||||||
throw runtime_error("Failed to create from file:" + what);
|
throw runtime_error("Failed to create from file:" + what);
|
||||||
}
|
}
|
||||||
|
} else if(type == "Math") {
|
||||||
|
mathFormula = QString::fromStdString(j.value("expression", ""));
|
||||||
|
if(j.contains("sources")) {
|
||||||
|
for(auto js : j["sources"]) {
|
||||||
|
auto hash = js.value("trace", 0);
|
||||||
|
QString varName = QString::fromStdString(js.value("variable", ""));
|
||||||
|
if(!addMathSource(hash, varName)) {
|
||||||
|
qWarning() << "Unable to find requested math source trace ( hash:"<<hash<<"), probably not loaded yet";
|
||||||
|
mathSourceUnresolvedHashes[hash] = varName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fromMath();
|
||||||
}
|
}
|
||||||
vFactor = j.value("velocityFactor", 0.66);
|
vFactor = j.value("velocityFactor", 0.66);
|
||||||
reflection = j.value("reflection", false);
|
reflection = j.value("reflection", false);
|
||||||
@ -453,12 +787,18 @@ void Trace::fromJSON(nlohmann::json j)
|
|||||||
enableMath(j.value("math_enabled", true));
|
enableMath(j.value("math_enabled", true));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Trace::toHash()
|
unsigned int Trace::toHash(bool forceUpdate)
|
||||||
{
|
{
|
||||||
|
if(!hashSet || forceUpdate) {
|
||||||
// taking the easy way: create the json string and hash it (already contains all necessary information)
|
// 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
|
// This is slower than it could be, but this function is only used when loading setups, so this isn't a big problem
|
||||||
|
JSONskipHash = true;
|
||||||
std::string json_string = toJSON().dump();
|
std::string json_string = toJSON().dump();
|
||||||
return hash<std::string>{}(json_string);
|
JSONskipHash = false;
|
||||||
|
hash = std::hash<std::string>{}(json_string);
|
||||||
|
hashSet = true;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Trace *> Trace::createFromTouchstone(Touchstone &t)
|
std::vector<Trace *> Trace::createFromTouchstone(Touchstone &t)
|
||||||
@ -687,9 +1027,9 @@ QString Trace::description()
|
|||||||
return name() + ": measured data";
|
return name() + ": measured data";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trace::setCalibration(bool value)
|
void Trace::setCalibration()
|
||||||
{
|
{
|
||||||
calibration = value;
|
source = Source::Calibration;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<Marker *> Trace::getMarkers() const
|
std::set<Marker *> Trace::getMarkers() const
|
||||||
@ -731,21 +1071,6 @@ bool Trace::isPaused()
|
|||||||
return paused;
|
return paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Trace::isFromFile()
|
|
||||||
{
|
|
||||||
return createdFromFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Trace::isCalibration()
|
|
||||||
{
|
|
||||||
return calibration;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Trace::isLive()
|
|
||||||
{
|
|
||||||
return !isCalibration() && !isFromFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Trace::isReflection()
|
bool Trace::isReflection()
|
||||||
{
|
{
|
||||||
return reflection;
|
return reflection;
|
||||||
@ -997,7 +1322,7 @@ unsigned int Trace::getFileParameter() const
|
|||||||
|
|
||||||
double Trace::getNoise(double frequency)
|
double Trace::getNoise(double frequency)
|
||||||
{
|
{
|
||||||
if(!isLive() || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2) || lastMath->getDataType() != DataType::Frequency) {
|
if(source != Trace::Source::Live || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2) || lastMath->getDataType() != DataType::Frequency) {
|
||||||
// data not suitable for noise calculation
|
// data not suitable for noise calculation
|
||||||
return std::numeric_limits<double>::quiet_NaN();
|
return std::numeric_limits<double>::quiet_NaN();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include "Device/device.h"
|
#include "Device/device.h"
|
||||||
#include "Math/tracemath.h"
|
#include "Math/tracemath.h"
|
||||||
#include "Tools/parameters.h"
|
#include "Tools/parameters.h"
|
||||||
#include "tracemodel.h"
|
|
||||||
#include "VNA/vnadata.h"
|
#include "VNA/vnadata.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@ -14,8 +13,10 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <QTime>
|
||||||
|
|
||||||
class Marker;
|
class Marker;
|
||||||
|
class TraceModel;
|
||||||
|
|
||||||
class Trace : public TraceMath
|
class Trace : public TraceMath
|
||||||
{
|
{
|
||||||
@ -24,6 +25,14 @@ public:
|
|||||||
|
|
||||||
using Data = TraceMath::Data;
|
using Data = TraceMath::Data;
|
||||||
|
|
||||||
|
enum class Source {
|
||||||
|
Live,
|
||||||
|
File,
|
||||||
|
Math,
|
||||||
|
Calibration,
|
||||||
|
Last,
|
||||||
|
};
|
||||||
|
|
||||||
enum class LiveParameter {
|
enum class LiveParameter {
|
||||||
S11,
|
S11,
|
||||||
S12,
|
S12,
|
||||||
@ -44,7 +53,7 @@ public:
|
|||||||
Invalid,
|
Invalid,
|
||||||
};
|
};
|
||||||
|
|
||||||
void clear();
|
void clear(bool force = false);
|
||||||
void addData(const Data& d, DataType domain, double reference_impedance = 50.0, int index = -1);
|
void addData(const Data& d, DataType domain, double reference_impedance = 50.0, int index = -1);
|
||||||
void addData(const Data& d, const Protocol::SpectrumAnalyzerSettings& s, int index = -1);
|
void addData(const Data& d, const Protocol::SpectrumAnalyzerSettings& s, int index = -1);
|
||||||
void setName(QString name);
|
void setName(QString name);
|
||||||
@ -53,15 +62,14 @@ public:
|
|||||||
QString fillFromCSV(CSV &csv, unsigned int parameter); // returns the suggested trace name (not yet set in member data)
|
QString fillFromCSV(CSV &csv, unsigned int parameter); // returns the suggested trace name (not yet set in member data)
|
||||||
static void fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector<VNAData> &data);
|
static void fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector<VNAData> &data);
|
||||||
void fromLivedata(LivedataType type, LiveParameter param);
|
void fromLivedata(LivedataType type, LiveParameter param);
|
||||||
|
void fromMath();
|
||||||
QString name() { return _name; }
|
QString name() { return _name; }
|
||||||
QColor color() { return _color; }
|
QColor color() { return _color; }
|
||||||
bool isVisible();
|
bool isVisible();
|
||||||
void pause();
|
void pause();
|
||||||
void resume();
|
void resume();
|
||||||
bool isPaused();
|
bool isPaused();
|
||||||
bool isFromFile();
|
Source getSource() {return source;}
|
||||||
bool isCalibration();
|
|
||||||
bool isLive();
|
|
||||||
bool isReflection();
|
bool isReflection();
|
||||||
LiveParameter liveParameter() { return _liveParam; }
|
LiveParameter liveParameter() { return _liveParam; }
|
||||||
LivedataType liveType() { return _liveType; }
|
LivedataType liveType() { return _liveType; }
|
||||||
@ -92,7 +100,7 @@ public:
|
|||||||
double getNoise(double frequency);
|
double getNoise(double frequency);
|
||||||
int index(double x);
|
int index(double x);
|
||||||
std::set<Marker *> getMarkers() const;
|
std::set<Marker *> getMarkers() const;
|
||||||
void setCalibration(bool value);
|
void setCalibration();
|
||||||
void setReflection(bool value);
|
void setReflection(bool value);
|
||||||
|
|
||||||
DataType outputType(DataType inputType) override;
|
DataType outputType(DataType inputType) override;
|
||||||
@ -129,7 +137,7 @@ public:
|
|||||||
// When saving the current graph configuration, the pointer is not useful. Instead a trace
|
// 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
|
// 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
|
// the trace can have (and its math function). It should not depend on the acquired trace samples
|
||||||
unsigned int toHash();
|
unsigned int toHash(bool forceUpdate = false);
|
||||||
|
|
||||||
static std::vector<Trace*> createFromTouchstone(Touchstone &t);
|
static std::vector<Trace*> createFromTouchstone(Touchstone &t);
|
||||||
static std::vector<Trace*> createFromCSV(CSV &csv);
|
static std::vector<Trace*> createFromCSV(CSV &csv);
|
||||||
@ -148,12 +156,28 @@ public:
|
|||||||
|
|
||||||
double getReferenceImpedance() const;
|
double getReferenceImpedance() const;
|
||||||
|
|
||||||
|
void setModel(TraceModel *newModel);
|
||||||
|
TraceModel *getModel() const;
|
||||||
|
|
||||||
|
const QString &getMathFormula() const;
|
||||||
|
void setMathFormula(const QString &newMathFormula);
|
||||||
|
bool mathFormularValid() const;
|
||||||
|
|
||||||
|
bool resolveMathSourceHashes();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setVisible(bool visible);
|
void setVisible(bool visible);
|
||||||
void setColor(QColor color);
|
void setColor(QColor color);
|
||||||
void addMarker(Marker *m);
|
void addMarker(Marker *m);
|
||||||
void removeMarker(Marker *m);
|
void removeMarker(Marker *m);
|
||||||
|
|
||||||
|
// functions for handling source == Source::Math
|
||||||
|
bool mathDependsOn(Trace *t, bool onlyDirectDependency = false);
|
||||||
|
bool canAddAsMathSource(Trace *t);
|
||||||
|
bool addMathSource(Trace *t, QString variableName);
|
||||||
|
void removeMathSource(Trace *t);
|
||||||
|
QString getSourceVariableName(Trace *t);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void cleared(Trace *t);
|
void cleared(Trace *t);
|
||||||
void typeChanged(Trace *t);
|
void typeChanged(Trace *t);
|
||||||
@ -170,21 +194,50 @@ signals:
|
|||||||
private slots:
|
private slots:
|
||||||
void markerVisibilityChanged(Marker *m);
|
void markerVisibilityChanged(Marker *m);
|
||||||
|
|
||||||
|
// functions for handling source == Source::Math
|
||||||
|
bool updateMathTracePoints();
|
||||||
|
void mathSourceTraceDeleted(Trace *t);
|
||||||
|
void scheduleMathCalculation(unsigned int begin, unsigned int end);
|
||||||
|
void calculateMath();
|
||||||
|
void clearMathSources();
|
||||||
|
|
||||||
|
bool addMathSource(unsigned int hash, QString variableName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TraceModel *model; // model which this trace will be part of
|
||||||
QString _name;
|
QString _name;
|
||||||
QColor _color;
|
QColor _color;
|
||||||
|
Source source;
|
||||||
|
|
||||||
|
unsigned int hash;
|
||||||
|
bool hashSet;
|
||||||
|
bool JSONskipHash;
|
||||||
|
|
||||||
|
// Members for when source == Source::Live
|
||||||
LivedataType _liveType;
|
LivedataType _liveType;
|
||||||
LiveParameter _liveParam;
|
LiveParameter _liveParam;
|
||||||
|
|
||||||
|
// Members for when source == Source::File
|
||||||
|
QString filename;
|
||||||
|
unsigned int fileParameter;
|
||||||
|
|
||||||
|
// Members for when source == Source::Math
|
||||||
|
std::map<Trace*,QString> mathSourceTraces;
|
||||||
|
std::map<unsigned int,QString> mathSourceUnresolvedHashes;
|
||||||
|
QString mathFormula;
|
||||||
|
static constexpr int MinMathUpdateInterval = 100;
|
||||||
|
QTime lastMathUpdate;
|
||||||
|
QTimer mathCalcTimer;
|
||||||
|
unsigned int mathUpdateBegin;
|
||||||
|
unsigned int mathUpdateEnd;
|
||||||
|
|
||||||
double vFactor;
|
double vFactor;
|
||||||
bool reflection;
|
bool reflection;
|
||||||
bool visible;
|
bool visible;
|
||||||
bool paused;
|
bool paused;
|
||||||
bool createdFromFile;
|
|
||||||
bool calibration;
|
|
||||||
double reference_impedance;
|
double reference_impedance;
|
||||||
DataType domain;
|
DataType domain;
|
||||||
QString filename;
|
|
||||||
unsigned int fileParameter;
|
|
||||||
std::set<Marker*> markers;
|
std::set<Marker*> markers;
|
||||||
struct {
|
struct {
|
||||||
union {
|
union {
|
||||||
|
@ -26,6 +26,11 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
|
|||||||
ui->impedance->setPrecision(3);
|
ui->impedance->setPrecision(3);
|
||||||
ui->impedance->setValue(t.getReferenceImpedance());
|
ui->impedance->setValue(t.getReferenceImpedance());
|
||||||
|
|
||||||
|
if(!t.getModel()) {
|
||||||
|
// without information about the other traces in the model, math is not available as a source
|
||||||
|
ui->bMath->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
connect(ui->bLive, &QPushButton::clicked, [=](bool live) {
|
connect(ui->bLive, &QPushButton::clicked, [=](bool live) {
|
||||||
if(live) {
|
if(live) {
|
||||||
ui->stack->setCurrentIndex(0);
|
ui->stack->setCurrentIndex(0);
|
||||||
@ -45,6 +50,12 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(ui->bMath, &QPushButton::clicked, [&](bool math){
|
||||||
|
if(math) {
|
||||||
|
ui->stack->setCurrentIndex(3);
|
||||||
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(t.mathFormularValid());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->color, &ColorPickerButton::colorChanged, [=](const QColor& color){
|
connect(ui->color, &ColorPickerButton::colorChanged, [=](const QColor& color){
|
||||||
trace.setColor(color);
|
trace.setColor(color);
|
||||||
@ -52,8 +63,9 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
|
|||||||
|
|
||||||
ui->GSource->setId(ui->bLive, 0);
|
ui->GSource->setId(ui->bLive, 0);
|
||||||
ui->GSource->setId(ui->bFile, 1);
|
ui->GSource->setId(ui->bFile, 1);
|
||||||
|
ui->GSource->setId(ui->bFile, 2);
|
||||||
|
|
||||||
if(t.isCalibration()) {
|
if(t.getSource() == Trace::Source::Calibration) {
|
||||||
// prevent editing imported calibration traces (and csv files for now)
|
// prevent editing imported calibration traces (and csv files for now)
|
||||||
ui->bLive->setEnabled(false);
|
ui->bLive->setEnabled(false);
|
||||||
ui->bFile->setEnabled(false);
|
ui->bFile->setEnabled(false);
|
||||||
@ -136,8 +148,83 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
|
|||||||
connect(ui->touchstoneImport, &TouchstoneImport::filenameChanged, updateTouchstoneFileStatus);
|
connect(ui->touchstoneImport, &TouchstoneImport::filenameChanged, updateTouchstoneFileStatus);
|
||||||
connect(ui->csvImport, &CSVImport::filenameChanged, updateCSVFileStatus);
|
connect(ui->csvImport, &CSVImport::filenameChanged, updateCSVFileStatus);
|
||||||
|
|
||||||
if(t.isFromFile()) {
|
// Math source configuration
|
||||||
ui->bFile->click();
|
if(t.getModel()) {
|
||||||
|
ui->lMathFormula->setText(t.getMathFormula());
|
||||||
|
connect(ui->lMathFormula, &QLineEdit::editingFinished, [&](){
|
||||||
|
t.setMathFormula(ui->lMathFormula->text());
|
||||||
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(t.mathFormularValid());
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->mathTraceTable->setColumnCount(2);
|
||||||
|
ui->mathTraceTable->setHorizontalHeaderItem(0, new QTableWidgetItem("Trace Name"));
|
||||||
|
ui->mathTraceTable->setHorizontalHeaderItem(1, new QTableWidgetItem("Variable Name"));
|
||||||
|
auto traces = t.getModel()->getTraces();
|
||||||
|
ui->mathTraceTable->setRowCount(traces.size());
|
||||||
|
for(unsigned int i=0;i<traces.size();i++) {
|
||||||
|
auto ts = traces[i];
|
||||||
|
auto traceItem = new QTableWidgetItem(ts->name());
|
||||||
|
auto flags = traceItem->flags() | Qt::ItemIsUserCheckable;
|
||||||
|
flags &= ~(Qt::ItemIsEditable | Qt::ItemIsEnabled);
|
||||||
|
if(t.canAddAsMathSource(ts)) {
|
||||||
|
flags |= Qt::ItemIsEnabled;
|
||||||
|
}
|
||||||
|
traceItem->setFlags(flags);
|
||||||
|
auto variableItem = new QTableWidgetItem(t.getSourceVariableName(ts));
|
||||||
|
variableItem->setFlags(variableItem->flags() & ~Qt::ItemIsEditable);
|
||||||
|
if(t.mathDependsOn(ts, true)) {
|
||||||
|
traceItem->setCheckState(Qt::Checked);
|
||||||
|
variableItem->setFlags(variableItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsEditable);
|
||||||
|
} else {
|
||||||
|
traceItem->setCheckState(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
ui->mathTraceTable->setItem(i, 0, traceItem);
|
||||||
|
ui->mathTraceTable->setItem(i, 1, variableItem);
|
||||||
|
}
|
||||||
|
connect(ui->mathTraceTable, &QTableWidget::itemChanged, [&](QTableWidgetItem *item){
|
||||||
|
auto row = ui->mathTraceTable->row(item);
|
||||||
|
auto column = ui->mathTraceTable->column(item);
|
||||||
|
qDebug() << "Item changed at row"<<row<<"column"<<column;
|
||||||
|
ui->mathTraceTable->blockSignals(true);
|
||||||
|
auto trace = t.getModel()->trace(row);
|
||||||
|
if(column == 0) {
|
||||||
|
auto variableItem = ui->mathTraceTable->item(row, 1);
|
||||||
|
// checked state changed
|
||||||
|
if(item->checkState() == Qt::Checked) {
|
||||||
|
// add this trace to the math sources, enable editing of variable name
|
||||||
|
t.addMathSource(trace, trace->name());
|
||||||
|
variableItem->setText(trace->name());
|
||||||
|
variableItem->setFlags(variableItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsEditable);
|
||||||
|
} else {
|
||||||
|
// trace disabled, remove from math sources
|
||||||
|
t.removeMathSource(trace);
|
||||||
|
variableItem->setText("");
|
||||||
|
variableItem->setFlags(variableItem->flags() & ~(Qt::ItemIsEnabled | Qt::ItemIsEditable));
|
||||||
|
}
|
||||||
|
// available trace selections may have changed, disable/enable other rows
|
||||||
|
for(unsigned int i=0;i<t.getModel()->getTraces().size();i++) {
|
||||||
|
auto traceItem = ui->mathTraceTable->item(i, 0);
|
||||||
|
auto flags = traceItem->flags();
|
||||||
|
if(t.canAddAsMathSource(t.getModel()->trace(i))) {
|
||||||
|
traceItem->setFlags(flags | Qt::ItemIsEnabled);
|
||||||
|
} else {
|
||||||
|
traceItem->setFlags(flags & ~Qt::ItemIsEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// changed the variable name text
|
||||||
|
t.addMathSource(trace, item->text());
|
||||||
|
}
|
||||||
|
ui->mathTraceTable->blockSignals(false);
|
||||||
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(t.mathFormularValid());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(t.getSource()) {
|
||||||
|
case Trace::Source::Live: ui->bLive->click(); break;
|
||||||
|
case Trace::Source::File: ui->bFile->click(); break;
|
||||||
|
case Trace::Source::Math: ui->bMath->click(); break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup math part of the GUI
|
// setup math part of the GUI
|
||||||
@ -254,7 +341,7 @@ void TraceEditDialog::on_buttonBox_accepted()
|
|||||||
{
|
{
|
||||||
trace.setName(ui->name->text());
|
trace.setName(ui->name->text());
|
||||||
trace.setVelocityFactor(ui->vFactor->value());
|
trace.setVelocityFactor(ui->vFactor->value());
|
||||||
if(!trace.isCalibration()) {
|
if(trace.getSource() != Trace::Source::Calibration) {
|
||||||
// only apply changes if it is not a calibration trace
|
// only apply changes if it is not a calibration trace
|
||||||
if (ui->bFile->isChecked()) {
|
if (ui->bFile->isChecked()) {
|
||||||
if(ui->stack->currentIndex() == 1) {
|
if(ui->stack->currentIndex() == 1) {
|
||||||
@ -265,7 +352,7 @@ void TraceEditDialog::on_buttonBox_accepted()
|
|||||||
// CSV page active
|
// CSV page active
|
||||||
ui->csvImport->fillTrace(trace);
|
ui->csvImport->fillTrace(trace);
|
||||||
}
|
}
|
||||||
} else {
|
} else if(ui->bLive->isChecked()) {
|
||||||
Trace::LivedataType type = Trace::LivedataType::Overwrite;
|
Trace::LivedataType type = Trace::LivedataType::Overwrite;
|
||||||
Trace::LiveParameter param = Trace::LiveParameter::S11;
|
Trace::LiveParameter param = Trace::LiveParameter::S11;
|
||||||
switch(ui->CLiveType->currentIndex()) {
|
switch(ui->CLiveType->currentIndex()) {
|
||||||
@ -287,6 +374,9 @@ void TraceEditDialog::on_buttonBox_accepted()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace.fromLivedata(type, param);
|
trace.fromLivedata(type, param);
|
||||||
|
} else {
|
||||||
|
// math operation trace
|
||||||
|
trace.fromMath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete this;
|
delete this;
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>931</width>
|
<width>979</width>
|
||||||
<height>392</height>
|
<height>487</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -110,6 +110,16 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="bMath">
|
||||||
|
<property name="text">
|
||||||
|
<string>From Math</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="buttonGroup">
|
||||||
|
<string notr="true">GSource</string>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -133,7 +143,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QStackedWidget" name="stack">
|
<widget class="QStackedWidget" name="stack">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="LivePage">
|
<widget class="QWidget" name="LivePage">
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
@ -203,6 +213,34 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="MathPage">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableWidget" name="mathTraceTable">
|
||||||
|
<attribute name="horizontalHeaderStretchLastSection">
|
||||||
|
<bool>true</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Formula:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lMathFormula"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -32,6 +32,7 @@ void TraceModel::addTrace(Trace *t)
|
|||||||
});
|
});
|
||||||
traces.push_back(t);
|
traces.push_back(t);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
t->setModel(this);
|
||||||
emit traceAdded(t);
|
emit traceAdded(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ QVariant TraceModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ColIndexPlayPause:
|
case ColIndexPlayPause:
|
||||||
if (role == Qt::DecorationRole && trace->isLive()) {
|
if (role == Qt::DecorationRole && trace->getSource() == Trace::Source::Live) { // TODO trace needs function to check if it may change due to live data
|
||||||
if (trace->isPaused()) {
|
if (trace->isPaused()) {
|
||||||
return QIcon(":/icons/pause.svg");
|
return QIcon(":/icons/pause.svg");
|
||||||
} else {
|
} else {
|
||||||
@ -157,7 +158,7 @@ std::vector<Trace *> TraceModel::getTraces() const
|
|||||||
bool TraceModel::PortExcitationRequired(int port)
|
bool TraceModel::PortExcitationRequired(int port)
|
||||||
{
|
{
|
||||||
for(auto t : traces) {
|
for(auto t : traces) {
|
||||||
if(t->isLive() && !t->isPaused()) {
|
if(t->getSource() == Trace::Source::Live && !t->isPaused()) {
|
||||||
// this trace needs measurements from VNA, check if port has to be excited for its measurement
|
// this trace needs measurements from VNA, check if port has to be excited for its measurement
|
||||||
auto param = t->liveParameter();
|
auto param = t->liveParameter();
|
||||||
if(port == 1 && (param == Trace::LiveParameter::S11 || param == Trace::LiveParameter::S21)) {
|
if(port == 1 && (param == Trace::LiveParameter::S11 || param == Trace::LiveParameter::S21)) {
|
||||||
@ -188,6 +189,7 @@ void TraceModel::fromJSON(nlohmann::json j)
|
|||||||
}
|
}
|
||||||
for(auto jt : j) {
|
for(auto jt : j) {
|
||||||
auto trace = new Trace();
|
auto trace = new Trace();
|
||||||
|
trace->setModel(this);
|
||||||
try {
|
try {
|
||||||
trace->fromJSON(jt);
|
trace->fromJSON(jt);
|
||||||
addTrace(trace);
|
addTrace(trace);
|
||||||
@ -195,12 +197,17 @@ void TraceModel::fromJSON(nlohmann::json j)
|
|||||||
qWarning() << "Failed to create trace:" << e.what();
|
qWarning() << "Failed to create trace:" << e.what();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for(auto t : traces) {
|
||||||
|
if(!t->resolveMathSourceHashes()) {
|
||||||
|
qWarning() << "Failed to resolve all math source hashes for"<<t;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceModel::clearLiveData()
|
void TraceModel::clearLiveData()
|
||||||
{
|
{
|
||||||
for(auto t : traces) {
|
for(auto t : traces) {
|
||||||
if (t->isLive()) {
|
if (t->getSource() == Trace::Source::Live) {
|
||||||
// this trace is fed from live data
|
// this trace is fed from live data
|
||||||
t->clear();
|
t->clear();
|
||||||
}
|
}
|
||||||
@ -211,7 +218,7 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype)
|
|||||||
{
|
{
|
||||||
source = DataSource::VNA;
|
source = DataSource::VNA;
|
||||||
for(auto t : traces) {
|
for(auto t : traces) {
|
||||||
if (t->isLive() && !t->isPaused()) {
|
if (t->getSource() == Trace::Source::Live && !t->isPaused()) {
|
||||||
int index = -1;
|
int index = -1;
|
||||||
Trace::Data td;
|
Trace::Data td;
|
||||||
switch(datatype) {
|
switch(datatype) {
|
||||||
@ -247,7 +254,7 @@ void TraceModel::addSAData(const Protocol::SpectrumAnalyzerResult& d, const Prot
|
|||||||
{
|
{
|
||||||
source = DataSource::SA;
|
source = DataSource::SA;
|
||||||
for(auto t : traces) {
|
for(auto t : traces) {
|
||||||
if (t->isLive() && !t->isPaused()) {
|
if (t->getSource() == Trace::Source::Live && !t->isPaused()) {
|
||||||
int index = -1;
|
int index = -1;
|
||||||
Trace::Data td;
|
Trace::Data td;
|
||||||
if(settings.f_start == settings.f_stop) {
|
if(settings.f_start == settings.f_stop) {
|
||||||
|
@ -426,6 +426,8 @@ void TraceWidget::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
auto duplicate = new Trace();
|
auto duplicate = new Trace();
|
||||||
duplicate->fromJSON(json);
|
duplicate->fromJSON(json);
|
||||||
duplicate->setName(duplicate->name() + " - Duplicate");
|
duplicate->setName(duplicate->name() + " - Duplicate");
|
||||||
|
// force update of hash
|
||||||
|
duplicate->toHash(true);
|
||||||
model.addTrace(duplicate);
|
model.addTrace(duplicate);
|
||||||
});
|
});
|
||||||
ctxmenu->addAction(action_duplicate);
|
ctxmenu->addAction(action_duplicate);
|
||||||
|
Loading…
Reference in New Issue
Block a user