多线程VC++和Matlab混编在信号采集和处理中的应用

 要:在 信号采集和处理过程中,Visual C++存在数据处理和结果显示方面的不足,Matlab存在可视化和数据采集方面的不足,本文在Visual C++环境下调用Matlab Engine函数,有效地解决了这两方面的问题;采用多线程编程技术,同时采集和显示信号,有效地防止了采样过程中的掉点。利用本方法对UA302型采集 卡采集到的信号进行处理,得到了满意的结果。

Singal Sampling and Processing Based on Mixed Programming with Multi-Thread VC++ and Matlab

Li Ning,Qin Shuren,Wu Ying

(Test Center, Chongqing University,Chongqing,400044,China)

AbstractIn signal sampling and processing system, VC shows inefficiency in data procession and results demonstration, while Matlab shows its inefficiency in virtualization and data acquisition. In this paper, a mixed programming used by invoking the Matlab engine in VC environment, effectively solved two problems. Multi-thread technology is developed to realize the synchronization of signal sampling and demonstration, and prevent the signal gathering from missing sample spots. An example of an sine wave acquired from UA302 using this using this technique is also introduced in the paper,which has been proved to be feasible in practice.

Key WordsVisual C++,Matlab Engine,Multi-thread technology,UA302

 

 

0引言

Visual C++自诞生以来,一直是Windows环境下最主要的应用开发系统,利用Visual C++开发系统可以完成各种应用程序的开发,从底层软件直到上层直接面向用户的软件都可以用Visual C++来开发,而且Visual C++强大的调试功能也为大型复杂软件的开发提供了有效的排错手段[1],但是其数值计算能力和二、三维图形显示方面却不理想。Matlab是由MathWorks公司于1984年推出的一套数值计算软件,其功能十分强大,可以实现数值分析、优化、统计、偏微分方程数值解、自动控制、信号处理、图像处理等若干个领域的计算和图形显示功能[2]。但是,Matlab在实现人机对话、数据的传输方面却不是很方便,将两者结合起

来,取长补短,发挥两种语言的优势,是开发人员研究的一个方向。

1 VCMatlab混合编程的方法:

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函数,对采集到的数据进行处理,并显示处理后的图形。

本文所用采集卡简介[8]

本 文采用UA302型USB采集卡,这种采集卡具有16bit的分辨率,满量程时精度优于0.02%,最高采样频率可达100KHz,输入通道为16或32 模入通道,可以采用定时器触发和软件触发两种触发方式。UA302采集卡可以使用各种Windows编程工具编程,专用的动态链接库UA300.DLL提 供了简洁高效的采集和控制函数,支持UA302采集卡的各种功能,用户可简单方便的调用这些函数完成各种采集工作。本文要用到的采集卡函数:

OpenUA300 打开UA302设备

CloseUA300 关闭UA302设备

minit 单或多通道多点采集初始化(第一种方式)

readdata 单或多通道多点采集(第一种方式)

混合编程实例:

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[i]=(double)(singleaddata[i]/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 分析后的频域波形

结束语

在Visual C ++中利用多线程编程可以同时采集和显示采集卡采集到的信号,防止采集过程中的掉点;调用Matlab Engine函数可以发挥两者的优势,利用Visual C++强大可视化功能,方便的对采集卡进行控制,利用Matlab Engine强大的数据处理能力,对采集到的数据进行数字信号处理,并可以方便的显示处理后的结果。

 

参考文献:

[1]David J.Kruglinski著.潘爱民,王国印译. Visual C++技术内幕[M]. 北京:清华大学出版社,1998

[2]董长虹主编. Matlab信号处理及应用[M]. 北京:国防工业出版社,2005.1

[3] 何晓涛,于春田. VC调用Matlab的方法[J].河北科技大学学报,2003,24(1):35-39

[4] 王安红,孙志毅. 一种VC++与Matlab混合编程的实现方法[J]. 计算机应用与软件,2003,20(6):12-13,77

[5]高崇明. VC++6.0与Matlab混合编程技术的原理与实现[J]. 无线电工程,2000,30(2):53-56

[6]肖永韧. VC与Matlab混合编程之DLL实现方法[J]. 计算机工程与应用,2001(13):174-176

[7]4亓波. 实现VC++6.0与Matlab的混合编程[J]. 电脑编程技巧与维护,2000(12):62-64

[8] UA302/H型A/D采集器使用说明

 

 

基金项目:重庆市重点科技项目(合同号:9293)

作者简介:李宁, 男,1980年生,重庆大学机械工程学院博士研究生,研究方向:智能测试及虚拟仪器技术

你可能感兴趣的:(matlab)