基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

内容分享13小时前发布 修梓文
0 0 0

基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

1 摘要

数字示波器实现的在很早以前就想构思实现一个简单的数字示波器,通过最近一段的时间的摸索基本实现了一个数字示波器的基本功能,包括四通道的数字采集、基于边沿触发、电平触发、FFT分析、自动测量、光标测量等功能,目前版本性能还需要提升,后续想结合四通道做实际的硬件应用。

2✨ 核心特性

📊 多通道波形显示

4通道独立控制,支持同步显示
可配置的电压灵敏度(V/div)
实时时基调整(s/div)
通道颜色编码:CH1(黄)、CH2(青)、CH3(紫)、CH4(绿)

⚡ 智能触发系统

触发模式:自动、正常、单次
触发类型:边沿触发、电平触发
触发边沿:上升沿、下降沿、双边沿
触发电平可调,实时触发状态显示

🔍 专业测量工具

自动测量
频率、周期、幅度、峰峰值
RMS值、平均值、上升/下降时间
占空比测量
实时数据表格显示
光标测量
双光标时间/电压测量
Δ时间、Δ电压计算
频率反算功能
交互式光标拖拽

📈 FFT频谱分析

实时快速傅里叶变换
多通道频谱显示
对数/线性刻度切换
可配置频率显示范围
🛠️ 技术架构
界面框架
自定义无边框标题栏
现代化深色主题
可停靠窗口系统
高级工具栏布局

🎮 用户交互

控制方式
工具栏:快速访问常用功能
控制面板:详细参数调整
键盘快捷键:
F5:运行/停止
F6:单次触发
Ctrl+A:自动缩放
方向键:波形移动

视图管理

网格显示控制
自动缩放功能
波形位置复位
多视图同步

💾 数据管理

文件操作
CSV格式数据保存
波形数据加载
测量结果导出
数据版本管理

🔧 技术亮点

高刷新率波形更新
低延迟触发检测
高效的FFT计算

精度

高精度测量算法
抗噪声信号处理
准确的频率检测

扩展性

模块化架构设计
易于功能扩展
插件式测量工具

🎯 应用场景

教育实验 – 电子电路教学演示
工程开发 – 信号分析验证
科研研究 – 波形特性分析
测试测量 – 虚拟仪器应用

📊 性能指标

最大采样率:100 kHz
时基范围:0.1ms/div – 1s/div
电压范围:0.1V/div – 10V/div
FFT分辨率:512点
显示点数:1024点/通道
本虚拟示波器设计结合了传统示波器的操作习惯以及软件的交互体验,为电子工程师、教育工作者和科研人员提供了强大的波形分析和测量工具。
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

实时显示测量,触发也是示波器的重要功能,当然也需要提供实时的测量,相对于更高的CPU频率和高性能的操作系统对系统的实时性提供良好的保障。

3 示波器设计

3.1 示波器显示窗口

显示窗口主要包括波形显示,光标测量,触发线、网格,时基,触发提示等功能。
(1)数字示波器通过移动双光标可是测试时间和幅度等信息。
(2)支持缩放功能。
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能
缩放功能
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

3.2 系统头文件


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTimer>
#include <QVector>
#include <QMap>
#include <QLabel>
#include <QButtonGroup>
#include "mytitlebar.h"
#include "fft.h"
// 前置声明
class QCustomPlot;
class QCPGraph;
class QCPItemTracer;
class QCPItemLine;
class QCPItemText;
class QCPItemStraightLine;  // 添加这个前置声明
class QTableWidget;
class QDockWidget;
class QComboBox;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

// 测量数据结构
struct MeasurementData {
    double frequency = 0.0;      // 频率 (Hz)
    double period = 0.0;         // 周期 (s)
    double amplitude = 0.0;      // 幅度 (V)
    double peakToPeak = 0.0;     // 峰峰值 (V)
    double rms = 0.0;            // RMS值 (V)
    double mean = 0.0;           // 平均值 (V)
    double max = 0.0;            // 最大值 (V)
    double min = 0.0;            // 最小值 (V)
    double riseTime = 0.0;       // 上升时间 (s)
    double fallTime = 0.0;       // 下降时间 (s)
    double dutyCycle = 0.0;      // 占空比 (%)
};

