《VC++编程实现对波形数据的频谱分析.doc》由会员分享,可在线阅读,更多相关《VC++编程实现对波形数据的频谱分析.doc(21页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、【精品文档】如有侵权,请联系网站删除,仅供学习与交流VC+编程实现对波形数据的频谱分析.精品文档.VC+编程实现对波形数据的频谱分析 摘要: 本文介绍了采用离散傅立叶变换(DFT)实现对采样得到的波形数据文件进行频谱分析的一般方法,并且为了提高运算效率、节省中间存储单元,最终采用了时间抽选奇偶分解的库利-图基算法实现快速离散傅立叶变换,对采样数据进行了高效的频谱分析,并用Microsoft Visual C+ 6.0编写实现。关键字:Microsoft Visual C + 6.0、离散傅立叶变换、快速傅立叶变换、采样 一、 引言频谱分析是电子工程上一个非常重要的手段,许多计算机辅助电路分析(
2、CAA)类软件都具备这种分析能力,以便电子工程师能清楚的看到某波形的频谱分布情况。而要对一个输入信号源作频谱分析,将其由时域信号转变为频域信号,就必然要用到傅立叶分析,而无论是在时域还是在频域,都要对连续函数进行积分运算。很显然,要通过计算机实现此变换必须预先通过抽样将原始的连续数据转变为离散数据,并将计算范围收缩到一个有限区间。因此在允许一定程度近似的条件下,可以使用离散傅立叶变换(DFT)对波形数据进行频谱分析。二、 快速傅立叶变换(FFT)算法构成原理要计算一个N点的离散傅立叶变换需要同一个N*N点的W矩阵(关于W矩阵请参阅信号与系统方面的书籍)相运算,随着N值的增大,运算次数显著上升,
3、当点数达到1024时,需要进行复数乘法运算1,048,576次,显然这种算法在实际运用中无法保证当点数较大时的运算速度,无法满足对信号的实时处理。根据W矩阵中W元素的周期性和对称性我们可以将一个N点的DFT运算分解为两组N/2点的DFT运算,然后取和即可,为进一步提高效率,将上述两个矩阵按奇偶顺序逐级分解下去。当采样点数为2的指数次方M时,可分解为M级子矩阵运算,全部工作量仅为:复数乘法:M*N/2次复数加法:N*M次而直接DFT需要的运算量为:复数乘法:N*N次复数加法:N*(N-1)次当点数N为几十个点时FFT的优势还不明显,而一旦达到几千、几百个点时优势是十分明显的:N=1024时:DF
4、T需1048576次运算,FFT仅需5120次运算,改善比204.8。N=2048时:DFT需4194304次运算,FFT仅需11264次运算,改善比达到372.4。三、 时间抽选奇偶分解快速离散傅立叶变换的程序实现当采样点数较多时,如变换前和变换后的序列都按自然顺序排列,则中间运算过程会占用大量的中间存储单元,造成效率的低下和存储单元的浪费。根据FFT的实现原理我们可以对采样序列进行逐次奇偶抽选,打乱以前的次序重新排序,然后按此顺序参加运算,可以实现即位运算提高存储单元的利用率。(一) 复数的描述方法进行傅立叶变换时不可避免的要用到复数,而在VC中并没有现成的可用于表示复数的数据类型,可以自
5、己定义一个含有两个成员变量的数据结构来表示复数,这两个成员变量可分别用于表示复数的实部与虚部: typedef struct tagComplex float Re; /复数的实部 float Im; /复数的虚部Complex;(二) 倒序的实现在进行快速傅立叶变换时,可以将输入的时域序列和输出的频域序列都按照自然顺序排列;也可以按照蝴蝶图所描述的计算方法对输入的时域序列按奇偶分解后的序列排序而输出的频域序列仍是按自然顺序排列的;还有一中方式是输入的时域序列是不进行抽选的自然序列,而输出的频域序列则是按奇偶分解后的顺序排列的。这三种方式各有优点,第一种对输入、输出不需要进一步排序,但由于自然
6、排序不符合蝴蝶图运算规律,会占用大量中间存储单元。而后两种则无须中间存储单元,但需要倒一次序。权衡利弊,当采样点较多时还是采用后两种方式好,多一次倒序运算对现在的高性能计算机而言并不是什么负担。下面代码用于对原始采样序列的时间抽选奇偶分解工作,其中A、N分别表示指向采样序列复数数组的指针和序列的长度。 int NV2=N/2;int NM1=N-1;int I,J,K=0;Complex T;/用于中介的复数变量TI=J=1;while(I=NM1) if(IJ) T=AJ-1;/将AJ-1的内容和AI-1的内容互换,借助于中间变量T AJ-1=AI-1; AI-1=T; K=NV2; whi
7、le(KJ) J-=K; K/=2; J+=K; I+;(三) 时域信号的频谱分析首先要将从外设输入或采集的时域波形数据经抽样量化后,通过CFile类的Open()、Read()等成员函数将其读取到缓存中,并将其转化为复变量存放于复变量数组A中,同时需要验证以下数据量的长度是否为2的整数次幂,如若不是则必须用0来补齐,否则无法用蝴蝶图进行分解运算。下面代码用于完成对原始采样时域序列的快速傅立叶变换,A、M分别表示指向原始采样数据数组的指针和序列长度的2的整数次幂: Complex U,W,T;int LE,LE1,I,J,IP;int N=(int)pow(2,M);/在此采用的是时间抽选奇偶
8、分解方式,所以在参加运算前首先要对时间序列进行倒序ReverseOrder(A,N);int L=1;while(L=M) LE=(int)pow(2,L); LE1=LE/2; U.Re=1.0f; U.Im=0.0f; W.Re=(float)cos(PI/(1.0*LE1);/计算W算子的值 W.Im=(float)-1.0*sin(PI/(1.0*LE1); if(abs(W.Re)1.0e-12) W.Re=0.0f; if(abs(W.Im)1.0e-12) W.Im=0.0f; J=1; while(J=LE1) I=J; while(I 摘 要:本文详细介绍了在VB集成环境下数
9、字波形高速显示的方法,同时对双通道波形显示和数字滤波方法也进行了介绍。关键词:数字;波形;显示 ;滤波1 前言:随着计算机技术及电子技术的发展,数字采集技术在检测领域的应用越来越广泛,检测速度越来越高,检测的数据量越来越大,特别是在无损检测领域,将检测数据通过计算机处理后绘制出波形,并实时显示,对及时发现伤损、分析伤损具有重要意义。2 波形显示检测数据通常是离散的数据,将离散的数据绘制出波形,可通过在两点间连接线段的方法实现。2.1 用Line方法显示波形VB提供了Line画直线方法,可在窗体上增加一个图片框控件,适当设置图片的大小和背景颜色,用Line方法将离散数据按检测顺序连接成线段,即可
10、将波形显示在图片框中。但该方法显示波形速度较慢,不适合高速显示的应用。2.2 Windows API函数显示波形在VB中两点间连线的另一种方法是用Windows API函数,Win32 API提供了以下两个函数,联合使用可实现波形的快速显示,经过测试,显示速度比使用Line方法快70%以上。LineTo函数:函数功能:画出由数组定义的点连接的一系列线段。函数原型:BOOL LineTo(HDC hdc,int nXEnd,int nYEnd);参数:hdc:设备环境句柄。nXEnd:定义线段终点的X坐标。nYEnd:定义线段终点的Y坐标。返回值:若函数调用成功,则返回非0值;若函数调用失败,则
11、返回值为0。MoveToEx函数:函数功能:将当前位置更新为指定的点,并有选择的返回原先的位置。函数原型:BOOL MoveToEx (HDC hdc,int X,int Y,LPPOINT lpPoint);参数:hdc:设备环境句柄。X:定义新位置的X坐标(逻辑坐标)。Y:定义新位置的Y坐标(逻辑坐标)。lpPoint:指向一个POINT结构,结构中存放原先的位置。若此参数为NULL,则不返回原先的位置返回值:若函数调用成功,则返回非0值;若函数调用失败,则返回值为0。在连接线段时,首先将检测数据放入一个数组中,用MoveToEx函数定位画线的起始点坐标,然后用LineTo函数画出起始点至
12、下一个点之间的线段,再用MoveToEx将画线的起始点定位到下一个点,继续用LineTo函数画线,如此循环,即可将离散点连接成波形。例:zz = MoveToEx(Picture1.hdc, i, Mwave(i ), LpPoint1)zz = LineTo(Picture1.hdc, i, Mwave(i+1)实时波形显示界面 通常计算机需要接收外部实时发送的数据并用十分形象的方式显示出来。例如柱状图、饼图等等。本应用程序则采用波形的形式显示,并接将之设计为可以接收多路数据的波形显示界面。本应用程序在VS2008环境下调试通过,源码下载连接如下一.程序界面点击该图放大上图中显示了两路波形即
13、三角波和正弦波,当然这两路波形是由程序计算出来的并不是从外部接收的。实际工作中则可以配合串口通讯设备接收它发来的数据并显示出来。二.波形控件类介绍本程序的实现主要依赖于那个波形显示控件。从下载连接那里可以下载该源码,里面的文件中,2DPushGraph.h和2DPushGraph.cpp即是该控件的类的定义文件和实现文件。分析之后得到该类的一些信息。其成员函数包括: 程序代码:COLORREF m_crTextColor;/标签文字颜色 COLORREF m_crBGColor;/背景颜色 COLORREF m_crGridColor;/栅格颜色 CStringm_strMaxLabel;/最
14、大值之处的标签 CStringm_strMinLabel;/最小值之处的标签 boolm_bShowMinMax;/最小最大值显示 boolm_bShowGrid;/是否显示栅格 boolm_bStylesModified;/是否被修改 int m_nMoveOffset;/偏移 int m_nMaxCoords;/最大缓冲 int m_nMaxPeek;/显示数据的最大值 int m_nMinPeek;/显示数据的最小值 int m_nGridSize;/栅格间距 int m_nPeekOffset;/峰值偏移其实现方法包括: 程序代码:bool CreateFromStatic( UINT
15、 nStaticID, CWnd* pParent );/该方法用于创建控件实例,通常在对话框初始化函数中调用,nStaticID为控件ID,pPatent为创建在那个窗口中的ID。 LPCTSTRGetLabelForMax() const;/获取最大值处的标签 LPCTSTRGetLabelForMin() const; /获取最大值处的标签 COLORREFGetBGColor() const;/获得背景色 COLORREFGetGridColor() const;/获得栅格颜色 void SetBGColor(COLORREF crColor); void SetGridColor(C
16、OLORREF crColor); void SetTextColor(COLORREF crColor); COLORREFGetLineColor( UINT uiLineID );/该方法用于获得ID号为uiLineID的波形的线条颜色,因为要在波形控件中画线必须先调用bool AddLine( UINT uiLineID, COLORREF crColor );创建一个波形,ID号为uiLineID。 bool SetLineColor( COLORREF crColor, UINT uiLineID ); COLORREFGetTextColor() const;/获得标签文本的颜色
17、 intGetGridSize()const;/获得栅格间隔 void SetGridSize( unsigned short usWidthAndHeight ); intGetMaxPeek()const;/获得最大值纵坐标 intGetMinPeek()const; /获得最小值纵坐标 void SetPeekRange(int nMin, int nMax); void SetMaxPeek(int nMax); void SetMinPeek(int nMin); unsigned short GetInterval() const; /获得横坐标间距 void SetInterva
18、l( unsigned short usInterval ); void SetLabelForMax( LPCTSTR lpszLabel ); void SetLabelForMin( LPCTSTR lpszLabel ); bool AddLine( UINT uiLineID, COLORREF crColor );/该方法用于在该控件上创建一个波形。 void RemoveLine( UINT uiLineID );/清除ID号为uiLineID的波形 bool Push( int nMagnitude, UINT uiLineID );/在ID号为uiLineID号的波形上添加一
19、个数据点,nMagnitude为幅度 void ShowAsBar( UINT uiLineID, bool bAsBar );/柱状图显示 void Update();/更新三.应用编程运用该控件在VS2008环境下的编程步骤如下:1.建立一个对话框的MFC工程,在对话框上按照上图所示的界面布置控件。其中波形控件那里布置一个Picture Control控件将其Modal Frame和Type均属性设置为true,其他均设置为False。注意给Picture Control取的ID!后面编程将会用到。2.将波形控件类的定义文件和实现文件拷贝至你的工程目录下。但这实际上并没有将该类真正添加到你
20、的工程下,需手动添加类。常规操作,不详述。3.在对话框的定义和实现文件中分别添加如下代码: 程序代码:#include 2DPushGraph.h4.在对话框定义文件中(我给的供下载的例程中的是DataRealTimeDlg.h这个文件)中定义一个该控件类的变量:程序代码:private:C2DPushGraph m_PushGraph;5.在对话框的实现文件中(我给的供下载的例程中的是DataRealTimeDlg.cpp这个文件)的对话框初始化函数中添加如下代码:程序代码: m_PushGraph.CreateFromStatic(IDC_REALCTRL, this); /这个IDC_R
21、EALCTRL即是那个Picture Control控件的ID号。 m_PushGraph.ModifyStyle(0, WS_THICKFRAME); /设置风格6.现在不妨试着运行以下该程序,应该可以观察到那个控件显示出来了吧。在添加以下代码:程序代码: m_PushGraph.AddLine(m_sin,RGB(255,255,255); m_PushGraph.AddLine(m_tra,RGB(255,0,0)这两行代码分别添加了一个正弦波形,ID号为m_sin,一个三角波形,ID号为m_tra。不过运行之后并没有数据点绘制出来。那是应为还没有调用bool Push( int nMa
22、gnitude, UINT uiLineID )函数添加数据点。试着添加几个数据点再运行即可观察到波形。7.还有一些控件的响应代码看看那个下载的例程吧。四.结束语 成功了吧,Any Problem,Contact me please!控件设计Nobis StatusChart - 野比的状态波形图控件 从构思到实现Nobis StatusChart - 野比的状态波形图控件从构思到实现野比 著源程序下载:Demo控件背景目前比较流行的 WinForm 程序设计都会提供形象的可视化数据流动记录功能,如 FlashGet 及其衍生软件的悬浮窗网速监视图,Windows 任务管理器的 CPU、内存使
23、用图等。构思为了在我们自己的程序中实现这种效果,就需要研究、分析它们的原理,掌握其规律,然后加以实现。很明显,从软件可重用性以及各种随之而来的好处考虑,我们要求将这个“波形显示”效果做成一个控件(Control)。分析还是以 FlashGet 的悬浮窗和 Windows 任务管理器作为研究对象。仔细观察它们的工作方式,发现它们有以下的共同点: 在右边更新当前的波形值 更新后的波形不消失,而是整体向左平移 可以设置波形颜色、更新速度等而通过深入研究,发现二者不同点如下: 不同的显示方式,有曲线显示和直方图显示 有无定位网格 各部分颜色可自定义设计通过分析,可以决定如下:凡是二者共同点,加以重点实
24、现;凡二者不同之处,通过设置属性(Property)进行更改。最后绘制时,基于所设置的属性,使用共同方法加以实现。因此自定义属性如下: BackColor(重写基类属性) Enabled(重写) ForeColor(重写) GridColor 网格颜色 GridHeight 网格每格高度 GridShiftting 是否平移网格 GridWidth 网格每格宽度 Interval 波形刷新间隔(单位:毫秒) Mode 波形显示方式(曲线/直方图) Range 数值范围 ShifttingIncrement 向左平移增量 Value 当前值控件因为要定时更新,因此具有一个内部的 Timer 对其
25、进行定时,其 Interval 由控件的 Interval 属性指定。对于此自定义控件,需要每次更新时在其 OnPaint() 事件中对整个控件进行绘制。绘制顺序为:背景 - 网格 - 波形,如此保证所有部分均正确画出且无遮挡。从波形看,很明显,我们需要一个长度至少等于波形控件宽度的数组来存放每时刻波形的值,因此可以确定这个数组是和控件绘图画布宽度一致的。算法绘图最重要的是算法部分,如何计算如网格位置,如何将图形整体平移,如何设置波形值是本控件的重点和难点部分。计算网格位置,以上面的 ShifttingIncrement 为 offset 参数传入/网格数(不计边缘)float div;flo
26、at pos = 0F;/先画 垂直 方向/可以少画一根线div = (float)w / (float)gridWidth + 1;for (int i = 0; i (int)div; i+) pos += gridWidth; g.DrawLine(penGrid, pos - offset, 0, pos - offset, h);/画 水平 方向div = (float)h / (float)gridHeight;pos = 0F;for (int i = 0; i (int)div; i+) pos += gridHeight; g.DrawLine(penGrid, 0, pos
27、, w, pos);对于波形,传入其波形值数组作为参数/从 0 到 w 绘制int len = w;/根据绘制方式if (chartMode = StatusChart.ChartMode.Histogram) for (int i = 0; i len; i+) g.DrawLine(p, i, h - vali, i, h); g.DrawLine(p, len, h - vallen - 1, len, h);else len-; for (int i = 0; i len; i+) g.DrawLine(p, i, h - vali, i + 1, h - vali + 1); len
28、+; g.DrawLine(p, len - 1, h - vallen - 2, len, h - vallen - 1);如何平移,是一个难点,需要在内部定时器的 Tick() 事件中加以处理/更新网格偏移/只有启用了网格移动才处理if (gridShiftting) iOffset += gridShifttingIncrement; iOffset %= gridWidth;/更新图形(整体左移)/必须在这里而不能在画图的同时移动,/若在画图中移动,则当画面被遮挡(OnPaint)事件不发生时无法更新int len = w;for (int i = 0; i len; i+) /判断数
29、组越界 if (i len - 1) vali = vali + 1; else vallen - 1 = currentValue;/break; /vallen = currentValue;Invalidate();最后引发控件的 Invalidate() 方法使控件重绘自身。效果最后的控件运行效果如下图所示:总结通过设计并实现 StatusChart 控件,我们研究了波形状态监控的相关现象和原理,复习并使用了控件的设计生成,最后使用 GDI+ 技术对其进行了实现。VC+编程实现对火焰的计算机动态仿真摘要:本文通过对真实火焰物理特性的分析,建立了火焰动态燃烧的数学模型,并在此数学模型基础
30、之上借助于DirectDraw技术对图形显示的加速,在VC+ 6.0下对火焰作了效果非常逼真的计算机动态仿真。关键词:火焰;DirectDraw;计算机仿真 引言计算机仿真技术的基本原理都是一样的,神秘复杂的核爆同水波、火焰、烟雾等非常平常的自然现象在仿真处理过程中并没有什么太大的区别。都是经历了从实体对象到物理特性的总结,再由此建立数学模型并在数学模型基础之上提出仿真算法,最后通过计算机将其动态仿真出来等一系列步骤。本文以火焰作为仿真对象,通过对热源、热扩散以及对流等特性的分析对其建立了数学模型及仿真算法,为了能充分发挥计算机对图形的硬件加速,使用DirectDraw技术对仿真结果显示进行了
31、加速,使之能逼真、流畅地对火焰的燃烧过程实行动态模拟。 简单近似模型设计虽然火焰在自然界是一种极普通的自然现象,但根据流体力学的相关知识,火焰可以表达为一个相当复杂的三维动态流体系统。如要在计算机中对这样一个复杂的流体系统做出精确的仿真将需要有相当庞大复杂的数学模型为基础,而且运算量将非常巨大,在现有的微型计算机中几乎很难保证其动态实时性,这也就失去了仿真的意义。因此,在仿真时应用尽可能简单的模型来实现尽可能逼真的效果。从物理角度分析,要产生火焰,首先要有火源,其次为了产生焰的效果,需要以火源为中心向上、向四周扩散,而且由于在扩散过程中逐渐远离火源,温度会逐渐下降,表现在视觉上就是火焰的冷却变
32、暗。另外,由于火焰的高温使周围空气受热膨胀比重下降,因此会有空气的对流出现,这将把火焰向上吹起,使火焰向四周扩散的距离要远小于向上扩散的距离。基于以上几点认识,可以采取对应的仿真措施:对火源的设置可以用一幅二值位图来标识,非火源以低亮度像素填充、火源点则设以高亮度像素,通过对位图像素值的判别可以断定当前点是否为火源。 对于火源的温度高低可用其所在点的亮度来描述;对于火焰扩散的模拟,为尽量减少运算量,在此简单地用某火源点(x,y)及其前后左右邻近四点的均值来近似,即Pixel(x,y)=(Pixel(x,y)+Pixel(x,y-1)+Pixel(x,y+1)+Pixel(x-1,y)+Pixe
33、l(x+1,y)/5,虽然该近似算法没有采取正余弦的方法精确,但运算速度极快,而且在后续的实验效果上同采用正余弦的方法几乎没有任何差别;由于在仿真过程中对火焰的温度是通过改变其亮度值来实现的,因此对于扩散过程的冷却可对像素点降低一个固定的亮度值来实现。衰减值的大小需要视所希望火焰冷却速度的快慢而定;对流对火焰产生的直接影响就是使火焰始终保持向上燃烧,因此可通过将当前火焰上滚一至两个像素来加以实现。根据前面描述的仿真运算法则,可将火焰的扩散和对流融合在一起实现,这将在一定程度上减少运算量,使产生的火焰在视觉上更加真实。实现上述近似模型的伪代码可大致设计如下: ARRAY_OF_BYTES: buffer1(xsize*ysize),buffer2(xsize*ysize)While(TRUE)for(y=1;yYSIZE-2;Y+)for(x=1;xXSIZE-2;X+