多线程VC++和Matlab混编在信号采集和处...

多线程VC++和Matlab混编在信号采集和处理中的应用2010-08-14 15:51:59
Visual C ++自诞生以来,一直是 Windows 环境下最主要的应用开发系统,利用 Visual C++ 开发系统可以完成各种应用程序的开发,从底层软件直到上层直接面向用户的软件都可以用 Visual C ++来开发,而且 Visual C ++强大的调试功能也为大型复杂软件的开发提供了有效的排错手段 [1] ,但是其数值计算能力和二、三维图形显示方面却不理想。 Matlab 是由 MathWorks 公司于 1984 年推出的一套数值计算软件,其功能十分强大,可以实现数值分析、优化、统计、偏微分方程数值解、自动控制、信号处理、图像处理等若干个领域的计算和图形显示功能 [2] 。但是, Matlab 在实现人机对话、数据的传输方面却不是很方便,将两者结合起 来,取长补短,发挥两种语言的优势,是开发人员研究的一个方向。 1 VC 与 Matlab 混合编程的方法: Visual C ++和 Matlab 混合编程有多种方法 [3-7] ,其中最主要的有以下三种方法: 1.1 在 VC ++中利用 Matlab Engine 函数 Matlab Engine 函数库是 MathWorks 公司提供的一组函数库,它提供了一种在用户程序进程中与独立的 Matlab 进程通讯的方法,它包括了和 Matlab 进行交互所必需的全部功能,用户不用去关心 Matlab Engine 是如何实现的,只需利用这些函数,开发者就可以方便的操纵 Matlab 完成所需的功能。具体应用中往往在 Visual C ++中设计程序框架,以编译的程序作为前端客户机,通过调用 Matlab Engine 在后台与 Matlab 服务器建立连接,实现动态通讯。 1.2 利用 Matlab 丰富的数学函数库 Matlab 中包含内容丰富的数学库函数,同时它还提供了 C 语言和 C ++语言的数学函数接口,从 Matab5.1 版本开始, MathWorks 公司推出了一系列的 Matlab 自带编译器来解决 Matlab 与 C ++接口问题, LCC 编译器可以将 Matlab 的 C/C++ 数学库编译成 VC ++能识别的代码嵌入 VC ++环境,用户可以方便的在 VC ++的 IDE (集成开发环境)中调用这些代码。 1.3 利用 Matcom Matcom 是 MathTools 公司推出的一个能将 M 文件转化成相同功能 C ++代码的工具,是为 Matlab 中的 M 文件进行高效解释和调试的集成开发环境, Matcom 编译 M 文件,先将 M 文件按照与 Matcom 的 cpp 库的对应关系,翻译为 cpp 源代码,然后用对应版本的 C 编译器将 .cpp 文件编译成相应的 .exe 文件。 本文采用的是第一种方法,利用 VC++ 强大的可视化功能编辑应用程序对话框,方便对采集卡的控制,采集数据,在 VC ++环境中调用 Matlab Engine 函数,对采集到的数据进行处理,并显示处理后的图形。 2 本文所用采集卡简介 [8] : 本文采用 UA302 型 USB 采集卡,这种采集卡具有 16bit 的分辨率,满量程时精度优于 0.02 %,最高采样频率可达 100KHz ,输入通道为 16 或 32 模入通道,可以采用定时器触发和软件触发两种触发方式。 UA302 采集卡可以使用各种 Windows 编程工具编程,专用的动态链接库 UA300.DLL 提供了简洁高效的采集和控制函数,支持 UA302 采集卡的各种功能,用户可简单方便的调用这些函数完成各种采集工作。本文要用到的采集卡函数: OpenUA300 打开 UA302 设备 CloseUA300 关闭 UA302 设备 minit 单或多通道多点采集初始化(第一种方式) readdata 单或多通道多点采集(第一种方式) 3 混合编程实例: 3.1 用 VC ++ 6.0 编写如图 1 所示的基于对话框的应用程序界面 对话框上的 Button 控件用来控制采集卡采集信号并对其进行处理,以灰色显示的按钮在程序运行时不可用, Picture 控件用来显示采集到的信号。 图 1 应用程序界面 3.2 本文用到的 MatlabEngine 函数: engopen 打开 Matlab 引擎 engcolse 关闭 Matlab 引擎 mxCreateDoubleMatrix 创建双精度数据类型的 Matlab 矩阵 engPutVariable 向 Matlab 引擎传输一个矩阵 engOutputBuffer 创建字符缓冲区以获取 Matlab 的文本输出 engEvalString 执行 Matlab 命令 3.3 程序的具体实现: 本程序使用了多线程编程技术,除了主线程外采用了两个辅线程,一个用来采集信号数据,另一个用来显示采集到的信号。 采集数据线程程序: UINT AdoptDataThreadProc(LPVOID pParam)// 采集数据线程函数,该线程不能停否则会有漏点 { // 初始化端口 , 用于单次采样 minit(husb,0,1,1); readdata(husb,singleaddata,6000000/SampFrequency,1024); return 1; } 绘制波形线程程序: UINT DrawThreadProc(LPVOID pParam) // 该线程负责绘制波形 { do { CDC *pDC=pWnd->GetWindowDC(); CBitmap *m_pBitmap; CDC *m_pdcmem; m_pdcmem=new CDC; m_pBitmap=new CBitmap; m_pdcmem->CreateCompatibleDC(pDC); m_pBitmap->CreateCompatibleBitmap(pDC,rect1.right,rect1.bottom); CBitmap* poldbitmap=m_pdcmem->SelectObject(m_pBitmap); NCCDraw(m_pdcmem,1); Sleep(100); pDC->BitBlt(0,0,rect1.Width(),rect1.Height(),m_pdcmem,0,0,SRCCOPY); m_pdcmem->SelectObject(poldbitmap); Sleep(1);// 可有可无 delete m_pBitmap; delete m_pdcmem; pWnd->ReleaseDC(pDC); }while(1); return 1; } 这两个线程在单击 “ 采集 ” 按钮时被启用,程序如下: void CVcMatlabDlg::OnBtnAdoptData() // 首先打开断端口,然后再开线程用来采集 { husb=OpenUA300(); AdoptDataThread=AfxBeginThread(AdoptDataThreadProc,0,THREAD_PRIORITY_NORMAL); DrawThread=AfxBeginThread(DrawThreadProc,0,THREAD_PRIORITY_NORMAL); GetDlgItem(IDC_BTN_ADOPT)->EnableWindow(FALSE); GetDlgItem(IDC_BTN_STOP)->EnableWindow(TRUE); } 用上面的程序采集频率为 1000Hz 的正弦信号后的图形如图 2 : 采集到的数据在单击 “FFT” 按钮时被处理, Matlab Engine 就是在这个函数里被调用的,程序如下: 图 2 采集到的正弦信号 void CVcMatlabDlg::OnBtnFft() { // TODO: Add your control notification handler code here Engine *ep; mxArray *X=NULL; mxArray *Num=NULL; mxArray *Frequency=NULL; char buffer[1024]; if (!(ep=engOpen("\0"))) // 打开 Matlab 引擎,建立与本地 Matlab 的连接 { AfxMessageBox("Can't start MATLAB engine!"); exit(-1); } // 参与 matlab 运算的参数都是矩阵形式,因而要将参数转换为 Matlab 可接收的矩阵形式 X=mxCreateDoubleMatrix(1,1024,mxREAL); Num=mxCreateDoubleMatrix(1,1,mxREAL); Frequency=mxCreateDoubleMatrix(1,1,mxREAL); double var1=(double)SampNum;// 强制转换 SamNum 和 SampFrequency 从 short 到 double double var2=(double)SampFrequency; memcpy((double*)mxGetPr(Num),(double*)&var1,sizeof(double)); memcpy((double*)mxGetPr(Frequency),(double*)&var2,sizeof(double)); double var3[1024]; for (int i=0;i<1024;i++) { var3=(double)(singleaddata/1); } memcpy((char*)mxGetPr(X),(char*)var3,1024*sizeof(double)); // 将转换的参数放入引擎中,可在 Matlab commond 窗口下察看参数值 engPutVariable(ep, "X", X); engPutVariable(ep, "Num",Num); engPutVariable(ep, "Frequency", Frequency); // 下面开始执行 MATLAB 命令 engEvalString(ep,"pxx=fft(X,Num);");//engEvalString() 函数是在 vc 中执行 matlab 函数 engOutputBuffer(ep,buffer,1024); engEvalString(ep,"ff1=-Frequency/2:1/Num*Frequency:Frequency/2-Frequency/Num;"); engEvalString(ep,"subplot(2,1,1);"); engEvalString(ep,"plot(ff1,fftshift(abs(pxx)));"); engEvalString(ep,"title(' 信号频谱图 ')"); engEvalString(ep,"xlabel(' 频率 /Hz')"); engEvalString(ep,"ylabel(' 幅值谱 ')"); engEvalString(ep,"p=pxx.*conj(pxx)/1024"); engEvalString(ep,"ff2=Frequency*(0:511)/1024;"); engEvalString(ep,"subplot(2,1,2);"); engEvalString(ep,"plot(ff2,p(1:512));");// 利用引擎画图 engEvalString(ep,"title(' 信号功率谱图 ')"); engEvalString(ep,"xlabel(' 频率 /Hz')"); engEvalString(ep,"ylabel(' 功率谱密度 ')"); mxDestroyArray(X);// 释放内存 mxDestroyArray(Num); mxDestroyArray(Frequency); engClose(ep);// 关闭引擎,图片随之关闭 GetDlgItem(IDC_BTN_FFT)->EnableWindow(FALSE); } 编译运行程序后,对图 2 所示的正弦信号进行处理,得到的图形如图 3 : 图 3 分析后的频域波形 4 结束语 在 Visual C ++ 中利用多线程编程可以同时采集和显示采集卡采集到的信号,防止采集过程中的掉点;调用 Matlab Engine 函数可以发挥两者的优势,利用 Visual C ++强大可视化功能,方便的对采集卡进行控制,利用 Matlab Engine 强大的数据处理能力,对采集到的数据进行数字信号处理,并可以方便的显示处理后的结果。
Tags: 

延伸阅读

最新评论

发表评论