Этот пример создает три тестов для регулируемого ограничителя скорости и анализирует полученное покрытие модели с помощью API командной строки инструмента Model Coverage.
Simulink ® Subsystem Adjustable Rate Limiter является ограничителем скорости в модели 'slvnvdemo _ ratelim _ обвязка. Он использует три блока switch, чтобы управлять, когда выход должен быть ограничен и тип предела для применения.
Входы генерируются с блоками From Workspace 'gain', 'ring limit' и 'ring limit', которые генерируют кусочно-линейные сигналы. Значения входов определены с шестью переменными, определенными в рабочей области MATLAB®: t_gain, u_gain, t_pos, u_pos, t_neg, и u_neg.
Откройте модель и Подсистему Регулируемого Ограничения Скорости.
modelName = 'slvnvdemo_ratelim_harness'; open_system(modelName); open_system([modelName,'/Adjustable Rate Limiter']);
Первый тест проверяет, что выход соответствует входу, когда входные значения не изменяются быстро. Он использует синусоиду в качестве изменяющегося во времени сигнала и констант для повышения и падения пределов.
t_gain = (0:0.02:2.0)'; u_gain = sin(2*pi*t_gain);
Вычислите минимальное и максимальное изменение времени изменения входа с помощью функции MATLAB diff.
max_change = max(diff(u_gain)) min_change = min(diff(u_gain))
max_change = 0.1253 min_change = -0.1253
Поскольку изменения сигнала намного меньше 1 и намного больше -1, установите пределы скорости 1 и -1. Все переменные хранятся в файле MAT 'within _ lim.mat', который загружается перед симуляцией.
t_pos = [0;2]; u_pos = [1;1]; t_neg = [0;2]; u_neg = [-1;-1]; save('within_lim.mat','t_gain','u_gain','t_pos','u_pos','t_neg','u_neg');
Второй тест дополняет первый случай увеличением усиления, которое превышает предел скорости. Через секунду он увеличивает предел скорости, так что изменения усиления ниже этого предела.
t_gain = [0;2]; u_gain = [0;4]; t_pos = [0;1;1;2]; u_pos = [1;1;5;5]*0.02; t_neg = [0;2]; u_neg = [0;0]; save('rising_gain.mat','t_gain','u_gain','t_pos','u_pos','t_neg','u_neg');
Третий тест является зеркальным изображением второго, при этом увеличение усиления заменяется падением усиления.
t_gain = [0;2]; u_gain = [-0.02;-4.02]; t_pos = [0;2]; u_pos = [0;0]; t_neg = [0;1;1;2]; u_neg = [-1;-1;-5;-5]*0.02; save('falling_gain.mat','t_gain','u_gain','t_pos','u_pos','t_neg','u_neg');
Организация и выполнение тестов осуществляется с помощью
.sim
В этом примере для настройки строения покрытия используется входной объект симуляции.
covSet = Simulink.SimulationInput(modelName); covSet = setModelParameter(covSet,'CovEnable','on'); covSet = setModelParameter(covSet,'CovMetricStructuralLevel','Decision'); covSet = setModelParameter(covSet,'CovSaveSingleToWorkspaceVar','on'); covSet = setModelParameter(covSet,'CovScope','Subsystem'); covSet = setModelParameter(covSet,'CovPath','/Adjustable Rate Limiter'); covSet = setModelParameter(covSet,'StartTime','0.0'); covSet = setModelParameter(covSet,'StopTime','2.0');
Загрузите данные для первого теста, установите имя переменной покрытия и выполните модель с помощью sim
.
load within_lim.mat covSet = setModelParameter(covSet,'CovSaveName','dataObj1'); simOut1 = sim(covSet); dataObj1
dataObj1 = ... cvdata version: (R2021a) id: 1684 type: TEST_DATA test: cvtest object rootID: 1686 checksum: [1x1 struct] modelinfo: [1x1 struct] startTime: 27-Jan-2021 07:27:10 stopTime: 27-Jan-2021 07:27:10 intervalStartTime: 0 intervalStopTime: 0 simulationStartTime: 0 simulationStopTime: 2 filter: simMode: Normal
Проверьте первый тест, проверив, что выходы совпадают с входами.
subplot(211) plot(simOut1.tout,simOut1.yout(:,1),simOut1.tout,simOut1.yout(:,4)) xlabel('Time (sec)'), ylabel('Value'), title('Gain input and output'); subplot(212) plot(simOut1.tout,simOut1.yout(:,1)-simOut1.yout(:,4)) xlabel('Time (sec)'),ylabel('Difference'), title('Difference between the gain input and output');
Выполните и постройте график результатов для второго теста таким же образом.
Заметьте, что после того, как ограниченный выход отличается от входного сигнала, он может восстановиться только с максимальной скоростью нарастания. Вот почему у графика необычная изгиб. Если вход и выход совпадают, оба изменяются вместе.
load rising_gain.mat covSet = setModelParameter(covSet,'CovSaveName','dataObj2'); simOut2 = sim(covSet); dataObj2 subplot(211) plot(simOut2.tout,simOut2.yout(:,1),simOut2.tout,simOut2.yout(:,4)) xlabel('Time (sec)'), ylabel('Value'), title('Gain input and output'); subplot(212) plot(simOut2.tout,simOut2.yout(:,1)-simOut2.yout(:,4)) xlabel('Time (sec)'), ylabel('Difference'), title('Difference between the gain input and output');
dataObj2 = ... cvdata version: (R2021a) id: 1800 type: TEST_DATA test: cvtest object rootID: 1686 checksum: [1x1 struct] modelinfo: [1x1 struct] startTime: 27-Jan-2021 07:27:12 stopTime: 27-Jan-2021 07:27:12 intervalStartTime: 0 intervalStopTime: 0 simulationStartTime: 0 simulationStopTime: 2 filter: simMode: Normal
Выполните и постройте график результатов для третьего теста.
load falling_gain.mat covSet = setModelParameter(covSet,'CovSaveName','dataObj3'); simOut3 = sim(covSet); dataObj3 subplot(211) plot(simOut3.tout,simOut3.yout(:,1),simOut3.tout,simOut3.yout(:,4)) xlabel('Time (sec)'), ylabel('Value'), title('Gain input and output'); subplot(212) plot(simOut3.tout,simOut3.yout(:,1)-simOut3.yout(:,4)) xlabel('Time (sec)'), ylabel('Difference'), title('Difference between the gain input and output');
dataObj3 = ... cvdata version: (R2021a) id: 1915 type: TEST_DATA test: cvtest object rootID: 1686 checksum: [1x1 struct] modelinfo: [1x1 struct] startTime: 27-Jan-2021 07:27:13 stopTime: 27-Jan-2021 07:27:13 intervalStartTime: 0 intervalStopTime: 0 simulationStartTime: 0 simulationStopTime: 2 filter: simMode: Normal
Принимая, что все тесты прошли, составьте комбинированный отчет из всех тестовых случаев, чтобы проверить достижение 100% покрытия. Проценты покрытия для каждого теста отображаются под заголовком «Иерархия модели». Хотя ни один из тестов индивидуально не достиг 100% охвата, в совокупности они достигают полного охвата.
cvhtml('combined_ratelim',dataObj1,dataObj2,dataObj3);
Сохраните собранные данные о покрытии в файле «ratelim_testdata.cvt» при помощи cvsave
.
cvsave('ratelim_testdata',dataObj1,dataObj2,dataObj3);
Закройте модель и выход из окружения покрытия
close_system('slvnvdemo_ratelim_harness',0); clear dataObj*
Восстановите сохраненные тесты покрытия из файла «ratelim_testdata.cvt» после открытия модели при помощи cvload
. Данные и тесты извлекаются из массива ячеек.
open_system('slvnvdemo_ratelim_harness'); [SavedTests,SavedData] = cvload('ratelim_testdata')
SavedTests = 1x3 cell array {1x1 cvtest} {1x1 cvtest} {1x1 cvtest} SavedData = 1x3 cell array {1x1 cvdata} {1x1 cvdata} {1x1 cvdata}
Манипулируйте объектами cvdata, используя перегруженные операторы: +, -, и *. Оператор * используется для поиска пересечения двух объектов данных о покрытии, что приводит к появлению другого объекта cvdata. Например, следующая команда создает HTML об общем покрытии из всех трех тестов.
common = SavedData{1} * SavedData{2} * SavedData{3}
cvhtml('intersection',common)
common = ... cvdata version: (R2021a) id: 0 type: DERIVED_DATA test: [] rootID: 219 checksum: [1x1 struct] modelinfo: [1x1 struct] startTime: 27-Jan-2021 07:27:10 stopTime: 27-Jan-2021 07:27:13 intervalStartTime: 0 intervalStopTime: 0 filter: simMode: Normal
Извлеките информацию о Decision Coverage из пути блока или указателя на блок при помощи decisioninfo
. Выход является вектором с достигнутыми и полными результатами для одного объекта модели, соответственно.
cov = decisioninfo(SavedData{1} + SavedData{2} + SavedData{3}, ... 'slvnvdemo_ratelim_harness/Adjustable Rate Limiter')
cov = 6 6
Используйте полученную информацию о покрытии для доступа к процентному покрытию.
percentCov = 100 * (cov(1)/cov(2))
percentCov = 100
Когда используются два выходных аргументов, decisioninfo
возвращает структуру, которая захватывает решения и результаты в блоке Simulink или объекте Stateflow ®.
[blockCov,desc] = decisioninfo(common, ... 'slvnvdemo_ratelim_harness/Adjustable Rate Limiter/Delta sign') descDecision = desc.decision outcome1 = desc.decision.outcome(1) outcome2 = desc.decision.outcome(2)
blockCov = 0 2 desc = struct with fields: isFiltered: 0 justifiedCoverage: 0 isJustified: 0 filterRationale: '' decision: [1x1 struct] descDecision = struct with fields: text: 'Switch trigger' filterRationale: '' isFiltered: 0 isJustified: 0 outcome: [1x2 struct] outcome1 = struct with fields: text: 'false (out = in3)' executionCount: 0 executedIn: [] isFiltered: 0 isJustified: 0 filterRationale: '' outcome2 = struct with fields: text: 'true (out = in1)' executionCount: 0 executedIn: [] isFiltered: 0 isJustified: 0 filterRationale: ''