Customizable constant lines in smith chart

This commit is contained in:
Jan Käberich 2022-01-08 00:46:17 +01:00
parent 483618befb
commit cd106b3040
3 changed files with 594 additions and 97 deletions

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>307</width> <width>856</width>
<height>255</height> <height>259</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -16,7 +16,11 @@
<property name="modal"> <property name="modal">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="title"> <property name="title">
@ -97,6 +101,80 @@
</layout> </layout>
</widget> </widget>
</item> </item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Constant Lines</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="lineTable">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>20</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="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="addLine">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-add" resource="../icons.qrc">
<normaloff>:/icons/add.png</normaloff>:/icons/add.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeLine">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-remove" resource="../icons.qrc">
<normaloff>:/icons/remove.png</normaloff>:/icons/remove.png</iconset>
</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>
</item>
</layout>
</item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
@ -116,7 +194,9 @@
<header>CustomWidgets/siunitedit.h</header> <header>CustomWidgets/siunitedit.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources>
<include location="../icons.qrc"/>
</resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -11,6 +11,7 @@
#include <array> #include <array>
#include <math.h> #include <math.h>
#include <QDebug> #include <QDebug>
#include <QColorDialog>
using namespace std; using namespace std;
@ -35,6 +36,11 @@ nlohmann::json TraceSmithChart::toJSON()
} }
} }
j["traces"] = jtraces; j["traces"] = jtraces;
nlohmann::json jlines;
for(auto line : constantLines) {
jlines.push_back(line.toJSON());
}
j["constantLines"] = jlines;
return j; return j;
} }
@ -57,6 +63,13 @@ void TraceSmithChart::fromJSON(nlohmann::json j)
qWarning() << "Unable to find trace with hash" << hash; qWarning() << "Unable to find trace with hash" << hash;
} }
} }
if(j.contains("constantLines")) {
for(auto jline : j["constantLines"]) {
SmithChartConstantLine line;
line.fromJSON(jline);
constantLines.push_back(line);
}
}
} }
void TraceSmithChart::wheelEvent(QWheelEvent *event) void TraceSmithChart::wheelEvent(QWheelEvent *event)
@ -91,6 +104,12 @@ void TraceSmithChart::axisSetupDialog()
ui->zoomFactor->setPrecision(3); ui->zoomFactor->setPrecision(3);
ui->zoomReflection->setValue(edgeReflection); ui->zoomReflection->setValue(edgeReflection);
ui->zoomFactor->setValue(1.0/edgeReflection); ui->zoomFactor->setValue(1.0/edgeReflection);
auto model = new SmithChartContantLineModel(*this);
ui->lineTable->setModel(model);
ui->lineTable->setItemDelegateForColumn(SmithChartContantLineModel::ColIndexType, new SmithChartTypeDelegate);
ui->lineTable->setItemDelegateForColumn(SmithChartContantLineModel::ColIndexParam, new SmithChartParamDelegate);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){ connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
limitToSpan = ui->displayModeFreq->currentIndex() == 1; limitToSpan = ui->displayModeFreq->currentIndex() == 1;
limitToEdge = ui->displayModeImp->currentIndex() == 1; limitToEdge = ui->displayModeImp->currentIndex() == 1;
@ -104,6 +123,48 @@ void TraceSmithChart::axisSetupDialog()
edgeReflection = ui->zoomReflection->value(); edgeReflection = ui->zoomReflection->value();
ui->zoomFactor->setValueQuiet(1.0 / edgeReflection); ui->zoomFactor->setValueQuiet(1.0 / edgeReflection);
}); });
connect(ui->lineTable, &QTableView::clicked, [=](const QModelIndex &index){
if(index.column() == SmithChartContantLineModel::ColIndexColor) {
auto line = &constantLines[index.row()];
auto newColor = QColorDialog::getColor(line->getColor(), parentWidget(), "Select color", QColorDialog::DontUseNativeDialog);
if(newColor.isValid()) {
line->setColor(newColor);
emit model->dataChanged(index, index);
triggerReplot();
}
}
});
auto updatePersistentEditors = [=](){
for(unsigned int i=0;i<constantLines.size();i++) {
ui->lineTable->openPersistentEditor(model->index(i, SmithChartContantLineModel::ColIndexType));
}
};
connect(ui->addLine, &QPushButton::clicked, [=](){
model->beginResetModel();
constantLines.push_back(SmithChartConstantLine());
model->endResetModel();
updatePersistentEditors();
});
connect(ui->removeLine, &QPushButton::clicked, [=](){
auto selected = ui->lineTable->selectionModel()->selectedRows();
// get indices of lines to delete
std::vector<int> toDelete;
for(auto s : selected) {
toDelete.push_back(s.row());
}
// delete starting with highest index (this makes sure that indices are not messed up after deleting an element
std::sort(toDelete.begin(), toDelete.end());
model->beginResetModel();
for (auto i = toDelete.rbegin(); i != toDelete.rend(); i++) {
constantLines.erase(constantLines.begin() + *i);
}
model->endResetModel();
updatePersistentEditors();
});
updatePersistentEditors();
dialog->show(); dialog->show();
} }
@ -213,7 +274,7 @@ void TraceSmithChart::draw(QPainter &p) {
transform = p.transform(); transform = p.transform();
p.restore(); p.restore();
auto drawArc = [&](Arc a) { auto drawArc = [&](SmithChartArc a) {
a.constrainToCircle(QPointF(0,0), edgeReflection); a.constrainToCircle(QPointF(0,0), edgeReflection);
auto topleft = dataToPixel(complex<double>(a.center.x() - a.radius, a.center.y() - a.radius)); auto topleft = dataToPixel(complex<double>(a.center.x() - a.radius, a.center.y() - a.radius));
auto bottomright = dataToPixel(complex<double>(a.center.x() + a.radius, a.center.y() + a.radius)); auto bottomright = dataToPixel(complex<double>(a.center.x() + a.radius, a.center.y() + a.radius));
@ -226,7 +287,7 @@ void TraceSmithChart::draw(QPainter &p) {
auto pen = QPen(pref.Graphs.Color.axis); auto pen = QPen(pref.Graphs.Color.axis);
pen.setCosmetic(true); pen.setCosmetic(true);
p.setPen(pen); p.setPen(pen);
drawArc(Arc(QPointF(0, 0), edgeReflection, 0, 2*M_PI)); drawArc(SmithChartArc(QPointF(0, 0), edgeReflection, 0, 2*M_PI));
constexpr int Circles = 6; constexpr int Circles = 6;
pen = QPen(pref.Graphs.Color.Ticks.divisions, 0.5, Qt::DashLine); pen = QPen(pref.Graphs.Color.Ticks.divisions, 0.5, Qt::DashLine);
@ -234,8 +295,8 @@ void TraceSmithChart::draw(QPainter &p) {
p.setPen(pen); p.setPen(pen);
for(int i=1;i<Circles * 2;i++) { for(int i=1;i<Circles * 2;i++) {
auto radius = (double) i / Circles; auto radius = (double) i / Circles;
drawArc(Arc(QPointF(1.0 - radius, 0.0), radius, 0, 2*M_PI)); drawArc(SmithChartArc(QPointF(1.0 - radius, 0.0), radius, 0, 2*M_PI));
drawArc(Arc(QPointF(1.0 + radius, 0.0), radius, 0, 2*M_PI)); drawArc(SmithChartArc(QPointF(1.0 + radius, 0.0), radius, 0, 2*M_PI));
} }
p.drawLine(dataToPixel(complex<double>(edgeReflection,0)),dataToPixel(complex<double>(-edgeReflection,0))); p.drawLine(dataToPixel(complex<double>(edgeReflection,0)),dataToPixel(complex<double>(-edgeReflection,0)));
@ -243,8 +304,18 @@ void TraceSmithChart::draw(QPainter &p) {
for(auto z : impedanceLines) { for(auto z : impedanceLines) {
z /= ReferenceImpedance; z /= ReferenceImpedance;
auto radius = 1.0/z; auto radius = 1.0/z;
drawArc(Arc(QPointF(1.0, radius), radius, 0, 2*M_PI)); drawArc(SmithChartArc(QPointF(1.0, radius), radius, 0, 2*M_PI));
drawArc(Arc(QPointF(1.0, -radius), radius, 0, 2*M_PI)); drawArc(SmithChartArc(QPointF(1.0, -radius), radius, 0, 2*M_PI));
}
// draw custom constant parameter lines
for(auto line : constantLines) {
pen = QPen(line.getColor(), pref.Graphs.lineWidth);
pen.setCosmetic(true);
p.setPen(pen);
for(auto arc : line.getArcs()) {
drawArc(arc);
}
} }
for(auto t : traces) { for(auto t : traces) {
@ -429,7 +500,7 @@ bool TraceSmithChart::dropSupported(Trace *t)
} }
} }
TraceSmithChart::Arc::Arc(QPointF center, double radius, double startAngle, double spanAngle) SmithChartArc::SmithChartArc(QPointF center, double radius, double startAngle, double spanAngle)
: center(center), : center(center),
radius(radius), radius(radius),
startAngle(startAngle), startAngle(startAngle),
@ -438,7 +509,7 @@ TraceSmithChart::Arc::Arc(QPointF center, double radius, double startAngle, doub
} }
void TraceSmithChart::Arc::constrainToCircle(QPointF center, double radius) void SmithChartArc::constrainToCircle(QPointF center, double radius)
{ {
// check if arc/circle intersect // check if arc/circle intersect
auto centerDiff = this->center - center; auto centerDiff = this->center - center;
@ -502,3 +573,267 @@ void TraceSmithChart::Arc::constrainToCircle(QPointF center, double radius)
} }
} }
} }
SmithChartConstantLine::SmithChartConstantLine()
{
type = Type::VSWR;
param = 10.0;
color = Qt::darkRed;
}
std::vector<SmithChartArc> SmithChartConstantLine::getArcs()
{
std::vector<SmithChartArc> arcs;
switch(type) {
case Type::VSWR:
arcs.push_back(SmithChartArc(QPointF(0.0, 0.0), (param - 1.0) / (param + 1.0)));
break;
case Type::Resistance: {
auto circleLeft = (param / 50.0 - 1.0) / (param / 50.0 + 1.0);
arcs.push_back(SmithChartArc(QPointF((circleLeft + 1.0) / 2, 0.0), (1.0 - circleLeft) / 2.0));
}
break;
case Type::Reactance: {
auto radius = 1.0/(param / 50.0);
if(radius > 0) {
arcs.push_back(SmithChartArc(QPointF(1.0, radius), radius));
} else {
arcs.push_back(SmithChartArc(QPointF(1.0, radius), -radius));
}
}
break;
case Type::Q: {
auto center = 1.0 / param;
auto radius = sqrt(center*center + 1.0);
arcs.push_back(SmithChartArc(QPointF(0.0, center), radius));
arcs.push_back(SmithChartArc(QPointF(0.0, -center), radius));
}
break;
case Type::Last:
break;
}
return arcs;
}
QColor SmithChartConstantLine::getColor() const
{
return color;
}
void SmithChartConstantLine::fromJSON(nlohmann::json j)
{
type = TypeFromString(QString::fromStdString(j.value("type", "VSWR")));
if(type == Type::Last) {
type = Type::VSWR;
}
param = j.value("param", 1.0);
color = QColor(QString::fromStdString(j.value("color", "red")));
}
nlohmann::json SmithChartConstantLine::toJSON()
{
nlohmann::json j;
j["type"] = TypeToString(type).toStdString();
j["param"] = param;
j["color"] = color.name().toStdString();
return j;
}
QString SmithChartConstantLine::getParamUnit()
{
switch(type) {
case Type::VSWR: return "";
case Type::Resistance: return "Ω";
case Type::Reactance: return "Ωj";
case Type::Q: return "";
case Type::Last: break;
}
return "";
}
QString SmithChartConstantLine::TypeToString(SmithChartConstantLine::Type type)
{
switch(type) {
case Type::VSWR: return "VSWR";
case Type::Resistance: return "Resistance";
case Type::Reactance: return "Reactance";
case Type::Q: return "Q";
case Type::Last:break;
}
// should never get here
return "Invalid";
}
SmithChartConstantLine::Type SmithChartConstantLine::TypeFromString(QString s)
{
for(unsigned int i=0;i<(unsigned int)Type::Last;i++) {
if(TypeToString((Type) i) == s) {
return (Type) i;
}
}
return Type::Last;
}
void SmithChartConstantLine::setColor(const QColor &value)
{
color = value;
}
double SmithChartConstantLine::getParam() const
{
return param;
}
void SmithChartConstantLine::setParam(double value)
{
param = value;
}
void SmithChartConstantLine::setType(const Type &value)
{
type = value;
}
SmithChartConstantLine::Type SmithChartConstantLine::getType() const
{
return type;
}
static constexpr int rowHeight = 21;
QSize SmithChartParamDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize(0, rowHeight);
}
QWidget *SmithChartParamDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
{
auto line = static_cast<const SmithChartContantLineModel*>(index.model())->lineFromIndex(index);
auto editor = new SIUnitEdit(line->getParamUnit(), "pnum kMG", 6);
editor->setValue(line->getParam());
editor->setMaximumHeight(rowHeight);
editor->setParent(parent);
connect(editor, &SIUnitEdit::valueUpdated, this, &SmithChartParamDelegate::commitData);
return editor;
}
void SmithChartParamDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
auto line = ((SmithChartContantLineModel*)model)->lineFromIndex(index);
auto si = (SIUnitEdit*) editor;
line->setParam(si->value());
}
QSize SmithChartTypeDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize(0, rowHeight);
}
QWidget *SmithChartTypeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
{
auto line = static_cast<const SmithChartContantLineModel*>(index.model())->lineFromIndex(index);
auto editor = new QComboBox();
for(unsigned int i=0;i<(unsigned int)SmithChartConstantLine::Type::Last;i++) {
editor->addItem(SmithChartConstantLine::TypeToString((SmithChartConstantLine::Type) i));
}
editor->setCurrentIndex((int) line->getType());
connect(editor, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) {
emit const_cast<SmithChartTypeDelegate*>(this)->commitData(editor);
});
editor->setMaximumHeight(rowHeight);
editor->setParent(parent);
return editor;
}
void SmithChartTypeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
auto line = ((SmithChartContantLineModel*)model)->lineFromIndex(index);
auto *cb = (QComboBox*) editor;
line->setType((SmithChartConstantLine::Type) cb->currentIndex());
// parameter unit may have changed, update model
auto paramIndex = model->index(index.row(), SmithChartContantLineModel::ColIndexParam);
emit model->dataChanged(paramIndex, paramIndex);
}
SmithChartContantLineModel::SmithChartContantLineModel(TraceSmithChart &chart, QObject *parent)
: chart(chart)
{
Q_UNUSED(parent);
}
int SmithChartContantLineModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return chart.constantLines.size();
}
int SmithChartContantLineModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return ColIndexLast;
}
QVariant SmithChartContantLineModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if ((unsigned int) index.row() >= chart.constantLines.size())
return QVariant();
auto line = chart.constantLines[index.row()];
switch(index.column()) {
case ColIndexColor:
if (role == Qt::BackgroundColorRole) {
return line.getColor();
}
break;
case ColIndexType:
if (role == Qt::DisplayRole) {
return SmithChartConstantLine::TypeToString(line.getType());
}
break;
case ColIndexParam:
if (role == Qt::DisplayRole) {
return Unit::ToString(line.getParam(), line.getParamUnit(), "pnum kMG", 6);
}
break;
default:
break;
}
return QVariant();
}
QVariant SmithChartContantLineModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation);
if(role == Qt::DisplayRole) {
switch(section) {
case ColIndexColor: return "Color";
case ColIndexType: return "Type";
case ColIndexParam: return "Parameter";
}
}
return QVariant();
}
Qt::ItemFlags SmithChartContantLineModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
switch(index.column()) {
case ColIndexType:
case ColIndexParam:
flags |= Qt::ItemIsEditable;
break;
}
return flags;
}
SmithChartConstantLine* SmithChartContantLineModel::lineFromIndex(const QModelIndex &index) const
{
if(index.isValid() && index.row() < (int) chart.constantLines.size()) {
return &chart.constantLines[index.row()];
} else {
return nullptr;
}
}

