使用QPainter绘制史密斯圆图坐标系及曲线,手绘可参考计算规则
目录
观察史密斯圆图,主要绘制圆(阻抗圆)和弧(阻抗弧),其中阻抗圆代表阻抗实部real(取值[0, +无穷)),阻抗弧代表阻抗虚部imag(取值(-无穷, +无穷))

一、绘制史密斯坐标系
1.绘制坐标系区域
新建一个widget窗口或者mainwindow窗口,重写保护函数paintEvent(QPaintEvent *event)
绘制史密斯圆图坐标系区域(半径R的最大阻抗圆),选择屏幕正中央为圆心
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
// 计算屏幕中间位置,以左上角为(0,0)坐标
int width = this->width();
int height = this->height();
int radius = qMin(width, height) / 2 - 50;
double center_x = width / 2;
double center_y = height / 2;
QPointF center(center_x, center_y);
// 绘制圆形白色背景
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
painter.drawEllipse(center, radius, radius);
// 实线绘制最大区域
painter.setPen(QPen(Qt::black, 1, Qt::SolidLine));
painter.drawEllipse(center, radius, radius);
}

2.阻抗圆与阻抗弧关系
观察发现
阻抗圆圆心坐标:(R - r, 0)即(x + R - r,y),都与(R,0)点相切,y轴不变
阻抗弧圆心坐标:(R,-r)即(x + R, y - r),都与(R,0)点相切,x轴不变
注:程序以左上角为(0,0),右下角方向均为正,和传统坐标系不一样

从这篇文章得到以下信息
绘制阻抗圆 圆心(x + real/(1+real), y) 半径 1/(real+1)
绘制电抗弧 圆心(x + R,1/imag) 半径 1/imag推断出:当绘制同样半径大小的圆时 real + 1 = imag
绘制同一半径r,得出以下关系
| 半径r | 阻抗圆x值(y不变) | 实部 | 虚部 | 阻抗弧y值(x不变) |
|---|---|---|---|---|
| r=1/10R | center_x + R - 1/10R | real=9 | imag=10 | center_y - r |
| r=1/5R | center_x + R - 1/5R | real=4 | imag=5 | center_y - r |
| r=1/2R | center_x + R - 1/2R | real=1 | imag=2 | center_y - r |
| r=2/3R | center_x + R - 2/3R | real=1/2 | imag=3/2 | center_y - r |
| r=5/6R | center_x + R - 6/5R | real=1/5 | imag=6/5 | center_y - r |
3.绘制阻抗圆
绘制阻抗圆,半径 r= 1/(real+1) *R
// 阻抗圆 圆心(center_x + R - r, center_y) 半径 r = (1/(real+1)) * R
std::vector<double> realVec = { 0, (double)1/5, (double)1/2, 1, 2 ,5 ,10 };
// 虚线绘制,没有背景色
painter.setPen(QPen(Qt::black, 1, Qt::DashLine));
painter.setBrush(Qt::NoBrush);
for (int i = 0; i < realVec.size(); i++) {
double r = radius * (1 / (realVec[i] + 1));
QPointF c(center_x + radius - r, center_y);
// 绘制圆 和 文字
painter.drawEllipse(c, r, r);
painter.drawText(c.x() - r - 5, c.y() + 10, QString::number(realVec[i]));
}

4.绘制阻抗弧
绘制阻抗弧,半径 r = (1 / imag) * R,其中阻抗弧是绘制圆的弧度

QRect rect(x, y, d, d);
painter.drawArc(rect, θ * 16, φ * 16); Qt 中的角度单位是以 1 / 16 度为单位的drawArc在长宽为d并且左上角坐标为(x,y)的矩形,以矩形中点为圆心,当前起始角度θ,沿顺时针方向绘制φ度的圆弧
painter.drawArc(rect, θ * 16, -φ * 16);
drawArc在长宽为d并且左上角坐标为(x,y)的矩形,以矩形中点为圆心,当前起始角度θ,沿逆时针方向绘制φ度的圆弧
其中theta>90°时,tanθ取值为负值;其中theta=90°时,tanθ等于无穷(非法数据)


