exponenta event banner

Создание кода C из кода MATLAB

MATLAB ® Coder™ генерирует высокооптимизированный код ANSI C и C++ из функций и системных объектов в Communications Toolbox™. Этот код можно развернуть в различных приложениях. В рабочем процессе, описанном в этом разделе, используются функции DSP System Toolbox™, но тот же рабочий процесс применим для Communications Toolbox.

В этом примере генерируется код 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). В этом примере показан рабочий процесс с использованием codegen функция. Дополнительные сведения о рабочем процессе приложения см. в разделе Создание кода C с помощью приложения кодера MATLAB (MATLAB Coder).

Настройка компилятора

Первым шагом является настройка поддерживаемого компилятора Си. MATLAB Coder автоматически находит и использует поддерживаемый установленный компилятор. Компилятор по умолчанию можно изменить с помощью mex -setup. Дополнительные сведения см. в разделе Изменение компилятора по умолчанию. Текущий список поддерживаемых компиляторов см. в разделе Поддерживаемые и совместимые компиляторы.

Разбейте вычислительную часть алгоритма на функцию MATLAB

Для генерации кода C точкой входа должна быть функция. Не требуется создавать код для всего приложения MATLAB. Если у вас есть определенные части, которые интенсивно вычисляются, создайте код из этих частей, чтобы ускорить алгоритм. Электрический жгут или драйвер, вызывающий эту функцию MATLAB, не должен генерировать код. Электрический жгут запускается в MATLAB и может содержать средства визуализации и другие средства проверки, которые фактически не являются частью тестируемой системы. Например, в примере конструирования синусоидального сигнала с использованием коэффициентов БПФ высокой энергии plot функции строят график входного сигнала и восстановленного сигнала. plot не поддерживается для создания кода и должен оставаться в кабельном жгуте. Чтобы создать код из электрического жгута, содержащего инструменты визуализации, перезаписайте электрический жгут как функцию и объявите функции визуализации как внешние функции с помощью coder.extrinsic (Кодер MATLAB). Для запуска созданного кода, содержащего внешние функции, на компьютере должен быть установлен MATLAB.

Код MATLAB в for цикл, который восстанавливает исходный сигнал с использованием высокоэнергетических коэффициентов БПФ, является вычислительно интенсивной частью этого алгоритма. Ускорить for цикл путем перемещения этой вычислительной части в свою собственную функцию, GenerateSignalWithHighEnergyFFTCoeffs.m.

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 для создания кода.

Проверка проблем во время проектирования

Первым шагом является устранение неподдерживаемых конструкций и проверка на наличие проблем создания кода. Список функций Communications Toolbox, поддерживаемых кодером MATLAB, см. в разделе Функции и системные объекты, поддерживаемые для генерации кода C. Список поддерживаемых языковых конструкций см. в разделе Языковые функции MATLAB, поддерживаемые для генерации кода C/C + + (кодер MATLAB).

Анализатор кода обнаруживает проблемы кодирования во время проектирования при вводе кода. Чтобы включить анализатор кода, необходимо добавить %#codegen pragma к файлу MATLAB.

Инструмент готовности к созданию кода отображает код MATLAB для функций, которые не поддерживаются для создания кода. Одним из способов доступа к этому инструменту является щелчок правой кнопкой мыши на файле MATLAB в текущей папке. Запуск инструмента создания кода на GenerateSignalWithHighEnergyFFTCoeffs.m не находит проблем.

Проверка проблем во время создания кода

Перед созданием кода C убедитесь, что код MATLAB успешно генерирует функцию MEX. codegen Команда (MATLAB Coder), используемая для генерации функции MEX, обнаруживает любые ошибки, которые не позволяют коду быть подходящим для генерации кода.

Управляемый codegen на GenerateSignalWithHighEnergyFFTCoeffs.m функция.

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 заключается не только в обнаружении проблем генерации кода и времени выполнения, но и в ускорении определенных частей алгоритма. Пример см. в разделе Ускорение алгоритма обработки сигналов в 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 кадров входного и восстановленного сигнала и количество коэффициентов БПФ, используемых для восстановления каждого кадра сигнала. Записать все эти данные в двоичный файл с именем data.bin с использованием dsp.BinaryFileWriter object™ системы. Этот пример регистрирует количество коэффициентов, которые являются скалярными значениями, как первый элемент каждого кадра входного сигнала и восстановленного сигнала. Записываемые данные имеют размер кадра M = L + 1 и имеют формат, похожий на этот рисунок.

N - количество коэффициентов БПФ, которые представляют 99,99% энергии сигнала текущего входного кадра. Метаданные двоичного файла определяют эту информацию. Отпустите устройство записи двоичных файлов и закройте двоичный файл в конце.

Обновленная функция электрического жгута, 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;
}

Дополнительные сведения о создании основной функции см. в разделе Создание автономных исполняемых файлов C/C + + из кода MATLAB (кодер MATLAB).

Установите CustomInclude свойства объекта конфигурации для указания местоположения основного файла. В этом примере расположение является текущей папкой.

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

Создайте исполняемый файл C, выполнив следующую команду в командной строке MATLAB:

codegen -config cfg -report reconstructSignalTestbench

Кодер MATLAB компилирует и связывает основную функцию с кодом C, который он генерирует из reconstructSignalTestbench.m.

Если вы используете Windows, вы можете видеть, что reconstructSignalTestbench.exe создается в текущей папке. При использовании Linux созданный исполняемый файл не имеет .exe расширение.

Чтение и проверка данных двоичного файла

При запуске исполняемого файла создается двоичный файл, data.bin, в текущем каталоге и записывает входной восстановленный сигнал и количество коэффициентов БПФ, используемых для восстановления сигнала.

!reconstructSignalTestbench

Вы можете прочитать эти данные из двоичного файла с помощью dsp.BinaryFileReader объект. Для проверки правильности записи данных считывайте данные из двоичного файла в MATLAB и сравнивайте выходные данные с переменными. InputAll и ReSignalAll.

Прототип заголовка должен иметь структуру, аналогичную структуре заголовка, записанной в файл. Считывайте данные как два канала.

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 и второй канал с ReSignalAll.

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

  logical

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

  logical

   1

Результаты точно совпадают, что указывает на успешную операцию записи.

Перенос кода в другую среду разработки

После создания кода из алгоритма MATLAB можно переместить код в другую среду разработки, например, систему или интегрированную среду разработки (IDE), которая не включает MATLAB. Вы можете упаковывать файлы в сжатый файл с помощью packNGo функция в командной строке или опция Пакет в приложении Кодер MATLAB. Пример, иллюстрирующий оба рабочих процесса, см. в разделе Код пакета для других сред разработки (Кодер MATLAB). Для получения дополнительной информации о packNGo опция, см. packNGo в методах RTW.BuildInfo (кодер MATLAB). Сжатый zip-файл можно переместить и распаковать с помощью стандартной утилиты zip. Пример упаковки исполняемого файла, созданного в этом примере, см. в разделе Перемещение кода, созданного из кода MATLAB, в другую среду разработки.

См. также

Функции

Связанные темы

Внешние веб-сайты