Main Content

MATLAB コードからの C コードの生成

MATLAB® Coder™ は、Communications Toolbox™ の関数および System object から、高度に最適化された ANSI® C および C++ コードを生成します。このコードを幅広いアプリケーションに展開できます。このトピックで説明されているワークフローでは DSP System Toolbox™ の機能を使用していますが、同じワークフローが Communications Toolbox にも適用されます。

この例では、高エネルギー FFT 係数を使用した正弦波信号の作成の例から C コードを生成し、生成されたコードから実行可能ファイルをビルドします。

この例の MATLAB コードを以下に示します。

L = 1020;
Sineobject = dsp.SineWave('SamplesPerFrame',L,...  
'PhaseOffset',10,'SampleRate',44100,'Frequency',1000);
ft = dsp.FFT('FFTImplementation','FFTW');
ift = dsp.IFFT('FFTImplementation','FFTW','ConjugateSymmetricInput',true);
rng(1);
numIter = 1000;
for Iter = 1:numIter
    Sinewave1 = Sineobject();
    Input = Sinewave1 + 0.01*randn(size(Sinewave1));
    FFTCoeff = ft(Input);
    FFTCoeffMagSq = abs(FFTCoeff).^2;
    
    EnergyFreqDomain = (1/L)*sum(FFTCoeffMagSq);
    [FFTCoeffSorted, ind] = sort(((1/L)*FFTCoeffMagSq),1,'descend');
    
    CumFFTCoeffs = cumsum(FFTCoeffSorted);
    EnergyPercent = (CumFFTCoeffs/EnergyFreqDomain)*100;
    Vec = find(EnergyPercent > 99.99);
    FFTCoeffsModified = zeros(L,1);
    FFTCoeffsModified(ind(1:Vec(1))) = FFTCoeff(ind(1:Vec(1)));
    ReconstrSignal = ift(FFTCoeffsModified);
end
max(abs(Input-ReconstrSignal))
plot(Input,'*');
hold on;
plot(ReconstrSignal,'o');
hold off;

生成された実行可能ファイルは MATLAB 環境内で実行できます。また、コードをパッケージ化して、MATLAB がインストールされていない他の開発環境に移動することもできます。MATLAB Coder アプリまたは関数 codegen (MATLAB Coder) を使用してコードを生成できます。この例では、関数 codegen を使用するワークフローを示します。アプリのワークフローの詳細については、MATLAB Coder アプリを使用した C コードの生成 (MATLAB Coder)を参照してください。

コンパイラの設定

最初の手順は、サポートされている C コンパイラを設定することです。MATLAB Coder は、サポートされているインストール済みコンパイラを自動的に探して使用します。mex -setup を使用して既定のコンパイラを変更できます。詳細については、既定のコンパイラの変更を参照してください。現在サポートされているコンパイラの一覧については、Supported and Compatible Compilers を参照してください。

アルゴリズムにおける計算部分を MATLAB 関数として分割

C コードを生成するには、エントリ ポイントを関数にしなければなりません。MATLAB アプリケーション全体のコードを生成する必要はありません。計算量の多い特定の部分がある場合は、アルゴリズムを高速化するためにその部分からコードを生成します。この MATLAB 関数を呼び出すハーネスまたはドライバーは、コードを生成する必要はありません。ハーネスは MATLAB で実行され、テスト対象システムの一部ではない、可視化ツールや他の検証ツールが含まれる場合があります。たとえば、高エネルギー FFT 係数を使用した正弦波信号の作成の例では、関数 plot は入力信号と再構成後の信号をプロットします。plot はコード生成でサポートされていないため、ハーネス内に留めなければなりません。可視化ツールを含むハーネスからコードを生成するには、ハーネスを関数として書き換え、coder.extrinsic (MATLAB Coder) を使用して可視化関数を外部関数として宣言します。外部関数を含む生成コードを実行するには、マシンに MATLAB がインストールされていなければなりません。

高エネルギー FFT 係数を使用して元の信号を再構成する for ループ内の MATLAB コードが、このアルゴリズムの計算量の多い部分です。この計算部分を独自の関数 GenerateSignalWithHighEnergyFFTCoeffs.m に移動して、for ループを高速化します。

L = 1020;
Sineobject = dsp.SineWave('SamplesPerFrame',L,...
    'SampleRate',44100,'Frequency',1000);