再绘制负方向的阻抗弧,
矩阵位置改变,绘制起始角度和绘制方向改变
for (int i = 0; i < imagVec.size(); i++) {
double r = radius * (1 / imagVec[i]);
QRect rect2(center_x + radius - r, center_y, 2 * r, 2 * r);
// 阻抗弧坐标系,与R阻抗圆相交 求角度2θ
// tanθ = r/R = 1/imag 2 * θ = 2 * atan2(r, R) 反正切
double tan = (1 / imagVec[i]);
double theta = 2 * atan2(r, radius) * 180 / 3.1415926;
painter.drawArc(rect2, 90 * 16, (180 - theta) * 16);
// tan2θ = 2*tanθ/(1 - tanθ*tanθ)
// 坐标(sqrt(1 / (1 + tan2θ * tan2θ)), tan2θ * x)
double tan2 = 2 * tan / (1 - tan*tan);
double x = sqrt(1 / (1 + tan2 * tan2)) * radius;
if (theta < 89) {
painter.drawText(center_x + x + 4, center_y + x*tan2 +12, "-" + QString::number(imagVec[i]) + "i");
} else if (theta > 91) {
painter.drawText(center_x - x - 18, center_y - x*tan2 + 12, "-" + QString::number(imagVec[i]) + "i");
} else {
painter.drawText(center_x, center_y + radius + 12, "-1i");
}
}
5.史密斯圆图坐标系完整代码
// MyWidget.h
#include <QWidget>
#include "ui_MyWidget.h"
#include <QPainter>
#include <QPen>
#include <QPoint>
#include <QPointF>
#include <QtCore\qmath.h>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = Q_NULLPTR);
~MyWidget();
protected:
void paintEvent(QPaintEvent *event) override;
private:
Ui::MyWidget ui;
};
// MyWidget.cpp
void MyWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
int width = this->width();
int height = this->height();
int radius = qMin(width, height) / 2 - 50;
double center_x = width / 2;
double center_y = height / 2;
QPointF center(center_x, center_y);
// 绘制圆形背景
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
painter.drawEllipse(center, radius, radius);
// 同半径 r: real + 1 = imag
// 阻抗圆 圆心(center_x + R - r, center_y) 半径 r = (1/(real+1)) * R
std::vector<double> realVec = { 0, (double)1/5, (double)1/2, 1, 2 ,5 ,10 };
painter.setPen(QPen(Qt::black, 1, Qt::SolidLine));
painter.setBrush(Qt::NoBrush);
for (int i = 0; i < realVec.size(); i++) {
double r = radius * (1 / (realVec[i] + 1));
QPointF c(center_x + radius - r, center_y);
painter.drawEllipse(c, r, r);
painter.drawText(c.x() - r - 12, c.y() + 12, QString::number(realVec[i]));
}
// 绘制中间直线,即最大阻抗圆直径
painter.drawLine(center_x - radius, center_y, center_x + radius, center_y);
// 电抗弧 圆心(center_x + R, center_y - r) 半径 r = (1/imag) * R
// 矩阵 (center_x + R - r, center_y - 2*r) 边长2*r
std::vector<double> imagVec = { 10, 5, 3, 2, (double)3 / 2,(double)6 / 5, 1, (double)4 / 5, (double)3 / 5,(double)2 / 5,(double)1 / 5 };
painter.setPen(QPen(Qt::black, 1, Qt::DashLine));
for (int i = 0; i < imagVec.size(); i++) {
double r = radius * (1 / imagVec[i]);
//Qt 中的角度单位是以 1 / 16 度为单位的,因此在设置起始角度和弧度时需要将角度值乘以 16.
QRect rect(center_x + radius - r, center_y - 2 * r, 2 * r, 2 * r);
QRect rect2(center_x + radius - r, center_y, 2 * r, 2 * r);
// 阻抗弧坐标系,与R阻抗圆相交 求角度2θ
// tanθ = r/R = 1/imag 2 * θ = 2 * atan2(r, R) 反正切
double tan = (1 / imagVec[i]);
double theta = 2 * atan2(r, radius) * 180 / 3.1415926;
painter.drawArc(rect, 270 * 16, -(180 - theta) * 16);
painter.drawArc(rect2, 90 * 16, (180 - theta) * 16);
// 实际值 求两圆交点 最大阻抗圆real = 0
// tan2θ = 2*tanθ/(1 - tanθ*tanθ)
// 坐标(sqrt(1 / (1 + tan2θ * tan2θ)), tan2θ * x)
double tan2 = 2 * tan / (1 - tan*tan);
double x = sqrt(1 / (1 + tan2 * tan2)) * radius;
if (theta < 89) {
painter.drawText(center_x + x , center_y - x*tan2 - 6, QString::number(imagVec[i]) + "i");
painter.drawText(center_x + x + 4, center_y + x*tan2 +12, "-" + QString::number(imagVec[i]) + "i");
} else if (theta > 91) {
painter.drawText(center_x - x - 13, center_y + x*tan2 - 10, QString::number(imagVec[i]) + "i");
painter.drawText(center_x - x - 20, center_y - x*tan2 + 15, "-" + QString::number(imagVec[i]) + "i");
} else {
painter.drawText(center_x, center_y - radius - 8, "1i");
painter.drawText(center_x, center_y + radius + 12, "-1i");
}
}
}
6.坐标系效果图