// 光标测量数据
struct CursorMeasurement {
    double time1 = 0.0;          // 光标1时间
    double time2 = 0.0;          // 光标2时间
    double voltage1 = 0.0;       // 光标1电压
    double voltage2 = 0.0;       // 光标2电压
    double deltaTime = 0.0;      // 时间差
    double deltaVoltage = 0.0;   // 电压差
    double frequency = 0.0;      // 频率 (1/ΔT)
};

// 触发设置
struct TriggerSettings {
    double level = 0.0;          // 触发电平
    int channel = 0;             // 触发通道
    int slope = 0;               // 触发边沿 0:上升沿, 1:下降沿, 2:双边沿
    int mode = 0;                // 触发模式 0:自动, 1:正常, 2:单次
    int type = 0;                // 触发类型 0:边沿触发, 1:电平触发
    bool armed = false;          // 触发就绪
    double triggerPoint = 0.0;   // 触发点时间
    int holdoff = 0;             // 触发保持
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onUpdateWaveform();
    void onChannelVisibilityChanged(int channel, bool visible);
    void onTimebaseChanged(double value);
    void onVoltageScaleChanged(int channel, double value);
    void onTriggerLevelChanged(double value);
    void onTriggerChannelChanged(int channel);
    void onTriggerSlopeChanged(int slope);
    void onTriggerModeChanged(int mode);
    void onRunStopClicked();
    void onSingleShotClicked();
    void onAutoScaleClicked();
    void onCursorToggled(bool enabled);
    void onMeasureToggled(bool enabled);
    void onFFTToggled(bool enabled);
    void onSaveDataClicked();
    void onLoadDataClicked();
    void onClearMeasurements();
    void onMouseMove(QMouseEvent *event);
    void onMousePress(QMouseEvent *event);
    void onMouseRelease(QMouseEvent *event);
    void onTriggerTypeChanged(int type);
    void onMoveLeftClicked();
    void onMoveRightClicked();
    void onMoveUpClicked();
    void onMoveDownClicked();
    void onResetPositionClicked();
    void onAboutClicked();
    void onToggleMaximize();


private:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void setupOscilloscope();
    void setupControls();
    void setupMeasurementsTable();
    void setupCursorMeasurements();
    void setupFFTAnalysis();
    void setupTriggerSystem();
    void generateWaveform();
    void updateMeasurements();
    void updateCursors();
    void updateFFT();
    void updateTrigger();
    void applyTheme();
    void updateTriggerLabel();
    void resetCursorAppearance();
    bool checkEdgeTrigger(const QVector<double>& signal);
    bool checkLevelTrigger(const QVector<double>& signal);
    bool saveWaveformData(const QString &fileName);
    bool loadWaveformData(const QString &fileName);
    void applyWaveOffsets();
    void setupStatusBar();
    void updateStatusBarInfo();
    void setupMenuBar();
    void setupAdvancedToolBar();
    void createRunControlSection();
    void createMeasureToolsSection();
    void createFileOperationsSection();
    void createPositionControlSection();
    void createDisplayControlsSection();
    void setupCustomTitleBar();


    // 触发相关函数
    bool checkTrigger(const QVector<double>& signal);
    int findTriggerPoint(const QVector<double>& signal);
    void applyTriggerDelay();
    void updateTriggerAppearance();

