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

简介:

引言

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采集器使用说明。

本文转自博客园知识天地的博客,原文链接:多线程VC++和Matlab混编在信号采集和处理中的应用,如需转载请自行联系原博主。


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
相关文章
|
2月前
|
存储 SQL 缓存
揭秘Java并发核心:深度剖析Java内存模型(JMM)与Volatile关键字的魔法底层,让你的多线程应用无懈可击
【8月更文挑战第4天】Java内存模型(JMM)是Java并发的核心,定义了多线程环境中变量的访问规则,确保原子性、可见性和有序性。JMM区分了主内存与工作内存,以提高性能但可能引入可见性问题。Volatile关键字确保变量的可见性和有序性,其作用于读写操作中插入内存屏障,避免缓存一致性问题。例如,在DCL单例模式中使用Volatile确保实例化过程的可见性。Volatile依赖内存屏障和缓存一致性协议,但不保证原子性,需与其他同步机制配合使用以构建安全的并发程序。
61 0
|
11天前
|
负载均衡 Java 调度
探索Python的并发编程:线程与进程的比较与应用
本文旨在深入探讨Python中的并发编程,重点比较线程与进程的异同、适用场景及实现方法。通过分析GIL对线程并发的影响,以及进程间通信的成本,我们将揭示何时选择线程或进程更为合理。同时,文章将提供实用的代码示例,帮助读者更好地理解并运用这些概念,以提升多任务处理的效率和性能。
|
13天前
|
Java 开发者
Java中的多线程基础与应用
【9月更文挑战第22天】在Java的世界中,多线程是一块基石,它支撑着现代并发编程的大厦。本文将深入浅出地介绍Java中多线程的基本概念、创建方法以及常见的应用场景,帮助读者理解并掌握这一核心技术。
|
16天前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
38 5
|
25天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
22天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
2月前
|
Java 数据库
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
这篇文章通过一个电商商品详情页的实战案例,展示了如何使用`CompletableFuture`进行异步编排,以解决在不同数据库表中查询商品信息的问题,并提供了详细的代码实现和遇到问题(如图片未显示)的解决方案。
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
|
5天前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
16 0
|
1月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
6天前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
6 0
下一篇
无影云桌面