View File

@ -6,10 +6,102 @@
#include <QPen> #include <QPen>
#include <QPainterPath> #include <QPainterPath>
#include <QTransform> #include <QTransform>
#include <QStyledItemDelegate>
class TraceSmithChart;
class SmithChartArc
{
public:
SmithChartArc(QPointF center, double radius, double startAngle = 0.0, double spanAngle = 2*M_PI);
void constrainToCircle(QPointF center, double radius);
QPointF center;
double radius;
double startAngle, spanAngle;
};
class SmithChartConstantLine : public Savable
{
public:
enum class Type {
VSWR,
Resistance,
Reactance,
Q,
Last,
};
SmithChartConstantLine();
std::vector<SmithChartArc> getArcs();
QColor getColor() const;
void setColor(const QColor &value);
void fromJSON(nlohmann::json j) override;
nlohmann::json toJSON() override;
QString getParamUnit();
Type getType() const;
void setType(const Type &value);
double getParam() const;
void setParam(double value);
static QString TypeToString(Type type);
static Type TypeFromString(QString s);
private:
QColor color;
double param;
Type type;
};
class SmithChartTypeDelegate : public QStyledItemDelegate
{
Q_OBJECT;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const override;
QWidget *createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
};
class SmithChartParamDelegate : public QStyledItemDelegate
{
Q_OBJECT;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const override;
QWidget *createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
};
class SmithChartContantLineModel : public QAbstractTableModel
{
Q_OBJECT
friend TraceSmithChart;
public:
SmithChartContantLineModel(TraceSmithChart &chart, QObject *parent = 0);
enum {
ColIndexColor,
ColIndexType,
ColIndexParam,
ColIndexLast,
};
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;
SmithChartConstantLine *lineFromIndex(const QModelIndex &index) const;
private:
TraceSmithChart &chart;
};
class TraceSmithChart : public TracePlot class TraceSmithChart : public TracePlot
{ {
Q_OBJECT Q_OBJECT
friend class SmithChartContantLineModel;
public: public:
TraceSmithChart(TraceModel &model, QWidget *parent = 0); TraceSmithChart(TraceModel &model, QWidget *parent = 0);
@ -27,17 +119,6 @@ protected:
static constexpr double screenUsage = 0.9; static constexpr double screenUsage = 0.9;
static constexpr double smithCoordMax = 4096; static constexpr double smithCoordMax = 4096;
class Arc
{
public:
Arc(QPointF center, double radius, double startAngle, double spanAngle);
void constrainToCircle(QPointF center, double radius);
QPointF center;
double radius;
double startAngle, spanAngle;
};
QPoint dataToPixel(std::complex<double> d); QPoint dataToPixel(std::complex<double> d);
QPoint dataToPixel(Trace::Data d); QPoint dataToPixel(Trace::Data d);
std::complex<double> pixelToData(QPoint p); std::complex<double> pixelToData(QPoint p);
@ -56,6 +137,7 @@ protected:
bool limitToEdge; bool limitToEdge;
double edgeReflection; // magnitude of reflection coefficient at the edge of the smith chart (zoom factor) double edgeReflection; // magnitude of reflection coefficient at the edge of the smith chart (zoom factor)
QTransform transform; QTransform transform;
std::vector<SmithChartConstantLine> constantLines;
}; };
#endif // TRACESMITHCHART_H #endif // TRACESMITHCHART_H