    // 测量计算函数
    MeasurementData calculateMeasurements(int channel);
    void clearMeasurementRow(int channel);
    double calculateFrequency(const QVector<double>& voltageData, int channel);
    double calculateRMS(const QVector<double>& voltageData);
    double calculateMean(const QVector<double>& voltageData);
    std::pair<double, double> findZeroCrossings(const QVector<double>& voltageData, int channel);
    std::pair<double, double> findRiseFallTimes(const QVector<double>& voltageData, int channel);
    double calculateDutyCycle(const QVector<double>& voltageData, int channel);
    void updateMeasurementTable(int channel, const MeasurementData& data);

    // 光标测量函数
    void updateCursorMeasurements();
    void moveCursor(int cursorIndex, const QPointF &position);
    QPointF pixelToCoord(const QPoint &pixelPos);

    // FFT函数
    void calculateFFT(QVector<double> &signal, QVector<double> &freq, QVector<double> &magnitude);
    void updateFFTDisplay();

#endif // MAINWINDOW_H


4 波形移动功能

4.1 通过十字导向键控制波形移动

在实际示波器仪器上可以通过旋转按钮控制波形的上下左右移动,在数字示波器中我们通过十字导向按钮对波形进行移动操作。
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

4.2 控制代码



	  //创建一个单独的位置控制组
     QGroupBox *positionGroup = new QGroupBox("波形位置");
     QGridLayout *positionLayout = new QGridLayout(positionGroup);

     // 创建更专业的移动按钮
     QPushButton *posLeft = new QPushButton("←");
     QPushButton *posRight = new QPushButton("→");
     QPushButton *posUp = new QPushButton("↑");
     QPushButton *posDown = new QPushButton("↓");
     QPushButton *posReset = new QPushButton("复位");

     // 设置按钮大小
     posLeft->setFixedSize(40, 30);
     posRight->setFixedSize(40, 30);
     posUp->setFixedSize(40, 30);
     posDown->setFixedSize(40, 30);
     posReset->setFixedSize(40, 30);

     // 添加到布局 - 创建十字形布局
     positionLayout->addWidget(posUp, 0, 1);
     positionLayout->addWidget(posLeft, 1, 0);
     positionLayout->addWidget(posReset, 1, 1);
     positionLayout->addWidget(posRight, 1, 2);
     positionLayout->addWidget(posDown, 2, 1);

