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 ファイルに追加しなければなりません。
コード生成の準備状態ツールは、コード生成でサポートされていない機能について、MATLAB コードをスクリーニングします。このツールにアクセスする方法の 1 つとして、現在のフォルダーにある MATLAB ファイルを右クリックします。GenerateSignalWithHighEnergyFFTCoeffs.m
に対してコード生成ツールを実行しても問題は検出されません。
コード生成時の問題の確認
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 になり、次の図のような形式になります。
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を参照してください。
参考
関数
codegen
(MATLAB Coder)
関連するトピック
- Relocate Code Generated from MATLAB Code to Another Development Environment
- What is C Code Generation from MATLAB?
- Simulink モデルからの C コードの生成
- MATLAB Coder アプリを使用した C コードの生成 (MATLAB Coder)
- コマンド ラインでの C コードの生成 (MATLAB Coder)
- コード生成のワークフロー (MATLAB Coder)