diff --git a/Software/PC_Application/LibreVNA-GUI.pro b/Software/PC_Application/LibreVNA-GUI.pro
index a603252..51c7ead 100644
--- a/Software/PC_Application/LibreVNA-GUI.pro
+++ b/Software/PC_Application/LibreVNA-GUI.pro
@@ -102,6 +102,7 @@ HEADERS += \
Traces/tracewaterfall.h \
Traces/tracewidget.h \
Traces/tracexyplot.h \
+ Traces/tracepolar.h \
Traces/waterfallaxisdialog.h \
Traces/xyplotaxisdialog.h \
Traces/tracepolarchart.h \
@@ -228,6 +229,7 @@ SOURCES += \
Traces/tracewaterfall.cpp \
Traces/tracewidget.cpp \
Traces/tracexyplot.cpp \
+ Traces/tracepolar.cpp \
Traces/waterfallaxisdialog.cpp \
Traces/xyplotaxisdialog.cpp \
Util/util.cpp \
diff --git a/Software/PC_Application/Traces/smithchartdialog.ui b/Software/PC_Application/Traces/smithchartdialog.ui
index 7843bad..1adddc7 100644
--- a/Software/PC_Application/Traces/smithchartdialog.ui
+++ b/Software/PC_Application/Traces/smithchartdialog.ui
@@ -117,6 +117,16 @@
-
+ -
+
+
+ Offset real axis
+
+
+
+ -
+
+
diff --git a/Software/PC_Application/Traces/tracepolar.cpp b/Software/PC_Application/Traces/tracepolar.cpp
new file mode 100644
index 0000000..61e5d2c
--- /dev/null
+++ b/Software/PC_Application/Traces/tracepolar.cpp
@@ -0,0 +1,312 @@
+#include "tracepolar.h"
+
+#include "Marker/marker.h"
+#include "Util/util.h"
+
+#include
+
+using namespace std;
+
+TracePolar::TracePolar(TraceModel &model, QWidget *parent)
+ : TracePlot(model, parent)
+{
+ limitToSpan = true;
+ limitToEdge = true;
+ edgeReflection = 1.0;
+ initializeTraceInfo();
+}
+
+nlohmann::json TracePolar::toJSON()
+{
+ nlohmann::json j;
+ j["limit_to_span"] = limitToSpan;
+ j["limit_to_edge"] = limitToEdge;
+ j["edge_reflection"] = edgeReflection;
+ j["offset_axis_x"] = dx;
+ nlohmann::json jtraces;
+ for(auto t : traces) {
+ if(t.second) {
+ jtraces.push_back(t.first->toHash());
+ }
+ }
+ j["traces"] = jtraces;
+ return j;
+}
+
+void TracePolar::fromJSON(nlohmann::json j)
+{
+ limitToSpan = j.value("limit_to_span", true);
+ limitToEdge = j.value("limit_to_edge", false);
+ edgeReflection = j.value("edge_reflection", 1.0);
+ dx = j.value("offset_axis_x", 0.0);
+ for(unsigned int hash : j["traces"]) {
+ // attempt to find the traces with this hash
+ bool found = false;
+ for(auto t : model.getTraces()) {
+ if(t->toHash() == hash) {
+ enableTrace(t, true);
+ found = true;
+ break;
+ }
+ }
+ if(!found) {
+ qWarning() << "Unable to find trace with hash" << hash;
+ }
+ }
+}
+
+void TracePolar::wheelEvent(QWheelEvent *event)
+{
+ // most mousewheel have 15 degree increments, the reported delta is in 1/8th degree -> 120
+ auto increment = event->angleDelta().y() / 120.0;
+ // round toward bigger step in case of special higher resolution mousewheel
+ int steps = increment > 0 ? ceil(increment) : floor(increment);
+
+ constexpr double zoomfactor = 1.1;
+ auto zoom = pow(zoomfactor, steps);
+ edgeReflection /= zoom;
+
+ auto incrementX = event->angleDelta().x() / 120.0;
+ dx += incrementX/10;
+
+ triggerReplot();
+}
+
+QPoint TracePolar::dataToPixel(std::complex d)
+{
+ return transform.map(QPoint(d.real() * polarCoordMax * (1.0 / edgeReflection), -d.imag() * polarCoordMax * (1.0 / edgeReflection)));
+}
+
+QPoint TracePolar::dataToPixel(Trace::Data d)
+{
+ return dataToPixel(d.y);
+}
+
+std::complex TracePolar::dataAddDx(std::complex d)
+{
+ auto dataShift = complex(dx, 0);
+ d = d + dataShift;
+ return d;
+}
+
+Trace::Data TracePolar::dataAddDx(Trace::Data d)
+{
+ d.y = dataAddDx(d.y);
+ return d;
+}
+
+std::complex TracePolar::pixelToData(QPoint p)
+{
+ auto data = transform.inverted().map(QPointF(p));
+ return complex(data.x() / polarCoordMax * edgeReflection, -data.y() / polarCoordMax * edgeReflection);
+}
+
+QPoint TracePolar::markerToPixel(Marker *m)
+{
+ QPoint ret = QPoint();
+// if(!m->isTimeDomain()) {
+ if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
+ auto d = m->getData();
+ d = dataAddDx(d);
+ ret = dataToPixel(d);
+ }
+// }
+ return ret;
+}
+
+double TracePolar::nearestTracePoint(Trace *t, QPoint pixel, double *distance)
+{
+ double closestDistance = numeric_limits::max();
+ double closestXpos = 0;
+ unsigned int closestIndex = 0;
+ auto samples = t->size();
+ for(unsigned int i=0;isample(i);
+ data = dataAddDx(data);
+ auto plotPoint = dataToPixel(data);
+ if (plotPoint.isNull()) {
+ // destination point outside of currently displayed range
+ continue;
+ }
+ auto diff = plotPoint - pixel;
+ unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
+ if(distance < closestDistance) {
+ closestDistance = distance;
+ closestXpos = t->sample(i).x;
+ closestIndex = i;
+ }
+ }
+ closestDistance = sqrt(closestDistance);
+
+ if(closestIndex > 0) {
+ auto l1 = dataToPixel(dataAddDx(t->sample(closestIndex-1)));
+ auto l2 = dataToPixel(dataAddDx(t->sample(closestIndex)));
+ double ratio;
+ auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio);
+ if(distance < closestDistance) {
+ closestDistance = distance;
+ closestXpos = t->sample(closestIndex-1).x + (t->sample(closestIndex).x - t->sample(closestIndex-1).x) * ratio;
+ }
+ }
+ if(closestIndex < t->size() - 1) {
+ auto l1 = dataToPixel(dataAddDx(t->sample(closestIndex)));
+ auto l2 = dataToPixel(dataAddDx(t->sample(closestIndex+1)));
+ double ratio;
+ auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio);
+ if(distance < closestDistance) {
+ closestDistance = distance;
+ closestXpos = t->sample(closestIndex).x + (t->sample(closestIndex+1).x - t->sample(closestIndex).x) * ratio;
+ }
+ }
+ if(distance) {
+ *distance = closestDistance;
+ }
+ return closestXpos;
+}
+
+bool TracePolar::markerVisible(double x)
+{
+ if(limitToSpan) {
+ if(x >= sweep_fmin && x <= sweep_fmax) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // complete traces visible
+ return true;
+ }
+}
+
+void TracePolar::updateContextMenu()
+{
+ contextmenu->clear();
+ auto setup = new QAction("Setup...", contextmenu);
+ connect(setup, &QAction::triggered, this, &TracePolar::axisSetupDialog, Qt::UniqueConnection);
+ contextmenu->addAction(setup);
+
+ contextmenu->addSeparator();
+ auto image = new QAction("Save image...", contextmenu);
+ contextmenu->addAction(image);
+ connect(image, &QAction::triggered, [=]() {
+ auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, QFileDialog::DontUseNativeDialog);
+ if(filename.isEmpty()) {
+ // aborted selection
+ return;
+ }
+ if(filename.endsWith(".png")) {
+ filename.chop(4);
+ }
+ filename += ".png";
+ grab().save(filename);
+ });
+
+ auto createMarker = contextmenu->addAction("Add marker here");
+ bool activeTraces = false;
+ for(auto t : traces) {
+ if(t.second) {
+ activeTraces = true;
+ break;
+ }
+ }
+ if(!activeTraces) {
+ createMarker->setEnabled(false);
+ }
+ connect(createMarker, &QAction::triggered, [=](){
+ createMarkerAtPosition(contextmenuClickpoint);
+ });
+
+ contextmenu->addSection("Traces");
+ // Populate context menu
+ for(auto t : orderedTraces()) {
+ if(!supported(t)) {
+ continue;
+ }
+ auto action = new QAction(t->name(), contextmenu);
+ action->setCheckable(true);
+ if(traces[t]) {
+ action->setChecked(true);
+ }
+ connect(action, &QAction::toggled, [=](bool active) {
+ enableTrace(t, active);
+ });
+ contextmenu->addAction(action);
+ }
+
+ finishContextMenu();
+}
+
+PolarArc::PolarArc(QPointF center, double radius, double startAngle, double spanAngle)
+ : center(center),
+ radius(radius),
+ startAngle(startAngle),
+ spanAngle(spanAngle)
+{
+
+}
+
+void PolarArc::constrainToCircle(QPointF center, double radius)
+{
+ // check if arc/circle intersect
+ auto centerDiff = this->center - center;
+ auto centerDistSquared = centerDiff.x() * centerDiff.x() + centerDiff.y() * centerDiff.y();
+ if (centerDistSquared >= (radius + this->radius) * (radius + this->radius)) {
+ // arc completely outside of constraining circle
+ spanAngle = 0.0;
+ return;
+ } else if (centerDistSquared <= (radius - this->radius) * (radius - this->radius)) {
+ if (radius >= this->radius) {
+ // arc completely in constraining circle, do nothing
+ return;
+ } else {
+ // arc completely outside of circle
+ spanAngle = 0.0;
+ return;
+ }
+ } else {
+ // there are intersections between the arc and the circle. Calculate points according to https://stackoverflow.com/questions/3349125/circle-circle-intersection-points
+ auto distance = sqrt(centerDistSquared);
+ auto a = (this->radius*this->radius-radius*radius+distance*distance) / (2*distance);
+ auto h = sqrt(this->radius*this->radius - a*a);
+ auto intersectMiddle = this->center + a*(center - this->center) / distance;
+ auto rotatedCenterDiff = center - this->center;
+ swap(rotatedCenterDiff.rx(), rotatedCenterDiff.ry());
+ rotatedCenterDiff.setY(-rotatedCenterDiff.y());
+ auto intersect1 = intersectMiddle + h * rotatedCenterDiff / distance;
+ auto intersect2 = intersectMiddle - h * rotatedCenterDiff / distance;
+
+ // got intersection points, convert into angles from arc center
+ auto wrapAngle = [](double angle) -> double {
+ double ret = fmod(angle, 2*M_PI);
+ if(ret < 0) {
+ ret += 2*M_PI;
+ }
+ return ret;
+ };
+
+ auto angle1 = wrapAngle(atan2((intersect1 - this->center).y(), (intersect1 - this->center).x()));
+ auto angle2 = wrapAngle(atan2((intersect2 - this->center).y(), (intersect2 - this->center).x()));
+
+ auto angleDiff = wrapAngle(angle2 - angle1);
+ if ((angleDiff >= M_PI) ^ (a > 0.0)) {
+ // allowed angles go from intersect1 to intersect2
+ if(startAngle < angle1) {
+ startAngle = angle1;
+ }
+ auto maxSpan = wrapAngle(angle2 - startAngle);
+ if(spanAngle > maxSpan) {
+ spanAngle = maxSpan;
+ }
+ } else {
+ // allowed angles go from intersect2 to intersect1
+ if(startAngle < angle2) {
+ startAngle = angle2;
+ }
+ auto maxSpan = wrapAngle(angle1 - startAngle);
+ if(spanAngle > maxSpan) {
+ spanAngle = maxSpan;
+ }
+ }
+ }
+}
+
diff --git a/Software/PC_Application/Traces/tracepolar.h b/Software/PC_Application/Traces/tracepolar.h
new file mode 100644
index 0000000..1865e32
--- /dev/null
+++ b/Software/PC_Application/Traces/tracepolar.h
@@ -0,0 +1,53 @@
+#ifndef TRACEPOLAR_H
+#define TRACEPOLAR_H
+
+#include "traceplot.h"
+
+class PolarArc {
+public:
+ PolarArc(QPointF center, double radius, double startAngle = 0.0, double spanAngle = 2*M_PI);
+ void constrainToCircle(QPointF center, double radius);
+ QPointF center;
+ double radius;
+ double startAngle, spanAngle;
+};
+
+class TracePolar : public TracePlot
+{
+ Q_OBJECT
+public:
+ TracePolar(TraceModel &model, QWidget *parent = 0);
+
+ virtual nlohmann::json toJSON() override; // derived classes must call TracePolar::joJSON before doing anything
+ virtual void fromJSON(nlohmann::json j) override; // derived classes must call TracePolar::joJSON before doing anything
+
+ void wheelEvent(QWheelEvent *event) override;
+
+public slots:
+ virtual void axisSetupDialog() {};
+
+protected:
+ static constexpr double polarCoordMax = 4096;
+
+ virtual std::complex dataAddDx(std::complex d);
+ virtual Trace::Data dataAddDx(Trace::Data d);
+
+ QPoint dataToPixel(std::complex d);
+ QPoint dataToPixel(Trace::Data d);
+ std::complex pixelToData(QPoint p);
+
+ QPoint markerToPixel(Marker *m) override;
+ double nearestTracePoint(Trace *t, QPoint pixel, double *distance = nullptr) override;
+ virtual bool markerVisible(double x) override;
+
+ virtual void updateContextMenu() override;
+ virtual bool supported(Trace *t) override {Q_UNUSED(t) return false;};
+
+ bool limitToSpan;
+ bool limitToEdge;
+ double edgeReflection; // magnitude of reflection coefficient at the edge of the polar chart (zoom factor)
+ double dx;
+ QTransform transform;
+};
+
+#endif // TRACEPOLAR_H
diff --git a/Software/PC_Application/Traces/tracepolarchart.cpp b/Software/PC_Application/Traces/tracepolarchart.cpp
index dee64dc..48e60f0 100644
--- a/Software/PC_Application/Traces/tracepolarchart.cpp
+++ b/Software/PC_Application/Traces/tracepolarchart.cpp
@@ -2,10 +2,7 @@
#include "ui_polarchartdialog.h"
#include "preferences.h"
-#include "tracesmithchart.h"
#include "unit.h"
-#include "Marker/marker.h"
-#include "Util/util.h"
#include "appwindow.h"
#include
@@ -14,29 +11,8 @@
using namespace std;
TracePolarChart::TracePolarChart(TraceModel &model, QWidget *parent)
- : TracePlot(model, parent)
+ : TracePolar(model, parent)
{
- limitToSpan = true;
- limitToEdge = true;
- edgeReflection = 1.0;
- dx = 0.0;
- initializeTraceInfo();
-}
-
-void TracePolarChart::wheelEvent(QWheelEvent *event)
-{
- // most mousewheel have 15 degree increments, the reported delta is in 1/8th degree -> 120
- auto increment = event->angleDelta().y() / 120.0;
- // round toward bigger step in case of special higher resolution mousewheel
- int steps = increment > 0 ? ceil(increment) : floor(increment);
-
- constexpr double zoomfactor = 1.1;
- auto zoom = pow(zoomfactor, steps);
- edgeReflection /= zoom;
-
- auto incrementX = event->angleDelta().x() / 120.0;
- dx += incrementX/10;
- triggerReplot();
}
void TracePolarChart::axisSetupDialog()
@@ -82,35 +58,6 @@ void TracePolarChart::axisSetupDialog()
}
}
-QPoint TracePolarChart::dataToPixel(std::complex d)
-{
- return transform.map(QPoint(d.real() * polarCoordMax * (1.0 / edgeReflection), -d.imag() * polarCoordMax * (1.0 / edgeReflection)));
-}
-
-QPoint TracePolarChart::dataToPixel(Trace::Data d)
-{
- return dataToPixel(d.y);
-}
-
-std::complex TracePolarChart::dataAddDx(std::complex d)
-{
- auto dataShift = complex(dx, 0);
- d = d + dataShift;
- return d;
-}
-
-Trace::Data TracePolarChart::dataAddDx(Trace::Data d)
-{
- d.y = dataAddDx(d.y);
- return d;
-}
-
-std::complex TracePolarChart::pixelToData(QPoint p)
-{
- auto data = transform.inverted().map(QPointF(p));
- return complex(data.x() / polarCoordMax * edgeReflection, -data.y() / polarCoordMax * edgeReflection);
-}
-
void TracePolarChart::draw(QPainter &p) {
auto pref = Preferences::getInstance();
@@ -124,7 +71,7 @@ void TracePolarChart::draw(QPainter &p) {
transform = p.transform();
p.restore();
- auto drawArc = [&](SmithChartArc a) {
+ auto drawArc = [&](PolarArc a) {
a.constrainToCircle(QPointF(0,0), edgeReflection);
auto topleft = dataToPixel(complex(a.center.x() - a.radius, a.center.y() - a.radius));
auto bottomright = dataToPixel(complex(a.center.x() + a.radius, a.center.y() + a.radius));
@@ -137,7 +84,7 @@ void TracePolarChart::draw(QPainter &p) {
auto pen = QPen(pref.Graphs.Color.axis);
pen.setCosmetic(true);
p.setPen(pen);
- drawArc(SmithChartArc(QPointF(0.0, 0.0), edgeReflection, 0, 2*M_PI));
+ drawArc(PolarArc(QPointF(0.0, 0.0), edgeReflection, 0, 2*M_PI));
constexpr int Circles = 6;
pen = QPen(pref.Graphs.Color.Ticks.divisions, 0.5, Qt::DashLine);
@@ -145,10 +92,10 @@ void TracePolarChart::draw(QPainter &p) {
p.setPen(pen);
for(int i=1;i(dx, cir.center.y() + cir.radius*sin(angle));
@@ -180,7 +127,7 @@ void TracePolarChart::draw(QPainter &p) {
constexpr int Lines = 6;
for(int i=0;itoHash() == hash) {
- enableTrace(t, true);
- found = true;
- break;
- }
- }
- if(!found) {
- qWarning() << "Unable to find trace with hash" << hash;
- }
- }
-}
-
-nlohmann::json TracePolarChart::toJSON()
-{
- nlohmann::json j;
- j["limit_to_span"] = limitToSpan;
- j["limit_to_edge"] = limitToEdge;
- j["edge_reflection"] = edgeReflection;
- j["offset_axis_x"] = dx;
- nlohmann::json jtraces;
- for(auto t : traces) {
- if(t.second) {
- jtraces.push_back(t.first->toHash());
- }
- }
- j["traces"] = jtraces;
- return j;
-}
-
-double TracePolarChart::nearestTracePoint(Trace *t, QPoint pixel, double *distance)
-{
- double closestDistance = numeric_limits::max();
- double closestXpos = 0;
- unsigned int closestIndex = 0;
- auto samples = t->size();
- for(unsigned int i=0;isample(i);
- data = dataAddDx(data);
- auto plotPoint = dataToPixel(data);
- if (plotPoint.isNull()) {
- // destination point outside of currently displayed range
- continue;
- }
- auto diff = plotPoint - pixel;
- unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
- if(distance < closestDistance) {
- closestDistance = distance;
- closestXpos = t->sample(i).x;
- closestIndex = i;
- }
- }
- closestDistance = sqrt(closestDistance);
-
- if(closestIndex > 0) {
- auto l1 = dataToPixel(dataAddDx(t->sample(closestIndex-1)));
- auto l2 = dataToPixel(dataAddDx(t->sample(closestIndex)));
- double ratio;
- auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio);
- if(distance < closestDistance) {
- closestDistance = distance;
- closestXpos = t->sample(closestIndex-1).x + (t->sample(closestIndex).x - t->sample(closestIndex-1).x) * ratio;
- }
- }
- if(closestIndex < t->size() - 1) {
- auto l1 = dataToPixel(dataAddDx(t->sample(closestIndex)));
- auto l2 = dataToPixel(dataAddDx(t->sample(closestIndex+1)));
- double ratio;
- auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio);
- if(distance < closestDistance) {
- closestDistance = distance;
- closestXpos = t->sample(closestIndex).x + (t->sample(closestIndex+1).x - t->sample(closestIndex).x) * ratio;
- }
- }
- if(distance) {
- *distance = closestDistance;
- }
- return closestXpos;
-}
-
bool TracePolarChart::dropSupported(Trace *t)
{
if(!t->isReflection()) {
@@ -368,95 +226,11 @@ bool TracePolarChart::dropSupported(Trace *t)
}
}
-bool TracePolarChart::markerVisible(double x)
-{
- if(limitToSpan) {
- if(x >= sweep_fmin && x <= sweep_fmax) {
- return true;
- } else {
- return false;
- }
- } else {
- // complete traces visible
- return true;
- }
-}
-
bool TracePolarChart::supported(Trace *t)
{
return dropSupported(t);
}
-void TracePolarChart::updateContextMenu()
-{
- contextmenu->clear();
- auto setup = new QAction("Setup...", contextmenu);
- connect(setup, &QAction::triggered, this, &TracePolarChart::axisSetupDialog);
- contextmenu->addAction(setup);
-
- contextmenu->addSeparator();
- auto image = new QAction("Save image...", contextmenu);
- contextmenu->addAction(image);
- connect(image, &QAction::triggered, [=]() {
- auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, QFileDialog::DontUseNativeDialog);
- if(filename.isEmpty()) {
- // aborted selection
- return;
- }
- if(filename.endsWith(".png")) {
- filename.chop(4);
- }
- filename += ".png";
- grab().save(filename);
- });
-
- auto createMarker = contextmenu->addAction("Add marker here");
- bool activeTraces = false;
- for(auto t : traces) {
- if(t.second) {
- activeTraces = true;
- break;
- }
- }
- if(!activeTraces) {
- createMarker->setEnabled(false);
- }
-
- connect(createMarker, &QAction::triggered, [=](){
- createMarkerAtPosition(contextmenuClickpoint);
- });
-
- contextmenu->addSection("Traces");
- // Populate context menu
- for(auto t : orderedTraces()) {
- if(!supported(t)) {
- continue;
- }
- auto action = new QAction(t->name(), contextmenu);
- action->setCheckable(true);
- if(traces[t]) {
- action->setChecked(true);
- }
- connect(action, &QAction::toggled, [=](bool active) {
- enableTrace(t, active);
- });
- contextmenu->addAction(action);
- }
-
- finishContextMenu();
-}
-
-QPoint TracePolarChart::markerToPixel(Marker *m)
-{
- QPoint ret = QPoint();
- if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
- auto d = m->getData();
- d = dataAddDx(d);
- ret = dataToPixel(d);
- }
- return ret;
-}
-
QString TracePolarChart::mouseText(QPoint pos)
{
auto dataDx = pixelToData(pos);
@@ -480,12 +254,3 @@ QString TracePolarChart::mouseText(QPoint pos)
return QString();
}
}
-
-PolarChartCircle::PolarChartCircle(QPointF center, double radius, double startAngle, double spanAngle)
- : center(center),
- radius(radius),
- startAngle(startAngle),
- spanAngle(spanAngle)
-{
-
-}
diff --git a/Software/PC_Application/Traces/tracepolarchart.h b/Software/PC_Application/Traces/tracepolarchart.h
index 44a6a16..18b89f4 100644
--- a/Software/PC_Application/Traces/tracepolarchart.h
+++ b/Software/PC_Application/Traces/tracepolarchart.h
@@ -1,19 +1,9 @@
#ifndef TRACEPOLARCHART_H
#define TRACEPOLARCHART_H
-#include "traceplot.h"
+#include "tracepolar.h"
-class PolarChartCircle
-{
-public:
- PolarChartCircle(QPointF center, double radius, double startAngle = 0.0, double spanAngle = 2*M_PI);
- QPointF center;
- double radius;
- double startAngle, spanAngle;
-};
-
-
-class TracePolarChart : public TracePlot
+class TracePolarChart : public TracePolar
{
Q_OBJECT
public:
@@ -21,36 +11,14 @@ public:
virtual Type getType() override { return Type::PolarChart;};
- virtual nlohmann::json toJSON() override;
- virtual void fromJSON(nlohmann::json j) override;
-
- void wheelEvent(QWheelEvent *event) override;
public slots:
- void axisSetupDialog();
+ virtual void axisSetupDialog() override;
private:
- static constexpr double polarCoordMax = 4096;
-
- std::complex dataAddDx(std::complex d);
- Trace::Data dataAddDx(Trace::Data d);
-
- QPoint dataToPixel(std::complex d);
- QPoint dataToPixel(Trace::Data d);
- std::complex pixelToData(QPoint p);
- QPoint markerToPixel(Marker *m) override;
- double nearestTracePoint(Trace *t, QPoint pixel, double *distance = nullptr) override;
- virtual bool markerVisible(double x);
-
- virtual void updateContextMenu() override;
bool supported(Trace *t) override;
virtual void draw(QPainter& painter) override;
virtual bool dropSupported(Trace *t) override;
QString mouseText(QPoint pos) override;
- bool limitToSpan;
- bool limitToEdge;
- double edgeReflection;
- double dx;
- QTransform transform;
};
#endif // TRACEPOLARCHART_H
diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp
index e690320..35d4180 100644
--- a/Software/PC_Application/Traces/tracesmithchart.cpp
+++ b/Software/PC_Application/Traces/tracesmithchart.cpp
@@ -1,11 +1,9 @@
#include "tracesmithchart.h"
-#include "Marker/marker.h"
#include "preferences.h"
#include "ui_smithchartdialog.h"
#include "unit.h"
#include "QFileDialog"
-#include "Util/util.h"
#include "appwindow.h"
#include "CustomWidgets/informationbox.h"
@@ -18,29 +16,16 @@
using namespace std;
TraceSmithChart::TraceSmithChart(TraceModel &model, QWidget *parent)
- : TracePlot(model, parent)
+ : TracePolar(model, parent)
{
- limitToSpan = true;
- limitToEdge = true;
- edgeReflection = 1.0;
Z0 = 50.0;
- initializeTraceInfo();
}
nlohmann::json TraceSmithChart::toJSON()
{
nlohmann::json j;
- j["limit_to_span"] = limitToSpan;
- j["limit_to_edge"] = limitToEdge;
- j["edge_reflection"] = edgeReflection;
+ j = TracePolar::toJSON();
j["Z0"] = Z0;
- nlohmann::json jtraces;
- for(auto t : traces) {
- if(t.second) {
- jtraces.push_back(t.first->toHash());
- }
- }
- j["traces"] = jtraces;
nlohmann::json jlines;
for(auto line : constantLines) {
jlines.push_back(line.toJSON());
@@ -51,24 +36,8 @@ nlohmann::json TraceSmithChart::toJSON()
void TraceSmithChart::fromJSON(nlohmann::json j)
{
- limitToSpan = j.value("limit_to_span", true);
- limitToEdge = j.value("limit_to_edge", false);
- edgeReflection = j.value("edge_reflection", 1.0);
+ TracePolar::fromJSON(j);
Z0 = j.value("Z0", 50.0);
- for(unsigned int hash : j["traces"]) {
- // attempt to find the traces with this hash
- bool found = false;
- for(auto t : model.getTraces()) {
- if(t->toHash() == hash) {
- enableTrace(t, true);
- found = true;
- break;
- }
- }
- if(!found) {
- qWarning() << "Unable to find trace with hash" << hash;
- }
- }
if(j.contains("constantLines")) {
for(auto jline : j["constantLines"]) {
SmithChartConstantLine line;
@@ -78,19 +47,6 @@ void TraceSmithChart::fromJSON(nlohmann::json j)
}
}
-void TraceSmithChart::wheelEvent(QWheelEvent *event)
-{
- // most mousewheel have 15 degree increments, the reported delta is in 1/8th degree -> 120
- auto increment = event->angleDelta().y() / 120.0;
- // round toward bigger step in case of special higher resolution mousewheel
- int steps = increment > 0 ? ceil(increment) : floor(increment);
-
- constexpr double zoomfactor = 1.1;
- auto zoom = pow(zoomfactor, steps);
- edgeReflection /= zoom;
- triggerReplot();
-}
-
void TraceSmithChart::axisSetupDialog()
{
auto dialog = new QDialog();
@@ -110,6 +66,7 @@ void TraceSmithChart::axisSetupDialog()
ui->zoomFactor->setPrecision(3);
ui->zoomReflection->setValue(edgeReflection);
ui->zoomFactor->setValue(1.0/edgeReflection);
+ ui->offsetRealAxis->setValue(dx);
ui->impedance->setUnit("Ω");
ui->impedance->setPrecision(3);
@@ -133,6 +90,9 @@ void TraceSmithChart::axisSetupDialog()
edgeReflection = ui->zoomReflection->value();
ui->zoomFactor->setValueQuiet(1.0 / edgeReflection);
});
+ connect(ui->offsetRealAxis, &SIUnitEdit::valueChanged, [=](){
+ dx = ui->offsetRealAxis->value();
+ });
connect(ui->impedance, &SIUnitEdit::valueChanged, [=](){
Z0 = ui->impedance->value();
for(auto t : traces) {
@@ -189,95 +149,6 @@ void TraceSmithChart::axisSetupDialog()
}
}
-QPoint TraceSmithChart::dataToPixel(std::complex d)
-{
- return transform.map(QPoint(d.real() * smithCoordMax * (1.0 / edgeReflection), -d.imag() * smithCoordMax * (1.0 / edgeReflection)));
-}
-
-QPoint TraceSmithChart::dataToPixel(Trace::Data d)
-{
- return dataToPixel(d.y);}
-
-std::complex TraceSmithChart::pixelToData(QPoint p)
-{
- auto data = transform.inverted().map(QPointF(p));
- return complex(data.x() / smithCoordMax * edgeReflection, -data.y() / smithCoordMax * edgeReflection);
-}
-
-QPoint TraceSmithChart::markerToPixel(Marker *m)
-{
- QPoint ret = QPoint();
-// if(!m->isTimeDomain()) {
- if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
- auto d = m->getData();
- ret = dataToPixel(d);
- }
-// }
- return ret;
-}
-
-double TraceSmithChart::nearestTracePoint(Trace *t, QPoint pixel, double *distance)
-{
- double closestDistance = numeric_limits::max();
- double closestXpos = 0;
- unsigned int closestIndex = 0;
- auto samples = t->size();
- for(unsigned int i=0;isample(i);
- auto plotPoint = dataToPixel(data);
- if (plotPoint.isNull()) {
- // destination point outside of currently displayed range
- continue;
- }
- auto diff = plotPoint - pixel;
- unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
- if(distance < closestDistance) {
- closestDistance = distance;
- closestXpos = t->sample(i).x;
- closestIndex = i;
- }
- }
- closestDistance = sqrt(closestDistance);
- if(closestIndex > 0) {
- auto l1 = dataToPixel(t->sample(closestIndex-1));
- auto l2 = dataToPixel(t->sample(closestIndex));
- double ratio;
- auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio);
- if(distance < closestDistance) {
- closestDistance = distance;
- closestXpos = t->sample(closestIndex-1).x + (t->sample(closestIndex).x - t->sample(closestIndex-1).x) * ratio;
- }
- }
- if(closestIndex < t->size() - 1) {
- auto l1 = dataToPixel(t->sample(closestIndex));
- auto l2 = dataToPixel(t->sample(closestIndex+1));
- double ratio;
- auto distance = Util::distanceToLine(pixel, l1, l2, nullptr, &ratio);
- if(distance < closestDistance) {
- closestDistance = distance;
- closestXpos = t->sample(closestIndex).x + (t->sample(closestIndex+1).x - t->sample(closestIndex).x) * ratio;
- }
- }
- if(distance) {
- *distance = closestDistance;
- }
- return closestXpos;
-}
-
-bool TraceSmithChart::markerVisible(double x)
-{
- if(limitToSpan) {
- if(x >= sweep_fmin && x <= sweep_fmax) {
- return true;
- } else {
- return false;
- }
- } else {
- // complete traces visible
- return true;
- }
-}
-
bool TraceSmithChart::configureForTrace(Trace *t)
{
if(dropSupported(t)) {
@@ -299,7 +170,7 @@ void TraceSmithChart::draw(QPainter &p) {
auto w = p.window();
p.save();
p.translate(w.width()/2, w.height()/2);
- auto scale = qMin(w.height(), w.width()) / (2.0 * smithCoordMax);
+ auto scale = qMin(w.height(), w.width()) / (2.0 * polarCoordMax);
p.scale(scale, scale);
transform = p.transform();
@@ -326,8 +197,8 @@ void TraceSmithChart::draw(QPainter &p) {
p.setPen(pen);
for(int i=1;i(edgeReflection,0)),dataToPixel(complex(-edgeReflection,0)));
@@ -335,8 +206,8 @@ void TraceSmithChart::draw(QPainter &p) {
for(auto z : impedanceLines) {
z /= Z0;
auto radius = 1.0/z;
- drawArc(SmithChartArc(QPointF(1.0, radius), radius, 0, 2*M_PI));
- drawArc(SmithChartArc(QPointF(1.0, -radius), radius, 0, 2*M_PI));
+ drawArc(SmithChartArc(QPointF(1.0+dx, radius), radius, 0, 2*M_PI));
+ drawArc(SmithChartArc(QPointF(1.0+dx, -radius), radius, 0, 2*M_PI));
}
// draw custom constant parameter lines
@@ -372,6 +243,10 @@ void TraceSmithChart::draw(QPainter &p) {
if(isnan(now.y.real())) {
break;
}
+
+ last = dataAddDx(last);
+ now = dataAddDx(now);
+
if (limitToEdge && (abs(last.y) > edgeReflection || abs(now.y) > edgeReflection)) {
// outside of visible area
continue;
@@ -400,6 +275,8 @@ void TraceSmithChart::draw(QPainter &p) {
continue;
}
auto coords = m->getData();
+ coords = dataAddDx(coords);
+
if (limitToEdge && abs(coords) > edgeReflection) {
// outside of visible area
continue;
@@ -415,7 +292,7 @@ void TraceSmithChart::draw(QPainter &p) {
p.setOpacity(0.5);
p.setBrush(Qt::white);
p.setPen(Qt::white);
- p.drawEllipse(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax);
+ p.drawEllipse(-polarCoordMax, -polarCoordMax, 2*polarCoordMax, 2*polarCoordMax);
auto font = p.font();
font.setPixelSize(20);
p.setFont(font);
@@ -459,8 +336,9 @@ bool TraceSmithChart::dropSupported(Trace *t)
QString TraceSmithChart::mouseText(QPoint pos)
{
- auto data = pixelToData(pos);
- if(abs(data) <= edgeReflection) {
+ auto dataDx = pixelToData(pos);
+ if(abs(dataDx) <= edgeReflection) {
+ auto data = complex(dataDx.real()-dx, dataDx.imag());
data = Z0 * (1.0 + data) / (1.0 - data);
auto ret = Unit::ToString(data.real(), "", " ", 3);
if(data.imag() >= 0) {
@@ -473,64 +351,6 @@ QString TraceSmithChart::mouseText(QPoint pos)
}
}
-void TraceSmithChart::updateContextMenu()
-{
- contextmenu->clear();
- auto setup = new QAction("Setup...", contextmenu);
- connect(setup, &QAction::triggered, this, &TraceSmithChart::axisSetupDialog);
- contextmenu->addAction(setup);
-
- contextmenu->addSeparator();
- auto image = new QAction("Save image...", contextmenu);
- contextmenu->addAction(image);
- connect(image, &QAction::triggered, [=]() {
- auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, QFileDialog::DontUseNativeDialog);
- if(filename.isEmpty()) {
- // aborted selection
- return;
- }
- if(filename.endsWith(".png")) {
- filename.chop(4);
- }
- filename += ".png";
- grab().save(filename);
- });
-
- auto createMarker = contextmenu->addAction("Add marker here");
- bool activeTraces = false;
- for(auto t : traces) {
- if(t.second) {
- activeTraces = true;
- break;
- }
- }
- if(!activeTraces) {
- createMarker->setEnabled(false);
- }
- connect(createMarker, &QAction::triggered, [=](){
- createMarkerAtPosition(contextmenuClickpoint);
- });
-
- contextmenu->addSection("Traces");
- // Populate context menu
- for(auto t : orderedTraces()) {
- if(!supported(t)) {
- continue;
- }
- auto action = new QAction(t->name(), contextmenu);
- action->setCheckable(true);
- if(traces[t]) {
- action->setChecked(true);
- }
- connect(action, &QAction::toggled, [=](bool active) {
- enableTrace(t, active);
- });
- contextmenu->addAction(action);
- }
-
- finishContextMenu();
-}
-
bool TraceSmithChart::supported(Trace *t)
{
if(t->getReferenceImpedance() != Z0) {
diff --git a/Software/PC_Application/Traces/tracesmithchart.h b/Software/PC_Application/Traces/tracesmithchart.h
index c8f9276..0764124 100644
--- a/Software/PC_Application/Traces/tracesmithchart.h
+++ b/Software/PC_Application/Traces/tracesmithchart.h
@@ -1,7 +1,7 @@
#ifndef TRACESMITHCHART_H
#define TRACESMITHCHART_H
-#include "traceplot.h"
+#include "tracepolar.h"
#include
#include
@@ -98,7 +98,7 @@ private:
TraceSmithChart &chart;
};
-class TraceSmithChart : public TracePlot
+class TraceSmithChart : public TracePolar
{
Q_OBJECT
friend class SmithChartContantLineModel;
@@ -110,34 +110,17 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
- void wheelEvent(QWheelEvent *event) override;
public slots:
- void axisSetupDialog();
+ virtual void axisSetupDialog() override;
protected:
- static constexpr double screenUsage = 0.9;
- static constexpr double smithCoordMax = 4096;
-
- QPoint dataToPixel(std::complex d);
- QPoint dataToPixel(Trace::Data d);
- std::complex pixelToData(QPoint p);
- QPoint markerToPixel(Marker *m) override;
- double nearestTracePoint(Trace *t, QPoint pixel, double *distance = nullptr) override;
- virtual bool markerVisible(double x);
-
- //void paintEvent(QPaintEvent *event) override;
- virtual bool configureForTrace(Trace *t);
- virtual void updateContextMenu() override;
+ virtual bool configureForTrace(Trace *t) override;
bool supported(Trace *t) override;
virtual void draw(QPainter& painter) override;
virtual void traceDropped(Trace *t, QPoint position) override;
virtual bool dropSupported(Trace *t) override;
QString mouseText(QPoint pos) override;
- bool limitToSpan;
- bool limitToEdge;
- double edgeReflection; // magnitude of reflection coefficient at the edge of the smith chart (zoom factor)
double Z0;
- QTransform transform;
std::vector constantLines;
};