rng(1);
numIter = 1000;
for Iter = 1:numIter
    Sinewave1 = Sineobject();
    Input = Sinewave1 + 0.01*randn(size(Sinewave1));
    [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input);
end
max(abs(Input-ReconstrSignal))
figure(1);
plot(Input)
hold on;
plot(ReconstrSignal,'*')
hold off
function [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input)

ft = dsp.FFT('FFTImplementation','FFTW');
ift = dsp.IFFT('FFTImplementation','FFTW','ConjugateSymmetricInput',true);

FFTCoeff = ft(Input);
FFTCoeffMagSq = abs(FFTCoeff).^2;
L = size(Input,1);
EnergyF = (1/L)*sum(FFTCoeffMagSq);
[FFTCoeffSorted, ind] = sort(((1/L)*FFTCoeffMagSq),1,'descend');

CumFFTCoeffs = cumsum(FFTCoeffSorted);
EnergyPercent = (CumFFTCoeffs/EnergyF)*100;
Vec = find(EnergyPercent > 99.99);
FFTCoeffsModified = zeros(L,1);
FFTCoeffsModified(ind(1:Vec(1))) = FFTCoeff(ind(1:Vec(1)));
numCoeff = Vec(1);
ReconstrSignal = ift(FFTCoeffsModified);
end

コード生成に適したコードの作成

コードを生成する前に、コード生成用の MATLAB コードを準備しなければなりません。

設計時の問題の確認

最初の手順は、サポートされていない構造を排除し、コード生成の問題がないかどうかを確認することです。MATLAB Coder によりサポートされている Communications Toolbox の機能の一覧については、Functions and System Objects Supported for C Code Generation を参照してください。サポートされている言語構成の一覧については、C/C++ コード生成でサポートされている MATLAB 言語機能 (MATLAB Coder)を参照してください。

コード アナライザーは、設計時のコード入力中にコーディングの問題を検出します。コード アナライザーを有効にするには、%#codegen プラグマを MATLAB ファイルに追加しなければなりません。

Snapshot of the GeneratSignalWithHighEnergyFFTCoeffs.m file. The snapshot shows the placement of %#codegen pragma at the end of the first line of the function. The first line of the function is function [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input) %#codegen

コード生成の準備状態ツールは、コード生成でサポートされていない機能について、MATLAB コードをスクリーニングします。このツールにアクセスする方法の 1 つとして、現在のフォルダーにある MATLAB ファイルを右クリックします。GenerateSignalWithHighEnergyFFTCoeffs.m に対してコード生成ツールを実行しても問題は検出されません。

Snapshot of code gen readiness tool. The code generation readiness score shows 5 green bars, indicating that the code is now suitable for code generation. This is a preliminary check and it found no issues. Some minor problems might be found if code generation is attempted, but if so, they should be easy to fix.

コード生成時の問題の確認

C コードを生成する前に、MATLAB コードが MEX 関数を正常に生成することを確認します。MEX 関数を生成するために使用される codegen (MATLAB Coder) コマンドは、コード生成に適しているコードを妨げるエラーを検出します。

関数 GenerateSignalWithHighEnergyFFTCoeffs.m に対して codegen を実行します。

codegen -args {Input} GenerateSignalWithHighEnergyFFTCoeffs 

以下のメッセージが MATLAB コマンド プロンプトに表示されます。

??? The left-hand side has been constrained to be non-complex, but the right-hand side 
is complex. To correct this problem, make the right-hand side real using the function 
REAL, or change the initial assignment to the left-hand side variable to be a complex 
value using the COMPLEX function.

Error in ==> GenerateSignalWithHighEnergy Line: 24 Column: 1
Code generation failed: View Error Report
Error using codegen
 
 

このメッセージは変数 FFTCoeffsModified を参照しています。コーダーは、この変数が複素変数として初期化されることを想定しています。この問題を解決するには、変数 FFTCoeffsModified を複素数として初期化します。

FFTCoeffsModified = zeros(L,1)+0i;

関数 codegen を再実行すると、MEX ファイルが .mex 拡張子を使用して現在のフォルダーに正常に生成されます。

codegen -args {Input} GenerateSignalWithHighEnergyFFTCoeffs 

実行時の問題の確認

生成された MEX 関数を実行して、実行時の問題が報告されるかどうかを確認します。これを行うには、ハーネス内で次の行を

[ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input);
ここで
[ReconstrSignalMex,numCoeffMex] = GenerateSignalWithHighEnergyFFTCoeffs_mex(Input);
置き換えます。

