diff --git a/Software/PC_Application/Calibration/calkitdialog.ui b/Software/PC_Application/Calibration/calkitdialog.ui
index 4048d94..2f95442 100644
--- a/Software/PC_Application/Calibration/calkitdialog.ui
+++ b/Software/PC_Application/Calibration/calkitdialog.ui
@@ -933,10 +933,10 @@
-
-
+
+
diff --git a/Software/PC_Application/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI.pro
index de8fd0c..f872b0c 100644
--- a/Software/PC_Application/LibreVNA-GUI.pro
+++ b/Software/PC_Application/LibreVNA-GUI.pro
@@ -79,6 +79,7 @@ HEADERS += \
Traces/Math/parser/suStringTokens.h \
Traces/Math/parser/utGeneric.h \
Traces/Math/tdr.h \
+ Traces/Math/timegate.h \
Traces/Math/tracemath.h \
Traces/Math/windowfunction.h \
Traces/fftcomplex.h \
@@ -190,6 +191,7 @@ SOURCES += \
Traces/Math/parser/mpValueCache.cpp \
Traces/Math/parser/mpVariable.cpp \
Traces/Math/tdr.cpp \
+ Traces/Math/timegate.cpp \
Traces/Math/tracemath.cpp \
Traces/Math/windowfunction.cpp \
Traces/fftcomplex.cpp \
@@ -260,6 +262,9 @@ FORMS += \
Traces/Math/newtracemathdialog.ui \
Traces/Math/tdrdialog.ui \
Traces/Math/tdrexplanationwidget.ui \
+ Traces/Math/timedomaingatingexplanationwidget.ui \
+ Traces/Math/timegatedialog.ui \
+ Traces/Math/timegateexplanationwidget.ui \
Traces/markerwidget.ui \
Traces/smithchartdialog.ui \
Traces/tracecsvexport.ui \
diff --git a/Software/PC_Application/Traces/Math/timedomaingatingexplanationwidget.ui b/Software/PC_Application/Traces/Math/timedomaingatingexplanationwidget.ui
new file mode 100644
index 0000000..bf6e88c
--- /dev/null
+++ b/Software/PC_Application/Traces/Math/timedomaingatingexplanationwidget.ui
@@ -0,0 +1,44 @@
+
+
+ TimeDomainGatingExplanationWidget
+
+
+
+ 0
+ 0
+ 364
+ 412
+
+
+
+ Form
+
+
+ -
+
+
+ <html><head/><body><p><span style=" font-weight:600;">Time Domain Gating</span></p><p>This is not a dedicated math operation but rather a convenience option to automatically setup a TDR with a time gate and followed by a DFT. The combination of these three operations performs time domain gating<br/></p><p><br/></p></body></html>
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
diff --git a/Software/PC_Application/Traces/Math/timegate.cpp b/Software/PC_Application/Traces/Math/timegate.cpp
new file mode 100644
index 0000000..8fd4974
--- /dev/null
+++ b/Software/PC_Application/Traces/Math/timegate.cpp
@@ -0,0 +1,404 @@
+#include "timegate.h"
+#include
+#include
+#include "ui_timegatedialog.h"
+#include "ui_timegateexplanationwidget.h"
+#include "preferences.h"
+#include
+#include "Util/util.h"
+#include "Traces/fftcomplex.h"
+#include "unit.h"
+#include
+
+Math::TimeGate::TimeGate()
+{
+ bandpass = true;
+// if(input->rData().size()) {
+// center = input->rData().back().x/2;
+// span = center / 2;
+// } else {
+ center = 10e-9;
+ span = 2e-9;
+// }
+ connect(&window, &WindowFunction::changed, this, &TimeGate::updateFilter);
+}
+
+TraceMath::DataType Math::TimeGate::outputType(TraceMath::DataType inputType)
+{
+ if(inputType == DataType::Time) {
+ return DataType::Time;
+ } else {
+ return DataType::Invalid;
+ }
+}
+
+QString Math::TimeGate::description()
+{
+ QString d = "Time gate (";
+ if(bandpass) {
+ d.append("bandpass");
+ } else {
+ d.append("notch");
+ }
+ d.append(") center: "+Unit::ToString(center, "s", "pnum ", 3)+", span: "+Unit::ToString(span, "s", "pnum ", 3));
+ d += ", window: " + window.getDescription();
+ return d;
+}
+
+void Math::TimeGate::edit()
+{
+ if(dataType == DataType::Invalid) {
+ return;
+ }
+
+ auto d = new QDialog();
+ auto ui = new Ui::TimeGateDialog();
+ ui->setupUi(d);
+ ui->graph->setGate(this);
+ ui->windowBox->setLayout(new QVBoxLayout);
+ ui->windowBox->layout()->addWidget(window.createEditor());
+
+ ui->start->setUnit("s");
+ ui->start->setPrefixes("pnum ");
+ ui->start->setValue(center - span / 2);
+
+ ui->stop->setUnit("s");
+ ui->stop->setPrefixes("pnum ");
+ ui->stop->setValue(center + span / 2);
+
+ ui->center->setUnit("s");
+ ui->center->setPrefixes("pnum ");
+ ui->center->setValue(center);
+
+ ui->span->setUnit("s");
+ ui->span->setPrefixes("pnum ");
+ ui->span->setValue(center);
+
+ if(bandpass) {
+ ui->type->setCurrentIndex(0);
+ } else {
+ ui->type->setCurrentIndex(1);
+ }
+
+ ui->graph->setFocus();
+
+ connect(ui->span, &SIUnitEdit::valueChanged, this, &TimeGate::setSpan);
+ connect(this, &TimeGate::spanChanged, ui->span, &SIUnitEdit::setValueQuiet);
+ connect(ui->center, &SIUnitEdit::valueChanged, this, &TimeGate::setCenter);
+ connect(this, &TimeGate::centerChanged, ui->center, &SIUnitEdit::setValueQuiet);
+ connect(ui->start, &SIUnitEdit::valueChanged, this, &TimeGate::setStart);
+ connect(this, &TimeGate::startChanged, ui->start, &SIUnitEdit::setValueQuiet);
+ connect(ui->stop, &SIUnitEdit::valueChanged, this, &TimeGate::setStop);
+ connect(this, &TimeGate::stopChanged, ui->stop, &SIUnitEdit::setValueQuiet);
+
+ connect(ui->type, qOverload(&QComboBox::currentIndexChanged), [=](int index) {
+ bandpass = index == 0;
+ updateFilter();
+ });
+
+ connect(this, &TimeGate::outputSamplesChanged, ui->graph, qOverload<>(&QWidget::update));
+ connect(this, &TimeGate::filterUpdated, ui->graph, qOverload<>(&QWidget::update));
+
+ updateFilter();
+
+ d->show();
+}
+
+QWidget *Math::TimeGate::createExplanationWidget()
+{
+ auto w = new QWidget();
+ auto ui = new Ui::TimeGateExplanationWidget;
+ ui->setupUi(w);
+ return w;
+}
+
+nlohmann::json Math::TimeGate::toJSON()
+{
+ nlohmann::json j;
+ j["center"] = center;
+ j["span"] = span;
+ j["bandpass"] = bandpass;
+ j["window"] = window.toJSON();
+ return j;
+}
+
+void Math::TimeGate::fromJSON(nlohmann::json j)
+{
+ if(j.contains("window")) {
+ window.fromJSON(j["window"]);
+ }
+ bandpass = j.value("bandpass", true);
+ center = j.value("center", center);
+ span = j.value("span", span);
+ updateFilter();
+}
+
+void Math::TimeGate::setStart(double start)
+{
+ double stop = center + span / 2;
+ if(start < stop) {
+ span = stop - start;
+ center = (stop + start) / 2;
+ updateFilter();
+ }
+ emit centerChanged(center);
+ emit startChanged(center - span / 2);
+ emit spanChanged(span);
+}
+
+void Math::TimeGate::setStop(double stop)
+{
+ double start = center - span / 2;
+ if(stop > start) {
+ span = stop - start;
+ center = (start + stop) / 2;
+ updateFilter();
+ }
+ emit centerChanged(center);
+ emit stopChanged(center + span / 2);
+ emit spanChanged(span);
+}
+
+void Math::TimeGate::setCenter(double center)
+{
+ this->center = center;
+ updateFilter();
+ emit centerChanged(center);
+ emit startChanged(center - span / 2);
+ emit stopChanged(center + span / 2);
+}
+
+void Math::TimeGate::setSpan(double span)
+{
+ this->span = span;
+ updateFilter();
+ emit spanChanged(span);
+ emit startChanged(center - span / 2);
+ emit stopChanged(center + span / 2);
+}
+
+double Math::TimeGate::getStart()
+{
+ return center - span / 2;
+}
+
+double Math::TimeGate::getStop()
+{
+ return center + span / 2;
+}
+
+void Math::TimeGate::inputSamplesChanged(unsigned int begin, unsigned int end)
+{
+ if(data.size() != input->rData().size()) {
+ data.resize(input->rData().size());
+ updateFilter();
+ }
+ for(auto i = begin;irData()[i];
+ data[i].y *= filter[i];
+ }
+ emit outputSamplesChanged(begin, end);
+ success();
+}
+
+void Math::TimeGate::updateFilter()
+{
+ if(!input) {
+ return;
+ }
+ std::vector> buf;
+ filter.clear();
+ buf.resize(input->rData().size() * 2);
+ if(!buf.size()) {
+ return;
+ }
+ auto maxX = input->rData().back().x;
+ auto minX = input->rData().front().x;
+ if(center - span / 2 < minX) {
+ span = (center - minX) * 2;
+ }
+ if(center + span / 2 > maxX) {
+ span = (maxX - center) * 2;
+ }
+ auto wc1 = Util::Scale(center - span / 2, minX, maxX, 0, 1);
+ auto wc2 = Util::Scale(center + span / 2, minX, maxX, 0, 1);
+
+ // create ideal filter coefficients
+ for(unsigned int i=0;irData().size());
+}
+
+Math::TimeGateGraph::TimeGateGraph(QWidget *parent)
+ : QWidget(parent)
+{
+ gate = nullptr;
+ grabbedStop = false;
+ grabbedStart = false;
+ setMouseTracking(true);
+}
+
+QPoint Math::TimeGateGraph::plotValueToPixel(double x, double y)
+{
+ auto input = gate->getInput()->rData();
+ auto minX = input.front().x;
+ auto maxX = input.back().x;
+
+ int plotLeft = 0;
+ int plotRight = size().width();
+ int plotTop = 0;
+ int plotBottom = size().height();
+
+ QPoint p;
+ p.setX(Util::Scale(x, minX, maxX, plotLeft, plotRight));
+ p.setY(Util::Scale(y, -120, 20, plotBottom, plotTop));
+ return p;
+}
+
+QPointF Math::TimeGateGraph::pixelToPlotValue(QPoint p)
+{
+ auto input = gate->getInput()->rData();
+ auto minX = input.front().x;
+ auto maxX = input.back().x;
+
+ int plotLeft = 0;
+ int plotRight = size().width();
+ int plotTop = 0;
+ int plotBottom = size().height();
+
+ QPointF ret;
+ ret.setX(Util::Scale(p.x(), plotLeft, plotRight, minX, maxX));
+ ret.setY(Util::Scale(p.y(), plotBottom, plotTop, -120, 20));
+ return ret;
+}
+
+void Math::TimeGateGraph::paintEvent(QPaintEvent *event)
+{
+ if(!gate) {
+ return;
+ }
+ // grab input data
+ auto input = gate->getInput()->rData();
+
+ Q_UNUSED(event)
+ auto pref = Preferences::getInstance();
+ QPainter p(this);
+ // fill background
+ p.setBackground(QBrush(pref.General.graphColors.background));
+ p.fillRect(0, 0, width(), height(), QBrush(pref.General.graphColors.background));
+
+ // plot trace
+ auto pen = QPen(Qt::green, 1);
+ pen.setCosmetic(true);
+ pen.setStyle(Qt::SolidLine);
+ p.setPen(pen);
+ for(unsigned int i=1;irFilter();
+ pen = QPen(Qt::red, 1);
+ p.setPen(pen);
+ for(unsigned int i=1;igetStart(), 0).x();
+ auto stopX = plotValueToPixel(gate->getStop(), 0).x();
+ if(abs(event->pos().x() - startX) < catchDistance) {
+ grabbedStart = true;
+ } else if(abs(event->pos().x() - stopX) < catchDistance) {
+ grabbedStop = true;
+ }
+}
+
+void Math::TimeGateGraph::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_UNUSED(event);
+ grabbedStop = false;
+ grabbedStart = false;
+}
+
+void Math::TimeGateGraph::mouseMoveEvent(QMouseEvent *event)
+{
+ if(!gate) {
+ return;
+ }
+ auto value = pixelToPlotValue(event->pos());
+ if(grabbedStop) {
+ gate->setStop(value.x());
+ } else if(grabbedStart) {
+ gate->setStart(value.x());
+ } else {
+ // nothing grabbed but might be above start/stop -> check and change cursor
+ auto startX = plotValueToPixel(gate->getStart(), 0).x();
+ auto stopX = plotValueToPixel(gate->getStop(), 0).x();
+ if(abs(event->pos().x() - startX) < catchDistance || abs(event->pos().x() - stopX) < catchDistance) {
+ setCursor(Qt::SizeHorCursor);
+ } else {
+ setCursor(Qt::ArrowCursor);
+ }
+ }
+}
diff --git a/Software/PC_Application/Traces/Math/timegate.h b/Software/PC_Application/Traces/Math/timegate.h
new file mode 100644
index 0000000..41c2fa4
--- /dev/null
+++ b/Software/PC_Application/Traces/Math/timegate.h
@@ -0,0 +1,86 @@
+#ifndef TIMEGATE_H
+#define TIMEGATE_H
+
+#include "tracemath.h"
+#include "windowfunction.h"
+
+namespace Math {
+
+class TimeGate;
+
+class TimeGateGraph : public QWidget {
+public:
+ TimeGateGraph(QWidget *parent);
+
+ void setGate(TimeGate *gate) {
+ this->gate = gate;
+ }
+private:
+ static constexpr int catchDistance = 5;
+
+ QPoint plotValueToPixel(double x, double y);
+ QPointF pixelToPlotValue(QPoint p);
+ void paintEvent(QPaintEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ TimeGate *gate;
+ bool grabbedStart;
+ bool grabbedStop;
+};
+
+class TimeGate : public TraceMath
+{
+ Q_OBJECT
+public:
+ TimeGate();
+
+ virtual DataType outputType(DataType inputType) override;
+ virtual QString description() override;
+
+ virtual void edit() override;
+
+ static QWidget *createExplanationWidget();
+
+ virtual nlohmann::json toJSON() override;
+ virtual void fromJSON(nlohmann::json j) override;
+ Type getType() override {return Type::TimeGate;};
+
+ const std::vector &rFilter() { return filter;};
+
+ double getStart();
+ double getStop();
+
+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;
+
+ void setStart(double start);
+ void setStop(double stop);
+ void setCenter(double center);
+ void setSpan(double span);
+
+private slots:
+ void updateFilter();
+signals:
+ void filterUpdated();
+ void startChanged(double newval);
+ void stopChanged(double newval);
+ void centerChanged(double newval);
+ void spanChanged(double newval);
+private:
+ enum class Filter {
+ None,
+ Hamming,
+ Hann
+ };
+
+ bool bandpass;
+ double center, span;
+ WindowFunction window;
+ std::vector filter;
+};
+
+}
+
+#endif // TIMEGATE_H
diff --git a/Software/PC_Application/Traces/Math/timegatedialog.ui b/Software/PC_Application/Traces/Math/timegatedialog.ui
new file mode 100644
index 0000000..796ca17
--- /dev/null
+++ b/Software/PC_Application/Traces/Math/timegatedialog.ui
@@ -0,0 +1,163 @@
+
+
+ TimeGateDialog
+
+
+
+ 0
+ 0
+ 973
+ 317
+
+
+
+ Time Gate
+
+
+ true
+
+
+ -
+
+
-
+
+
-
+
+
-
+
+
+ Start:
+
+
+
+ -
+
+
+ -
+
+
+ Stop:
+
+
+
+ -
+
+
+ -
+
+
+ Center:
+
+
+
+ -
+
+
+ -
+
+
+ Span:
+
+
+
+ -
+
+
+ -
+
+
+ Type:
+
+
+
+ -
+
+
-
+
+ Bandpass
+
+
+ -
+
+ Notch
+
+
+
+
+
+
+ -
+
+
+ Shapewindow:
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+
+
+
+
+
+ SIUnitEdit
+ QLineEdit
+ CustomWidgets/siunitedit.h
+
+
+ Math::TimeGateGraph
+ QWidget
+
+ 1
+
+
+
+
+
+ buttonBox
+ accepted()
+ TimeGateDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ TimeGateDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/Software/PC_Application/Traces/Math/timegateexplanationwidget.ui b/Software/PC_Application/Traces/Math/timegateexplanationwidget.ui
new file mode 100644
index 0000000..323e013
--- /dev/null
+++ b/Software/PC_Application/Traces/Math/timegateexplanationwidget.ui
@@ -0,0 +1,44 @@
+
+
+ TimeGateExplanationWidget
+
+
+
+ 0
+ 0
+ 364
+ 412
+
+
+
+ Form
+
+
+ -
+
+
+ <html><head/><body><p><span style=" font-weight:600;">Time Gate</span></p><p>A bandpass or notch filter in the time domain. Depending on the setup, this operation can isolate or remove a response at a certain distance in time.<br/></p><p><br/></p></body></html>
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
diff --git a/Software/PC_Application/Traces/Math/tracemath.cpp b/Software/PC_Application/Traces/Math/tracemath.cpp
index fce714a..44a6192 100644
--- a/Software/PC_Application/Traces/Math/tracemath.cpp
+++ b/Software/PC_Application/Traces/Math/tracemath.cpp
@@ -4,7 +4,9 @@
#include "tdr.h"
#include "dft.h"
#include "expression.h"
+#include "timegate.h"
#include "Traces/trace.h"
+#include "ui_timedomaingatingexplanationwidget.h"
TraceMath::TraceMath()
{
@@ -13,20 +15,34 @@ TraceMath::TraceMath()
error("Invalid input");
}
-TraceMath *TraceMath::createMath(TraceMath::Type type)
+std::vector TraceMath::createMath(TraceMath::Type type)
{
+ std::vector ret;
switch(type) {
case Type::MedianFilter:
- return new Math::MedianFilter();
+ ret.push_back(new Math::MedianFilter());
+ break;
case Type::TDR:
- return new Math::TDR();
+ ret.push_back(new Math::TDR());
+ break;
case Type::DFT:
- return new Math::DFT();
+ ret.push_back(new Math::DFT());
+ break;
case Type::Expression:
- return new Math::Expression();
+ ret.push_back(new Math::Expression());
+ break;
+ case Type::TimeGate:
+ ret.push_back(new Math::TimeGate());
+ break;
+ case Type::TimeDomainGating:
+ ret.push_back(new Math::TDR());
+ ret.push_back(new Math::TimeGate());
+ ret.push_back(new Math::DFT());
+ break;
default:
- return nullptr;
+ break;
}
+ return ret;
}
TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
@@ -49,6 +65,17 @@ TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
ret.name = "Custom Expression";
ret.explanationWidget = Math::Expression::createExplanationWidget();
break;
+ case Type::TimeGate:
+ ret.name = "Time Gate";
+ ret.explanationWidget = Math::TimeGate::createExplanationWidget();
+ break;
+ case Type::TimeDomainGating: {
+ ret.name = "Time Domain Gating";
+ ret.explanationWidget = new QWidget();
+ auto ui = new Ui::TimeDomainGatingExplanationWidget;
+ ui->setupUi(ret.explanationWidget);
+ }
+ break;
default:
break;
}
diff --git a/Software/PC_Application/Traces/Math/tracemath.h b/Software/PC_Application/Traces/Math/tracemath.h
index ff29cbb..ecb6015 100644
--- a/Software/PC_Application/Traces/Math/tracemath.h
+++ b/Software/PC_Application/Traces/Math/tracemath.h
@@ -74,11 +74,13 @@ public:
TDR,
DFT,
Expression,
+ TimeGate,
+ TimeDomainGating,
// Add new math operations here, do not explicitly assign values and keep the Last entry at the last position
Last,
};
- static TraceMath *createMath(Type type);
+ static std::vector createMath(Type type);
class TypeInfo {
public:
QString name;
diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp
index 0f55b9d..751bba0 100644
--- a/Software/PC_Application/Traces/trace.cpp
+++ b/Software/PC_Application/Traces/trace.cpp
@@ -396,7 +396,7 @@ void Trace::fromJSON(nlohmann::json j)
continue;
}
qDebug() << "Creating math operation of type:" << operation;
- auto op = TraceMath::createMath(type);
+ auto op = TraceMath::createMath(type)[0];
if(jm.contains("settings")) {
op->fromJSON(jm["settings"]);
}
@@ -731,6 +731,18 @@ void Trace::addMathOperation(TraceMath *math)
updateLastMath(mathOps.rbegin());
}
+void Trace::addMathOperations(std::vector maths)
+{
+ TraceMath *input = lastMath;
+ for(auto m : maths) {
+ MathInfo info = {.math = m, .enabled = true};
+ m->assignInput(input);
+ input = m;
+ mathOps.push_back(info);
+ }
+ updateLastMath(mathOps.rbegin());
+}
+
void Trace::removeMathOperation(unsigned int index)
{
if(index < 1 || index >= mathOps.size()) {
diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h
index 8f51b31..b2d197a 100644
--- a/Software/PC_Application/Traces/trace.h
+++ b/Software/PC_Application/Traces/trace.h
@@ -99,7 +99,8 @@ public:
bool hasMathOperations(); // check if math operations are set up (not necessarily enabled)
void enableMath(bool enable);
// Adds a new math operation at the end of the list and enables it
- void addMathOperation(TraceMath *mathOps);
+ void addMathOperation(TraceMath *math);
+ void addMathOperations(std::vector maths);
// removes the math operation at the given index. Index 0 is invalid as this would be the trace itself
void removeMathOperation(unsigned int index);
// swaps the order of math operations at index and index+1. Does nothing if either index is invalid
diff --git a/Software/PC_Application/Traces/traceeditdialog.cpp b/Software/PC_Application/Traces/traceeditdialog.cpp
index a535794..8010880 100644
--- a/Software/PC_Application/Traces/traceeditdialog.cpp
+++ b/Software/PC_Application/Traces/traceeditdialog.cpp
@@ -141,10 +141,23 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
connect(ui->list, &QListWidget::doubleClicked, ui->buttonBox, &QDialogButtonBox::accepted);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
- auto newMath = TraceMath::createMath(static_cast(ui->list->currentRow()));
- if(newMath) {
- model->addOperation(newMath);
- }
+ auto type = static_cast(ui->list->currentRow());
+ auto newMath = TraceMath::createMath(type);
+ model->addOperations(newMath);
+ if(newMath.size() == 1) {
+ // any normal math operation added, edit now
+ newMath[0]->edit();
+ } else {
+ // composite operation added, check which one and edit the correct suboperation
+ switch(type) {
+ case TraceMath::Type::TimeDomainGating:
+ // TDR/DFT can be left at default, edit the actual gate
+ newMath[1]->edit();
+ break;
+ default:
+ break;
+ }
+ }
});
ui->list->setCurrentRow(0);
ui->stack->setCurrentIndex(0);
@@ -305,8 +318,13 @@ void MathModel::addOperation(TraceMath *math)
beginInsertRows(QModelIndex(), t.getMathOperations().size(), t.getMathOperations().size());
t.addMathOperation(math);
endInsertRows();
- // open the editor for the newly added operation
- math->edit();
+}
+
+void MathModel::addOperations(std::vector maths)
+{
+ beginInsertRows(QModelIndex(), t.getMathOperations().size(), t.getMathOperations().size() + maths.size() - 1);
+ t.addMathOperations(maths);
+ endInsertRows();
}
void MathModel::deleteRow(unsigned int row)
diff --git a/Software/PC_Application/Traces/traceeditdialog.h b/Software/PC_Application/Traces/traceeditdialog.h
index a4a794b..a590548 100644
--- a/Software/PC_Application/Traces/traceeditdialog.h
+++ b/Software/PC_Application/Traces/traceeditdialog.h
@@ -29,6 +29,7 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const override;
void addOperation(TraceMath *math);
+ void addOperations(std::vector maths);
void deleteRow(unsigned int row);
private:
diff --git a/Software/PC_Application/Traces/tracemarker.cpp b/Software/PC_Application/Traces/tracemarker.cpp
index 12e807b..39fbbac 100644
--- a/Software/PC_Application/Traces/tracemarker.cpp
+++ b/Software/PC_Application/Traces/tracemarker.cpp
@@ -7,6 +7,8 @@
#include
#include "tracemarkermodel.h"
#include "unit.h"
+#include
+#include
using namespace std;
@@ -19,13 +21,16 @@ TraceMarker::TraceMarker(TraceMarkerModel *model, int number, TraceMarker *paren
data(0),
type(Type::Manual),
description(descr),
+ contextmenu(nullptr),
delta(nullptr),
parent(parent),
cutoffAmplitude(-3.0),
peakThreshold(-40.0),
offset(10000)
{
-
+ connect(this, &TraceMarker::traceChanged, this, &TraceMarker::updateContextmenu);
+ connect(this, &TraceMarker::typeChanged, this, &TraceMarker::updateContextmenu);
+ updateContextmenu();
}
TraceMarker::~TraceMarker()
@@ -363,6 +368,29 @@ void TraceMarker::deltaDeleted()
update();
}
+void TraceMarker::updateContextmenu()
+{
+ if(contextmenu) {
+ delete contextmenu;
+ }
+ contextmenu = new QMenu();
+ auto typemenu = new QMenu("Type");
+ auto typegroup = new QActionGroup(contextmenu);
+ for(auto t : getSupportedTypes()) {
+ auto setTypeAction = new QAction(typeToString(t));
+ setTypeAction->setCheckable(true);
+ if(t == type) {
+ setTypeAction->setChecked(true);
+ }
+ connect(setTypeAction, &QAction::triggered, [=](){
+ setType(t);
+ });
+ typegroup->addAction(setTypeAction);
+ typemenu->addAction(setTypeAction);
+ }
+ contextmenu->addMenu(typemenu);
+}
+
std::set TraceMarker::getSupportedTypes()
{
set supported;
diff --git a/Software/PC_Application/Traces/tracemarker.h b/Software/PC_Application/Traces/tracemarker.h
index 5d9700d..8f2fbe0 100644
--- a/Software/PC_Application/Traces/tracemarker.h
+++ b/Software/PC_Application/Traces/tracemarker.h
@@ -57,6 +57,8 @@ public:
SIUnitEdit* getSettingsEditor();
void adjustSettings(double value);
+ QMenu *getContextMenu() { return contextmenu;}
+
// Updates marker position and data on automatic markers. Should be called whenever the tracedata is complete
void update();
TraceMarker *getParent() const;
@@ -93,6 +95,7 @@ private slots:
void updateSymbol();
void checkDeltaMarker();
void deltaDeleted();
+ void updateContextmenu();
signals:
void rawDataChanged();
void domainChanged();
@@ -133,6 +136,8 @@ private:
QString suffix;
QString description;
+ QMenu *contextmenu;
+
TraceMarker *delta;
std::vector helperMarkers;
TraceMarker *parent;
diff --git a/Software/PC_Application/Traces/traceplot.cpp b/Software/PC_Application/Traces/traceplot.cpp
index 08dd6b8..34aca49 100644
--- a/Software/PC_Application/Traces/traceplot.cpp
+++ b/Software/PC_Application/Traces/traceplot.cpp
@@ -92,7 +92,16 @@ void TracePlot::initializeTraceInfo()
void TracePlot::contextMenuEvent(QContextMenuEvent *event)
{
- contextmenu->exec(event->globalPos());
+ auto m = markerAtPosition(event->pos());
+ QMenu *menu;
+ if(m) {
+ // right click on marker, execute its contextmenu
+ menu = m->getContextMenu();
+ } else {
+ // no marker, contextmenu of graph
+ menu = contextmenu;
+ }
+ menu->exec(event->globalPos());
if(markedForDeletion) {
emit deleted(this);
delete this;
@@ -138,38 +147,7 @@ void TracePlot::paintEvent(QPaintEvent *event)
void TracePlot::mousePressEvent(QMouseEvent *event)
{
- auto clickPoint = event->pos() - QPoint(marginLeft, marginTop);
- // check if click was near a marker
- unsigned int closestDistance = numeric_limits::max();
- TraceMarker *closestMarker = nullptr;
- for(auto t : traces) {
- if(!t.second) {
- // this trace is disabled, skip
- continue;
- }
- auto markers = t.first->getMarkers();
- for(auto m : markers) {
- if(!m->isMovable()) {
- continue;
- }
- auto markerPoint = markerToPixel(m);
- if(markerPoint.isNull()) {
- // invalid, skip
- continue;
- }
- auto diff = markerPoint - clickPoint;
- unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
- if(distance < closestDistance) {
- closestDistance = distance;
- closestMarker = m;
- }
- }
- }
- if(closestDistance <= 400) {
- selectedMarker = closestMarker;
- } else {
- selectedMarker = nullptr;
- }
+ selectedMarker = markerAtPosition(event->pos(), true);
}
void TracePlot::mouseReleaseEvent(QMouseEvent *event)
@@ -205,6 +183,46 @@ void TracePlot::leaveEvent(QEvent *event)
selectedMarker = nullptr;
}
+TraceMarker *TracePlot::markerAtPosition(QPoint p, bool onlyMovable)
+{
+ auto clickPoint = p - QPoint(marginLeft, marginTop);
+ // check if click was near a marker
+ unsigned int closestDistance = numeric_limits::max();
+ TraceMarker *closestMarker = nullptr;
+ for(auto t : traces) {
+ if(!t.second) {
+ // this trace is disabled, skip
+ continue;
+ }
+ auto markers = t.first->getMarkers();
+ for(auto m : markers) {
+ if(!m->isMovable() && onlyMovable) {
+ continue;
+ }
+ auto markerPoint = markerToPixel(m);
+ if(markerPoint.isNull()) {
+ // invalid, skip
+ continue;
+ }
+ auto diff = markerPoint - clickPoint;
+ unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
+ if(distance < closestDistance) {
+ closestDistance = distance;
+ if(m->getParent()) {
+ closestMarker = m->getParent();
+ } else {
+ closestMarker = m;
+ }
+ }
+ }
+ }
+ if(closestDistance <= 400) {
+ return closestMarker;
+ } else {
+ return nullptr;
+ }
+}
+
void TracePlot::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("trace/pointer")) {
diff --git a/Software/PC_Application/Traces/traceplot.h b/Software/PC_Application/Traces/traceplot.h
index 28790e5..afdb63a 100644
--- a/Software/PC_Application/Traces/traceplot.h
+++ b/Software/PC_Application/Traces/traceplot.h
@@ -55,6 +55,8 @@ protected:
void mouseMoveEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
+ TraceMarker *markerAtPosition(QPoint p, bool onlyMovable = false);
+
// handle trace drops
virtual bool dropSupported(Trace *t) = 0;
void dragEnterEvent(QDragEnterEvent *event) override;
diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp
index 9c8dd71..b23f0d9 100644
--- a/Software/PC_Application/Traces/tracexyplot.cpp
+++ b/Software/PC_Application/Traces/tracexyplot.cpp
@@ -859,7 +859,7 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
ret.setY(real(data.y));
break;
case YAxisType::ImpulseMag:
- ret.setY(abs(data.y));
+ ret.setY(20*log10(abs(data.y)));
break;
case YAxisType::Step:
ret.setY(t->sample(sample, Trace::SampleType::TimeStep).y.real());
@@ -945,7 +945,7 @@ void TraceXYPlot::traceDropped(Trace *t, QPoint position)
InformationBox::ShowMessage("X Axis Domain Change", "You dropped a time domain trace but the graph is still set up for the frequency domain."
" All current traces will be removed and the graph changed to time domain.");
setXAxis(XAxisType::Time, XAxisMode::FitTraces, 0, 1, 0.1);
- setYAxis(0, YAxisType::ImpulseReal, false, true, 0, 1, 1.0);
+ setYAxis(0, YAxisType::ImpulseMag, false, true, 0, 1, 1.0);
setYAxis(1, YAxisType::Disabled, false, true, 0, 1, 1.0);
}
@@ -1002,7 +1002,7 @@ QString TraceXYPlot::AxisUnit(TraceXYPlot::YAxisType type)
case TraceXYPlot::YAxisType::Phase: return "°"; break;
case TraceXYPlot::YAxisType::VSWR: return ""; break;
case TraceXYPlot::YAxisType::ImpulseReal: return ""; break;
- case TraceXYPlot::YAxisType::ImpulseMag: return ""; break;
+ case TraceXYPlot::YAxisType::ImpulseMag: return "db"; break;
case TraceXYPlot::YAxisType::Step: return ""; break;
case TraceXYPlot::YAxisType::Impedance: return "Ohm"; break;
default: return ""; break;