2020-10-28 05:07:14 +08:00
|
|
|
#include "tracexyplot.h"
|
2020-08-31 04:03:41 +08:00
|
|
|
#include <QGridLayout>
|
|
|
|
#include "qwtplotpiecewisecurve.h"
|
|
|
|
#include "qwt_series_data.h"
|
|
|
|
#include "trace.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include <QFrame>
|
|
|
|
#include <qwt_plot_canvas.h>
|
|
|
|
#include <qwt_scale_div.h>
|
|
|
|
#include <qwt_plot_layout.h>
|
|
|
|
#include "tracemarker.h"
|
|
|
|
#include <qwt_symbol.h>
|
|
|
|
#include <qwt_picker_machine.h>
|
2020-10-28 05:07:14 +08:00
|
|
|
#include "xyplotaxisdialog.h"
|
2020-10-23 03:12:33 +08:00
|
|
|
#include <preferences.h>
|
2020-08-31 04:03:41 +08:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
set<TraceXYPlot*> TraceXYPlot::allPlots;
|
2020-10-23 03:12:33 +08:00
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
const set<TraceXYPlot::YAxisType> TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled,
|
|
|
|
TraceXYPlot::YAxisType::Magnitude,
|
|
|
|
TraceXYPlot::YAxisType::Phase,
|
|
|
|
TraceXYPlot::YAxisType::VSWR,
|
|
|
|
TraceXYPlot::YAxisType::Impulse,
|
|
|
|
TraceXYPlot::YAxisType::Step,
|
|
|
|
TraceXYPlot::YAxisType::Impedance};
|
|
|
|
|
|
|
|
static double FrequencyAxisTransformation(TraceXYPlot::YAxisType type, complex<double> data) {
|
2020-08-31 04:03:41 +08:00
|
|
|
switch(type) {
|
2020-10-28 05:07:14 +08:00
|
|
|
case TraceXYPlot::YAxisType::Magnitude: return 20*log10(abs(data)); break;
|
|
|
|
case TraceXYPlot::YAxisType::Phase: return arg(data) * 180.0 / M_PI; break;
|
|
|
|
case TraceXYPlot::YAxisType::VSWR:
|
2020-08-31 04:03:41 +08:00
|
|
|
if(abs(data) < 1.0) {
|
|
|
|
return (1+abs(data)) / (1-abs(data));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return numeric_limits<double>::quiet_NaN();
|
|
|
|
}
|
2020-10-28 05:07:14 +08:00
|
|
|
static double TimeAxisTransformation(TraceXYPlot::YAxisType type, Trace *t, int index) {
|
|
|
|
auto timeData = t->getTDR()[index];
|
|
|
|
switch(type) {
|
|
|
|
case TraceXYPlot::YAxisType::Impulse: return timeData.impulseResponse; break;
|
|
|
|
case TraceXYPlot::YAxisType::Step: return timeData.stepResponse; break;
|
|
|
|
case TraceXYPlot::YAxisType::Impedance:
|
|
|
|
if(abs(timeData.stepResponse) < 1.0) {
|
|
|
|
return 50 * (1+timeData.stepResponse) / (1-timeData.stepResponse);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
return numeric_limits<double>::quiet_NaN();
|
|
|
|
}
|
2020-08-31 04:03:41 +08:00
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
class QwtTraceSeries : public QwtSeriesData<QPointF> {
|
2020-08-31 04:03:41 +08:00
|
|
|
public:
|
2020-10-28 05:07:14 +08:00
|
|
|
QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, TraceXYPlot::XAxisType Xtype)
|
2020-08-31 04:03:41 +08:00
|
|
|
: QwtSeriesData<QPointF>(),
|
2020-10-28 05:07:14 +08:00
|
|
|
Ytype(Ytype),
|
|
|
|
Xtype(Xtype),
|
|
|
|
t(t){}
|
2020-08-31 04:03:41 +08:00
|
|
|
size_t size() const override {
|
2020-10-28 05:07:14 +08:00
|
|
|
switch(Ytype) {
|
|
|
|
case TraceXYPlot::YAxisType::Magnitude:
|
|
|
|
case TraceXYPlot::YAxisType::Phase:
|
|
|
|
case TraceXYPlot::YAxisType::VSWR:
|
|
|
|
return t.size();
|
|
|
|
case TraceXYPlot::YAxisType::Impulse:
|
|
|
|
case TraceXYPlot::YAxisType::Step:
|
|
|
|
case TraceXYPlot::YAxisType::Impedance:
|
|
|
|
return t.getTDR().size();
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
QPointF sample(size_t i) const override {
|
2020-10-28 05:07:14 +08:00
|
|
|
switch(Ytype) {
|
|
|
|
case TraceXYPlot::YAxisType::Magnitude:
|
|
|
|
case TraceXYPlot::YAxisType::Phase:
|
|
|
|
case TraceXYPlot::YAxisType::VSWR: {
|
|
|
|
Trace::Data d = t.sample(i);
|
|
|
|
QPointF p;
|
|
|
|
p.setX(d.frequency);
|
|
|
|
p.setY(FrequencyAxisTransformation(Ytype, d.S));
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
case TraceXYPlot::YAxisType::Impulse:
|
|
|
|
case TraceXYPlot::YAxisType::Step:
|
|
|
|
case TraceXYPlot::YAxisType::Impedance: {
|
|
|
|
auto sample = t.getTDR()[i];
|
|
|
|
QPointF p;
|
|
|
|
// TODO set distance
|
|
|
|
p.setX(sample.time);
|
|
|
|
p.setY(TimeAxisTransformation(Ytype, &t, i));
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return QPointF();
|
|
|
|
}
|
|
|
|
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
QRectF boundingRect() const override {
|
|
|
|
return qwtBoundingRect(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2020-10-28 05:07:14 +08:00
|
|
|
TraceXYPlot::YAxisType Ytype;
|
|
|
|
TraceXYPlot::XAxisType Xtype;
|
2020-08-31 04:03:41 +08:00
|
|
|
Trace &t;
|
|
|
|
};
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
|
2020-08-31 04:03:41 +08:00
|
|
|
: TracePlot(parent),
|
|
|
|
selectedMarker(nullptr)
|
|
|
|
{
|
2020-11-01 06:03:34 +08:00
|
|
|
YAxis[0].log = false;
|
|
|
|
YAxis[0].Ytype = YAxisType::Disabled;
|
|
|
|
YAxis[0].rangeDiv = 1;
|
|
|
|
YAxis[0].rangeMax = 10;
|
|
|
|
YAxis[0].rangeMin = 0;
|
|
|
|
YAxis[0].autorange = false;
|
|
|
|
YAxis[1].log = false;
|
|
|
|
YAxis[1].Ytype = YAxisType::Disabled;
|
|
|
|
YAxis[1].rangeDiv = 1;
|
|
|
|
YAxis[1].rangeMax = 10;
|
|
|
|
YAxis[1].rangeMin = 0;
|
|
|
|
YAxis[1].autorange = false;
|
|
|
|
XAxis.Xtype = XAxisType::Frequency;
|
|
|
|
XAxis.log = false;
|
|
|
|
XAxis.rangeDiv = 1;
|
|
|
|
XAxis.rangeMax = 10;
|
|
|
|
XAxis.rangeMin = 0;
|
|
|
|
XAxis.autorange = true;
|
|
|
|
|
2020-08-31 04:03:41 +08:00
|
|
|
plot = new QwtPlot(this);
|
2020-10-23 03:12:33 +08:00
|
|
|
|
2020-08-31 04:03:41 +08:00
|
|
|
auto canvas = new QwtPlotCanvas(plot);
|
|
|
|
canvas->setFrameStyle(QFrame::Plain);
|
|
|
|
plot->setCanvas(canvas);
|
|
|
|
plot->setAutoFillBackground(true);
|
2020-10-23 03:12:33 +08:00
|
|
|
grid = new QwtPlotGrid();
|
|
|
|
grid->attach(plot);
|
|
|
|
setColorFromPreferences();
|
2020-08-31 04:03:41 +08:00
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
auto selectPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas());
|
2020-08-31 04:03:41 +08:00
|
|
|
selectPicker->setStateMachine(new QwtPickerClickPointMachine);
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
drawPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas());
|
2020-08-31 04:03:41 +08:00
|
|
|
drawPicker->setStateMachine(new QwtPickerDragPointMachine);
|
|
|
|
drawPicker->setTrackerPen(QPen(Qt::white));
|
|
|
|
|
|
|
|
// Marker selection
|
2020-10-20 01:39:16 +08:00
|
|
|
connect(selectPicker, SIGNAL(selected(QPointF)), this, SLOT(clicked(QPointF)));;
|
2020-08-31 04:03:41 +08:00
|
|
|
// Marker movement
|
2020-10-20 01:39:16 +08:00
|
|
|
connect(drawPicker, SIGNAL(moved(QPointF)), this, SLOT(moved(QPointF)));
|
2020-08-31 04:03:41 +08:00
|
|
|
|
|
|
|
auto layout = new QGridLayout;
|
|
|
|
layout->addWidget(plot);
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
setLayout(layout);
|
|
|
|
plot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
|
|
initializeTraceInfo(model);
|
|
|
|
setAutoFillBackground(true);
|
|
|
|
|
|
|
|
// Setup default axis
|
|
|
|
setYAxis(0, YAxisType::Magnitude, false, false, -120, 20, 10);
|
|
|
|
setYAxis(1, YAxisType::Phase, false, false, -180, 180, 30);
|
|
|
|
// enable autoscaling and set for full span (no information about actual span available yet)
|
|
|
|
setXAxis(0, 6000000000);
|
2020-10-28 05:07:14 +08:00
|
|
|
setXAxis(XAxisType::Frequency, true, 0, 6000000000, 600000000);
|
2020-09-18 01:54:03 +08:00
|
|
|
// get notified when the span changes
|
2020-10-28 05:07:14 +08:00
|
|
|
connect(&model, &TraceModel::SpanChanged, this, qOverload<double, double>(&TraceXYPlot::setXAxis));
|
2020-10-23 03:12:33 +08:00
|
|
|
|
|
|
|
allPlots.insert(this);
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
TraceXYPlot::~TraceXYPlot()
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
for(int axis = 0;axis < 2;axis++) {
|
|
|
|
for(auto pd : curves[axis]) {
|
|
|
|
delete pd.second.curve;
|
|
|
|
}
|
|
|
|
}
|
2020-10-20 01:39:16 +08:00
|
|
|
delete drawPicker;
|
2020-10-23 03:12:33 +08:00
|
|
|
allPlots.erase(this);
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::setXAxis(double min, double max)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
sweep_fmin = min;
|
|
|
|
sweep_fmax = max;
|
|
|
|
updateXAxis();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool autorange, double min, double max, double div)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
2020-10-28 05:07:14 +08:00
|
|
|
if(YAxis[axis].Ytype != type) {
|
2020-08-31 04:03:41 +08:00
|
|
|
// remove traces that are active but not supported with the new axis type
|
|
|
|
bool erased = false;
|
|
|
|
do {
|
|
|
|
erased = false;
|
|
|
|
for(auto t : tracesAxis[axis]) {
|
|
|
|
if(!supported(t, type)) {
|
|
|
|
enableTraceAxis(t, axis, false);
|
|
|
|
erased = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while(erased);
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
if(isTDRtype(YAxis[axis].Ytype)) {
|
|
|
|
for(auto t : tracesAxis[axis]) {
|
|
|
|
t->removeTDRinterest();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
YAxis[axis].Ytype = type;
|
|
|
|
|
2020-08-31 04:03:41 +08:00
|
|
|
for(auto t : tracesAxis[axis]) {
|
|
|
|
// supported but needs an adjusted QwtSeriesData
|
|
|
|
auto td = curves[axis][t];
|
|
|
|
td.data = createQwtSeriesData(*t, axis);
|
|
|
|
// call to setSamples deletes old QwtSeriesData
|
|
|
|
td.curve->setSamples(td.data);
|
|
|
|
if(axis == 0) {
|
|
|
|
// update marker data
|
|
|
|
auto marker = t->getMarkers();
|
|
|
|
for(auto m : marker) {
|
|
|
|
markerDataChanged(m);
|
|
|
|
}
|
|
|
|
}
|
2020-10-28 05:07:14 +08:00
|
|
|
if(isTDRtype(type)) {
|
|
|
|
t->addTDRinterest();
|
|
|
|
}
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
YAxis[axis].log = log;
|
|
|
|
YAxis[axis].autorange = autorange;
|
|
|
|
YAxis[axis].rangeMin = min;
|
|
|
|
YAxis[axis].rangeMax = max;
|
|
|
|
YAxis[axis].rangeDiv = div;
|
|
|
|
// enable/disable y axis
|
|
|
|
auto qwtaxis = axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight;
|
|
|
|
plot->enableAxis(qwtaxis, type != YAxisType::Disabled);
|
|
|
|
if(autorange) {
|
|
|
|
plot->setAxisAutoScale(qwtaxis, true);
|
|
|
|
} else {
|
|
|
|
plot->setAxisScale(qwtaxis, min, max, div);
|
|
|
|
}
|
|
|
|
updateContextMenu();
|
|
|
|
replot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::setXAxis(XAxisType type, bool autorange, double min, double max, double div)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
2020-10-28 05:07:14 +08:00
|
|
|
XAxis.Xtype = type;
|
2020-08-31 04:03:41 +08:00
|
|
|
XAxis.autorange = autorange;
|
|
|
|
XAxis.rangeMin = min;
|
|
|
|
XAxis.rangeMax = max;
|
|
|
|
XAxis.rangeDiv = div;
|
|
|
|
updateXAxis();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::enableTrace(Trace *t, bool enabled)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
for(int axis = 0;axis < 2;axis++) {
|
2020-10-28 05:07:14 +08:00
|
|
|
if(supported(t, YAxis[axis].Ytype)) {
|
2020-08-31 04:03:41 +08:00
|
|
|
enableTraceAxis(t, axis, enabled);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::updateGraphColors()
|
2020-10-23 03:12:33 +08:00
|
|
|
{
|
|
|
|
for(auto p : allPlots) {
|
|
|
|
p->setColorFromPreferences();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
bool TraceXYPlot::isTDRtype(TraceXYPlot::YAxisType type)
|
|
|
|
{
|
|
|
|
switch(type) {
|
|
|
|
case YAxisType::Impulse:
|
|
|
|
case YAxisType::Step:
|
|
|
|
case YAxisType::Impedance:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TraceXYPlot::updateContextMenu()
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
contextmenu->clear();
|
2020-11-01 06:03:34 +08:00
|
|
|
auto setup = new QAction("Axis setup...", contextmenu);
|
2020-08-31 04:03:41 +08:00
|
|
|
connect(setup, &QAction::triggered, [this]() {
|
2020-10-28 05:07:14 +08:00
|
|
|
auto setup = new XYplotAxisDialog(this);
|
2020-08-31 04:03:41 +08:00
|
|
|
setup->show();
|
|
|
|
});
|
|
|
|
contextmenu->addAction(setup);
|
|
|
|
for(int axis = 0;axis < 2;axis++) {
|
2020-10-28 05:07:14 +08:00
|
|
|
if(YAxis[axis].Ytype == YAxisType::Disabled) {
|
2020-08-31 04:03:41 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(axis == 0) {
|
|
|
|
contextmenu->addSection("Primary Traces");
|
|
|
|
} else {
|
|
|
|
contextmenu->addSection("Secondary Traces");
|
|
|
|
}
|
|
|
|
for(auto t : traces) {
|
|
|
|
// Skip traces that are not applicable for the selected axis type
|
2020-10-28 05:07:14 +08:00
|
|
|
if(!supported(t.first, YAxis[axis].Ytype)) {
|
2020-08-31 04:03:41 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-11-01 06:03:34 +08:00
|
|
|
auto action = new QAction(t.first->name(), contextmenu);
|
2020-08-31 04:03:41 +08:00
|
|
|
action->setCheckable(true);
|
|
|
|
if(tracesAxis[axis].find(t.first) != tracesAxis[axis].end()) {
|
|
|
|
action->setChecked(true);
|
|
|
|
}
|
|
|
|
connect(action, &QAction::toggled, [=](bool active) {
|
|
|
|
enableTraceAxis(t.first, axis, active);
|
|
|
|
});
|
|
|
|
contextmenu->addAction(action);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
contextmenu->addSeparator();
|
2020-11-01 06:03:34 +08:00
|
|
|
auto close = new QAction("Close", contextmenu);
|
2020-08-31 04:03:41 +08:00
|
|
|
contextmenu->addAction(close);
|
|
|
|
connect(close, &QAction::triggered, [=]() {
|
|
|
|
markedForDeletion = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
bool TraceXYPlot::supported(Trace *)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
// potentially possible to add every kind of trace (depends on axis)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::replot()
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
plot->replot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
switch(type) {
|
|
|
|
case YAxisType::Disabled: return "Disabled"; break;
|
|
|
|
case YAxisType::Magnitude: return "Magnitude"; break;
|
|
|
|
case YAxisType::Phase: return "Phase"; break;
|
|
|
|
case YAxisType::VSWR: return "VSWR"; break;
|
|
|
|
default: return "Unknown"; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
bool alreadyEnabled = tracesAxis[axis].find(t) != tracesAxis[axis].end();
|
|
|
|
if(alreadyEnabled != enabled) {
|
|
|
|
if(enabled) {
|
|
|
|
tracesAxis[axis].insert(t);
|
|
|
|
CurveData cd;
|
|
|
|
cd.data = createQwtSeriesData(*t, axis);
|
|
|
|
cd.curve = new QwtPlotPiecewiseCurve();
|
|
|
|
cd.curve->attach(plot);
|
|
|
|
cd.curve->setYAxis(axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight);
|
|
|
|
cd.curve->setSamples(cd.data);
|
|
|
|
curves[axis][t] = cd;
|
|
|
|
// connect signals
|
2020-10-28 05:07:14 +08:00
|
|
|
connect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot);
|
|
|
|
connect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged);
|
|
|
|
connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged);
|
|
|
|
connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot);
|
2020-08-31 04:03:41 +08:00
|
|
|
if(axis == 0) {
|
2020-10-28 05:07:14 +08:00
|
|
|
connect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded);
|
|
|
|
connect(t, &Trace::markerRemoved, this, &TraceXYPlot::markerRemoved);
|
2020-08-31 04:03:41 +08:00
|
|
|
auto tracemarkers = t->getMarkers();
|
|
|
|
for(auto m : tracemarkers) {
|
|
|
|
markerAdded(m);
|
|
|
|
}
|
|
|
|
}
|
2020-10-28 05:07:14 +08:00
|
|
|
if(isTDRtype(YAxis[axis].Ytype)) {
|
|
|
|
t->addTDRinterest();
|
|
|
|
}
|
2020-08-31 04:03:41 +08:00
|
|
|
traceColorChanged(t);
|
|
|
|
} else {
|
2020-10-28 05:07:14 +08:00
|
|
|
if(isTDRtype(YAxis[axis].Ytype)) {
|
|
|
|
t->removeTDRinterest();
|
|
|
|
}
|
2020-08-31 04:03:41 +08:00
|
|
|
tracesAxis[axis].erase(t);
|
|
|
|
// clean up and delete
|
|
|
|
if(curves[axis].find(t) != curves[axis].end()) {
|
2020-10-01 23:52:42 +08:00
|
|
|
delete curves[axis][t].curve;
|
2020-08-31 04:03:41 +08:00
|
|
|
curves[axis].erase(t);
|
|
|
|
}
|
|
|
|
int otherAxis = axis == 0 ? 1 : 0;
|
|
|
|
if(curves[otherAxis].find(t) == curves[otherAxis].end()) {
|
|
|
|
// this trace is not used anymore, disconnect from notifications
|
2020-10-28 05:07:14 +08:00
|
|
|
disconnect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot);
|
|
|
|
disconnect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged);
|
|
|
|
disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged);
|
|
|
|
disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot);
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
if(axis == 0) {
|
2020-10-28 05:07:14 +08:00
|
|
|
disconnect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded);
|
|
|
|
disconnect(t, &Trace::markerRemoved, this, &TraceXYPlot::markerRemoved);
|
2020-08-31 04:03:41 +08:00
|
|
|
auto tracemarkers = t->getMarkers();
|
|
|
|
for(auto m : tracemarkers) {
|
|
|
|
markerRemoved(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateContextMenu();
|
|
|
|
replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
switch(type) {
|
|
|
|
case YAxisType::Disabled:
|
|
|
|
return false;
|
|
|
|
case YAxisType::VSWR:
|
|
|
|
if(!t->isReflection()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::updateXAxis()
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
2020-09-12 05:07:15 +08:00
|
|
|
if(XAxis.autorange && sweep_fmax-sweep_fmin > 0) {
|
2020-08-31 04:03:41 +08:00
|
|
|
QList<double> tickList;
|
|
|
|
for(double tick = sweep_fmin;tick <= sweep_fmax;tick+= (sweep_fmax-sweep_fmin)/10) {
|
|
|
|
tickList.append(tick);
|
|
|
|
}
|
|
|
|
QwtScaleDiv scalediv(sweep_fmin, sweep_fmax, QList<double>(), QList<double>(), tickList);
|
|
|
|
plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv);
|
|
|
|
} else {
|
|
|
|
plot->setAxisScale(QwtPlot::xBottom, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv);
|
|
|
|
}
|
|
|
|
triggerReplot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
QwtSeriesData<QPointF> *TraceXYPlot::createQwtSeriesData(Trace &t, int axis)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
2020-10-28 05:07:14 +08:00
|
|
|
return new QwtTraceSeries(t, YAxis[axis].Ytype, XAxis.Xtype);
|
2020-08-31 04:03:41 +08:00
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::traceColorChanged(Trace *t)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
for(int axis = 0;axis < 2;axis++) {
|
|
|
|
if(curves[axis].find(t) != curves[axis].end()) {
|
|
|
|
// trace active, change the pen color
|
|
|
|
if(t->isVisible()) {
|
|
|
|
if(axis == 0) {
|
|
|
|
curves[axis][t].curve->setPen(t->color());
|
|
|
|
} else {
|
|
|
|
curves[axis][t].curve->setPen(t->color(), 1.0, Qt::DashLine);
|
|
|
|
}
|
|
|
|
for(auto m : t->getMarkers()) {
|
|
|
|
if(markers.count(m)) {
|
|
|
|
markers[m]->attach(plot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
curves[axis][t].curve->setPen(t->color(), 0.0, Qt::NoPen);
|
|
|
|
for(auto m : t->getMarkers()) {
|
|
|
|
if(markers.count(m)) {
|
|
|
|
markers[m]->detach();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::markerAdded(TraceMarker *m)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
if(markers.count(m)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto qwtMarker = new QwtPlotMarker;
|
|
|
|
markers[m] = qwtMarker;
|
2020-10-20 23:03:49 +08:00
|
|
|
markerSymbolChanged(m);
|
2020-10-28 05:07:14 +08:00
|
|
|
connect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged);
|
|
|
|
connect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged);
|
2020-08-31 04:03:41 +08:00
|
|
|
markerDataChanged(m);
|
|
|
|
qwtMarker->attach(plot);
|
|
|
|
triggerReplot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::markerRemoved(TraceMarker *m)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
2020-10-28 05:07:14 +08:00
|
|
|
disconnect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged);
|
|
|
|
disconnect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged);
|
2020-08-31 04:03:41 +08:00
|
|
|
if(markers.count(m)) {
|
|
|
|
markers[m]->detach();
|
|
|
|
delete markers[m];
|
|
|
|
markers.erase(m);
|
|
|
|
}
|
|
|
|
triggerReplot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::markerDataChanged(TraceMarker *m)
|
2020-08-31 04:03:41 +08:00
|
|
|
{
|
|
|
|
auto qwtMarker = markers[m];
|
|
|
|
qwtMarker->setXValue(m->getFrequency());
|
2020-10-28 05:07:14 +08:00
|
|
|
qwtMarker->setYValue(FrequencyAxisTransformation(YAxis[0].Ytype, m->getData()));
|
2020-08-31 04:03:41 +08:00
|
|
|
triggerReplot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::markerSymbolChanged(TraceMarker *m)
|
2020-10-20 23:03:49 +08:00
|
|
|
{
|
|
|
|
auto qwtMarker = markers[m];
|
|
|
|
auto old_sym = qwtMarker->symbol();
|
|
|
|
qwtMarker->setSymbol(nullptr);
|
|
|
|
delete old_sym;
|
|
|
|
|
|
|
|
QwtSymbol *sym=new QwtSymbol;
|
|
|
|
sym->setPixmap(m->getSymbol());
|
|
|
|
sym->setPinPoint(QPointF(m->getSymbol().width()/2, m->getSymbol().height()));
|
|
|
|
qwtMarker->setSymbol(sym);
|
|
|
|
triggerReplot();
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::clicked(const QPointF pos)
|
2020-10-20 01:39:16 +08:00
|
|
|
{
|
|
|
|
auto clickPoint = drawPicker->plotToPixel(pos);
|
|
|
|
unsigned int closestDistance = numeric_limits<unsigned int>::max();
|
|
|
|
TraceMarker *closestMarker = nullptr;
|
|
|
|
for(auto m : markers) {
|
|
|
|
auto markerPoint = drawPicker->plotToPixel(m.second->value());
|
|
|
|
auto yDiff = abs(markerPoint.y() - clickPoint.y());
|
|
|
|
auto xDiff = abs(markerPoint.x() - clickPoint.x());
|
|
|
|
unsigned int distance = xDiff * xDiff + yDiff * yDiff;
|
|
|
|
if(distance < closestDistance) {
|
|
|
|
closestDistance = distance;
|
|
|
|
closestMarker = m.first;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(closestDistance <= 400) {
|
|
|
|
selectedMarker = closestMarker;
|
|
|
|
selectedCurve = curves[0][selectedMarker->trace()].curve;
|
|
|
|
} else {
|
|
|
|
selectedMarker = nullptr;
|
|
|
|
selectedCurve = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::moved(const QPointF pos)
|
2020-10-20 01:39:16 +08:00
|
|
|
{
|
|
|
|
if(!selectedMarker || !selectedCurve) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
selectedMarker->setFrequency(pos.x());
|
|
|
|
}
|
|
|
|
|
2020-10-28 05:07:14 +08:00
|
|
|
void TraceXYPlot::setColorFromPreferences()
|
2020-10-23 03:12:33 +08:00
|
|
|
{
|
|
|
|
auto pref = Preferences::getInstance();
|
|
|
|
plot->setCanvasBackground(pref.General.graphColors.background);
|
|
|
|
auto pal = plot->palette();
|
|
|
|
pal.setColor(QPalette::Window, pref.General.graphColors.background);
|
|
|
|
pal.setColor(QPalette::WindowText, pref.General.graphColors.axis);
|
|
|
|
pal.setColor(QPalette::Text, pref.General.graphColors.axis);
|
|
|
|
plot->setPalette(pal);
|
|
|
|
grid->setPen(pref.General.graphColors.divisions);
|
|
|
|
}
|
|
|
|
|