《VC++编程实现对波形数据的频谱分析教学教材.doc》由会员分享,可在线阅读,更多相关《VC++编程实现对波形数据的频谱分析教学教材.doc(34页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Good is good, but better carries it.精益求精,善益求善。VC+编程实现对波形数据的频谱分析-VC+编程实现对波形数据的频谱分析-摘要:本文介绍了采用离散傅立叶变换(DFT)实现对采样得到的波形数据文件进行频谱分析的一般方法,并且为了提高运算效率、节省中间存储单元,最终采用了时间抽选奇偶分解的库利-图基算法实现快速离散傅立叶变换,对采样数据进行了高效的频谱分析,并用MicrosoftVisualC+6.0编写实现。关键字:MicrosoftVisualC+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
4、时:DFT需1048576次运算,FFT仅需5120次运算,改善比204.8。N=2048时:DFT需4194304次运算,FFT仅需11264次运算,改善比达到372.4。三、时间抽选奇偶分解快速离散傅立叶变换的程序实现当采样点数较多时,如变换前和变换后的序列都按自然顺序排列,则中间运算过程会占用大量的中间存储单元,造成效率的低下和存储单元的浪费。根据FFT的实现原理我们可以对采样序列进行逐次奇偶抽选,打乱以前的次序重新排序,然后按此顺序参加运算,可以实现即位运算提高存储单元的利用率。(一)复数的描述方法进行傅立叶变换时不可避免的要用到复数,而在VC中并没有现成的可用于表示复数的数据类型,可
5、以自己定义一个含有两个成员变量的数据结构来表示复数,这两个成员变量可分别用于表示复数的实部与虚部:typedefstructtagComplexfloatRe;/复数的实部floatIm;/复数的虚部Complex;(二)倒序的实现在进行快速傅立叶变换时,可以将输入的时域序列和输出的频域序列都按照自然顺序排列;也可以按照蝴蝶图所描述的计算方法对输入的时域序列按奇偶分解后的序列排序而输出的频域序列仍是按自然顺序排列的;还有一中方式是输入的时域序列是不进行抽选的自然序列,而输出的频域序列则是按奇偶分解后的顺序排列的。这三种方式各有优点,第一种对输入、输出不需要进一步排序,但由于自然排序不符合蝴蝶图
6、运算规律,会占用大量中间存储单元。而后两种则无须中间存储单元,但需要倒一次序。权衡利弊,当采样点较多时还是采用后两种方式好,多一次倒序运算对现在的高性能计算机而言并不是什么负担。下面代码用于对原始采样序列的时间抽选奇偶分解工作,其中A、N分别表示指向采样序列复数数组的指针和序列的长度。intNV2=N/2;intNM1=N-1;intI,J,K=0;ComplexT;/用于中介的复数变量TI=J=1;while(I=NM1)if(IJ)T=AJ-1;/将AJ-1的内容和AI-1的内容互换,借助于中间变量TAJ-1=AI-1;AI-1=T;K=NV2;while(KJ)J-=K;K/=2;J+=
7、K;I+;(三)时域信号的频谱分析首先要将从外设输入或采集的时域波形数据经抽样量化后,通过CFile类的Open()、Read()等成员函数将其读取到缓存中,并将其转化为复变量存放于复变量数组A中,同时需要验证以下数据量的长度是否为2的整数次幂,如若不是则必须用0来补齐,否则无法用蝴蝶图进行分解运算。下面代码用于完成对原始采样时域序列的快速傅立叶变换,A、M分别表示指向原始采样数据数组的指针和序列长度的2的整数次幂:ComplexU,W,T;intLE,LE1,I,J,IP;intN=(int)pow(2,M);/在此采用的是时间抽选奇偶分解方式,所以在参加运算前首先要对时间序列进行倒序Rev
8、erseOrder(A,N);intL=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方法将离散数据按检测顺序连接成线段,即可将波形显示在图片框中。但该方法显示波形速度较慢,不适合高速显示的应用。2.2WindowsAPI函
10、数显示波形在VB中两点间连线的另一种方法是用WindowsAPI函数,Win32API提供了以下两个函数,联合使用可实现波形的快速显示,经过测试,显示速度比使用Line方法快70%以上。LineTo函数:函数功能:画出由数组定义的点连接的一系列线段。函数原型:BOOLLineTo(HDChdc,intnXEnd,intnYEnd);参数:hdc:设备环境句柄。nXEnd:定义线段终点的X坐标。nYEnd:定义线段终点的Y坐标。返回值:若函数调用成功,则返回非0值;若函数调用失败,则返回值为0。MoveToEx函数:函数功能:将当前位置更新为指定的点,并有选择的返回原先的位置。函数原型:BOOL
11、MoveToEx(HDChdc,intX,intY,LPPOINTlpPoint);参数:hdc:设备环境句柄。X:定义新位置的X坐标(逻辑坐标)。Y:定义新位置的Y坐标(逻辑坐标)。lpPoint:指向一个POINT结构,结构中存放原先的位置。若此参数为NULL,则不返回原先的位置返回值:若函数调用成功,则返回非0值;若函数调用失败,则返回值为0。在连接线段时,首先将检测数据放入一个数组中,用MoveToEx函数定位画线的起始点坐标,然后用LineTo函数画出起始点至下一个点之间的线段,再用MoveToEx将画线的起始点定位到下一个点,继续用LineTo函数画线,如此循环,即可将离散点连接成
12、波形。例:zz=MoveToEx(Picture1.hdc,i,Mwave(i),LpPoint1)zz=LineTo(Picture1.hdc,i,Mwave(i+1)实时波形显示界面通常计算机需要接收外部实时发送的数据并用十分形象的方式显示出来。例如柱状图、饼图等等。本应用程序则采用波形的形式显示,并接将之设计为可以接收多路数据的波形显示界面。本应用程序在VS2008环境下调试通过,源码下载连接如下点击该图放大上图中显示了两路波形即三角波和正弦波,当然这两路波形是由程序计算出来的并不是从外部接收的。实际工作中则可以配合串口通讯设备接收它发来的数据并显示出来。二.波形控件类介绍本程序的实现主
13、要依赖于那个波形显示控件。从下载连接那里可以下载该源码,里面的文件中,2DPushGraph.h和2DPushGraph.cpp即是该控件的类的定义文件和实现文件。分析之后得到该类的一些信息。其成员函数包括:程序代码:COLORREFm_crTextColor;/标签文字颜色COLORREFm_crBGColor;/背景颜色COLORREFm_crGridColor;/栅格颜色CStringm_strMaxLabel;/最大值之处的标签CStringm_strMinLabel;/最小值之处的标签boolm_bShowMinMax;/最小最大值显示boolm_bShowGrid;/是否显示栅格b
14、oolm_bStylesModified;/是否被修改intm_nMoveOffset;/偏移intm_nMaxCoords;/最大缓冲intm_nMaxPeek;/显示数据的最大值intm_nMinPeek;/显示数据的最小值intm_nGridSize;/栅格间距intm_nPeekOffset;/峰值偏移其实现方法包括:程序代码:boolCreateFromStatic(UINTnStaticID,CWnd*pParent);/该方法用于创建控件实例,通常在对话框初始化函数中调用,nStaticID为控件ID,pPatent为创建在那个窗口中的ID。LPCTSTRGetLabelForM
15、ax()const;/获取最大值处的标签LPCTSTRGetLabelForMin()const;/获取最大值处的标签COLORREFGetBGColor()const;/获得背景色COLORREFGetGridColor()const;/获得栅格颜色voidSetBGColor(COLORREFcrColor);voidSetGridColor(COLORREFcrColor);voidSetTextColor(COLORREFcrColor);COLORREFGetLineColor(UINTuiLineID);/该方法用于获得ID号为uiLineID的波形的线条颜色,因为要在波形控件中画
16、线必须先调用boolAddLine(UINTuiLineID,COLORREFcrColor);创建一个波形,ID号为uiLineID。boolSetLineColor(COLORREFcrColor,UINTuiLineID);COLORREFGetTextColor()const;/获得标签文本的颜色intGetGridSize()const;/获得栅格间隔voidSetGridSize(unsignedshortusWidthAndHeight);intGetMaxPeek()const;/获得最大值纵坐标intGetMinPeek()const;/获得最小值纵坐标voidSetPeek
17、Range(intnMin,intnMax);voidSetMaxPeek(intnMax);voidSetMinPeek(intnMin);unsignedshortGetInterval()const;/获得横坐标间距voidSetInterval(unsignedshortusInterval);voidSetLabelForMax(LPCTSTRlpszLabel);voidSetLabelForMin(LPCTSTRlpszLabel);boolAddLine(UINTuiLineID,COLORREFcrColor);/该方法用于在该控件上创建一个波形。voidRemoveLine
18、(UINTuiLineID);/清除ID号为uiLineID的波形boolPush(intnMagnitude,UINTuiLineID);/在ID号为uiLineID号的波形上添加一个数据点,nMagnitude为幅度voidShowAsBar(UINTuiLineID,boolbAsBar);/柱状图显示voidUpdate();/更新三.应用编程运用该控件在VS2008环境下的编程步骤如下:1.建立一个对话框的MFC工程,在对话框上按照上图所示的界面布置控件。其中波形控件那里布置一个PictureControl控件将其ModalFrame和Type均属性设置为true,其他均设置为Fal
19、se。注意给PictureControl取的ID!后面编程将会用到。2.将波形控件类的定义文件和实现文件拷贝至你的工程目录下。但这实际上并没有将该类真正添加到你的工程下,需手动添加类。常规操作,不详述。3.在对话框的定义和实现文件中分别添加如下代码:程序代码:#include2DPushGraph.h4.在对话框定义文件中(我给的供下载的例程中的是DataRealTimeDlg.h这个文件)中定义一个该控件类的变量:程序代码:private:C2DPushGraphm_PushGraph;5.在对话框的实现文件中(我给的供下载的例程中的是DataRealTimeDlg.cpp这个文件)的对话框
20、初始化函数中添加如下代码:程序代码:m_PushGraph.CreateFromStatic(IDC_REALCTRL,this);/这个IDC_REALCTRL即是那个PictureControl控件的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
21、号为m_sin,一个三角波形,ID号为m_tra。不过运行之后并没有数据点绘制出来。那是应为还没有调用boolPush(intnMagnitude,UINTuiLineID)函数添加数据点。试着添加几个数据点再运行即可观察到波形。7.还有一些控件的响应代码看看那个下载的例程吧。四.结束语成功了吧,AnyProblem,Contactmeplease!控件设计NobisStatusChart-野比的状态波形图控件从构思到实现NobisStatusChart-野比的状态波形图控件从构思到实现野比著源程序下载:Demo控件背景目前比较流行的WinForm程序设计都会提供形象的可视化数据流动记录功能,
22、如FlashGet及其衍生软件的悬浮窗网速监视图,Windows任务管理器的CPU、内存使用图等。构思为了在我们自己的程序中实现这种效果,就需要研究、分析它们的原理,掌握其规律,然后加以实现。很明显,从软件可重用性以及各种随之而来的好处考虑,我们要求将这个“波形显示”效果做成一个控件(Control)。分析还是以FlashGet的悬浮窗和Windows任务管理器作为研究对象。仔细观察它们的工作方式,发现它们有以下的共同点:在右边更新当前的波形值 更新后的波形不消失,而是整体向左平移 可以设置波形颜色、更新速度等 而通过深入研究,发现二者不同点如下:不同的显示方式,有曲线显示和直方图显示 有无定
23、位网格 各部分颜色可自定义 设计通过分析,可以决定如下:凡是二者共同点,加以重点实现;凡二者不同之处,通过设置属性(Property)进行更改。最后绘制时,基于所设置的属性,使用共同方法加以实现。因此自定义属性如下:BackColor(重写基类属性) Enabled(重写) ForeColor(重写) GridColor网格颜色 GridHeight网格每格高度 GridShiftting是否平移网格 GridWidth网格每格宽度 Interval波形刷新间隔(单位:毫秒) Mode波形显示方式(曲线/直方图) Range数值范围 ShifttingIncrement向左平移增量 Value
24、当前值控件因为要定时更新,因此具有一个内部的Timer对其进行定时,其Interval由控件的Interval属性指定。对于此自定义控件,需要每次更新时在其OnPaint()事件中对整个控件进行绘制。绘制顺序为:背景-网格-波形,如此保证所有部分均正确画出且无遮挡。从波形看,很明显,我们需要一个长度至少等于波形控件宽度的数组来存放每时刻波形的值,因此可以确定这个数组是和控件绘图画布宽度一致的。算法绘图最重要的是算法部分,如何计算如网格位置,如何将图形整体平移,如何设置波形值是本控件的重点和难点部分。计算网格位置,以上面的ShifttingIncrement为offset参数传入/网格数(不计边
25、缘)floatdiv;floatpos=0F;/先画垂直方向/可以少画一根线div=(float)w/(float)gridWidth+1;for(inti=0;i(int)div;i+)pos+=gridWidth;g.DrawLine(penGrid,pos-offset,0,pos-offset,h);/画水平方向div=(float)h/(float)gridHeight;pos=0F;for(inti=0;i(int)div;i+)pos+=gridHeight;g.DrawLine(penGrid,0,pos,w,pos);对于波形,传入其波形值数组作为参数/从0到w绘制intle
26、n=w;/根据绘制方式if(chartMode=StatusChart.ChartMode.Histogram)for(inti=0;ilen;i+)g.DrawLine(p,i,h-vali,i,h);g.DrawLine(p,len,h-vallen-1,len,h);elselen-;for(inti=0;ilen;i+)g.DrawLine(p,i,h-vali,i+1,h-vali+1);len+;g.DrawLine(p,len-1,h-vallen-2,len,h-vallen-1);如何平移,是一个难点,需要在内部定时器的Tick()事件中加以处理/更新网格偏移/只有启用了网格
27、移动才处理if(gridShiftting)iOffset+=gridShifttingIncrement;iOffset%=gridWidth;/更新图形(整体左移)/必须在这里而不能在画图的同时移动,/若在画图中移动,则当画面被遮挡(OnPaint)事件不发生时无法更新intlen=w;for(inti=0;ilen;i+)/判断数组越界if(ilen-1)vali=vali+1;elsevallen-1=currentValue;/break;/vallen=currentValue;Invalidate();最后引发控件的Invalidate()方法使控件重绘自身。效果最后的控件运行效
28、果如下图所示:总结通过设计并实现StatusChart控件,我们研究了波形状态监控的相关现象和原理,复习并使用了控件的设计生成,最后使用GDI+技术对其进行了实现。VC+编程实现对火焰的计算机动态仿真-摘要:本文通过对真实火焰物理特性的分析,建立了火焰动态燃烧的数学模型,并在此数学模型基础之上借助于DirectDraw技术对图形显示的加速,在VC+6.0下对火焰作了效果非常逼真的计算机动态仿真。关键词:火焰;DirectDraw;计算机仿真引言计算机仿真技术的基本原理都是一样的,神秘复杂的核爆同水波、火焰、烟雾等非常平常的自然现象在仿真处理过程中并没有什么太大的区别。都是经历了从实体对象到物理
29、特性的总结,再由此建立数学模型并在数学模型基础之上提出仿真算法,最后通过计算机将其动态仿真出来等一系列步骤。本文以火焰作为仿真对象,通过对热源、热扩散以及对流等特性的分析对其建立了数学模型及仿真算法,为了能充分发挥计算机对图形的硬件加速,使用DirectDraw技术对仿真结果显示进行了加速,使之能逼真、流畅地对火焰的燃烧过程实行动态模拟。简单近似模型设计虽然火焰在自然界是一种极普通的自然现象,但根据流体力学的相关知识,火焰可以表达为一个相当复杂的三维动态流体系统。如要在计算机中对这样一个复杂的流体系统做出精确的仿真将需要有相当庞大复杂的数学模型为基础,而且运算量将非常巨大,在现有的微型计算机中
30、几乎很难保证其动态实时性,这也就失去了仿真的意义。因此,在仿真时应用尽可能简单的模型来实现尽可能逼真的效果。从物理角度分析,要产生火焰,首先要有火源,其次为了产生焰的效果,需要以火源为中心向上、向四周扩散,而且由于在扩散过程中逐渐远离火源,温度会逐渐下降,表现在视觉上就是火焰的冷却变暗。另外,由于火焰的高温使周围空气受热膨胀比重下降,因此会有空气的对流出现,这将把火焰向上吹起,使火焰向四周扩散的距离要远小于向上扩散的距离。基于以上几点认识,可以采取对应的仿真措施:对火源的设置可以用一幅二值位图来标识,非火源以低亮度像素填充、火源点则设以高亮度像素,通过对位图像素值的判别可以断定当前点是否为火源
31、。对于火源的温度高低可用其所在点的亮度来描述;对于火焰扩散的模拟,为尽量减少运算量,在此简单地用某火源点(x,y)及其前后左右邻近四点的均值来近似,即Pixel(x,y)=(Pixel(x,y)+Pixel(x,y-1)+Pixel(x,y+1)+Pixel(x-1,y)+Pixel(x+1,y)/5,虽然该近似算法没有采取正余弦的方法精确,但运算速度极快,而且在后续的实验效果上同采用正余弦的方法几乎没有任何差别;由于在仿真过程中对火焰的温度是通过改变其亮度值来实现的,因此对于扩散过程的冷却可对像素点降低一个固定的亮度值来实现。衰减值的大小需要视所希望火焰冷却速度的快慢而定;对流对火焰产生的直
32、接影响就是使火焰始终保持向上燃烧,因此可通过将当前火焰上滚一至两个像素来加以实现。根据前面描述的仿真运算法则,可将火焰的扩散和对流融合在一起实现,这将在一定程度上减少运算量,使产生的火焰在视觉上更加真实。实现上述近似模型的伪代码可大致设计如下:ARRAY_OF_BYTES:buffer1(xsize*ysize),buffer2(xsize*ysize)While(TRUE)for(y=1;yYSIZE-2;Y+)for(x=1;xXSIZE-2;X+)n1=buffer1(x+1,y)/读取4相临像素值n2=buffer1(x-1,y)n3=buffer1(x,y+1)n4=buffer1(x,y-1)p=(n1+n2+n3+n4+p)/5);/四临像素均值p=p-c;/同一固定冷却衰减值相减if(p127ThenAscW会把二进制的中文双字节字符高位和低位反转,所以要先把中文的高低位反转ccc=ccc&Chr(AscW(MidB(binstr,i+1,1)&clow)skipflag=1Elseccc=ccc&Chr(AscB(clow)EndIfElseskipflag=0EndIfNextEndI