ハーネスは次のようになります。

L = 1020;
Sineobject = dsp.SineWave('SamplesPerFrame',L,...
    'SampleRate',44100,'Frequency',1000);
rng(1);
numIter = 1000;
for Iter = 1:numIter
    Sinewave1 = Sineobject();
    Input = Sinewave1 + 0.01*randn(size(Sinewave1));
    [ReconstrSignalMex,numCoeffMex] = GenerateSignalWithHighEnergyFFTCoeffs_mex(Input,L);
end
max(abs(Input-ReconstrSignalMex))
figure(1);
plot(Input)
hold on;
plot(ReconstrSignalMex,'*')
hold off

コードが正常に実行され、実行時のエラーがないことが示されます。

MEX 関数とシミュレーションの比較

ハーネスでは、通常の関数と比較して、MEX 関数の方がはるかに高速に実行されることに注目してください。MEX 関数を生成する理由は、コード生成と実行時の問題を検出するためだけではなく、アルゴリズムの特定の部分を高速化するためでもあります。例については、Signal Processing Algorithm Acceleration in MATLABを参照してください。

また、MEX からの数値出力結果と通常の関数が一致することを確認しなければなりません。関数 GenerateSignalWithHighEnergyFFTCoeffs.m により生成された再構成後の信号と、それに対応する MEX の GenerateSignalWithHighEnergyFFTCoeffs_mex を比較します。

max(abs(ReconstrSignal-ReconstrSignalMex))

ans =

     2.2204e-16

結果は非常によく一致し、コード生成が正常に完了したことが確認できます。

スタンドアロン実行可能ファイルの生成

MATLAB 環境内での生成コードの実行が目的の場合は、MEX 関数をビルド ターゲットにできます。別のアプリケーションへのコード展開が目的の場合は、アプリケーション全体からスタンドアロンの実行可能ファイルを生成します。これを行うには、ハーネスはサブ関数 GenerateSignalWithHighEnergyFFTCoeffs を呼び出す関数でなければなりません。ハーネスを関数として書き換えます。

function reconstructSignalTestbench()
L = 1020;
Sineobject = dsp.SineWave('SamplesPerFrame',L,...
    'SampleRate',44100,'Frequency',1000);
rng(1);
numIter = 1000;
for Iter = 1:numIter
    Sinewave1 = Sineobject();
    Input = Sinewave1 + 0.01*randn(size(Sinewave1));
    [ReconstrSignal,numCoeff] = GenerateSignalWithHighEnergyFFTCoeffs(Input,L);
end

入力信号および再構成後の信号の 1000 フレームすべてと、信号の各フレームを再構成するために使用された FFT 係数の数を記録します。dsp.BinaryFileWriter System object™ を使用して、data.bin という名前のバイナリ ファイルにこのすべてのデータを書き込みます。この例では、入力信号および再構成後の信号の各フレームの最初の要素として、スカラー値である係数の数を記録します。書き込まれるデータのフレーム サイズは M = L + 1 になり、次の図のような形式になります。

Two matrices on both sides of an equal sign. On the left of the equal sign, the first frame is N followed by input signal. The second frame is N followed by reconstructed signal. On the right of the equal sign, the first frame is 'InputAll' signal and the second frame is 'ReSignalAll'.

N は、現在の入力フレームの信号エネルギーの 99.99% を表す FFT 係数の数です。バイナリ ファイルのメタデータはこの情報を指定します。最後にバイナリ ファイル ライターを解放し、バイナリ ファイルを閉じます。

更新されたハーネス関数 reconstructSignalTestbench を以下に示します。

function reconstructSignalTestbench()
L = 1020;
Sineobject = dsp.SineWave('SamplesPerFrame',L,...
    'SampleRate',44100,'Frequency',1000);
header = struct('FirstElemInBothCols','Number of Coefficients',...
    'FirstColumn','Input','SecondColumn','ReconstructedSignal');
bfw = dsp.BinaryFileWriter('data.bin','HeaderStructure',header);
numIter = 1000;

M = L+1;
ReSignalAll = zeros(M*numIter,1);
InputAll = zeros(M*numIter,1);
rng(1);

