呵呵,几年前在课堂上为学生做的。我希望你知道示波器是如何工作的,所以这里只是基础知识:
时基
尝试使用尽可能大的频率,(44100,48000, ???)
以便检测到的最大频率fsmpl/2
为您提供时基轴的顶部。下限由您的缓冲区长度给出
画
创建将从指定起始地址(缓冲区内)呈现采样缓冲区的函数:
- Y-scale ...幅度设置
- Y-offset ... 垂直光束位置
- X-offset ... 时移或水平位置
这可以通过修改起始地址或仅通过 X 偏移曲线来完成
等级
创建将模拟关卡功能的函数。所以从起始地址搜索缓冲区,如果幅度超过电平则停止。你可以有更多的模式,但这些是你应该实现的基础:
- 振幅:
( < lvl ) -> ( > lvl )
- 振幅:
( > lvl ) -> ( < lvl )
还有许多其他级别的可能性,例如故障,相对边缘,...
预习
您可以将所有这些放在一起,例如:您有start address
变量,因此将数据连续采样到某个缓冲区并在计时器调用level
时使用start address
(并更新它)。然后用 new 调用 drawstart address
并添加timebase period
到start address
(当然就您的样本而言)
多通道
我使用Line IN,所以我有立体声输入(A,B = 左,右),因此我可以添加一些其他内容,例如:
- 电平源 (
A,B
,none)
- 渲染模式(时基,切比雪夫(李萨如曲线,如果关闭))
- Chebyshev =
x
axis is A
, y
axis isB
这会创建著名的切比雪夫图像,这些图像对相关的正弦信号很有用。通常形成圆形、椭圆形、扭曲的环......
杂项
您可以为模拟电容或输入接地等通道添加滤波器
图形用户界面
你需要很多设置我更喜欢模拟旋钮而不是按钮/滚动条/滑块,就像在真正的示波器上一样
- (半)模拟值:Amplitude,TimeBase,Level,X-offset,Y-offset
- 离散值:电平模式(/,),电平源(A,B,-),每个通道(直接开启,接地,关闭,容量开启)
这是我的示波器的一些屏幕截图:
这是我的生成器的屏幕截图:
最后在添加了一些 FFT 之后还有频谱分析仪
PS。
- 我从DirectSound开始,但由于有错误/无功能的缓冲区回调,它很糟糕
- 我现在对我的应用程序中的所有声音都使用WinAPI WaveIn/Out。经过一些怪癖之后,它最适合我的需求并且具有最佳延迟(Directsound 太慢了 10 倍以上)但对于示波器它没有任何优点(我需要低延迟主要用于模拟器)
顺便提一句。我将这三个应用程序作为可链接的 C++ 子窗口类(Borland)
- 最后与我的 ATMega168 仿真器一起用于我的无传感器 BLDC 驱动程序调试
- 在这里你可以试试我的示波器、发生器和频谱分析仪
如果您需要任何帮助,希望对您有所帮助,请评论我
[Edit1] 触发器
您一次触发所有通道,但通常只从一个通道检查触发条件现在实现很简单,例如让触发条件为A(左)通道高于水平,因此:
首先在没有触发器的情况下进行连续播放,你写的是这样的:
for ( int i = 0, j = 0; i < countSamples ; ++j)
{
YVectorRight[j]=Samples[i++];
YVectorLeft[j] =Samples[i++];
}
// here draw or FFT,draw buffers YVectorRight,YVectorLeft
添加触发器
要添加触发条件,您只需找到满足它的样本并开始从中绘制,以便将其更改为类似这样
// static or global variables
static int i0=0; // actual start for drawing
static bool _copy_data=true; // flag that new samples need to be copied
static int level=35; // trigger level value datatype should be the same as your samples...
int i,j;
for (;;)
{
// copy new samples to buffer if needed
if (_copy_data)
for (_copy_data=false,i=0,j=0;i<countSamples;++j)
{
YVectorRight[j]=Samples[i++];
YVectorLeft[j] =Samples[i++];
}
// now search for new start
for (i=i0+1;i<countSamples>>1;i++)
if (YVectorLeft[i-1]<level) // lower then level before i
if (YVectorLeft[i]>=level) // higher then level after i
{
i0=i;
break;
}
if (i0>=(countSamples>>1)-view_samples) { i0=0; _copy_data=true; continue; }
break;
}
// here draw or FFT,draw buffers YVectorRight,YVectorLeft from i0 position
- 这
view_samples
是查看/处理的数据大小(对于一个或多个屏幕)它应该比(countSamples>>1)
- 此代码可以在边界区域松动一个屏幕以避免您需要实现循环缓冲区(环)但对于初学者来说甚至可以
- 只需通过一些 if 或 switch 语句对所有触发条件进行编码