基於Matlab Coder將matlab程式碼轉換成c程式碼

wsqyouth發表於2018-12-05

最近在做訊號處理相關的功能,首先在matlab上做模擬,之後使用c/c++實現,然後嘗試使用Matlab Coder做轉換功能。
參考:
基於MATLAB Coder將matlab程式碼轉換成C程式碼
官網視訊講解
matlab呼叫c/c++程式碼
特此感謝!

一、概念及開發流程

MATLAB Coder可以從MATLAB程式碼生成獨立的、可讀性強、可移植的C/C++程式碼。
通俗地說,就是將寫好的matlab功能模組程式碼轉換為c/c++程式碼,然後我們在此基礎上進行改進。
本文開發環境:Win10 64、VS2015、Matlab R2016A。

開發流程:使用MATLAB Coder產生C程式碼的3個步驟:

  1. 開發實現特定演算法功能的MATLAB程式碼及其測試檔案
  2. 檢查MATLAB程式碼的相容性,確保MATLAB程式碼中的語句都能被轉換成C程式碼(有些matlab程式碼語句並不能生成c/c++程式碼,例如matlab中的imread、imshow、plot等函式)
  3. 利用MATLAB Coder生成c/c++程式碼,並在VS2015中驗證。

二、實踐測試

參考的文章中給出了功能模組程式碼與測試程式碼分離的思想,因此本文借鑑這種思想,進行實踐測試。
當然功能模組a.m檔案可以作為b.m檔案的呼叫,在編譯時兩個都要選中進行編譯(可以理解為依賴),不過本文暫時不涉及該功能的實現。

2.1 Matlab功能模組程式碼及其測試程式碼

myfft.m
對輸入的資料去除直流分量並進行FFT。其中,%#codegen用來防止出現警告錯誤

 %#codegen
 function B = myfft(v1,N)
 v1=v1 - mean(v1);
 B = fft(v1,N);

test_main.m
產生資料集並測試

f=100;                  %訊號頻率Hz
Fs=1000;                %取樣頻率
N=256;                  %取樣點數
t=(0:N-1)/Fs;           %取樣時間s
S=1000*sin(2*pi*f*t)+20*randn(size(t));  %訊號取樣值

%B = meanVal(S,N) 
B = myfft(S,N);
f =((-N/2):(N/2-1))*Fs/N;
plot(abs(B))
title('Amplitude Spectrum of S')
xlabel('f (Hz)')
ylabel('|P1(f)|')

matlab輸出:
在這裡插入圖片描述
f = (N-1)Fs/N = 261000/256=100Hz。

2.2 利用MATLAB Coder生成c/c++程式碼
  1. 在命令視窗,輸入mex -setup,選中一個存在的編譯器,筆者這裡自帶了一個c編譯器,讀者也可以切換為c++編譯器(輸入mex -setup C++)。
    在這裡插入圖片描述

  2. 在命令視窗輸入coder(圖形介面),回車,彈出MATLAB Coder Project對話方塊(或在matlab軟體的介面中點選MatLab Coder圖示)。
    (1)在Select模組中選擇我們想要編譯的功能模組myfft.m
    在這裡插入圖片描述
    (2)點選Next
    在這裡插入圖片描述
    上圖紅色方框處也可以繼續新增功能模組的.m檔案。
    (3)點選Next,進入Define Input Types介面。輸入test_main.m測試檔案,點選Autodefine Input Types按鈕,從而使得matlab能自動檢測出功能模組函式的介面變數的維度和型別。
    在這裡插入圖片描述
    (4)點選Next,點選check for issues按鈕。
    在這裡插入圖片描述
    (5)點選Next,點選Generate按鈕,生成了C/C++程式碼。
    在這裡插入圖片描述
    生成成功後如下圖,可以在lib檔案下檢視到該檔案。
    在這裡插入圖片描述

2.3 VS2015驗證

利用VS2015在lib同級目錄下的資料夾libvs中建立工程myfftprj,將所有的.c .h匯入工程。
由於物理路徑的關係,修改當前工程標頭檔案路徑即可。
在這裡插入圖片描述
我們發現工程中輸入例項輸入為空,因此根據test_main.m檔案需要進行修改。

