示波软件

示波软件使用“像素点”的高低表示输入值的大小。

设输入值 \(x \in \left[source_{min},source_{max}\right]\),像素点的高度 \(y \in \left[target_{min},target_{max}\right]\),我们需要把 x 缩放成 y 。

可以用线性方程(一次方程)进行缩放,以保证数值分布的均匀性。

待定缩放方程 \(y = mx + b\)

其斜率 \(m = \frac{\Delta y}{\Delta x} = \frac{target_{max} - target_{min}}{source_{max} - source_{min}}\)

因方程过点 \(\left(source_{min},target_{min}\right)\), 有 \(y - target_{min} = m(x - source_{min})\)

代入 m,得 \(y = \frac{target_{max} - target_{min}}{source_{max} - source_{min}}(x - source_{min}) + target_{min}\)

\(source_{min} = target_{min} = 0\) 时,缩放方程可简化为 \(y = \frac{target_{max}}{source_{max}}x\)

Form.cs

using System;
using System.IO.Ports;
using System.Text.RegularExpressions;

namespace wave
{
    public partial class Form : System.Windows.Forms.Form
    {
        private SerialPort? serialPort;
        private readonly List<Point> targetPoints = [];
        private readonly List<Point> actualPoints = [];
        private Point minLeftPoint, maxLeftPoint, minRightPoint, maxRightPoint;
        private const int STEP = 2;
        private int pointX = -STEP;
        private int minValue = int.MaxValue;
        private int maxValue = int.MinValue;

        public Form()
        {
            InitializeComponent();
            this.DoubleBuffered = true;
        }

        /**
         * 绘图
         */
        private void PanelChart_Paint(object sender, PaintEventArgs e)
        {
            if (pointX > 0)
            {
                Graphics g = e.Graphics;
                // 画目标值折线
                using Pen penTarget = new(Color.Red, 2);
                g.DrawLines(penTarget, targetPoints.ToArray());
                // 画实际值折线
                using Pen penActual = new(Color.Blue, 2);
                g.DrawLines(penActual, actualPoints.ToArray());
                // 画实际值的边界线
                using Pen penEdge = new(Color.Yellow, 2);
                g.DrawLine(penEdge, minLeftPoint, minRightPoint);// 最小值
                g.DrawLine(penEdge, maxLeftPoint, maxRightPoint);// 最大值
                this.labelStatus.Text =  minValue + "," + maxValue;
            }
        }

        /**
         * 启动
         */
        private void ButtonStart_Click(object sender, EventArgs e)
        {
            serialPort = new()
            {
                PortName = this.comboBoxCOM.Text,
                BaudRate = 9600,
                Parity = Parity.None,
                DataBits = 8,
                StopBits = StopBits.One
            };
            serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceivedHandler);
            serialPort.Open();// 打开串口
            this.buttonStart.Enabled = false;
            this.buttonStop.Enabled = true;
        }

        /**
         * 接收数据
         */
        private void SerialDataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            string data = sp.ReadExisting();
            string pattern = @"Val:(\d+),(\d+)";
            Match match = Regex.Match(data, pattern);
            if (match.Success)
            {
                // 提取目标值,实际值,实际值边界。
                int targetValue = int.Parse(match.Groups[1].Value);
                int actualValue = int.Parse(match.Groups[2].Value);
                if (actualValue < minValue) minValue = actualValue;
                if (actualValue > maxValue) maxValue = actualValue;
                // 计算像素横坐标
                pointX += STEP;
                // 横坐标到达右侧边界后,立即返回左侧边界,从头绘制。
                if (pointX > this.panelChart.Width)
                {
                    pointX = 0;
                    minValue = int.MaxValue;
                    maxValue = int.MinValue;
                    targetPoints.Clear();
                    actualPoints.Clear();
                }
                // 生成像素
                targetPoints.Add(new Point(pointX, CalcPointY(targetValue)));
                actualPoints.Add(new Point(pointX, CalcPointY(actualValue)));
                minLeftPoint = new Point(0, CalcPointY(minValue));
                maxLeftPoint = new Point(0, CalcPointY(maxValue));
                minRightPoint = new Point(this.panelChart.Width, CalcPointY(minValue));
                maxRightPoint = new Point(this.panelChart.Width, CalcPointY(maxValue));
                // 开始绘图
                this.panelChart.Invalidate();
            }
        }

        /**
         * 停止
         */
        private void ButtonStop_Click(object sender, EventArgs e)
        {
            serialPort?.Close();
            this.buttonStart.Enabled = true;
            this.buttonStop.Enabled = false;
        }

        /**
         * 计算像素纵坐标
         */
        public int CalcPointY(int sourceValue)
        {
            /**
             * 将 sourceValue 从原始区间[sourceMin,sourceMax]线性缩放至目标区间[targetMin,targetMax]。
             */
            int sourceMin = 0;
            int sourceMax = 4095;
            int targetMin = 0;
            int targetMax = this.panelChart.Height;
            double targetValue = 1.0 * (targetMax - targetMin) / (sourceMax - sourceMin)
                * (sourceValue - sourceMin)
                + targetMin;
            /**
             * 像素坐标系原点位于容器(panelChart)左上角,向右的方向为x轴正方向,向下的方向为y轴正方向。
             * 将缩放后的数值进一步转换成像素纵坐标。
             */
            return Convert.ToInt32(this.panelChart.Height - targetValue);
        }

        /**
         * 识别串口
         */
        private void ComboBoxCOM_Click(object sender, EventArgs e)
        {
            string[] names = SerialPort.GetPortNames();
            comboBoxCOM.Items.Clear();
            comboBoxCOM.Items.AddRange(names);
        }
    }
}

posted on 2025-10-26 22:17  星辰河岳  阅读(20)  评论(0)    收藏  举报