mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-05 14:35:23 +00:00
PC Application: partial firmware update dialog
This commit is contained in:
parent
8c8749accd
commit
07ba714f1f
134 changed files with 13954 additions and 7 deletions
118
Software/PC_Application/Traces/bodeplotaxisdialog.cpp
Normal file
118
Software/PC_Application/Traces/bodeplotaxisdialog.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#include "bodeplotaxisdialog.h"
|
||||
#include "ui_bodeplotaxisdialog.h"
|
||||
|
||||
BodeplotAxisDialog::BodeplotAxisDialog(TraceBodePlot *plot) :
|
||||
QDialog(nullptr),
|
||||
ui(new Ui::BodeplotAxisDialog),
|
||||
plot(plot)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Setup GUI connections
|
||||
connect(ui->Y1type, qOverload<int>(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
ui->Y1log->setEnabled(index != 0);
|
||||
ui->Y1linear->setEnabled(index != 0);
|
||||
ui->Y1auto->setEnabled(index != 0);
|
||||
bool autoRange = ui->Y1auto->isChecked();
|
||||
ui->Y1min->setEnabled(index != 0 && !autoRange);
|
||||
ui->Y1max->setEnabled(index != 0 && !autoRange);
|
||||
ui->Y1divs->setEnabled(index != 0 && !autoRange);
|
||||
auto type = (TraceBodePlot::YAxisType) index;
|
||||
QString unit;
|
||||
switch(type) {
|
||||
case TraceBodePlot::YAxisType::Magnitude: unit = "db"; break;
|
||||
case TraceBodePlot::YAxisType::Phase: unit = "°"; break;
|
||||
case TraceBodePlot::YAxisType::VSWR: unit = ""; break;
|
||||
default: unit = ""; break;
|
||||
}
|
||||
ui->Y1min->setUnit(unit);
|
||||
ui->Y1max->setUnit(unit);
|
||||
ui->Y1divs->setUnit(unit);
|
||||
});
|
||||
connect(ui->Y1auto, &QCheckBox::toggled, [this](bool checked) {
|
||||
ui->Y1min->setEnabled(!checked);
|
||||
ui->Y1max->setEnabled(!checked);
|
||||
ui->Y1divs->setEnabled(!checked);
|
||||
});
|
||||
|
||||
connect(ui->Y2type, qOverload<int>(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
ui->Y2log->setEnabled(index != 0);
|
||||
ui->Y2linear->setEnabled(index != 0);
|
||||
ui->Y2auto->setEnabled(index != 0);
|
||||
bool autoRange = ui->Y2auto->isChecked();
|
||||
ui->Y2min->setEnabled(index != 0 && !autoRange);
|
||||
ui->Y2max->setEnabled(index != 0 && !autoRange);
|
||||
ui->Y2divs->setEnabled(index != 0 && !autoRange);
|
||||
auto type = (TraceBodePlot::YAxisType) index;
|
||||
QString unit;
|
||||
switch(type) {
|
||||
case TraceBodePlot::YAxisType::Magnitude: unit = "db"; break;
|
||||
case TraceBodePlot::YAxisType::Phase: unit = "°"; break;
|
||||
case TraceBodePlot::YAxisType::VSWR: unit = ""; break;
|
||||
default: unit = ""; break;
|
||||
}
|
||||
ui->Y2min->setUnit(unit);
|
||||
ui->Y2max->setUnit(unit);
|
||||
ui->Y2divs->setUnit(unit);
|
||||
});
|
||||
connect(ui->Y2auto, &QCheckBox::toggled, [this](bool checked) {
|
||||
ui->Y2min->setEnabled(!checked);
|
||||
ui->Y2max->setEnabled(!checked);
|
||||
ui->Y2divs->setEnabled(!checked);
|
||||
});
|
||||
|
||||
connect(ui->Xauto, &QCheckBox::toggled, [this](bool checked) {
|
||||
ui->Xmin->setEnabled(!checked);
|
||||
ui->Xmax->setEnabled(!checked);
|
||||
ui->Xdivs->setEnabled(!checked);
|
||||
});
|
||||
|
||||
ui->Xmin->setUnit("Hz");
|
||||
ui->Xmax->setUnit("Hz");
|
||||
ui->Xdivs->setUnit("Hz");
|
||||
ui->Xmin->setPrefixes(" kMG");
|
||||
ui->Xmax->setPrefixes(" kMG");
|
||||
ui->Xdivs->setPrefixes(" kMG");
|
||||
|
||||
// Fill initial values
|
||||
// assume same order in YAxisType enum as in ComboBox items
|
||||
ui->Y1type->setCurrentIndex((int) plot->YAxis[0].type);
|
||||
if(plot->YAxis[0].log) {
|
||||
ui->Y1log->setChecked(true);
|
||||
} else {
|
||||
ui->Y1linear->setChecked(true);
|
||||
}
|
||||
ui->Y1auto->setChecked(plot->YAxis[0].autorange);
|
||||
ui->Y1min->setValueQuiet(plot->YAxis[0].rangeMin);
|
||||
ui->Y1max->setValueQuiet(plot->YAxis[0].rangeMax);
|
||||
ui->Y1divs->setValueQuiet(plot->YAxis[0].rangeDiv);
|
||||
|
||||
ui->Y2type->setCurrentIndex((int) plot->YAxis[1].type);
|
||||
if(plot->YAxis[1].log) {
|
||||
ui->Y2log->setChecked(true);
|
||||
} else {
|
||||
ui->Y2linear->setChecked(true);
|
||||
}
|
||||
ui->Y2auto->setChecked(plot->YAxis[1].autorange);
|
||||
ui->Y2min->setValueQuiet(plot->YAxis[1].rangeMin);
|
||||
ui->Y2max->setValueQuiet(plot->YAxis[1].rangeMax);
|
||||
ui->Y2divs->setValueQuiet(plot->YAxis[1].rangeDiv);
|
||||
|
||||
ui->Xauto->setChecked(plot->XAxis.autorange);
|
||||
ui->Xmin->setValueQuiet(plot->XAxis.rangeMin);
|
||||
ui->Xmax->setValueQuiet(plot->XAxis.rangeMax);
|
||||
ui->Xdivs->setValueQuiet(plot->XAxis.rangeDiv);
|
||||
}
|
||||
|
||||
BodeplotAxisDialog::~BodeplotAxisDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void BodeplotAxisDialog::on_buttonBox_accepted()
|
||||
{
|
||||
// set plot values to the ones selected in the dialog
|
||||
plot->setYAxis(0, (TraceBodePlot::YAxisType) ui->Y1type->currentIndex(), ui->Y1log->isChecked(), ui->Y1auto->isChecked(), ui->Y1min->value(), ui->Y1max->value(), ui->Y1divs->value());
|
||||
plot->setYAxis(1, (TraceBodePlot::YAxisType) ui->Y2type->currentIndex(), ui->Y2log->isChecked(), ui->Y2auto->isChecked(), ui->Y2min->value(), ui->Y2max->value(), ui->Y2divs->value());
|
||||
plot->setXAxis(ui->Xauto->isChecked(), ui->Xmin->value(), ui->Xmax->value(), ui->Xdivs->value());
|
||||
}
|
||||
27
Software/PC_Application/Traces/bodeplotaxisdialog.h
Normal file
27
Software/PC_Application/Traces/bodeplotaxisdialog.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef BODEPLOTAXISDIALOG_H
|
||||
#define BODEPLOTAXISDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "tracebodeplot.h"
|
||||
|
||||
namespace Ui {
|
||||
class BodeplotAxisDialog;
|
||||
}
|
||||
|
||||
class BodeplotAxisDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BodeplotAxisDialog(TraceBodePlot *plot);
|
||||
~BodeplotAxisDialog();
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
|
||||
private:
|
||||
Ui::BodeplotAxisDialog *ui;
|
||||
TraceBodePlot *plot;
|
||||
};
|
||||
|
||||
#endif // BODEPLOTAXISDIALOG_H
|
||||
462
Software/PC_Application/Traces/bodeplotaxisdialog.ui
Normal file
462
Software/PC_Application/Traces/bodeplotaxisdialog.ui
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BodeplotAxisDialog</class>
|
||||
<widget class="QDialog" name="BodeplotAxisDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>715</width>
|
||||
<height>282</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Axis Setup</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>15</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Primary Y axis</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="Y1type">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Magnitude</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Phase</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VSWR</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="Y1linear">
|
||||
<property name="text">
|
||||
<string>Linear</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">Y1group</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="Y1log">
|
||||
<property name="text">
|
||||
<string>Log</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">Y1group</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Range:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="Y1auto">
|
||||
<property name="text">
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Maximum:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="SIUnitEdit" name="Y1max"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Minimum:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="SIUnitEdit" name="Y1min"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Divisions:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="SIUnitEdit" name="Y1divs"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>15</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Secondary Y axis</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="Y2type">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Magnitude</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Phase</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VSWR</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="Y2linear">
|
||||
<property name="text">
|
||||
<string>Linear</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">Y2group</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="Y2log">
|
||||
<property name="text">
|
||||
<string>Log</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">Y2group</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Range:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="Y2auto">
|
||||
<property name="text">
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Maximum:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="SIUnitEdit" name="Y2max"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Minimum:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="SIUnitEdit" name="Y2min"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Divisions:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="SIUnitEdit" name="Y2divs"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>15</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X axis</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout_6">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Range:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="Xauto">
|
||||
<property name="text">
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>Maximum:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="SIUnitEdit" name="Xmax"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>Minimum:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="SIUnitEdit" name="Xmin"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Divisions:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="SIUnitEdit" name="Xdivs"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>SIUnitEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>CustomWidgets/siunitedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>BodeplotAxisDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>BodeplotAxisDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="Y1group"/>
|
||||
<buttongroup name="Y2group"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
41
Software/PC_Application/Traces/markerwidget.cpp
Normal file
41
Software/PC_Application/Traces/markerwidget.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include "markerwidget.h"
|
||||
#include "ui_markerwidget.h"
|
||||
|
||||
MarkerWidget::MarkerWidget(TraceMarkerModel &model, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::MarkerWidget),
|
||||
model(model)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->tableView->setModel(&model);
|
||||
ui->tableView->setItemDelegateForColumn(1, new TraceChooserDelegate);
|
||||
|
||||
connect(&model.getModel(), &TraceModel::traceAdded, this, &MarkerWidget::updatePersistentEditors);
|
||||
connect(&model.getModel(), &TraceModel::traceRemoved, this, &MarkerWidget::updatePersistentEditors);
|
||||
}
|
||||
|
||||
MarkerWidget::~MarkerWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MarkerWidget::on_bDelete_clicked()
|
||||
{
|
||||
model.removeMarker(ui->tableView->currentIndex().row());
|
||||
}
|
||||
|
||||
void MarkerWidget::on_bAdd_clicked()
|
||||
{
|
||||
auto marker = model.createDefaultMarker();
|
||||
model.addMarker(marker);
|
||||
updatePersistentEditors();
|
||||
}
|
||||
|
||||
void MarkerWidget::updatePersistentEditors(Trace *)
|
||||
{
|
||||
for(int i=0;i<model.rowCount();i++) {
|
||||
auto index = model.index(i, TraceMarkerModel::ColIndexTrace);
|
||||
ui->tableView->closePersistentEditor(index);
|
||||
ui->tableView->openPersistentEditor(index);
|
||||
}
|
||||
}
|
||||
29
Software/PC_Application/Traces/markerwidget.h
Normal file
29
Software/PC_Application/Traces/markerwidget.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef MARKERWIDGET_H
|
||||
#define MARKERWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "tracemarkermodel.h"
|
||||
|
||||
namespace Ui {
|
||||
class MarkerWidget;
|
||||
}
|
||||
|
||||
class MarkerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MarkerWidget(TraceMarkerModel &model, QWidget *parent = nullptr);
|
||||
~MarkerWidget();
|
||||
|
||||
private slots:
|
||||
void on_bDelete_clicked();
|
||||
void on_bAdd_clicked();
|
||||
void updatePersistentEditors(Trace *dummy = nullptr);
|
||||
|
||||
private:
|
||||
Ui::MarkerWidget *ui;
|
||||
TraceMarkerModel &model;
|
||||
};
|
||||
|
||||
#endif // MARKERWIDGET_H
|
||||
89
Software/PC_Application/Traces/markerwidget.ui
Normal file
89
Software/PC_Application/Traces/markerwidget.ui
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MarkerWidget</class>
|
||||
<widget class="QWidget" name="MarkerWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>729</width>
|
||||
<height>195</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="bAdd">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bDelete">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
238
Software/PC_Application/Traces/trace.cpp
Normal file
238
Software/PC_Application/Traces/trace.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#include "trace.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Trace::Trace(QString name, QColor color)
|
||||
: _name(name),
|
||||
_color(color),
|
||||
_liveType(LivedataType::Overwrite),
|
||||
reflection(true),
|
||||
visible(true),
|
||||
paused(false),
|
||||
touchstone(false),
|
||||
calibration(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Trace::~Trace()
|
||||
{
|
||||
emit deleted(this);
|
||||
}
|
||||
|
||||
void Trace::clear() {
|
||||
if(paused) {
|
||||
return;
|
||||
}
|
||||
_data.clear();
|
||||
emit cleared(this);
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
void Trace::addData(Trace::Data d) {
|
||||
// add or replace data in vector while keeping it sorted with increasing frequency
|
||||
auto lower = lower_bound(_data.begin(), _data.end(), d, [](const Data &lhs, const Data &rhs) -> bool {
|
||||
return lhs.frequency < rhs.frequency;
|
||||
});
|
||||
if(lower == _data.end()) {
|
||||
// highest frequency yet, add to vector
|
||||
_data.push_back(d);
|
||||
} else if(lower->frequency == d.frequency) {
|
||||
switch(_liveType) {
|
||||
case LivedataType::Overwrite:
|
||||
// replace this data element
|
||||
*lower = d;
|
||||
break;
|
||||
case LivedataType::MaxHold:
|
||||
// replace this data element
|
||||
if(abs(d.S) > abs(lower->S)) {
|
||||
*lower = d;
|
||||
}
|
||||
break;
|
||||
case LivedataType::MinHold:
|
||||
// replace this data element
|
||||
if(abs(d.S) < abs(lower->S)) {
|
||||
*lower = d;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
// insert at this position
|
||||
_data.insert(lower, d);
|
||||
}
|
||||
emit dataAdded(this, d);
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
void Trace::setName(QString name) {
|
||||
_name = name;
|
||||
emit nameChanged();
|
||||
}
|
||||
|
||||
void Trace::fillFromTouchstone(Touchstone &t, unsigned int parameter, QString filename)
|
||||
{
|
||||
if(parameter >= t.ports()*t.ports()) {
|
||||
throw runtime_error("Parameter for touchstone out of range");
|
||||
}
|
||||
clear();
|
||||
setTouchstoneParameter(parameter);
|
||||
setTouchstoneFilename(filename);
|
||||
for(unsigned int i=0;i<t.points();i++) {
|
||||
auto tData = t.point(i);
|
||||
Data d;
|
||||
d.frequency = tData.frequency;
|
||||
d.S = t.point(i).S[parameter];
|
||||
addData(d);
|
||||
}
|
||||
// check if parameter is square (e.i. S11/S22/S33/...)
|
||||
parameter++;
|
||||
bool isSquare = false;
|
||||
for (unsigned int i = 1; i * i <= parameter; i++) {
|
||||
|
||||
// If (i * i = n)
|
||||
if ((parameter % i == 0) && (parameter / i == i)) {
|
||||
isSquare = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isSquare == 1) {
|
||||
reflection = true;
|
||||
} else {
|
||||
reflection = false;
|
||||
}
|
||||
touchstone = true;
|
||||
emit typeChanged(this);
|
||||
}
|
||||
|
||||
void Trace::fromLivedata(Trace::LivedataType type, LiveParameter param)
|
||||
{
|
||||
touchstone = false;
|
||||
_liveType = type;
|
||||
_liveParam = param;
|
||||
if(param == LiveParameter::S11 || param == LiveParameter::S22) {
|
||||
reflection = true;
|
||||
} else {
|
||||
reflection = false;
|
||||
}
|
||||
emit typeChanged(this);
|
||||
}
|
||||
|
||||
void Trace::setColor(QColor color) {
|
||||
if(_color != color) {
|
||||
_color = color;
|
||||
emit colorChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::addMarker(TraceMarker *m)
|
||||
{
|
||||
markers.insert(m);
|
||||
emit markerAdded(m);
|
||||
}
|
||||
|
||||
void Trace::removeMarker(TraceMarker *m)
|
||||
{
|
||||
markers.erase(m);
|
||||
emit markerRemoved(m);
|
||||
}
|
||||
|
||||
void Trace::setReflection(bool value)
|
||||
{
|
||||
reflection = value;
|
||||
}
|
||||
|
||||
void Trace::setCalibration(bool value)
|
||||
{
|
||||
calibration = value;
|
||||
}
|
||||
|
||||
std::set<TraceMarker *> Trace::getMarkers() const
|
||||
{
|
||||
return markers;
|
||||
}
|
||||
|
||||
void Trace::setVisible(bool visible)
|
||||
{
|
||||
if(visible != this->visible) {
|
||||
this->visible = visible;
|
||||
emit visibilityChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool Trace::isVisible()
|
||||
{
|
||||
return visible;
|
||||
}
|
||||
|
||||
void Trace::pause()
|
||||
{
|
||||
paused = true;
|
||||
}
|
||||
|
||||
void Trace::resume()
|
||||
{
|
||||
paused = false;
|
||||
}
|
||||
|
||||
bool Trace::isPaused()
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
|
||||
bool Trace::isTouchstone()
|
||||
{
|
||||
return touchstone;
|
||||
}
|
||||
|
||||
bool Trace::isCalibration()
|
||||
{
|
||||
return calibration;
|
||||
}
|
||||
|
||||
bool Trace::isLive()
|
||||
{
|
||||
return !isCalibration() && !isTouchstone() && !isPaused();
|
||||
}
|
||||
|
||||
bool Trace::isReflection()
|
||||
{
|
||||
return reflection;
|
||||
}
|
||||
|
||||
QString Trace::getTouchstoneFilename() const
|
||||
{
|
||||
return touchstoneFilename;
|
||||
}
|
||||
|
||||
void Trace::setTouchstoneFilename(const QString &value)
|
||||
{
|
||||
touchstoneFilename = value;
|
||||
}
|
||||
|
||||
unsigned int Trace::getTouchstoneParameter() const
|
||||
{
|
||||
return touchstoneParameter;
|
||||
}
|
||||
|
||||
std::complex<double> Trace::getData(double frequency)
|
||||
{
|
||||
if(_data.size() == 0 || frequency < minFreq() || frequency > maxFreq()) {
|
||||
return std::numeric_limits<std::complex<double>>::quiet_NaN();
|
||||
}
|
||||
|
||||
return sample(index(frequency)).S;
|
||||
}
|
||||
|
||||
int Trace::index(double frequency)
|
||||
{
|
||||
auto lower = lower_bound(_data.begin(), _data.end(), frequency, [](const Data &lhs, const double freq) -> bool {
|
||||
return lhs.frequency < freq;
|
||||
});
|
||||
return lower - _data.begin();
|
||||
}
|
||||
|
||||
void Trace::setTouchstoneParameter(int value)
|
||||
{
|
||||
touchstoneParameter = value;
|
||||
}
|
||||
105
Software/PC_Application/Traces/trace.h
Normal file
105
Software/PC_Application/Traces/trace.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#ifndef TRACE_H
|
||||
#define TRACE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <complex>
|
||||
#include <map>
|
||||
#include <QColor>
|
||||
#include <set>
|
||||
#include "touchstone.h"
|
||||
|
||||
class TraceMarker;
|
||||
|
||||
class Trace : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
class Data {
|
||||
public:
|
||||
double frequency;
|
||||
std::complex<double> S;
|
||||
};
|
||||
|
||||
Trace(QString name = QString(), QColor color = Qt::darkYellow);
|
||||
~Trace();
|
||||
|
||||
enum class LivedataType {
|
||||
Overwrite,
|
||||
MaxHold,
|
||||
MinHold,
|
||||
};
|
||||
enum class LiveParameter {
|
||||
S11,
|
||||
S12,
|
||||
S21,
|
||||
S22,
|
||||
};
|
||||
|
||||
void clear();
|
||||
void addData(Data d);
|
||||
void setName(QString name);
|
||||
void fillFromTouchstone(Touchstone &t, unsigned int parameter, QString filename = QString());
|
||||
void fromLivedata(LivedataType type, LiveParameter param);
|
||||
QString name() { return _name; };
|
||||
QColor color() { return _color; };
|
||||
bool isVisible();
|
||||
void pause();
|
||||
void resume();
|
||||
bool isPaused();
|
||||
bool isTouchstone();
|
||||
bool isCalibration();
|
||||
bool isLive();
|
||||
bool isReflection();
|
||||
LiveParameter liveParameter() { return _liveParam; }
|
||||
LivedataType liveType() { return _liveType; }
|
||||
unsigned int size() { return _data.size(); }
|
||||
double minFreq() { return _data.front().frequency; };
|
||||
double maxFreq() { return _data.back().frequency; };
|
||||
Data sample(unsigned int index) { return _data.at(index); }
|
||||
QString getTouchstoneFilename() const;
|
||||
unsigned int getTouchstoneParameter() const;
|
||||
std::complex<double> getData(double frequency);
|
||||
int index(double frequency);
|
||||
std::set<TraceMarker *> getMarkers() const;
|
||||
void setCalibration(bool value);
|
||||
void setReflection(bool value);
|
||||
|
||||
public slots:
|
||||
void setTouchstoneParameter(int value);
|
||||
void setTouchstoneFilename(const QString &value);
|
||||
void setVisible(bool visible);
|
||||
void setColor(QColor color);
|
||||
void addMarker(TraceMarker *m);
|
||||
void removeMarker(TraceMarker *m);
|
||||
|
||||
private:
|
||||
signals:
|
||||
void cleared(Trace *t);
|
||||
void typeChanged(Trace *t);
|
||||
void dataAdded(Trace *t, Data d);
|
||||
void deleted(Trace *t);
|
||||
void visibilityChanged(Trace *t);
|
||||
void dataChanged();
|
||||
void nameChanged();
|
||||
void colorChanged(Trace *t);
|
||||
void markerAdded(TraceMarker *m);
|
||||
void markerRemoved(TraceMarker *m);
|
||||
|
||||
private:
|
||||
std::vector<Data> _data;
|
||||
QString _name;
|
||||
QColor _color;
|
||||
LivedataType _liveType;
|
||||
LiveParameter _liveParam;
|
||||
bool reflection;
|
||||
bool visible;
|
||||
bool paused;
|
||||
bool touchstone;
|
||||
bool calibration;
|
||||
QString touchstoneFilename;
|
||||
unsigned int touchstoneParameter;
|
||||
std::set<TraceMarker*> markers;
|
||||
};
|
||||
|
||||
#endif // TRACE_H
|
||||
489
Software/PC_Application/Traces/tracebodeplot.cpp
Normal file
489
Software/PC_Application/Traces/tracebodeplot.cpp
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
#include "tracebodeplot.h"
|
||||
#include <QGridLayout>
|
||||
#include <qwt_plot_grid.h>
|
||||
#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_plot_picker.h>
|
||||
#include <qwt_picker_machine.h>
|
||||
#include "bodeplotaxisdialog.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static double AxisTransformation(TraceBodePlot::YAxisType type, complex<double> data) {
|
||||
switch(type) {
|
||||
case TraceBodePlot::YAxisType::Magnitude: return 20*log10(abs(data)); break;
|
||||
case TraceBodePlot::YAxisType::Phase: return arg(data) * 180.0 / M_PI; break;
|
||||
case TraceBodePlot::YAxisType::VSWR:
|
||||
if(abs(data) < 1.0) {
|
||||
return (1+abs(data)) / (1-abs(data));
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
template<TraceBodePlot::YAxisType E> class QwtTraceSeries : public QwtSeriesData<QPointF> {
|
||||
public:
|
||||
QwtTraceSeries(Trace &t)
|
||||
: QwtSeriesData<QPointF>(),
|
||||
t(t){};
|
||||
size_t size() const override {
|
||||
return t.size();
|
||||
}
|
||||
QPointF sample(size_t i) const override {
|
||||
Trace::Data d = t.sample(i);
|
||||
QPointF p;
|
||||
p.setX(d.frequency);
|
||||
p.setY(AxisTransformation(E, d.S));
|
||||
return p;
|
||||
}
|
||||
QRectF boundingRect() const override {
|
||||
return qwtBoundingRect(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
Trace &t;
|
||||
};
|
||||
|
||||
// Derived plotpicker, exposing transformation functions
|
||||
class BodeplotPicker : public QwtPlotPicker {
|
||||
public:
|
||||
BodeplotPicker(int xAxis, int yAxis, RubberBand rubberBand, DisplayMode trackerMode, QWidget *w)
|
||||
: QwtPlotPicker(xAxis, yAxis, rubberBand, trackerMode, w) {};
|
||||
QPoint plotToPixel(const QPointF &pos) {
|
||||
return transform(pos);
|
||||
}
|
||||
QPointF pixelToPlot(const QPoint &pos) {
|
||||
return invTransform(pos);
|
||||
}
|
||||
};
|
||||
|
||||
TraceBodePlot::TraceBodePlot(TraceModel &model, QWidget *parent)
|
||||
: TracePlot(parent),
|
||||
selectedMarker(nullptr)
|
||||
{
|
||||
plot = new QwtPlot(this);
|
||||
plot->setCanvasBackground(Background);
|
||||
auto pal = plot->palette();
|
||||
pal.setColor(QPalette::Window, Background);
|
||||
pal.setColor(QPalette::WindowText, Border);
|
||||
pal.setColor(QPalette::Text, Border);
|
||||
auto canvas = new QwtPlotCanvas(plot);
|
||||
canvas->setFrameStyle(QFrame::Plain);
|
||||
plot->setCanvas(canvas);
|
||||
plot->setPalette(pal);
|
||||
plot->setAutoFillBackground(true);
|
||||
|
||||
auto selectPicker = new BodeplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas());
|
||||
selectPicker->setStateMachine(new QwtPickerClickPointMachine);
|
||||
|
||||
auto drawPicker = new BodeplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas());
|
||||
drawPicker->setStateMachine(new QwtPickerDragPointMachine);
|
||||
drawPicker->setTrackerPen(QPen(Qt::white));
|
||||
|
||||
// Marker selection
|
||||
connect(selectPicker, qOverload<const QPointF&>(&QwtPlotPicker::selected), [=](const QPointF pos) {
|
||||
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;
|
||||
}
|
||||
});
|
||||
// Marker movement
|
||||
connect(drawPicker, qOverload<const QPointF&>(&QwtPlotPicker::moved), [=](const QPointF pos) {
|
||||
if(!selectedMarker || !selectedCurve) {
|
||||
return;
|
||||
}
|
||||
// int index = selectedCurve->closestPoint(pos.toPoint());
|
||||
// qDebug() << index;
|
||||
// if(index < 0) {
|
||||
// // unable to find closest point
|
||||
// return;
|
||||
// }
|
||||
// selectedMarker->setFrequency(selectedCurve->sample(index).x());
|
||||
selectedMarker->setFrequency(pos.x());
|
||||
});
|
||||
|
||||
QwtPlotGrid *grid = new QwtPlotGrid();
|
||||
grid->setMajorPen(QPen(Divisions, 1.0, Qt::DotLine));
|
||||
grid->attach(plot);
|
||||
auto layout = new QGridLayout;
|
||||
layout->addWidget(plot);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(layout);
|
||||
plot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
// plot->plotLayout()->setAlignCanvasToScales(true);
|
||||
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);
|
||||
setXAxis(true, 0, 6000000000, 600000000);
|
||||
}
|
||||
|
||||
TraceBodePlot::~TraceBodePlot()
|
||||
{
|
||||
for(int axis = 0;axis < 2;axis++) {
|
||||
for(auto pd : curves[axis]) {
|
||||
delete pd.second.curve;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceBodePlot::setXAxis(double min, double max)
|
||||
{
|
||||
sweep_fmin = min;
|
||||
sweep_fmax = max;
|
||||
updateXAxis();
|
||||
}
|
||||
|
||||
void TraceBodePlot::setYAxis(int axis, TraceBodePlot::YAxisType type, bool log, bool autorange, double min, double max, double div)
|
||||
{
|
||||
if(YAxis[axis].type != type) {
|
||||
YAxis[axis].type = type;
|
||||
// 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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
void TraceBodePlot::setXAxis(bool autorange, double min, double max, double div)
|
||||
{
|
||||
XAxis.autorange = autorange;
|
||||
XAxis.rangeMin = min;
|
||||
XAxis.rangeMax = max;
|
||||
XAxis.rangeDiv = div;
|
||||
updateXAxis();
|
||||
}
|
||||
|
||||
void TraceBodePlot::enableTrace(Trace *t, bool enabled)
|
||||
{
|
||||
for(int axis = 0;axis < 2;axis++) {
|
||||
if(supported(t, YAxis[axis].type)) {
|
||||
enableTraceAxis(t, axis, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceBodePlot::updateContextMenu()
|
||||
{
|
||||
contextmenu->clear();
|
||||
// for(int axis = 0;axis < 2;axis++) {
|
||||
// QMenu *axisMenu;
|
||||
// if(axis == 0) {
|
||||
// axisMenu = contextmenu->addMenu("Primary Axis");
|
||||
// } else {
|
||||
// axisMenu = contextmenu->addMenu("Secondary Axis");
|
||||
// }
|
||||
// auto group = new QActionGroup(this);
|
||||
// for(int i=0;i<(int) YAxisType::Last;i++) {
|
||||
// auto action = new QAction(AxisTypeToName((YAxisType) i));
|
||||
// action->setCheckable(true);
|
||||
// group->addAction(action);
|
||||
// if(YAxis[axis].type == (YAxisType) i) {
|
||||
// action->setChecked(true);
|
||||
// }
|
||||
// connect(action, &QAction::triggered, [=](bool active) {
|
||||
// if(active) {
|
||||
// setYAxisType(axis, (YAxisType) i);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// axisMenu->addActions(group->actions());
|
||||
// }
|
||||
auto setup = new QAction("Axis setup...");
|
||||
connect(setup, &QAction::triggered, [this]() {
|
||||
auto setup = new BodeplotAxisDialog(this);
|
||||
setup->show();
|
||||
});
|
||||
contextmenu->addAction(setup);
|
||||
for(int axis = 0;axis < 2;axis++) {
|
||||
if(YAxis[axis].type == YAxisType::Disabled) {
|
||||
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
|
||||
if(!supported(t.first, YAxis[axis].type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto action = new QAction(t.first->name());
|
||||
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();
|
||||
auto close = new QAction("Close");
|
||||
contextmenu->addAction(close);
|
||||
connect(close, &QAction::triggered, [=]() {
|
||||
markedForDeletion = true;
|
||||
});
|
||||
}
|
||||
|
||||
bool TraceBodePlot::supported(Trace *)
|
||||
{
|
||||
// potentially possible to add every kind of trace (depends on axis)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TraceBodePlot::replot()
|
||||
{
|
||||
plot->replot();
|
||||
}
|
||||
|
||||
QString TraceBodePlot::AxisTypeToName(TraceBodePlot::YAxisType type)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceBodePlot::enableTraceAxis(Trace *t, int axis, bool enabled)
|
||||
{
|
||||
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
|
||||
connect(t, &Trace::dataChanged, this, &TraceBodePlot::triggerReplot);
|
||||
connect(t, &Trace::colorChanged, this, &TraceBodePlot::traceColorChanged);
|
||||
connect(t, &Trace::visibilityChanged, this, &TraceBodePlot::traceColorChanged);
|
||||
connect(t, &Trace::visibilityChanged, this, &TraceBodePlot::triggerReplot);
|
||||
if(axis == 0) {
|
||||
connect(t, &Trace::markerAdded, this, &TraceBodePlot::markerAdded);
|
||||
connect(t, &Trace::markerRemoved, this, &TraceBodePlot::markerRemoved);
|
||||
auto tracemarkers = t->getMarkers();
|
||||
for(auto m : tracemarkers) {
|
||||
markerAdded(m);
|
||||
}
|
||||
}
|
||||
traceColorChanged(t);
|
||||
} else {
|
||||
tracesAxis[axis].erase(t);
|
||||
// clean up and delete
|
||||
if(curves[axis].find(t) != curves[axis].end()) {
|
||||
if(curves[axis][t].curve) {
|
||||
delete curves[axis][t].curve;
|
||||
}
|
||||
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
|
||||
disconnect(t, &Trace::dataChanged, this, &TraceBodePlot::triggerReplot);
|
||||
disconnect(t, &Trace::colorChanged, this, &TraceBodePlot::traceColorChanged);
|
||||
disconnect(t, &Trace::visibilityChanged, this, &TraceBodePlot::traceColorChanged);
|
||||
disconnect(t, &Trace::visibilityChanged, this, &TraceBodePlot::triggerReplot);
|
||||
}
|
||||
if(axis == 0) {
|
||||
disconnect(t, &Trace::markerAdded, this, &TraceBodePlot::markerAdded);
|
||||
disconnect(t, &Trace::markerRemoved, this, &TraceBodePlot::markerRemoved);
|
||||
auto tracemarkers = t->getMarkers();
|
||||
for(auto m : tracemarkers) {
|
||||
markerRemoved(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateContextMenu();
|
||||
replot();
|
||||
}
|
||||
}
|
||||
|
||||
bool TraceBodePlot::supported(Trace *t, TraceBodePlot::YAxisType type)
|
||||
{
|
||||
switch(type) {
|
||||
case YAxisType::Disabled:
|
||||
return false;
|
||||
case YAxisType::VSWR:
|
||||
if(!t->isReflection()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TraceBodePlot::updateXAxis()
|
||||
{
|
||||
if(XAxis.autorange) {
|
||||
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();
|
||||
}
|
||||
|
||||
QwtSeriesData<QPointF> *TraceBodePlot::createQwtSeriesData(Trace &t, int axis)
|
||||
{
|
||||
switch(YAxis[axis].type) {
|
||||
case YAxisType::Magnitude:
|
||||
return new QwtTraceSeries<YAxisType::Magnitude>(t);
|
||||
case YAxisType::Phase:
|
||||
return new QwtTraceSeries<YAxisType::Phase>(t);
|
||||
case YAxisType::VSWR:
|
||||
return new QwtTraceSeries<YAxisType::VSWR>(t);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceBodePlot::traceColorChanged(Trace *t)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceBodePlot::markerAdded(TraceMarker *m)
|
||||
{
|
||||
if(markers.count(m)) {
|
||||
return;
|
||||
}
|
||||
QwtSymbol *sym=new QwtSymbol;
|
||||
sym->setPixmap(m->getSymbol());
|
||||
sym->setPinPoint(QPointF(m->getSymbol().width()/2, m->getSymbol().height()));
|
||||
auto qwtMarker = new QwtPlotMarker;
|
||||
qwtMarker->setSymbol(sym);
|
||||
connect(m, &TraceMarker::dataChanged, this, &TraceBodePlot::markerDataChanged);
|
||||
markers[m] = qwtMarker;
|
||||
markerDataChanged(m);
|
||||
qwtMarker->attach(plot);
|
||||
triggerReplot();
|
||||
}
|
||||
|
||||
void TraceBodePlot::markerRemoved(TraceMarker *m)
|
||||
{
|
||||
disconnect(m, &TraceMarker::dataChanged, this, &TraceBodePlot::markerDataChanged);
|
||||
if(markers.count(m)) {
|
||||
markers[m]->detach();
|
||||
delete markers[m];
|
||||
markers.erase(m);
|
||||
}
|
||||
triggerReplot();
|
||||
}
|
||||
|
||||
void TraceBodePlot::markerDataChanged(TraceMarker *m)
|
||||
{
|
||||
auto qwtMarker = markers[m];
|
||||
qwtMarker->setXValue(m->getFrequency());
|
||||
qwtMarker->setYValue(AxisTransformation(YAxis[0].type, m->getData()));
|
||||
triggerReplot();
|
||||
}
|
||||
|
||||
76
Software/PC_Application/Traces/tracebodeplot.h
Normal file
76
Software/PC_Application/Traces/tracebodeplot.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef TRACEBODEPLOT_H
|
||||
#define TRACEBODEPLOT_H
|
||||
|
||||
#include "traceplot.h"
|
||||
#include <set>
|
||||
#include <qwt_plot.h>
|
||||
#include <qwt_plot_curve.h>
|
||||
#include <qwt_series_data.h>
|
||||
#include <qwt_plot_marker.h>
|
||||
|
||||
class TraceBodePlot : public TracePlot
|
||||
{
|
||||
friend class BodeplotAxisDialog;
|
||||
Q_OBJECT
|
||||
public:
|
||||
TraceBodePlot(TraceModel &model, QWidget *parent = nullptr);
|
||||
~TraceBodePlot();
|
||||
|
||||
enum class YAxisType {
|
||||
Disabled = 0,
|
||||
Magnitude = 1,
|
||||
Phase = 2,
|
||||
VSWR = 3,
|
||||
Last,
|
||||
};
|
||||
|
||||
virtual void setXAxis(double min, double max) override;
|
||||
void setYAxis(int axis, YAxisType type, bool log, bool autorange, double min, double max, double div);
|
||||
void setXAxis(bool autorange, double min, double max, double div);
|
||||
void enableTrace(Trace *t, bool enabled) override;
|
||||
|
||||
protected:
|
||||
virtual void updateContextMenu();
|
||||
virtual bool supported(Trace *t);
|
||||
void replot() override;
|
||||
|
||||
private slots:
|
||||
void traceColorChanged(Trace *t);
|
||||
void markerAdded(TraceMarker *m) override;
|
||||
void markerRemoved(TraceMarker *m) override;
|
||||
void markerDataChanged(TraceMarker *m);
|
||||
private:
|
||||
QString AxisTypeToName(YAxisType type);
|
||||
void enableTraceAxis(Trace *t, int axis, bool enabled);
|
||||
bool supported(Trace *t, YAxisType type);
|
||||
void updateXAxis();
|
||||
QwtSeriesData<QPointF> *createQwtSeriesData(Trace &t, int axis);
|
||||
|
||||
std::set<Trace*> tracesAxis[2];
|
||||
|
||||
class Axis {
|
||||
public:
|
||||
YAxisType type;
|
||||
bool log;
|
||||
bool autorange;
|
||||
double rangeMin;
|
||||
double rangeMax;
|
||||
double rangeDiv;
|
||||
};
|
||||
Axis YAxis[2];
|
||||
Axis XAxis;
|
||||
double sweep_fmin, sweep_fmax;
|
||||
|
||||
using CurveData = struct {
|
||||
QwtPlotCurve *curve;
|
||||
QwtSeriesData<QPointF> *data;
|
||||
};
|
||||
|
||||
std::map<Trace*, CurveData> curves[2];
|
||||
std::map<TraceMarker*, QwtPlotMarker*> markers;
|
||||
QwtPlot *plot;
|
||||
TraceMarker *selectedMarker;
|
||||
QwtPlotCurve *selectedCurve;
|
||||
};
|
||||
|
||||
#endif // TRACEBODEPLOT_H
|
||||
123
Software/PC_Application/Traces/traceeditdialog.cpp
Normal file
123
Software/PC_Application/Traces/traceeditdialog.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#include "traceeditdialog.h"
|
||||
#include "ui_traceeditdialog.h"
|
||||
#include <QColorDialog>
|
||||
#include <QFileDialog>
|
||||
|
||||
TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::TraceEditDialog),
|
||||
trace(t)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->name->setText(t.name());
|
||||
setColor(trace.color());
|
||||
|
||||
ui->GSource->setId(ui->bLive, 0);
|
||||
ui->GSource->setId(ui->bFile, 1);
|
||||
|
||||
if(t.isCalibration()) {
|
||||
// prevent editing imported calibration traces
|
||||
ui->bLive->setEnabled(false);
|
||||
ui->bFile->setEnabled(false);
|
||||
ui->CLiveType->setEnabled(false);
|
||||
ui->CLiveParam->setEnabled(false);
|
||||
}
|
||||
|
||||
if(t.isTouchstone()) {
|
||||
ui->bFile->click();
|
||||
ui->touchstoneImport->setFile(t.getTouchstoneFilename());
|
||||
}
|
||||
|
||||
auto updateFileStatus = [this]() {
|
||||
// remove all options from paramater combo box
|
||||
while(ui->CParameter->count() > 0) {
|
||||
ui->CParameter->removeItem(0);
|
||||
}
|
||||
if (ui->bFile->isChecked() && !ui->touchstoneImport->getStatus()) {
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
} else {
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
auto touchstone = ui->touchstoneImport->getTouchstone();
|
||||
for(unsigned int i=0;i<touchstone.ports();i++) {
|
||||
for(unsigned int j=0;j<touchstone.ports();j++) {
|
||||
QString name = "S"+QString::number(i+1)+QString::number(j+1);
|
||||
ui->CParameter->addItem(name);
|
||||
}
|
||||
}
|
||||
if(trace.getTouchstoneParameter() < touchstone.ports()*touchstone.ports()) {
|
||||
ui->CParameter->setCurrentIndex(trace.getTouchstoneParameter());
|
||||
} else {
|
||||
ui->CParameter->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch(t.liveType()) {
|
||||
case Trace::LivedataType::Overwrite: ui->CLiveType->setCurrentIndex(0); break;
|
||||
case Trace::LivedataType::MaxHold: ui->CLiveType->setCurrentIndex(1); break;
|
||||
case Trace::LivedataType::MinHold: ui->CLiveType->setCurrentIndex(2); break;
|
||||
}
|
||||
|
||||
switch(t.liveParameter()) {
|
||||
case Trace::LiveParameter::S11: ui->CLiveParam->setCurrentIndex(0); break;
|
||||
case Trace::LiveParameter::S12: ui->CLiveParam->setCurrentIndex(1); break;
|
||||
case Trace::LiveParameter::S21: ui->CLiveParam->setCurrentIndex(2); break;
|
||||
case Trace::LiveParameter::S22: ui->CLiveParam->setCurrentIndex(3); break;
|
||||
}
|
||||
|
||||
connect(ui->GSource, qOverload<int>(&QButtonGroup::buttonClicked), updateFileStatus);
|
||||
connect(ui->touchstoneImport, &TouchstoneImport::statusChanged, updateFileStatus);
|
||||
connect(ui->touchstoneImport, &TouchstoneImport::filenameChanged, updateFileStatus);
|
||||
|
||||
updateFileStatus();
|
||||
}
|
||||
|
||||
TraceEditDialog::~TraceEditDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void TraceEditDialog::on_color_clicked()
|
||||
{
|
||||
auto color = QColorDialog::getColor(trace.color(), this, "Select color", QColorDialog::DontUseNativeDialog);
|
||||
setColor(color);
|
||||
}
|
||||
|
||||
|
||||
void TraceEditDialog::on_buttonBox_accepted()
|
||||
{
|
||||
trace.setName(ui->name->text());
|
||||
if(!trace.isCalibration()) {
|
||||
// only apply changes if it is not a calibration trace
|
||||
if (ui->bFile->isChecked()) {
|
||||
auto t = ui->touchstoneImport->getTouchstone();
|
||||
trace.fillFromTouchstone(t, ui->CParameter->currentIndex(), ui->touchstoneImport->getFilename());
|
||||
} else {
|
||||
Trace::LivedataType type;
|
||||
Trace::LiveParameter param;
|
||||
switch(ui->CLiveType->currentIndex()) {
|
||||
case 0: type = Trace::LivedataType::Overwrite; break;
|
||||
case 1: type = Trace::LivedataType::MaxHold; break;
|
||||
case 2: type = Trace::LivedataType::MinHold; break;
|
||||
}
|
||||
switch(ui->CLiveParam->currentIndex()) {
|
||||
case 0: param = Trace::LiveParameter::S11; break;
|
||||
case 1: param = Trace::LiveParameter::S12; break;
|
||||
case 2: param = Trace::LiveParameter::S21; break;
|
||||
case 3: param = Trace::LiveParameter::S22; break;
|
||||
}
|
||||
trace.fromLivedata(type, param);
|
||||
}
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void TraceEditDialog::setColor(QColor c)
|
||||
{
|
||||
QPalette pal = ui->color->palette();
|
||||
pal.setColor(QPalette::Button, c);
|
||||
ui->color->setAutoFillBackground(true);
|
||||
ui->color->setPalette(pal);
|
||||
ui->color->update();
|
||||
trace.setColor(c);
|
||||
}
|
||||
29
Software/PC_Application/Traces/traceeditdialog.h
Normal file
29
Software/PC_Application/Traces/traceeditdialog.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef TRACEEDITDIALOG_H
|
||||
#define TRACEEDITDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "trace.h"
|
||||
|
||||
namespace Ui {
|
||||
class TraceEditDialog;
|
||||
}
|
||||
|
||||
class TraceEditDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TraceEditDialog(Trace &t, QWidget *parent = nullptr);
|
||||
~TraceEditDialog();
|
||||
|
||||
private slots:
|
||||
void on_color_clicked();
|
||||
void on_buttonBox_accepted();
|
||||
|
||||
private:
|
||||
void setColor(QColor c);
|
||||
Ui::TraceEditDialog *ui;
|
||||
Trace &trace;
|
||||
};
|
||||
|
||||
#endif // TRACEEDITDIALOG_H
|
||||
209
Software/PC_Application/Traces/traceeditdialog.ui
Normal file
209
Software/PC_Application/Traces/traceeditdialog.ui
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TraceEditDialog</class>
|
||||
<widget class="QDialog" name="TraceEditDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>282</width>
|
||||
<height>355</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Trace</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="name"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Color:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="color">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="bLive">
|
||||
<property name="text">
|
||||
<string>Live Capture</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">GSource</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="bFile">
|
||||
<property name="text">
|
||||
<string>From File</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">GSource</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stack">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="CLiveType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Overwrite</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Max hold</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Min hold</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Parameter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="CLiveParam">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S11</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S12</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S21</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S22</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="TouchstoneImport" name="touchstoneImport" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Parameter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="CParameter"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TouchstoneImport</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>CustomWidgets/touchstoneimport.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>GSource</sender>
|
||||
<signal>buttonClicked(int)</signal>
|
||||
<receiver>stack</receiver>
|
||||
<slot>setCurrentIndex(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>146</x>
|
||||
<y>216</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="GSource"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
186
Software/PC_Application/Traces/traceexportdialog.cpp
Normal file
186
Software/PC_Application/Traces/traceexportdialog.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
#include "traceexportdialog.h"
|
||||
#include "ui_traceexportdialog.h"
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include "touchstone.h"
|
||||
#include <QPushButton>
|
||||
|
||||
TraceExportDialog::TraceExportDialog(TraceModel &model, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::TraceExportDialog),
|
||||
model(model),
|
||||
freqsSet(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||
ui->gbTraces->setLayout(new QGridLayout);
|
||||
on_sbPorts_valueChanged(ui->sbPorts->value());
|
||||
}
|
||||
|
||||
TraceExportDialog::~TraceExportDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool TraceExportDialog::setTrace(int portFrom, int portTo, Trace *t)
|
||||
{
|
||||
if(portFrom < 0 || portFrom >= ui->sbPorts->value() || portTo < 0 || portTo >= ui->sbPorts->value()) {
|
||||
// invalid port selection
|
||||
return false;
|
||||
}
|
||||
auto c = cTraces[portTo][portFrom];
|
||||
if(t) {
|
||||
for(int i=1;i<c->count();i++) {
|
||||
if(t == qvariant_cast<Trace*>(c->itemData(i))) {
|
||||
// select this trace
|
||||
c->setCurrentIndex(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// requested trace is not an option
|
||||
return false;
|
||||
} else {
|
||||
// select 'none' option
|
||||
c->setCurrentIndex(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TraceExportDialog::setPortNum(int ports)
|
||||
{
|
||||
if(ports < 1 || ports > 4) {
|
||||
return false;
|
||||
}
|
||||
ui->sbPorts->setValue(ports);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TraceExportDialog::on_buttonBox_accepted()
|
||||
{
|
||||
auto filename = QFileDialog::getSaveFileName(this, "Select file for exporting traces", "", "Touchstone files (*.s1p *.s2p *.s3p *.s4p)", nullptr, QFileDialog::DontUseNativeDialog);
|
||||
if(filename.length() > 0) {
|
||||
auto ports = ui->sbPorts->value();
|
||||
auto t = Touchstone(ports);
|
||||
// add trace points to touchstone
|
||||
for(unsigned int s=0;s<points;s++) {
|
||||
Touchstone::Datapoint tData;
|
||||
for(int i=0;i<ports;i++) {
|
||||
for(int j=0;j<ports;j++) {
|
||||
if(cTraces[i][j]->currentIndex() == 0) {
|
||||
// missing trace, set to 0
|
||||
tData.S.push_back(0.0);
|
||||
} else {
|
||||
Trace *t = qvariant_cast<Trace*>(cTraces[i][j]->itemData(cTraces[i][j]->currentIndex()));
|
||||
// extract frequency (will overwrite for each trace but all traces have the same frequency points anyway)
|
||||
tData.frequency = t->sample(s).frequency;
|
||||
// add S parameter from trace to touchstone
|
||||
tData.S.push_back(t->sample(s).S);
|
||||
}
|
||||
}
|
||||
}
|
||||
t.AddDatapoint(tData);
|
||||
}
|
||||
Touchstone::Unit unit;
|
||||
switch(ui->cUnit->currentIndex()) {
|
||||
case 0: unit = Touchstone::Unit::Hz; break;
|
||||
case 1: unit = Touchstone::Unit::kHz; break;
|
||||
case 2: unit = Touchstone::Unit::MHz; break;
|
||||
case 3: unit = Touchstone::Unit::GHz; break;
|
||||
}
|
||||
Touchstone::Format format;
|
||||
switch(ui->cFormat->currentIndex()) {
|
||||
case 0: format = Touchstone::Format::DBAngle; break;
|
||||
case 1: format = Touchstone::Format::MagnitudeAngle; break;
|
||||
case 2: format = Touchstone::Format::RealImaginary; break;
|
||||
}
|
||||
|
||||
t.toFile(filename.toStdString(), unit, format);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceExportDialog::on_sbPorts_valueChanged(int ports)
|
||||
{
|
||||
// remove the previous widgets
|
||||
QGridLayout *layout = static_cast<QGridLayout*>(ui->gbTraces->layout());
|
||||
QLayoutItem *child;
|
||||
while ((child = layout->takeAt(0)) != 0) {
|
||||
delete child->widget();
|
||||
delete child;
|
||||
}
|
||||
cTraces.clear();
|
||||
auto availableTraces = model.getTraces();
|
||||
for(int i=0;i<ports;i++) {
|
||||
cTraces.push_back(std::vector<QComboBox*>());
|
||||
for(int j=0;j<ports;j++) {
|
||||
auto l = new QLabel("S"+QString::number(i+1)+QString::number(j+1)+":");
|
||||
auto c = new QComboBox();
|
||||
// create possible trace selections
|
||||
c->addItem("None");
|
||||
for(auto t : availableTraces) {
|
||||
if(i == j && !t->isReflection()) {
|
||||
// can not add through measurement at reflection port
|
||||
continue;
|
||||
} else if(i != j && t->isReflection()) {
|
||||
// can not add reflection measurement at through port
|
||||
continue;
|
||||
}
|
||||
c->addItem(t->name(), QVariant::fromValue<Trace*>(t));
|
||||
}
|
||||
connect(c, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) {
|
||||
selectionChanged(c);
|
||||
});
|
||||
cTraces[i].push_back(c);
|
||||
layout->addWidget(l, i, j*2);
|
||||
layout->addWidget(c, i, j*2 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceExportDialog::selectionChanged(QComboBox *w)
|
||||
{
|
||||
if(w->currentIndex() != 0 && !freqsSet) {
|
||||
// the first trace has been selected, extract frequency info
|
||||
Trace *t = qvariant_cast<Trace*>(w->itemData(w->currentIndex()));
|
||||
points = t->size();
|
||||
ui->points->setText(QString::number(points));
|
||||
if(points > 0) {
|
||||
lowerFreq = t->minFreq();
|
||||
upperFreq = t->maxFreq();
|
||||
ui->lowerFreq->setText(QString::number(lowerFreq));
|
||||
ui->upperFreq->setText(QString::number(upperFreq));
|
||||
}
|
||||
freqsSet = true;
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
|
||||
// remove all trace options with incompatible frequencies
|
||||
for(auto v1 : cTraces) {
|
||||
for(auto c : v1) {
|
||||
for(int i=1;i<c->count();i++) {
|
||||
Trace *t = qvariant_cast<Trace*>(c->itemData(i));
|
||||
if(t->size() != points || (points > 0 && (t->minFreq() != lowerFreq || t->maxFreq() != upperFreq))) {
|
||||
// this trace is not available anymore
|
||||
c->removeItem(i);
|
||||
// decrement to check the next index in the next loop iteration
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(w->currentIndex() == 0 && freqsSet) {
|
||||
// Check if all trace selections are set for none
|
||||
for(auto v1 : cTraces) {
|
||||
for(auto c : v1) {
|
||||
if(c->currentIndex() != 0) {
|
||||
// some trace is still selected, abort
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// all traces set for none
|
||||
freqsSet = false;
|
||||
ui->points->clear();
|
||||
ui->lowerFreq->clear();
|
||||
ui->upperFreq->clear();
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||
}
|
||||
}
|
||||
38
Software/PC_Application/Traces/traceexportdialog.h
Normal file
38
Software/PC_Application/Traces/traceexportdialog.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef TRACEEXPORTDIALOG_H
|
||||
#define TRACEEXPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QComboBox>
|
||||
#include "tracemodel.h"
|
||||
#include <QSignalMapper>
|
||||
|
||||
namespace Ui {
|
||||
class TraceExportDialog;
|
||||
}
|
||||
|
||||
class TraceExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TraceExportDialog(TraceModel &model, QWidget *parent = nullptr);
|
||||
~TraceExportDialog();
|
||||
bool setTrace(int portFrom, int portTo, Trace *t);
|
||||
bool setPortNum(int ports);
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
void on_sbPorts_valueChanged(int ports);
|
||||
void selectionChanged(QComboBox *w);
|
||||
|
||||
private:
|
||||
Ui::TraceExportDialog *ui;
|
||||
TraceModel &model;
|
||||
std::vector<std::vector<QComboBox*>> cTraces;
|
||||
|
||||
unsigned int points;
|
||||
double lowerFreq, upperFreq;
|
||||
bool freqsSet;
|
||||
};
|
||||
|
||||
#endif // TRACEEXPORTDIALOG_H
|
||||
243
Software/PC_Application/Traces/traceexportdialog.ui
Normal file
243
Software/PC_Application/Traces/traceexportdialog.ui
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TraceExportDialog</class>
|
||||
<widget class="QDialog" name="TraceExportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>516</width>
|
||||
<height>486</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ports:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="sbPorts">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gbTraces">
|
||||
<property name="title">
|
||||
<string>Traces</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Touchstone Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Unit:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="cUnit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Hz</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>kHz</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>MHz</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GHz</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Format:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cFormat">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>db/Angle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mag/Angle</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Real/Imag</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Trace Info</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>Points:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="points">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Lower Frequency:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lowerFreq">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="text">
|
||||
<string>Upper Frequency:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="upperFreq">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>TraceExportDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
178
Software/PC_Application/Traces/traceimportdialog.cpp
Normal file
178
Software/PC_Application/Traces/traceimportdialog.cpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "traceimportdialog.h"
|
||||
#include "ui_traceimportdialog.h"
|
||||
#include <QAbstractTableModel>
|
||||
#include <QObject>
|
||||
#include <QModelIndex>
|
||||
#include <QColorDialog>
|
||||
|
||||
TraceImportDialog::TraceImportDialog(TraceModel &model, std::vector<Trace*> traces, QString prefix, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::TraceImportDialog),
|
||||
model(model)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
tableModel = new TraceParameterModel(traces, prefix);
|
||||
ui->tableView->setModel(tableModel);
|
||||
}
|
||||
|
||||
TraceImportDialog::~TraceImportDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete tableModel;
|
||||
}
|
||||
|
||||
void TraceImportDialog::on_buttonBox_accepted()
|
||||
{
|
||||
tableModel->import(model);
|
||||
}
|
||||
|
||||
TraceParameterModel::TraceParameterModel(std::vector<Trace *> traces, QString prefix, QObject *parent)
|
||||
: QAbstractTableModel(parent),
|
||||
traces(traces)
|
||||
{
|
||||
int hue = 0;
|
||||
for(auto t : traces) {
|
||||
Parameter p;
|
||||
p.name = prefix + t->name();
|
||||
p.color = QColor::fromHsl((hue++ * 30) % 360, 250, 128);
|
||||
p.enabled = true;
|
||||
p.trace = t->name();
|
||||
params.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
int TraceParameterModel::rowCount(const QModelIndex &) const {
|
||||
return params.size();
|
||||
}
|
||||
|
||||
int TraceParameterModel::columnCount(const QModelIndex &) const {
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariant TraceParameterModel::data(const QModelIndex &index, int role) const {
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if ((unsigned int) index.row() >= params.size())
|
||||
return QVariant();
|
||||
auto p = params[index.row()];
|
||||
if (index.column() == 0) {
|
||||
if (role == Qt::DisplayRole) {
|
||||
return p.trace;
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else if (index.column() == 1) {
|
||||
if (role == Qt::CheckStateRole) {
|
||||
if(p.enabled) {
|
||||
return Qt::Checked;
|
||||
} else {
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else if (index.column() == 2) {
|
||||
if (role == Qt::BackgroundRole || role == Qt::EditRole) {
|
||||
if(p.enabled) {
|
||||
return p.color;
|
||||
} else {
|
||||
return (QColor) Qt::gray;
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else if (index.column() == 3) {
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
return p.name;
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant TraceParameterModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch(section) {
|
||||
case 0: return "Parameter"; break;
|
||||
case 1: return "Import"; break;
|
||||
case 2: return "Color"; break;
|
||||
case 3: return "Tracename"; break;
|
||||
default: return QVariant(); break;
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool TraceParameterModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if((unsigned int) index.row() >= params.size()) {
|
||||
return false;
|
||||
}
|
||||
auto &p = params[index.row()];
|
||||
if(role == Qt::CheckStateRole && index.column()==1) {
|
||||
if((Qt::CheckState)value.toInt() == Qt::Checked) {
|
||||
p.enabled = true;
|
||||
} else {
|
||||
p.enabled = false;
|
||||
}
|
||||
dataChanged(this->index(index.row(),2), this->index(index.row(),3));
|
||||
return true;
|
||||
} else if(role == Qt::EditRole && index.column() == 2) {
|
||||
p.color = value.value<QColor>();
|
||||
return true;
|
||||
} else if(role == Qt::EditRole && index.column() == 3) {
|
||||
p.name = value.toString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags TraceParameterModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
int flags = Qt::NoItemFlags;
|
||||
if(index.column() == 0) {
|
||||
flags |= Qt::ItemIsEnabled;
|
||||
} else if(index.column() == 1) {
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
flags |= Qt::ItemIsEnabled;
|
||||
} else if(index.column() == 2) {
|
||||
if(params[index.row()].enabled) {
|
||||
flags |= Qt::ItemIsEnabled;
|
||||
}
|
||||
} else if(index.column() == 3) {
|
||||
flags |= Qt::ItemIsEditable;
|
||||
if(params[index.row()].enabled) {
|
||||
flags |= Qt::ItemIsEnabled;
|
||||
}
|
||||
}
|
||||
return (Qt::ItemFlags) flags;
|
||||
}
|
||||
|
||||
void TraceParameterModel::import(TraceModel &model)
|
||||
{
|
||||
for(unsigned int i=0;i<params.size();i++) {
|
||||
if(params[i].enabled) {
|
||||
traces[i]->setColor(params[i].color);
|
||||
traces[i]->setName(params[i].name);
|
||||
model.addTrace(traces[i]);
|
||||
} else {
|
||||
delete traces[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceImportDialog::on_tableView_doubleClicked(const QModelIndex &index)
|
||||
{
|
||||
if(index.column() == 2 && tableModel->params[index.row()].enabled) {
|
||||
auto initialColor = tableModel->params[index.row()].color;
|
||||
auto newColor = QColorDialog::getColor(initialColor, this, "Select color", QColorDialog::DontUseNativeDialog);
|
||||
if(newColor.isValid()) {
|
||||
tableModel->setData(index, newColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Software/PC_Application/Traces/traceimportdialog.h
Normal file
59
Software/PC_Application/Traces/traceimportdialog.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef TRACEIMPORTDIALOG_H
|
||||
#define TRACEIMPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "tracemodel.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Ui {
|
||||
class TraceImportDialog;
|
||||
}
|
||||
|
||||
class TraceParameterModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class TraceImportDialog;
|
||||
public:
|
||||
TraceParameterModel(std::vector<Trace*> traces, QString prefix, QObject *parent = 0);
|
||||
~TraceParameterModel(){};
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
// adds all enabled traces to the model, deletes other traces
|
||||
void import(TraceModel &model);
|
||||
private:
|
||||
class Parameter {
|
||||
public:
|
||||
bool enabled;
|
||||
QString trace;
|
||||
QString name;
|
||||
QColor color;
|
||||
};
|
||||
std::vector<Parameter> params;
|
||||
std::vector<Trace*> traces;
|
||||
};
|
||||
|
||||
class TraceImportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TraceImportDialog(TraceModel &model, std::vector<Trace*> traces, QString prefix = QString(), QWidget *parent = nullptr);
|
||||
~TraceImportDialog();
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_accepted();
|
||||
void on_tableView_doubleClicked(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
Ui::TraceImportDialog *ui;
|
||||
TraceModel &model;
|
||||
TraceParameterModel *tableModel;
|
||||
};
|
||||
|
||||
#endif // TRACEIMPORTDIALOG_H
|
||||
83
Software/PC_Application/Traces/traceimportdialog.ui
Normal file
83
Software/PC_Application/Traces/traceimportdialog.ui
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TraceImportDialog</class>
|
||||
<widget class="QDialog" name="TraceImportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>420</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Import Traces</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView">
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>21</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>80</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>TraceImportDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>TraceImportDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
122
Software/PC_Application/Traces/tracemarker.cpp
Normal file
122
Software/PC_Application/Traces/tracemarker.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include "tracemarker.h"
|
||||
#include <QPainter>
|
||||
|
||||
TraceMarker::TraceMarker()
|
||||
: parentTrace(nullptr),
|
||||
frequency(1000000000),
|
||||
number(1),
|
||||
data(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TraceMarker::~TraceMarker()
|
||||
{
|
||||
if(parentTrace) {
|
||||
parentTrace->removeMarker(this);
|
||||
}
|
||||
emit deleted(this);
|
||||
}
|
||||
|
||||
void TraceMarker::assignTrace(Trace *t)
|
||||
{
|
||||
if(parentTrace) {
|
||||
// remove connection from previous parent trace
|
||||
parentTrace->removeMarker(this);
|
||||
disconnect(parentTrace, &Trace::deleted, this, &TraceMarker::parentTraceDeleted);
|
||||
disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
|
||||
disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
|
||||
}
|
||||
parentTrace = t;
|
||||
connect(parentTrace, &Trace::deleted, this, &TraceMarker::parentTraceDeleted);
|
||||
connect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
|
||||
connect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
|
||||
constrainFrequency();
|
||||
updateSymbol();
|
||||
parentTrace->addMarker(this);
|
||||
}
|
||||
|
||||
Trace *TraceMarker::trace()
|
||||
{
|
||||
return parentTrace;
|
||||
}
|
||||
|
||||
QString TraceMarker::readableData()
|
||||
{
|
||||
auto db = 20*log10(abs(data));
|
||||
auto phase = arg(data);
|
||||
return QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||
}
|
||||
|
||||
void TraceMarker::setFrequency(double freq)
|
||||
{
|
||||
frequency = freq;
|
||||
constrainFrequency();
|
||||
}
|
||||
|
||||
void TraceMarker::parentTraceDeleted(Trace *t)
|
||||
{
|
||||
if(t == parentTrace) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceMarker::traceDataChanged()
|
||||
{
|
||||
// some data of the parent trace changed, check if marker data also changed
|
||||
auto tracedata = parentTrace->getData(frequency);
|
||||
if(tracedata != data) {
|
||||
data = tracedata;
|
||||
emit dataChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceMarker::updateSymbol()
|
||||
{
|
||||
constexpr int width = 15, height = 15;
|
||||
symbol = QPixmap(width, height);
|
||||
symbol.fill(Qt::transparent);
|
||||
QPainter p(&symbol);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
QPointF points[] = {QPointF(0,0),QPointF(width,0),QPointF(width/2,height)};
|
||||
auto traceColor = parentTrace->color();
|
||||
p.setPen(traceColor);
|
||||
p.setBrush(traceColor);
|
||||
p.drawConvexPolygon(points, 3);
|
||||
auto brightness = traceColor.redF() * 0.299 + traceColor.greenF() * 0.587 + traceColor.blueF() * 0.114;
|
||||
p.setPen((brightness > 0.6) ? Qt::black : Qt::white);
|
||||
p.drawText(QRectF(0,0,width, height*2.0/3.0), Qt::AlignCenter, QString::number(number));
|
||||
}
|
||||
|
||||
void TraceMarker::constrainFrequency()
|
||||
{
|
||||
if(parentTrace && parentTrace->size() > 0) {
|
||||
if(frequency > parentTrace->maxFreq()) {
|
||||
frequency = parentTrace->maxFreq();
|
||||
} else if(frequency < parentTrace->minFreq()) {
|
||||
frequency = parentTrace->minFreq();
|
||||
}
|
||||
traceDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int TraceMarker::getNumber() const
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
std::complex<double> TraceMarker::getData() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
QPixmap &TraceMarker::getSymbol()
|
||||
{
|
||||
return symbol;
|
||||
}
|
||||
|
||||
double TraceMarker::getFrequency() const
|
||||
{
|
||||
return frequency;
|
||||
}
|
||||
|
||||
45
Software/PC_Application/Traces/tracemarker.h
Normal file
45
Software/PC_Application/Traces/tracemarker.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef TRACEMARKER_H
|
||||
#define TRACEMARKER_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QObject>
|
||||
#include "trace.h"
|
||||
|
||||
class TraceMarker : public QObject
|
||||
{
|
||||
friend class TraceMarkerModel;
|
||||
Q_OBJECT;
|
||||
public:
|
||||
TraceMarker();
|
||||
~TraceMarker();
|
||||
void assignTrace(Trace *t);
|
||||
Trace* trace();
|
||||
QString readableData();
|
||||
|
||||
double getFrequency() const;
|
||||
std::complex<double> getData() const;
|
||||
|
||||
QPixmap& getSymbol();
|
||||
|
||||
int getNumber() const;
|
||||
|
||||
public slots:
|
||||
void setFrequency(double freq);
|
||||
signals:
|
||||
void deleted(TraceMarker *m);
|
||||
void dataChanged(TraceMarker *m);
|
||||
|
||||
private slots:
|
||||
void parentTraceDeleted(Trace *t);
|
||||
void traceDataChanged();
|
||||
void updateSymbol();
|
||||
private:
|
||||
void constrainFrequency();
|
||||
Trace *parentTrace;
|
||||
double frequency;
|
||||
int number;
|
||||
std::complex<double> data;
|
||||
QPixmap symbol;
|
||||
};
|
||||
|
||||
#endif // TRACEMARKER_H
|
||||
228
Software/PC_Application/Traces/tracemarkermodel.cpp
Normal file
228
Software/PC_Application/Traces/tracemarkermodel.cpp
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#include "tracemarkermodel.h"
|
||||
#include "unit.h"
|
||||
#include <QComboBox>
|
||||
#include <QApplication>
|
||||
|
||||
TraceMarkerModel::TraceMarkerModel(TraceModel &model, QObject *parent)
|
||||
: QAbstractTableModel(parent),
|
||||
model(model)
|
||||
{
|
||||
markers.clear();
|
||||
}
|
||||
|
||||
TraceMarker *TraceMarkerModel::createDefaultMarker()
|
||||
{
|
||||
// find lowest free number
|
||||
int number = 0;
|
||||
bool used;
|
||||
do {
|
||||
number++;
|
||||
used = false;
|
||||
for(auto m : markers) {
|
||||
if(m->number == number) {
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (used);
|
||||
auto marker = new TraceMarker();
|
||||
marker->number = number;
|
||||
marker->frequency = 2150000000;
|
||||
marker->assignTrace(model.trace(0));
|
||||
return marker;
|
||||
}
|
||||
|
||||
void TraceMarkerModel::addMarker(TraceMarker *t)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), markers.size(), markers.size());
|
||||
markers.push_back(t);
|
||||
endInsertRows();
|
||||
connect(t, &TraceMarker::dataChanged, this, &TraceMarkerModel::markerDataChanged);
|
||||
connect(t, &TraceMarker::deleted, this, qOverload<TraceMarker*>(&TraceMarkerModel::removeMarker));
|
||||
emit markerAdded(t);
|
||||
}
|
||||
|
||||
void TraceMarkerModel::removeMarker(unsigned int index, bool delete_marker)
|
||||
{
|
||||
if (index < markers.size()) {
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
if(delete_marker) {
|
||||
delete markers[index];
|
||||
}
|
||||
markers.erase(markers.begin() + index);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceMarkerModel::removeMarker(TraceMarker *m)
|
||||
{
|
||||
auto it = std::find(markers.begin(), markers.end(), m);
|
||||
if(it != markers.end()) {
|
||||
removeMarker(it - markers.begin(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceMarkerModel::markerDataChanged(TraceMarker *)
|
||||
{
|
||||
emit dataChanged(index(0, ColIndexFreq), index(markers.size()-1, ColIndexData));
|
||||
}
|
||||
|
||||
TraceMarker *TraceMarkerModel::marker(int index)
|
||||
{
|
||||
return markers.at(index);
|
||||
}
|
||||
|
||||
int TraceMarkerModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return markers.size();
|
||||
}
|
||||
|
||||
int TraceMarkerModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariant TraceMarkerModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
auto marker = markers[index.row()];
|
||||
switch(index.column()) {
|
||||
case ColIndexNumber:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole: return QVariant((unsigned int)marker->number); break;
|
||||
}
|
||||
case ColIndexTrace:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole:
|
||||
if(marker->parentTrace) {
|
||||
return marker->parentTrace->name();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ColIndexFreq:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole: return Unit::ToString(marker->frequency, "Hz", " kMG", 6); break;
|
||||
}
|
||||
case ColIndexData:
|
||||
switch(role) {
|
||||
case Qt::DisplayRole: return marker->readableData(); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant TraceMarkerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch(section) {
|
||||
case ColIndexNumber: return "#"; break;
|
||||
case ColIndexTrace: return "Trace"; break;
|
||||
case ColIndexFreq: return "Frequency"; break;
|
||||
case ColIndexData: return "Data"; break;
|
||||
default: return QVariant(); break;
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool TraceMarkerModel::setData(const QModelIndex &index, const QVariant &value, int)
|
||||
{
|
||||
if((unsigned int) index.row() >= markers.size()) {
|
||||
return false;
|
||||
}
|
||||
auto m = markers[index.row()];
|
||||
switch(index.column()) {
|
||||
case ColIndexNumber: {
|
||||
bool convertOk;
|
||||
unsigned int number;
|
||||
number = value.toUInt(&convertOk);
|
||||
if(convertOk) {
|
||||
m->number = number;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ColIndexTrace: {
|
||||
auto trace = qvariant_cast<Trace*>(value);
|
||||
m->assignTrace(trace);
|
||||
}
|
||||
break;
|
||||
case ColIndexFreq: {
|
||||
auto newval = Unit::FromString(value.toString(), "Hz", " kMG");
|
||||
if(!qIsNaN(newval)) {
|
||||
m->setFrequency(newval);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags TraceMarkerModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
int flags = Qt::NoItemFlags;
|
||||
switch(index.column()) {
|
||||
case ColIndexNumber: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break;
|
||||
case ColIndexTrace: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break;
|
||||
case ColIndexFreq: flags |= Qt::ItemIsEnabled | Qt::ItemIsEditable; break;
|
||||
case ColIndexData: flags |= Qt::ItemIsEnabled; break;
|
||||
}
|
||||
return (Qt::ItemFlags) flags;
|
||||
}
|
||||
|
||||
std::vector<TraceMarker *> TraceMarkerModel::getMarker()
|
||||
{
|
||||
return markers;
|
||||
}
|
||||
|
||||
std::vector<TraceMarker *> TraceMarkerModel::getMarker(Trace *t)
|
||||
{
|
||||
std::vector<TraceMarker*> attachedMarkers;
|
||||
for(auto m : markers) {
|
||||
if(m->parentTrace == t) {
|
||||
attachedMarkers.push_back(m);
|
||||
}
|
||||
}
|
||||
return attachedMarkers;
|
||||
}
|
||||
|
||||
TraceModel &TraceMarkerModel::getModel()
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
QWidget *TraceChooserDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
|
||||
{
|
||||
auto model = (TraceMarkerModel*) index.model();
|
||||
auto c = new QComboBox(parent);
|
||||
connect(c, qOverload<int>(&QComboBox::currentIndexChanged), [c](int) {
|
||||
c->clearFocus();
|
||||
});
|
||||
auto traces = model->getModel().getTraces();
|
||||
for(auto t : traces) {
|
||||
c->addItem(t->name(), QVariant::fromValue<Trace*>(t));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void TraceChooserDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
auto model = (TraceMarkerModel*) index.model();
|
||||
auto marker = model->getMarker()[index.row()];
|
||||
auto c = (QComboBox*) editor;
|
||||
for(int i=0;i<c->count();i++) {
|
||||
if(qvariant_cast<Trace*>(c->itemData(i)) == marker->trace()) {
|
||||
c->setCurrentIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceChooserDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
auto markerModel = (TraceMarkerModel*) model;
|
||||
auto c = (QComboBox*) editor;
|
||||
markerModel->setData(index, c->itemData(c->currentIndex()));
|
||||
}
|
||||
61
Software/PC_Application/Traces/tracemarkermodel.h
Normal file
61
Software/PC_Application/Traces/tracemarkermodel.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef TRACEMARKERMODEL_H
|
||||
#define TRACEMARKERMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include "tracemarker.h"
|
||||
#include <vector>
|
||||
#include "tracemodel.h"
|
||||
#include <QItemDelegate>
|
||||
|
||||
class TraceChooserDelegate : public QItemDelegate
|
||||
{
|
||||
Q_OBJECT;
|
||||
QWidget *createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
|
||||
void setEditorData(QWidget * editor, const QModelIndex & index) const override;
|
||||
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
|
||||
};
|
||||
|
||||
class TraceMarkerModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TraceMarkerModel(TraceModel &model, QObject *parent = 0);
|
||||
|
||||
enum {
|
||||
ColIndexNumber = 0,
|
||||
ColIndexTrace = 1,
|
||||
ColIndexFreq = 2,
|
||||
ColIndexData = 3,
|
||||
};
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
TraceMarker* createDefaultMarker();
|
||||
TraceMarker *marker(int index);
|
||||
std::vector<TraceMarker*> getMarker();
|
||||
std::vector<TraceMarker*> getMarker(Trace *t);
|
||||
TraceModel& getModel();
|
||||
|
||||
public slots:
|
||||
void addMarker(TraceMarker *t);
|
||||
void removeMarker(unsigned int index, bool delete_marker = true);
|
||||
void removeMarker(TraceMarker *m);
|
||||
|
||||
|
||||
signals:
|
||||
void markerAdded(TraceMarker *t);
|
||||
|
||||
private slots:
|
||||
void markerDataChanged(TraceMarker *m);
|
||||
private:
|
||||
std::vector<TraceMarker*> markers;
|
||||
TraceModel &model;
|
||||
|
||||
};
|
||||
|
||||
#endif // TRACEMARKERMODEL_H
|
||||
137
Software/PC_Application/Traces/tracemodel.cpp
Normal file
137
Software/PC_Application/Traces/tracemodel.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "tracemodel.h"
|
||||
#include <QIcon>
|
||||
|
||||
using namespace std;
|
||||
|
||||
TraceModel::TraceModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
traces.clear();
|
||||
}
|
||||
|
||||
void TraceModel::addTrace(Trace *t)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), traces.size(), traces.size());
|
||||
traces.push_back(t);
|
||||
endInsertRows();
|
||||
emit traceAdded(t);
|
||||
}
|
||||
|
||||
void TraceModel::removeTrace(unsigned int index)
|
||||
{
|
||||
if (index < traces.size()) {
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
auto trace = traces[index];
|
||||
delete trace;
|
||||
traces.erase(traces.begin() + index);
|
||||
endRemoveRows();
|
||||
emit traceRemoved(trace);
|
||||
}
|
||||
}
|
||||
|
||||
Trace *TraceModel::trace(unsigned int index)
|
||||
{
|
||||
return traces.at(index);
|
||||
}
|
||||
|
||||
void TraceModel::toggleVisibility(unsigned int index)
|
||||
{
|
||||
if (index < traces.size()) {
|
||||
traces[index]->setVisible(!traces[index]->isVisible());
|
||||
emit dataChanged(createIndex(index, 0), createIndex(index, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void TraceModel::togglePause(unsigned int index)
|
||||
{
|
||||
if (index < traces.size()) {
|
||||
if(traces[index]->isPaused()) {
|
||||
traces[index]->resume();
|
||||
} else {
|
||||
traces[index]->pause();
|
||||
}
|
||||
emit dataChanged(createIndex(index, 1), createIndex(index, 1));
|
||||
}
|
||||
}
|
||||
|
||||
int TraceModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return traces.size();
|
||||
}
|
||||
|
||||
int TraceModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
QVariant TraceModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if ((unsigned int) index.row() >= traces.size())
|
||||
return QVariant();
|
||||
if (index.column() == 0) {
|
||||
if (role == Qt::DecorationRole) {
|
||||
if (traces[index.row()]->isVisible()) {
|
||||
return QIcon(":/icons/visible.svg");
|
||||
} else {
|
||||
return QIcon(":/icons/invisible.svg");
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else if (index.column() == 1) {
|
||||
if (role == Qt::DecorationRole && !traces[index.row()]->isTouchstone()) {
|
||||
if (traces[index.row()]->isPaused()) {
|
||||
return QIcon(":/icons/pause.svg");
|
||||
} else {
|
||||
return QIcon(":/icons/play.svg");
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else if (index.column() == 2) {
|
||||
if (role == Qt::DisplayRole) {
|
||||
return traces[index.row()]->name();
|
||||
} else if (role == Qt::ForegroundRole) {
|
||||
return traces[index.row()]->color();
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Trace *> TraceModel::getTraces()
|
||||
{
|
||||
return traces;
|
||||
}
|
||||
|
||||
void TraceModel::clearVNAData()
|
||||
{
|
||||
for(auto t : traces) {
|
||||
if (!t->isTouchstone()) {
|
||||
// this trace is fed from live data
|
||||
t->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceModel::addVNAData(Protocol::Datapoint d)
|
||||
{
|
||||
for(auto t : traces) {
|
||||
if (t->isLive()) {
|
||||
Trace::Data td;
|
||||
td.frequency = d.frequency;
|
||||
switch(t->liveParameter()) {
|
||||
case Trace::LiveParameter::S11: td.S = complex<double>(d.real_S11, d.imag_S11); break;
|
||||
case Trace::LiveParameter::S12: td.S = complex<double>(d.real_S12, d.imag_S12); break;
|
||||
case Trace::LiveParameter::S21: td.S = complex<double>(d.real_S21, d.imag_S21); break;
|
||||
case Trace::LiveParameter::S22: td.S = complex<double>(d.real_S22, d.imag_S22); break;
|
||||
}
|
||||
t->addData(td);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Software/PC_Application/Traces/tracemodel.h
Normal file
38
Software/PC_Application/Traces/tracemodel.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef TRACEMODEL_H
|
||||
#define TRACEMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include "trace.h"
|
||||
#include <vector>
|
||||
#include "Device/device.h"
|
||||
|
||||
class TraceModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TraceModel(QObject *parent = 0);
|
||||
|
||||
void addTrace(Trace *t);
|
||||
void removeTrace(unsigned int index);
|
||||
Trace *trace(unsigned int index);
|
||||
void toggleVisibility(unsigned int index);
|
||||
void togglePause(unsigned int index);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
std::vector<Trace*> getTraces();
|
||||
signals:
|
||||
void traceAdded(Trace *t);
|
||||
void traceRemoved(Trace *t);
|
||||
|
||||
public slots:
|
||||
void clearVNAData();
|
||||
void addVNAData(Protocol::Datapoint d);
|
||||
|
||||
private:
|
||||
std::vector<Trace*> traces;
|
||||
};
|
||||
|
||||
#endif // TRACEMODEL_H
|
||||
145
Software/PC_Application/Traces/traceplot.cpp
Normal file
145
Software/PC_Application/Traces/traceplot.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#include "traceplot.h"
|
||||
|
||||
const QColor TracePlot::Background = QColor(0,0,0);
|
||||
const QColor TracePlot::Border = QColor(255,255,255);
|
||||
const QColor TracePlot::Divisions = QColor(255,255,255);
|
||||
#include "tracemarker.h"
|
||||
|
||||
std::set<TracePlot*> TracePlot::plots;
|
||||
|
||||
TracePlot::TracePlot(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
contextmenu = new QMenu();
|
||||
markedForDeletion = false;
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
lastUpdate = QTime::currentTime();
|
||||
plots.insert(this);
|
||||
}
|
||||
|
||||
TracePlot::~TracePlot()
|
||||
{
|
||||
plots.erase(this);
|
||||
delete contextmenu;
|
||||
}
|
||||
|
||||
void TracePlot::enableTrace(Trace *t, bool enabled)
|
||||
{
|
||||
if(traces[t] != enabled) {
|
||||
traces[t] = enabled;
|
||||
if(enabled) {
|
||||
// connect signals
|
||||
connect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot);
|
||||
connect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
||||
connect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
||||
connect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
||||
} else {
|
||||
// disconnect from notifications
|
||||
disconnect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot);
|
||||
disconnect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
||||
disconnect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
||||
disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
||||
}
|
||||
updateContextMenu();
|
||||
triggerReplot();
|
||||
}
|
||||
}
|
||||
|
||||
void TracePlot::mouseDoubleClickEvent(QMouseEvent *) {
|
||||
emit doubleClicked(this);
|
||||
}
|
||||
|
||||
void TracePlot::UpdateSpan(double fmin, double fmax)
|
||||
{
|
||||
for(auto p : plots) {
|
||||
p->setXAxis(fmin, fmax);
|
||||
}
|
||||
}
|
||||
|
||||
void TracePlot::initializeTraceInfo(TraceModel &model)
|
||||
{
|
||||
// Populate already present traces
|
||||
auto tvect = model.getTraces();
|
||||
for(auto t : tvect) {
|
||||
newTraceAvailable(t);
|
||||
}
|
||||
|
||||
// connect notification of traces added at later point
|
||||
connect(&model, &TraceModel::traceAdded, this, &TracePlot::newTraceAvailable);
|
||||
}
|
||||
|
||||
void TracePlot::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
contextmenu->exec(event->globalPos());
|
||||
if(markedForDeletion) {
|
||||
emit deleted(this);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void TracePlot::updateContextMenu()
|
||||
{
|
||||
contextmenu->clear();
|
||||
contextmenu->addSection("Traces");
|
||||
// Populate context menu
|
||||
for(auto t : traces) {
|
||||
auto action = new QAction(t.first->name());
|
||||
action->setCheckable(true);
|
||||
if(t.second) {
|
||||
action->setChecked(true);
|
||||
}
|
||||
connect(action, &QAction::toggled, [=](bool active) {
|
||||
enableTrace(t.first, active);
|
||||
});
|
||||
contextmenu->addAction(action);
|
||||
}
|
||||
contextmenu->addSeparator();
|
||||
auto close = new QAction("Close");
|
||||
contextmenu->addAction(close);
|
||||
connect(close, &QAction::triggered, [=]() {
|
||||
markedForDeletion = true;
|
||||
});
|
||||
}
|
||||
|
||||
std::set<TracePlot *> TracePlot::getPlots()
|
||||
{
|
||||
return plots;
|
||||
}
|
||||
|
||||
void TracePlot::newTraceAvailable(Trace *t)
|
||||
{
|
||||
if(supported(t)) {
|
||||
traces[t] = false;
|
||||
connect(t, &Trace::deleted, this, &TracePlot::traceDeleted);
|
||||
connect(t, &Trace::nameChanged, this, &TracePlot::updateContextMenu);
|
||||
connect(t, &Trace::typeChanged, this, &TracePlot::updateContextMenu);
|
||||
}
|
||||
updateContextMenu();
|
||||
}
|
||||
|
||||
void TracePlot::traceDeleted(Trace *t)
|
||||
{
|
||||
enableTrace(t, false);
|
||||
traces.erase(t);
|
||||
updateContextMenu();
|
||||
triggerReplot();
|
||||
}
|
||||
|
||||
void TracePlot::triggerReplot()
|
||||
{
|
||||
auto now = QTime::currentTime();
|
||||
if (lastUpdate.msecsTo(now) >= MinUpdateInterval) {
|
||||
replot();
|
||||
lastUpdate = now;
|
||||
}
|
||||
}
|
||||
|
||||
void TracePlot::markerAdded(TraceMarker *m)
|
||||
{
|
||||
connect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot);
|
||||
triggerReplot();
|
||||
}
|
||||
|
||||
void TracePlot::markerRemoved(TraceMarker *)
|
||||
{
|
||||
triggerReplot();
|
||||
}
|
||||
55
Software/PC_Application/Traces/traceplot.h
Normal file
55
Software/PC_Application/Traces/traceplot.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef TRACEPLOT_H
|
||||
#define TRACEPLOT_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "tracemodel.h"
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QTime>
|
||||
|
||||
class TracePlot : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TracePlot( QWidget *parent = nullptr);
|
||||
~TracePlot();
|
||||
|
||||
virtual void enableTrace(Trace *t, bool enabled);
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
virtual void setXAxis(double min, double max){Q_UNUSED(min);Q_UNUSED(max)};
|
||||
|
||||
static std::set<TracePlot *> getPlots();
|
||||
static void UpdateSpan(double fmin, double fmax);
|
||||
|
||||
signals:
|
||||
void doubleClicked(QWidget *w);
|
||||
void deleted(TracePlot*);
|
||||
|
||||
protected:
|
||||
static const QColor Background;// = QColor(0,0,0);
|
||||
static const QColor Border;// = QColor(255,255,255);
|
||||
static const QColor Divisions;// = QColor(255,255,255);
|
||||
static constexpr int MinUpdateInterval = 100;
|
||||
// need to be called in derived class constructor
|
||||
void initializeTraceInfo(TraceModel &model);
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
virtual void updateContextMenu();
|
||||
virtual bool supported(Trace *t) = 0;
|
||||
virtual void replot(){};
|
||||
std::map<Trace*, bool> traces;
|
||||
QMenu *contextmenu;
|
||||
QTime lastUpdate;
|
||||
bool markedForDeletion;
|
||||
|
||||
static std::set<TracePlot*> plots;
|
||||
|
||||
protected slots:
|
||||
void newTraceAvailable(Trace *t);
|
||||
void traceDeleted(Trace *t);
|
||||
void triggerReplot();
|
||||
virtual void markerAdded(TraceMarker *m);
|
||||
virtual void markerRemoved(TraceMarker *m);
|
||||
|
||||
};
|
||||
|
||||
#endif // TRACEPLOT_H
|
||||
184
Software/PC_Application/Traces/tracesmithchart.cpp
Normal file
184
Software/PC_Application/Traces/tracesmithchart.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#include "tracesmithchart.h"
|
||||
#include <QPainter>
|
||||
#include <array>
|
||||
#include <math.h>
|
||||
#include "tracemarker.h"
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std;
|
||||
|
||||
TraceSmithChart::TraceSmithChart(TraceModel &model, QWidget *parent)
|
||||
: TracePlot(parent)
|
||||
{
|
||||
chartLinesPen = QPen(palette().windowText(), 0.75);
|
||||
thinPen = QPen(palette().windowText(), 0.25);
|
||||
textPen = QPen(palette().windowText(), 0.25);
|
||||
pointDataPen = QPen(QColor("red"), 4.0, Qt::SolidLine, Qt::RoundCap);
|
||||
lineDataPen = QPen(QColor("blue"), 1.0);
|
||||
initializeTraceInfo(model);
|
||||
}
|
||||
|
||||
QPoint TraceSmithChart::plotToPixel(std::complex<double> S)
|
||||
{
|
||||
QPoint ret;
|
||||
ret.setX(S.real() * plotToPixelXScale + plotToPixelXOffset);
|
||||
ret.setY(S.imag() * plotToPixelYScale + plotToPixelYOffset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::complex<double> TraceSmithChart::pixelToPlot(const QPoint &pos)
|
||||
{
|
||||
return complex<double>((pos.x() - plotToPixelXOffset) / plotToPixelXScale, (pos.y() - plotToPixelYOffset) / plotToPixelYScale);
|
||||
}
|
||||
|
||||
void TraceSmithChart::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
auto clickPoint = event->pos();
|
||||
unsigned int closestDistance = numeric_limits<unsigned int>::max();
|
||||
TraceMarker *closestMarker = nullptr;
|
||||
for(auto t : traces) {
|
||||
auto markers = t.first->getMarkers();
|
||||
for(auto m : markers) {
|
||||
auto S = m->getData();
|
||||
auto markerPoint = plotToPixel(S);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(closestDistance <= 400) {
|
||||
selectedMarker = closestMarker;
|
||||
} else {
|
||||
selectedMarker = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceSmithChart::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if(selectedMarker) {
|
||||
auto t = selectedMarker->trace();
|
||||
auto mouseS = pixelToPlot(event->pos());
|
||||
auto samples = t->size();
|
||||
double closestDistance = numeric_limits<double>::max();
|
||||
unsigned int closestIndex = 0;
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
auto data = t->sample(i);
|
||||
auto distance = norm(data.S - mouseS);
|
||||
if(distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestIndex = i;
|
||||
}
|
||||
}
|
||||
selectedMarker->setFrequency(t->sample(closestIndex).frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceSmithChart::draw(QPainter * painter, double width_factor) {
|
||||
painter->setPen(QPen(1.0 * width_factor));
|
||||
painter->setBrush(palette().windowText());
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// // Display parameter name
|
||||
// QFont font = painter->font();
|
||||
// font.setPixelSize(48);
|
||||
// font.setBold(true);
|
||||
// painter->setFont(font);
|
||||
// painter->drawText(-512, -512, title);
|
||||
|
||||
// Outer circle
|
||||
painter->setPen(QPen(Border, 1.5 * width_factor));
|
||||
QRectF rectangle(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax);
|
||||
painter->drawArc(rectangle, 0, 5760);
|
||||
|
||||
constexpr int Circles = 6;
|
||||
painter->setPen(QPen(Divisions, 0.5 * width_factor, Qt::DashLine));
|
||||
for(int i=1;i<Circles;i++) {
|
||||
rectangle.adjust(2*smithCoordMax/Circles, smithCoordMax/Circles, 0, -smithCoordMax/Circles);
|
||||
painter->drawArc(rectangle, 0, 5760);
|
||||
}
|
||||
|
||||
painter->drawLine(-smithCoordMax, 0, smithCoordMax, 0);
|
||||
constexpr std::array<double, 5> impedanceLines = {10, 25, 50, 100, 250};
|
||||
for(auto z : impedanceLines) {
|
||||
z /= ReferenceImpedance;
|
||||
auto radius = smithCoordMax * 1.0/z;
|
||||
double span = M_PI - 2 * atan(radius/smithCoordMax);
|
||||
span *= 5760 / (2 * M_PI);
|
||||
QRectF rectangle(smithCoordMax - radius, -2*radius, 2 * radius, 2 * radius);
|
||||
painter->drawArc(rectangle, 4320 - span, span);
|
||||
rectangle = QRectF(smithCoordMax - radius, 0, 2 * radius, 2 * radius);
|
||||
painter->drawArc(rectangle, 1440, span);
|
||||
}
|
||||
|
||||
for(auto t : traces) {
|
||||
if(!t.second) {
|
||||
// trace not enabled in plot
|
||||
continue;
|
||||
}
|
||||
auto trace = t.first;
|
||||
if(!trace->isVisible()) {
|
||||
// trace marked invisible
|
||||
continue;
|
||||
}
|
||||
painter->setPen(QPen(trace->color(), 1.5 * width_factor));
|
||||
int nPoints = trace->size();
|
||||
for(int i=1;i<nPoints;i++) {
|
||||
auto last = trace->sample(i-1).S;
|
||||
auto now = trace->sample(i).S;
|
||||
if(isnan(now.real())) {
|
||||
break;
|
||||
}
|
||||
// scale to size of smith diagram
|
||||
last *= smithCoordMax;
|
||||
now *= smithCoordMax;
|
||||
// draw line
|
||||
painter->drawLine(std::real(last), -std::imag(last), std::real(now), -std::imag(now));
|
||||
}
|
||||
auto markers = t.first->getMarkers();
|
||||
for(auto m : markers) {
|
||||
auto coords = m->getData();
|
||||
coords *= smithCoordMax;
|
||||
auto symbol = m->getSymbol();
|
||||
symbol = symbol.scaled(symbol.width()*width_factor, symbol.height()*width_factor);
|
||||
painter->drawPixmap(coords.real() - symbol.width()/2, -coords.imag() - symbol.height(), symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceSmithChart::replot()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void TraceSmithChart::paintEvent(QPaintEvent * /* the event */)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setBackground(QBrush(Background));
|
||||
painter.fillRect(0, 0, width(), height(), QBrush(Background));
|
||||
|
||||
double side = qMin(width(), height()) * screenUsage;
|
||||
|
||||
painter.setViewport((width()-side)/2, (height()-side)/2, side, side);
|
||||
painter.setWindow(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax);
|
||||
|
||||
plotToPixelXOffset = width()/2;
|
||||
plotToPixelYOffset = height()/2;
|
||||
plotToPixelXScale = side/2;
|
||||
plotToPixelYScale = -side/2;
|
||||
|
||||
draw(&painter, 2*smithCoordMax/side);
|
||||
}
|
||||
|
||||
bool TraceSmithChart::supported(Trace *t)
|
||||
{
|
||||
if(t->isReflection()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
44
Software/PC_Application/Traces/tracesmithchart.h
Normal file
44
Software/PC_Application/Traces/tracesmithchart.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef TRACESMITHCHART_H
|
||||
#define TRACESMITHCHART_H
|
||||
|
||||
#include "traceplot.h"
|
||||
#include <QPen>
|
||||
|
||||
class TraceSmithChart : public TracePlot
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TraceSmithChart(TraceModel &model, QWidget *parent = 0);
|
||||
|
||||
protected:
|
||||
static constexpr double ReferenceImpedance = 50.0;
|
||||
static constexpr double screenUsage = 0.9;
|
||||
static constexpr int smithCoordMax = 4096;
|
||||
|
||||
QPoint plotToPixel(std::complex<double> S);
|
||||
std::complex<double> pixelToPlot(const QPoint &pos);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
bool supported(Trace *t) override;
|
||||
void draw(QPainter * painter, double width_factor);
|
||||
void replot() override;
|
||||
QPen textPen;
|
||||
QPen chartLinesPen;
|
||||
QPen thinPen;
|
||||
QPen pointDataPen;
|
||||
QPen lineDataPen;
|
||||
|
||||
/// Path for the thin arcs
|
||||
QPainterPath thinArcsPath;
|
||||
/// Path for the thick arcs
|
||||
QPainterPath thickArcsPath;
|
||||
|
||||
double plotToPixelXOffset, plotToPixelXScale;
|
||||
double plotToPixelYOffset, plotToPixelYScale;
|
||||
TraceMarker *selectedMarker;
|
||||
};
|
||||
|
||||
#endif // TRACESMITHCHART_H
|
||||
121
Software/PC_Application/Traces/tracewidget.cpp
Normal file
121
Software/PC_Application/Traces/tracewidget.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "tracewidget.h"
|
||||
#include "ui_tracewidget.h"
|
||||
#include "trace.h"
|
||||
#include <QKeyEvent>
|
||||
#include "traceeditdialog.h"
|
||||
#include "traceimportdialog.h"
|
||||
#include "traceexportdialog.h"
|
||||
#include <QFileDialog>
|
||||
|
||||
TraceWidget::TraceWidget(TraceModel &model, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::TraceWidget),
|
||||
model(model)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->view->setModel(&model);
|
||||
ui->view->setAutoScroll(false);
|
||||
installEventFilter(this);
|
||||
createCount = 0;
|
||||
}
|
||||
|
||||
TraceWidget::~TraceWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void TraceWidget::on_add_clicked()
|
||||
{
|
||||
createCount++;
|
||||
auto t = new Trace("Trace #"+QString::number(createCount));
|
||||
t->setColor(QColor::fromHsl((createCount * 50) % 360, 250, 128));
|
||||
model.addTrace(t);
|
||||
}
|
||||
|
||||
void TraceWidget::on_remove_clicked()
|
||||
{
|
||||
model.removeTrace(ui->view->currentIndex().row());
|
||||
}
|
||||
|
||||
bool TraceWidget::eventFilter(QObject *, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
int key = static_cast<QKeyEvent *>(event)->key();
|
||||
if(key == Qt::Key_Escape) {
|
||||
ui->view->clearSelection();
|
||||
return true;
|
||||
} else if(key == Qt::Key_Delete) {
|
||||
model.removeTrace(ui->view->currentIndex().row());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TraceWidget::on_edit_clicked()
|
||||
{
|
||||
if(ui->view->currentIndex().isValid()) {
|
||||
auto edit = new TraceEditDialog(*model.trace(ui->view->currentIndex().row()));
|
||||
edit->show();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceWidget::on_view_doubleClicked(const QModelIndex &index)
|
||||
{
|
||||
if(index.column() == 2) {
|
||||
auto edit = new TraceEditDialog(*model.trace(index.row()));
|
||||
edit->show();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceWidget::on_view_clicked(const QModelIndex &index)
|
||||
{
|
||||
if(index.column()==0) {
|
||||
model.toggleVisibility(index.row());
|
||||
} else if(index.column()==1) {
|
||||
model.togglePause(index.row());
|
||||
}
|
||||
}
|
||||
|
||||
void TraceWidget::on_bImport_clicked()
|
||||
{
|
||||
auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "Touchstone files (*.s1p *.s2p *.s3p *.s4p)", nullptr, QFileDialog::DontUseNativeDialog);
|
||||
if (filename.length() > 0) {
|
||||
auto t = Touchstone::fromFile(filename.toStdString());
|
||||
std::vector<Trace*> traces;
|
||||
for(unsigned int i=0;i<t.ports()*t.ports();i++) {
|
||||
auto trace = new Trace();
|
||||
trace->fillFromTouchstone(t, i, filename);
|
||||
unsigned int sink = i / t.ports() + 1;
|
||||
unsigned int source = i % t.ports() + 1;
|
||||
trace->setName("S"+QString::number(sink)+QString::number(source));
|
||||
traces.push_back(trace);
|
||||
}
|
||||
// contruct prefix from filename
|
||||
// remove any directory names (keep only the filename itself)
|
||||
int lastSlash = qMax(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
|
||||
if(lastSlash != -1) {
|
||||
filename.remove(0, lastSlash + 1);
|
||||
}
|
||||
// remove file type
|
||||
filename.truncate(filename.indexOf('.'));
|
||||
auto i = new TraceImportDialog(model, traces, filename+"_");
|
||||
i->show();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceWidget::on_bExport_clicked()
|
||||
{
|
||||
auto e = new TraceExportDialog(model);
|
||||
// Attempt to set default traces (this will result in correctly populated
|
||||
// 2 port export if the initial 4 traces have not been modified)
|
||||
e->setPortNum(2);
|
||||
auto traces = model.getTraces();
|
||||
for(unsigned int i=0;i<4;i++) {
|
||||
if(i >= traces.size()) {
|
||||
break;
|
||||
}
|
||||
e->setTrace(i%2, i/2, traces[i]);
|
||||
}
|
||||
e->show();
|
||||
}
|
||||
41
Software/PC_Application/Traces/tracewidget.h
Normal file
41
Software/PC_Application/Traces/tracewidget.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef TRACEWIDGET_H
|
||||
#define TRACEWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "tracemodel.h"
|
||||
|
||||
namespace Ui {
|
||||
class TraceWidget;
|
||||
}
|
||||
|
||||
class TraceWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TraceWidget(TraceModel &model, QWidget *parent = nullptr);
|
||||
~TraceWidget();
|
||||
|
||||
public slots:
|
||||
void on_add_clicked();
|
||||
|
||||
private slots:
|
||||
void on_remove_clicked();
|
||||
void on_edit_clicked();
|
||||
|
||||
void on_view_doubleClicked(const QModelIndex &index);
|
||||
|
||||
void on_view_clicked(const QModelIndex &index);
|
||||
|
||||
void on_bImport_clicked();
|
||||
|
||||
void on_bExport_clicked();
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
Ui::TraceWidget *ui;
|
||||
TraceModel &model;
|
||||
int createCount;
|
||||
};
|
||||
|
||||
#endif // TRACEWIDGET_H
|
||||
179
Software/PC_Application/Traces/tracewidget.ui
Normal file
179
Software/PC_Application/Traces/tracewidget.ui
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TraceWidget</class>
|
||||
<widget class="QWidget" name="TraceWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>206</width>
|
||||
<height>268</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="view">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Ignored" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>0</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="add">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add"/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="remove">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bImport">
|
||||
<property name="toolTip">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/import.svg</normaloff>:/icons/import.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bExport">
|
||||
<property name="toolTip">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/export.svg</normaloff>:/icons/export.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="accessories-text-editor"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
Loading…
Add table
Add a link
Reference in a new issue