/*
 * Arguments    : double result[256]
 * Return Type  : void
 */
static void argInit_1x256_real_T(double result[256])
{
	int idx1;
	const double pi = 3.141592653589793;
	const double Fs = 1000; //取樣頻率
							/* Loop over the array to initialize each element. */
	for (idx1 = 0; idx1 < 256; idx1++) {
		/* Set the value of the array element.
		Change this value to the value that the application requires. */
		double t = idx1 / Fs;
		result[idx1] = 100 * sin(2 * pi * 100 * t);;//argInit_real_T()
	}
}

同時輸入引數N也要進行適當修正,新增<stdio.h>和getchar()等輔助列印函式。
這裡需要注意的是,生成的c++程式碼輸出變數是根據實際輸出決定的一個結構體型別變數,我們要在理解該結構體的基礎上進行除錯及列印。
比如當輸出複數時,型別為emxArray_creal_T,當輸出實數時,型別為emxArray_real_T
程式碼:

/* Include Files */
#include "../rt_nonfinite.h"
#include "../myfft.h"
#include "main.h"
#include "../myfft_terminate.h"
#include "../myfft_emxAPI.h"
#include "../myfft_initialize.h"

#include <stdio.h>

/* Function Definitions */
void printArr(emxArray_creal_T *arr, int size)
{
	for (int i = 0; i < size; i++)
	{
		  printf("%4d    %8.4f    %8.4f \n", i, (arr->data[i]).re, (arr->data[i]).im);
		//printf("%4d    %8.4f \n", i, arr->data[i]);
	}
}
/* Function Declarations */
static void argInit_1x256_real_T(double result[256]);
static double argInit_real_T(void);
static void main_myfft(void);

/* Function Definitions */

/*
 * Arguments    : double result[256]
 * Return Type  : void
 */
static void argInit_1x256_real_T(double result[256])
{
	int idx1;
	const double pi = 3.141592653589793;
	const double Fs = 1000; //取樣頻率
							/* Loop over the array to initialize each element. */
	for (idx1 = 0; idx1 < 256; idx1++) {
		/* Set the value of the array element.
		Change this value to the value that the application requires. */
		double t = idx1 / Fs;
		result[idx1] = 100 * sin(2 * pi * 100 * t);;//argInit_real_T()
	}
}

/*
 * Arguments    : void
 * Return Type  : double
 */
static double argInit_real_T(void)
{
  return 0.0;
}

/*
 * Arguments    : void
 * Return Type  : void
 */
static void main_myfft(void)
{
  emxArray_creal_T *B;
  double dv0[256];
  emxInitArray_creal_T(&B, 2);

  /* Initialize function 'myfft' input arguments. */
  /* Initialize function input argument 'v1'. */
  /* Call the entry-point 'myfft'. */
  argInit_1x256_real_T(dv0);
  myfft(dv0,256 , B);  //argInit_real_T()
  printArr(B, 256);
  emxDestroyArray_creal_T(B);
}

/*
 * Arguments    : int argc
 *                const char * const argv[]
 * Return Type  : int
 */
int main(int argc, const char * const argv[])
{
  (void)argc;
  (void)argv;

  /* Initialize the application.
     You do not need to do this more than one time. */
  myfft_initialize();

  /* Invoke the entry-point functions.
     You can call entry-point functions multiple times. */
  main_myfft();

  /* Terminate the application.
     You do not need to do this more than one time. */
  myfft_terminate();

  getchar();
  return 0;
}

輸出:
在這裡插入圖片描述

三、Matlab小技巧

  • 不以科學計數法顯示(參考文章
format short g
#include "mex.h"
//使用MEX必須包含的標頭檔案
void mexFunction(int nlhs, mxArray *plhs[], 
                 int nrhs, const mxArray *prhs[]){
    mexPrintf("hello world\n");
}
mex hello.c
hello  %指令碼執行

相關文章