2

正如标题所说:

我有 2 个轨迹栏的表格。一种用于频率,一种用于幅度。我为即时更改设置了计时器。

private void timer1_Tick(object sender, EventArgs e)
    {
        float amplitude, frequency;

        amplitude = Convert.ToSingle(trackBar1.Value) / 100;
        label1.Text = amplitude.ToString() + " V";

        frequency = trackBar2.Value;
        label2.Text = frequency.ToString() + " Hz";
    }

我还有 4 个单选按钮来决定,将显示哪种类型的信号(正弦、正方形、三角形、锯齿形)

现在我用 ImageList 实现了这个(改变信号的图像)。

如何绘制信号类型并使用轨迹条对其进行调节?所以它就像在示波器中一样。

感谢您的回答和代码。

4

3 回答 3

1

让我们从创建不同的信号类型开始,这是一个创建振幅为 1 的波长的函数:

private PointF[] CreateBaseSignal(SignalType signalType)
{
  switch (signalType)
  {
    case SignalType.Sine:
      const int oversampling = 32;
      PointF[] signal = new PointF[oversampling];
      for (int i = 0; i < signal.Length; i++)
      {
        signal[i].X = (float) i / oversampling;
        signal[i].Y = Convert.ToSingle(Math.Sin((double) i / oversampling * 2 * Math.PI));
      }
      return signal;

    case SignalType.Square:
      return new PointF[]
      {
        new PointF(0.0f, -1.0f),
        new PointF(0.5f, -1.0f),
        new PointF(0.5f, 1.0f),
        new PointF(1.0f, 1.0f),
      };

    case SignalType.Triangle:
      return new PointF[]
      {
        new PointF(0.0f, -1.0f),
        new PointF(0.5f, 1.0f),
      };

    case SignalType.Sawtooth:
      return new PointF[]
      {
        new PointF(0.0f, -1.0f),
        new PointF(1.0f, 1.0f),
      };

    default:
      throw new ArgumentException("Invalid signal type", "signalType");
  }
}

然后我们创建具有选定幅度和频率的实际信号:

private PointF[] CreateSignal(PointF[] baseSignal, float frequency, float amplitude)
{
  PointF[] signal = new PointF[Convert.ToInt32(Math.Ceiling(baseSignal.Length * frequency))];
  for(int i = 0; i < signal.Length; i++)
  {
    signal[i].X = baseSignal[i % baseSignal.Length].X / frequency + (i / baseSignal.Length) / frequency;
    signal[i].Y = baseSignal[i % baseSignal.Length].Y * amplitude;
  }
  return signal;
}

在尝试将此信号绘制到 PictureBox 之前,我们缩放信号以适应宽度和高度:

private PointF[] ScaleSignal(PointF[] signal, int width, int height)
{
  const float maximumAmplitude = 10.0f;
  PointF[] scaledSignal = new PointF[signal.Length];
  for(int i = 0; i < signal.Length; i++)
  {
    scaledSignal[i].X = signal[i].X * width;
    scaledSignal[i].Y = signal[i].Y * height / 2 / maximumAmplitude;
  }
  return scaledSignal;
}

使用 Graphics.DrawLine 绘制信号比 Bitmap.SetPixel 好得多,因为即使在高频下数据点也会连接。Bitmap.SetPixel 也很慢,您确实需要使用 Bitmap.LockBits 和不安全的代码来操作单个像素以实现任何不错的性能。使用 Graphics.DrawLine,您还可以控制线宽、抗锯齿等。

由于我们已将信号存储在 PointF 数组中,因此我们可以使用简单的 Graphics.DrawLines 方法来绘制信号,而不是遍历数据点:

private void PlotSignal(PointF[] signal, PictureBox pictureBox)
{
  Bitmap bmp = new Bitmap(pictureBox.ClientSize.Width, pictureBox.ClientSize.Height);
  signal = ScaleSignal(signal, bmp.Width, bmp.Height); // Scale signal to fit image

  using(Graphics gfx = Graphics.FromImage(bmp))
  {
    gfx.SmoothingMode = SmoothingMode.HighQuality;
    gfx.TranslateTransform(0, bmp.Height / 2); // Move Y=0 to center of image
    gfx.ScaleTransform(1, -1); // Make positive Y axis point upward
    gfx.DrawLine(Pens.Black, 0, 0, bmp.Width, 0); // Draw zero axis
    gfx.DrawLines(Pens.Blue, signal); // Draw signal
  }

  // Make sure the bitmap is disposed the next time around
  Image old = pictureBox.Image;
  pictureBox.Image = bmp;
  if(old != null)
    old.Dispose();
}

如果您经常重绘信号,您可能希望重用 Bitmap 和 Graphics 对象,而不是每次都创建新对象。只需记住在每次重绘之间调用 Graphics.Clear。

将所有内容放在一个重要的声明中:

PlotSignal(
  CreateSignal(
    CreateBaseSignal(signalType),
    frequency,
    amplitude),
  thePictureBox);
于 2012-04-14T12:12:15.423 回答
0

如果您想要一个快速绘图库,我真的很喜欢动态数据显示

动态数据显示

这是一个 WPF 组件,但对于快速、流畅的绘图应用程序,我真的认为尽早移植到 WPF 是值得的。无论如何,感觉你现在对你的项目还不算太远。

该组件的 WPF 开发似乎已停止(尽管 Silverlight 仍在继续开发)。文档很糟糕,但源代码可从上面的链接获得,因此您可以根据需要对其进行扩展(它编写得非常好并且非常可扩展),并且源代码对于几乎完全缺乏任何文档的替代品来说是无价的。

于 2012-04-13T13:02:19.080 回答
0

假设您要在图片框控件上绘制正弦波,请在表单上创建图片框控件,然后执行以下操作:

    int width = pictureBox1.Width;
    int height = pictureBox1.Height;
    Bitmap b = new Bitmap(width, height);

    for (int i = 0; i < width; i++)
    {
        int y = (int)((Math.Sin((double)i * 2.0 * Math.PI / width) + 1.0) * (height - 1) / 2.0);
        b.SetPixel(i, y, System.Drawing.Color.Red);
    }
    pictureBox1.Image = b;
于 2012-04-13T13:22:12.580 回答