二、 绘制点及曲线
1.计算并绘制点
计算实际点值的位置,此时的输入阻抗实部为real,输入阻抗虚部为imag

通过三角函数公式 计算得到直角三角形的另外两边,已知sinθ值和直角三角形第三边r(左图)
观察得出此时A的x轴值,center_x + R - r - xA(直角三角形水平边 边长)(右图)
尝试绘制一些点来验证,效果如下:

void MyWidget::drawSmithCircle()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
int width = this->width();
int height = this->height();
int radius = qMin(width, height) / 2 - 50;
double center_x = width / 2;
double center_y = height / 2;
QPointF center(center_x, center_y);
painter.setPen(QPen(Qt::red, 5, Qt::DashLine));
// 阻抗圆 圆心(center_x + R - r, center_y) 半径 r = (1/(real+1)) * R
// 电抗弧 圆心(center_x + R, center_y - r) 半径 r = (1/imag) * R
std::vector<double> realV = { 0.2, 0.5, 0.5, 1, 2, 1.5 }; // 4 1 1/2
std::vector<double> imagV = { -1, 1, -0.8, 0.8, 3, 0.4 };
int size = realV.size();
if (size < imagV.size())
size = imagV.size();
double real_r = 0, imag_r = 0;
for (int i = 0; i < size; i++) {
double real = realV[i];
double imag = imagV[i];
real_r = (1 / (real + 1)) * radius;
if (imag != (double)0)
imag_r = (1 / imag) * radius;
// 实际值 求两圆交点 point_y = sin2α*real_r
// sin2α = 2cosαsinα = 2*real_r*imag_r/(pow(real_r) + pow(imag_r))
double sin2 = 2 * real_r * imag_r / (real_r * real_r + imag_r * imag_r);
double point_y = sin2 * real_r;
double point_x = sqrt(real_r * real_r - point_y * point_y);
painter.drawPoint(center_x + radius - real_r - point_x, center_y - point_y);
}
}
2.连接点 绘制曲线
QVector<QPointF> curvePoints; // 点
for (int i = 0; i < size; i++) {
...
...
double x = center_x + radius - real_r - point_x;
double y = center_y - point_y;
painter.drawPoint(center_x + radius - real_r - point_x, center_y - point_y);
curvePoints.append(QPointF(x, y));
}
// 将点连接 绘制曲线
painter.setPen(QPen(Qt::red, 2, Qt::SolidLine));
painter.drawPolyline(curvePoints.data(), curvePoints.size());

3.绘制点和曲线代码
// 绘制反射系数点
void SmithChart::drawSmithCircle()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
int width = this->width();
int height = this->height();
int radius = qMin(width, height) / 2 - 60;
double center_x = width / 2;
double center_y = height / 2;
QPointF center(center_x, center_y);
painter.setPen(QPen(Qt::red, 5, Qt::DashLine));
// 阻抗圆 圆心(center_x + R - r, center_y) 半径 r = (1/(real+1)) * R
// 电抗弧 圆心(center_x + R, center_y - r) 半径 r = (1/imag) * R
QVector<QPointF> curvePoints;
//std::vector<double> realV = { 0.2, 0.5, 0.5, 1, 2, 1.5 }; // 4 1 1/2
//std::vector<double> imagV = { -1, 1, -0.8, 0.8, 3, 0.4 };
int size = realV.size();
if (size < imagV.size())
size = imagV.size();
double real_r = 0, imag_r = 0;
for (int i = 0; i < size; i++) {
double real = realV[i];
double imag = imagV[i];
real_r = (1 / (real + 1)) * radius;
if (imag != (double)0)
imag_r = (1 / imag) * radius;
// 实际值 求两圆交点
// sin2α = 2cosαsinα = 2*real_r*imag_r/(pow(real_r) + pow(imag_r))
// point_y = sin2α*real_r
double sin2 = 2 * real_r * imag_r / (real_r * real_r + imag_r * imag_r);
double point_y = sin2 * real_r;
double point_x = sqrt(real_r * real_r - point_y * point_y);
double x = center_x + radius - real_r - point_x;
double y = center_y - point_y;
painter.drawPoint(center_x + radius - real_r - point_x, center_y - point_y);
curvePoints.append(QPointF(x, y));
}
// 绘制曲线
painter.setPen(QPen(Qt::red, 2, Qt::SolidLine));
painter.drawPolyline(curvePoints.data(), curvePoints.size());
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)