0
引言
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[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
分析后的频域波形
4
结束语
在
Visual C ++
中利用多线程编程可以同时采集和显示采集卡采集到的信号,防止采集过程中的掉点;调用
Matlab Engine
函数可以发挥两者的优势,利用
Visual C
++强大可视化功能,方便的对采集卡进行控制,利用
Matlab Engine
强大的数据处理能力,对采集到的数据进行数字信号处理,并可以方便的显示处理后的结果。