for Iter = 1 : numIter
    Sinewave1 = Sineobject();
    Input = Sinewave1 + 0.01*randn(size(Sinewave1));
    [ReconstrSignal,numCoeffs] = GenerateSignalWithHighEnergyFFTCoeffs(Input);
    InputAll(((Iter-1)*M)+1:Iter*M) = [numCoeffs;Input];
    ReSignalAll(((Iter-1)*M)+1:Iter*M) = [numCoeffs;ReconstrSignal];
end

bfw([InputAll ReSignalAll]);   
release(bfw);

C 実行可能ファイルを生成するための次の手順は、実行可能ファイル用の coder.config オブジェクトを作成し、このオブジェクトに関数 main.c を提供することです。

cfg =  coder.config('exe');
cfg.CustomSource = 'reconstructSignalTestbench_Main.c';

この例の関数 reconstructSignalTestbench_Main.c がどのようになるかを以下に示します。

/*
** reconstructSignalTestbench_main.c
*
* Copyright 2017 The MathWorks, Inc.
*/
#include <stdio.h>
#include <stdlib.h>

#include "reconstructSignalTestbench_initialize.h"
#include "reconstructSignalTestbench.h"
#include "reconstructSignalTestbench_terminate.h"

int main()
{
    reconstructSignalTestbench_initialize();
    reconstructSignalTestbench();    
    reconstructSignalTestbench_terminate();
    
    return 0;
}

main 関数の作成の詳細については、MATLAB コードからのスタンドアロン C/C++ 実行可能ファイルの生成 (MATLAB Coder)を参照してください。

構成オブジェクトの CustomInclude プロパティを設定して、メイン ファイルの場所を指定します。この例では、この場所は現在のフォルダーです。

cfg.CustomInclude = ['"',pwd,'"'];

MATLAB コマンド プロンプトで次のコマンドを実行して、C 実行可能ファイルを生成します。

codegen -config cfg -report reconstructSignalTestbench

MATLAB Coder は main 関数をコンパイルし、reconstructSignalTestbench.m から生成した C コードとリンクします。

Windows を使用している場合は、現在のフォルダーに reconstructSignalTestbench.exe が生成されます。Linux を使用している場合は、生成された実行可能ファイルには .exe 拡張子は付きません。

バイナリ ファイル データの読み取りと検証

実行可能ファイルを実行すると、現在のディレクトリにバイナリ ファイル data.bin が作成され、入力信号、再構成後の信号、および信号を再構成するために使用された FFT 係数の数が書き込まれます。

!reconstructSignalTestbench

dsp.BinaryFileReader オブジェクトを使用してバイナリ ファイルからこのデータを読み取ることができます。データが正しく書き込まれたことを検証するには、MATLAB のバイナリ ファイルからデータを読み取り、その出力を変数 InputAll および ReSignalAll と比較します。

ヘッダー プロトタイプは、ファイルに書き込まれるヘッダー構造と類似した構造をもつ必要があります。データを 2 つのチャネルとして読み取ります。

M = 1021;
numIter = 1000;
headerPro = struct('FirstElemInBothCols','Number of Coefficients',...
    'FirstColumn','Input','SecondColumn','ReconstructedSignal');
bfr = dsp.BinaryFileReader('data.bin','HeaderStructure',...
headerPro,'SamplesPerFrame',M*numIter,'NumChannels',2);
Data = bfr();

最初のチャネルと InputAll、2 番目のチャネルと ReSignalAll を比較します。

isequal(InputAll,Data(:,1))
ans =

  logical

   1
isequal(ReSignalAll,Data(:,2))
ans =

  logical

   1

結果は厳密に一致し、書き込み操作が成功したことを示しています。

他の開発環境へのコードの移動

MATLAB アルゴリズムからコードを生成したら、MATLAB を含まないシステムや統合開発環境 (IDE) などの、他の開発環境にコードを移動できます。コマンド ラインの関数 packNGo または MATLAB Coder アプリの [パッケージ] オプションを使用して、ファイルを圧縮ファイルにパッケージ化できます。両方のワークフローを示す例については、他の開発環境向けのコードのパッケージ化 (MATLAB Coder)を参照してください。packNGo オプションの詳細については、RTW.BuildInfo メソッド (MATLAB Coder)packNGo を参照してください。標準の zip ユーティリティを使用して zip 圧縮ファイルを移動および解凍できます。この例で生成された実行可能ファイルをパッケージ化する方法の例については、Relocate Code Generated from MATLAB Code to Another Development Environmentを参照してください。

参考

関数

関連するトピック

外部の Web サイト