     // 连接信号
     connect(posLeft, &QPushButton::clicked, this, &MainWindow::onMoveLeftClicked);
     connect(posRight, &QPushButton::clicked, this, &MainWindow::onMoveRightClicked);
     connect(posUp, &QPushButton::clicked, this, &MainWindow::onMoveUpClicked);
     connect(posDown, &QPushButton::clicked, this, &MainWindow::onMoveDownClicked);
     connect(posReset, &QPushButton::clicked, this, &MainWindow::onResetPositionClicked);

4.3 移动实现代码


// 波形移动功能实现
void MainWindow::onMoveLeftClicked()
{
    m_waveOffsetX -= m_timebase * 0.5; // 每次移动半个时基格
    applyWaveOffsets();
    m_plot->replot();
    updateStatusBarInfo();  //状态更新
    // 状态提示
    m_statusBar->showMessage(QString("波形左移: X偏移 = %1 s").arg(m_waveOffsetX, 0, 'f', 6), 2000);
}

void MainWindow::onMoveRightClicked()
{
    m_waveOffsetX += m_timebase * 0.5; // 每次移动半个时基格
    applyWaveOffsets();
    updateStatusBarInfo();  //状态更新
    m_plot->replot();

    statusBar()->showMessage(QString("波形右移: X偏移 = %1 s").arg(m_waveOffsetX, 0, 'f', 6), 2000);
}

void MainWindow::onMoveUpClicked()
{
    m_waveOffsetY += 0.5; // 每次移动半个电压格
    applyWaveOffsets();
    updateStatusBarInfo();  //状态更新
    m_plot->replot();

    statusBar()->showMessage(QString("波形上移: Y偏移 = %1 V").arg(m_waveOffsetY, 0, 'f', 3), 2000);
}

void MainWindow::onMoveDownClicked()
{
    m_waveOffsetY -= 0.5; // 每次移动半个电压格
    applyWaveOffsets();
    updateStatusBarInfo();  //状态更新
    m_plot->replot();

    statusBar()->showMessage(QString("波形下移: Y偏移 = %1 V").arg(m_waveOffsetY, 0, 'f', 3), 2000);
}

void MainWindow::onResetPositionClicked()
{
    m_waveOffsetX = 0.0;
    m_waveOffsetY = 0.0;
    applyWaveOffsets();
    m_plot->replot();

    statusBar()->showMessage("波形位置已复位", 2000);
}

4.4 触发控制代码


bool MainWindow::checkTrigger(const QVector<double>& signal)
{
    if (signal.size() < 2) return false;

    // 触发迟滞量
    double hysteresis = 0.02;

    // 使用更小的检查范围,只在信号中间部分检查
    int startIndex = signal.size() / 4;  // 从25%开始
    int endIndex = signal.size() * 3 / 4; // 到75%结束
    int step = 2; // 跳步检查,提高性能

    for (int i = startIndex; i < endIndex; i += step) {
        if (i >= signal.size() - 1) break;

        double prev = signal[i-1];
        double curr = signal[i];

        bool triggered = false;

        if (m_trigger.type == 0) { // 边沿触发
            switch (m_trigger.slope) {
            case 0: // 上升沿
                triggered = (prev < m_trigger.level - hysteresis) &&
                           (curr >= m_trigger.level + hysteresis);
                break;
            case 1: // 下降沿
                triggered = (prev > m_trigger.level + hysteresis) &&
                           (curr <= m_trigger.level - hysteresis);
                break;
            case 2: // 双边沿
                triggered = ((prev < m_trigger.level - hysteresis && curr >= m_trigger.level + hysteresis) ||
                            (prev > m_trigger.level + hysteresis && curr <= m_trigger.level - hysteresis));
                break;
            }
        } else { // 电平触发
            switch (m_trigger.slope) {
            case 0: // 高电平
                triggered = (curr >= m_trigger.level + hysteresis);
                break;
            case 1: // 低电平
                triggered = (curr <= m_trigger.level - hysteresis);
                break;
            case 2: // 窗口触发
                double window = 0.1 * std::abs(m_trigger.level);
                triggered = (curr >= m_trigger.level - window) &&
                           (curr <= m_trigger.level + window);
                break;
            }
        }

        if (triggered) {
            m_trigger.triggerPoint = m_timeData[i];
            return true;
        }
    }

    return false;
}

4.5 测量实现代码


void MainWindow::updateMeasurements()
{
    if (!m_measureEnabled) return;

    static int measureFrameCount = 0;
    measureFrameCount++;

    // 每2帧更新一次测量,减少计算量
    if (measureFrameCount % 2 != 0) {
        return;
    }

    // 对每个可见通道进行测量
    for (int ch = 0; ch < 4; ++ch) {
        if (m_channels[ch]->visible()) {
            MeasurementData data = calculateMeasurements(ch);
            m_measurements[ch] = data;
            updateMeasurementTable(ch, data);
        } else {
            clearMeasurementRow(ch);
        }
    }
}

5 测试结果

测试通过输入5000Hz的正弦波、2000Hz的方波以及其他频率的三角波形和噪声等信号。测试结果如下

5.1 波形输入

5000Hz的正弦波
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能
2000Hz的方波
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

5.2 测量结果

测量数据,结果下图

基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

FFT结果
基于Qt开发的虚拟数字示波器软件,提供专业的波形显示、测量和分析功能

通过测试输入波形和测量一致。数据交互成功。

6 总结

本文介绍了Qt实现数字示波器,实现波形交互、测量、FFT等数字信号处理。实现缩放、平移等交互操作,集成电压、频率等参数自动测量功能。实现实时FFT频谱分析。后续则继续分享四通道示波器的高阶应用,开发不易珍惜每一分原创和劳动成果,同时注意平时开发过程中的经验积累总结。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...