Math traces bugfixes + comments

This commit is contained in:
Jan Käberich 2022-07-11 23:53:24 +02:00
parent cf31fd7908
commit 17454ebba2
5 changed files with 104 additions and 26 deletions

View File

@ -297,6 +297,8 @@ void Trace::fromMath()
{ {
source = Source::Math; source = Source::Math;
clear(); clear();
mathUpdateEnd = 0;
mathUpdateBegin = numeric_limits<unsigned int>::max();
updateMathTracePoints(); updateMathTracePoints();
scheduleMathCalculation(0, data.size()); scheduleMathCalculation(0, data.size());
emit typeChanged(this); emit typeChanged(this);
@ -386,10 +388,10 @@ bool Trace::resolveMathSourceHashes()
return success; return success;
} }
bool Trace::updateMathTracePoints() void Trace::updateMathTracePoints()
{ {
if(!mathSourceTraces.size()) { if(!mathSourceTraces.size()) {
return false; return;
} }
double startX = std::numeric_limits<double>::lowest(); double startX = std::numeric_limits<double>::lowest();
double stopX = std::numeric_limits<double>::max(); double stopX = std::numeric_limits<double>::max();
@ -410,25 +412,20 @@ bool Trace::updateMathTracePoints()
} }
} }
unsigned int samples = round((stopX - startX) / stepSize + 1); unsigned int samples = round((stopX - startX) / stepSize + 1);
bool fullUpdate = false; // qDebug() << "Updated trace points, now"<<samples<<"points from"<<startX<<"to"<<stopX;
if(samples != data.size()) { if(samples != data.size()) {
auto oldSize = data.size();
data.resize(samples); data.resize(samples);
fullUpdate = true; if(oldSize < samples) {
} for(unsigned int i=oldSize;i<samples;i++) {
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].x = startX + i * stepSize;
data[i].y = numeric_limits<complex<double>>::quiet_NaN(); data[i].y = numeric_limits<complex<double>>::quiet_NaN();
} }
return true; }
} else { }
return false; if(samples > 0 && (startX != data.front().x || stopX != data.back().x)) {
mathUpdateBegin = 0;
mathUpdateEnd = samples;
} }
} }
@ -441,6 +438,7 @@ void Trace::mathSourceTraceDeleted(Trace *t)
void Trace::scheduleMathCalculation(unsigned int begin, unsigned int end) void Trace::scheduleMathCalculation(unsigned int begin, unsigned int end)
{ {
// qDebug() << "Scheduling calculation from"<<begin<<"to"<<end;
if(source != Source::Math) { if(source != Source::Math) {
return; return;
} }
@ -462,6 +460,7 @@ void Trace::calculateMath()
{ {
lastMathUpdate = QTime::currentTime(); lastMathUpdate = QTime::currentTime();
if(mathUpdateBegin >= data.size() || mathUpdateEnd >= data.size() + 1) { if(mathUpdateBegin >= data.size() || mathUpdateEnd >= data.size() + 1) {
qWarning() << "Not calculating math trace, out of limits. Requested from" << mathUpdateBegin << "to" << mathUpdateEnd <<" but data is of size" << data.size();
return; return;
} }
if(mathFormula.isEmpty()) { if(mathFormula.isEmpty()) {
@ -573,8 +572,25 @@ bool Trace::addMathSource(Trace *t, QString variableName)
connect(t, &Trace::dataChanged, this, [=](unsigned int begin, unsigned int end){ connect(t, &Trace::dataChanged, this, [=](unsigned int begin, unsigned int end){
updateMathTracePoints(); updateMathTracePoints();
auto startX = t->sample(begin).x; auto startX = t->sample(begin).x;
auto stopX = t->sample(end).x; auto stopX = t->sample(end-1).x;
scheduleMathCalculation(index(startX), index(stopX));
// qDebug() << "Source update from"<<startX<<"to"<<stopX<<". Index from"<<begin<<"to"<<end;
auto calcIndex = [&](double x) -> int {
if(data.size() == 0) {
return 0;
}
auto lower = lower_bound(data.begin(), data.end(), x, [](const Data &lhs, const double x) -> bool {
return lhs.x < x;
});
if(lower == data.end()) {
// actually beyond the last sample, return the index of the last anyway to avoid access past data
return data.size() - 1;
}
return lower - data.begin();
};
scheduleMathCalculation(calcIndex(startX), calcIndex(stopX)+1);
}); });
return true; return true;
} }
@ -1050,6 +1066,28 @@ bool Trace::isVisible()
return visible; return visible;
} }
bool Trace::canBePaused()
{
switch(source) {
case Source::Live:
return true;
case Source::File:
return false;
case Source::Calibration:
return false;
case Source::Math:
// depends on the math, if it depends on any live data, it may be paused
for(auto ms : mathSourceTraces) {
if(ms.first->canBePaused()) {
return true;
}
}
return false;
default:
return false;
}
}
void Trace::pause() void Trace::pause()
{ {
if(!paused) { if(!paused) {

View File

@ -66,6 +66,7 @@ public:
QString name() { return _name; } QString name() { return _name; }
QColor color() { return _color; } QColor color() { return _color; }
bool isVisible(); bool isVisible();
bool canBePaused();
void pause(); void pause();
void resume(); void resume();
bool isPaused(); bool isPaused();
@ -163,6 +164,9 @@ public:
void setMathFormula(const QString &newMathFormula); void setMathFormula(const QString &newMathFormula);
bool mathFormularValid() const; bool mathFormularValid() const;
// When loading setups, some traces may be used as a math source before they are loaded.
// If that happens, their hashes are added to a list. Call this function for every new trace
// after all traces from the setup file have been created. It will look for the missing traces
bool resolveMathSourceHashes(); bool resolveMathSourceHashes();
public slots: public slots:
@ -171,11 +175,23 @@ public slots:
void addMarker(Marker *m); void addMarker(Marker *m);
void removeMarker(Marker *m); void removeMarker(Marker *m);
// functions for handling source == Source::Math // Functions for handling source == Source::Math
// Checks whether the trace data depends on trace t.
// If onlyDirectDependency is true, only the direct math sources are checked (i.e. the math sources of this trace).
// If onlyDirectDependency is false, math sources and all their sources are checked recursively
bool mathDependsOn(Trace *t, bool onlyDirectDependency = false); bool mathDependsOn(Trace *t, bool onlyDirectDependency = false);
// Checks whether a trace can potentially be used as a math source. It can not be used if:
// - it is the trace itself
// - it is in a different domain than an already used math source
// - it depends on this trace
bool canAddAsMathSource(Trace *t); bool canAddAsMathSource(Trace *t);
// Adds another trace as a math source, with a custom variable name. The same trace may be added multiple times with
// different names, older variable names will be replaced with the new one
bool addMathSource(Trace *t, QString variableName); bool addMathSource(Trace *t, QString variableName);
// Removes a trace as a math source (if its variable name is still used in the mathFormula, this will break the calculation)
void removeMathSource(Trace *t); void removeMathSource(Trace *t);
// Retrieves the variable name used for the specified trace. If the trace is not used as a math source, an emptry string is returned
QString getSourceVariableName(Trace *t); QString getSourceVariableName(Trace *t);
signals: signals:
@ -194,13 +210,25 @@ signals:
private slots: private slots:
void markerVisibilityChanged(Marker *m); void markerVisibilityChanged(Marker *m);
// functions for handling source == Source::Math // Functions for handling source == Source::Math
bool updateMathTracePoints();
void mathSourceTraceDeleted(Trace *t);
void scheduleMathCalculation(unsigned int begin, unsigned int end);
void calculateMath();
void clearMathSources();
// Updates the datapoints, based on the available span of all math sources.
// The least common span is used for this trace. This function only creates the datapoints
// and sets the X coordinate. The Y coordinates are set in calculateMath()
void updateMathTracePoints();
// Keeps track of deleted traces and removes them from the math sources
void mathSourceTraceDeleted(Trace *t);
// Schedules an update of this trace, should be called whenever any source data changes.
// As it is likely that multiple source traces will change directly after each other, no
// calculation is performed directly. Instead the calculation is only scheduled and executed
// shortly after. This prevents calculating data that will be overwritten immediately afterwards
void scheduleMathCalculation(unsigned int begin, unsigned int end);
// Actually calculates the Y coordinate values of this trace. It expects that the data vector is
// already set up with the required amount of points and X coordinates
void calculateMath();
// Removes all math sources, use this when switching to a different source mode
void clearMathSources();
// Attempts to add a math source using the trace hash instead of a pointer (when loading setups)
bool addMathSource(unsigned int hash, QString variableName); bool addMathSource(unsigned int hash, QString variableName);
private: private:
@ -209,6 +237,10 @@ private:
QColor _color; QColor _color;
Source source; Source source;
// Hash for identifying the trace when loading/saving setup files.
// When writing setup files, the hash is created from the JSON representation of the trace.
// When loading setup files, the hash specified in the setup file is used (prevents different
// hashes when the JSON format changes in newer versions)
unsigned int hash; unsigned int hash;
bool hashSet; bool hashSet;
bool JSONskipHash; bool JSONskipHash;

View File

@ -63,7 +63,7 @@ void TraceModel::toggleVisibility(unsigned int index)
void TraceModel::togglePause(unsigned int index) void TraceModel::togglePause(unsigned int index)
{ {
if (index < traces.size()) { if (index < traces.size() && traces[index]->canBePaused()) {
if(traces[index]->isPaused()) { if(traces[index]->isPaused()) {
traces[index]->resume(); traces[index]->resume();
} else { } else {
@ -118,7 +118,7 @@ QVariant TraceModel::data(const QModelIndex &index, int role) const
} }
break; break;
case ColIndexPlayPause: case ColIndexPlayPause:
if (role == Qt::DecorationRole && trace->getSource() == Trace::Source::Live) { // TODO trace needs function to check if it may change due to live data if (role == Qt::DecorationRole && trace->canBePaused()) {
if (trace->isPaused()) { if (trace->isPaused()) {
return QIcon(":/icons/pause.svg"); return QIcon(":/icons/pause.svg");
} else { } else {

View File

@ -530,6 +530,9 @@ void TraceWaterfall::traceDataChanged(unsigned int begin, unsigned int end)
data.back()[i] = trace->sample(i); data.back()[i] = trace->sample(i);
if(yAxis.getAutorange() && !YAxisUpdateRequired) { if(yAxis.getAutorange() && !YAxisUpdateRequired) {
double val = yAxis.sampleToCoordinate(trace->sample(i)); double val = yAxis.sampleToCoordinate(trace->sample(i));
if(isnan(val) || isinf(val)) {
continue;
}
if(val < min) { if(val < min) {
min = val; min = val;
} }

View File

@ -781,6 +781,11 @@ void TraceXYPlot::updateAxisTicks()
continue; continue;
} }
if(isnan(point.y()) || isinf(point.y())) {
// skip NaN and Infty values
continue;
}
if(point.y() > max) { if(point.y() > max) {
max = point.y(); max = point.y();
} }