Описание пользователя
Глава 3. Использование стандартных модулей автокомпиляции
§3.6. Принципы создания автокомпилируемых моделей блоков
§3.6.4. Моделирование длящихся во времени процессов
§3.6.4.4. Создание динамического блока по шаблону
Описывается создание динамического блока при помощи стандартного шаблона модели, входящего в состав RDS. Использование шаблона упрощает написание модели блока, поскольку в этот шаблон уже включена связь с динамической переменной времени «DynTime», вычисление шага расчета и возможность чтения начальных условий с входов блока.
В §3.6.4.2 и §3.6.4.3 описаны модели блоков, которые численно рассчитывали процессы в системах, описываемых дифференциальными уравнениями (которые мы переводили в разностные), причем начальные условия этих уравнений подаются на входы блока. В каждой из этих моделей мы вычисляли шаг расчета как разность текущего времени и времени прошлого шага, а для определения правильного момента считывания начальных начальных условий мы использовали специальный логический флаг инициализации. Таким образом, в наших программах моделей динамических блоков содержатся похожие фрагменты, а в структуре статических переменных блока – переменные одинакового назначения. В составе стандартного модуля автокомпиляции есть шаблон динамического блока, в котором эти фрагменты и переменные уже введены, и разработчику остается только добавить свои переменные и дописать внутри модели две подпрограммы: чтения начальных условий с входов и выполнения вычислений для шага расчета. Во многих случаях это удобнее создания модели «с нуля».
Создание модели по шаблону отличается от создания пустой модели только на одном шаге: после нажатия кнопки «» на вкладке «» окна параметров блока, для которого создается модель в окне «» нужно вместо флажка «» установить флажок «» и выбрать название шаблона в списке под флажком (рис. 320).
В §3.6.4.2 рассматривалась модель блока, рассчитывающего траекторию тела, брошенного под углом к горизонту при наличии тяготения и отсутствии сопротивления воздуха. В окончательном варианте этой модели начальная скорость и угол броска считывались с входов блока «v0» и «Alpha» соответственно. Создадим точно такую же модель, но с использованием стандартного шаблона. Для этого необходимо выполнить следующие действия:
- нажать на свободном месте окна подсистемы правую кнопку мыши;
- выбрать в открывшемся контекстном меню пункт «»;
- нажать на созданном блоке (он будет выглядеть как белый квадрат с черной рамкой) правую кнопку мыши;
- выбрать в открывшемся контекстном меню пункт «»;
- на вкладке «» открывшегося окна включить флажок «» на панели «»;
- на вкладке «» установить флажок «», выбрать в выпадающем списке подключенный модуль автокомпиляции и нажать кнопку «»;
- в появившемся окне «» выбрать флажок «», выбрать в списке шаблон «» (см. рис. 320), а затем закрыть окно кнопкой «» (это единственное отличие от последовательности действий, которая выполняется при создании пустой модели);
- в диалоге сохранения модели ввести имя нового файла, в который будет записана создаваемая модель, и нажать кнопку «»;
- закрыть окно параметров блока кнопкой «».
Рис. 320 (повтор). Выбор создания модели по шаблону
После выполнения перечисленных выше действий откроется окно редактора, в котором вместо пустой вкладки «», которая открывалась при создании моделей «с нуля», будет открыта частично заполненная вкладка «» (рис. 413).
Рис. 413. Окно редактора модели, созданной по стандартному шаблону
На этой вкладке размещается следующий текст:
//---------------------------------------------------------
// ВНИМАНИЕ! Переменные rdsbcppTime0, rdsbcppFirstStep и
// DynTime нужны для работы блока, их не следует изменять
// или удалять.
//---------------------------------------------------------
// Функции, заполняемые пользователем
#pragma argsused
void InitVars(double h)
{
// Здесь можно считать начальные условия с входов блока,
// если это необходимо.
// h - шаг расчета.
}
//---------------------------------------------------------
#pragma argsused
void DoStep(double h)
{
// Здесь можно вычислить новые значения выходов блока на
// момент времени DynTime (интервал времени с прошлого
// вычисления - h)
}
//---------------------------------------------------------
В описаниях внутри класса блока в этом шаблоне находятся две пустых функции, внутрь которых пользователь должен вставить свои фрагменты программы: в функцию InitVars записывается чтение начальных условий с входов блока, в функцию DoStep – вычисления в шаге расчета. В обе этих функции шаг расчета передается в параметре h.
К модели блока уже подключена динамическая переменная времени «DynTime», структура статических переменных блока тоже уже частично заполнена. В ней находятся следующие переменные:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 0 |
| Ready | Сигнал | Выход | 0 | |
| rdsbcppTime0 | double | Внутренняя | 0 | |
| rdsbcppFirstStep | Логический | Внутренняя | 1 |
Помимо стандартных сигналов «Start» и «Ready», которые есть у любого простого блока, в эту структуру добавлены две внутренние переменные: вещественная «rdsbcppTime0» и логическая «rdsbcppFirstStep». В переменной «rdsbcppTime0» хранится значение времени при прошлом срабатывании блока, необходимое для вычисления значения шага расчета – в моделях, которые мы создавали ранее, аналогичную переменную мы называли «t0». Логическая переменная «rdsbcppFirstStep» с единичным начальным значением используется как флаг инициализации блока – в предыдущих примерах моделей для этих целей мы использовали переменную «Init».
Эти добавленные переменные необходимы для работы модели, созданной по шаблону динамического блока. Во фрагменте программы на вкладке «» они не используются (там есть только комментарий об их необходимости), они используются в реакции блока на такт расчета: хотя вкладка «» и не открылась по умолчанию, соответствующий ей фрагмент программы присутствует в модели, и его можно увидеть в редакторе, открыв эту вкладку вручную. Сделаем это: выберем на левой панели редактора вкладку «» (рис. 414), раскроем на ней группу «» и дважды щелкнем на пункте «».
Рис. 414. События и описания модели,
созданной по шаблону
После этого в правой части окна редактора появится уже знакомая нам вкладка «» со следующим текстом:
double h;
if(DynTime==rdsbcppTime0) // Время не изменилось
return;
// Время изменилось
h=DynTime-rdsbcppTime0; // Вычисление шага расчета
if(rdsbcppFirstStep) // Инициализация
{ InitVars(h);
rdsbcppFirstStep=0;
}
// Выполнение шага расчета
DoStep(h);
// Запоминание времени последнего расчета
rdsbcppTime0=DynTime;
Этот текст уже знаком нам по предыдущим моделям, только раньше мы писали его вручную, а здесь он является частью стандартного шаблона. Как и раньше, сначала текущее время DynTime сравнивается с временем прошлого шага расчета rdsbcppTime0 и, если время не изменилось, модель немедленно завершается. Если время изменилось, вычисляется шаг расчета h и проверяется значение переменной rdsbcppFirstStep. Если оно равно единице, значит, выполняется самый первый шаг расчета – при этом вызывается функция InitVars из описаний в классе, внутрь которой мы должны вставить программу чтения начальных значений, после чего rdsbcppFirstStep сбрасывается. Затем вызывается функция DoStep, внутрь которой мы вставим вычисления значений переменных блока на новом шаге, и новое время запоминается в переменной rdsbcppTime0. При желании, в текст этой программы можно внести какие-либо изменения, но все, что необходимо для расчета, в ней уже есть, поэтому можно вернуться на вкладку «» и наполнить содержимым две ее пустые функции. Но сначала добавим в блок необходимые для нашей модели переменные.
В рассмотренной в §3.6.4.2 модели свободного полета тела, получавшей начальные значения с входов блока, блок имел входы «v0» и «Alpha», на которые подавались начальная скорость в м/с и угол броска к горизонту в градусах соответственно, выходы «x» и «y», на которые выдавались текущие координаты летящего тела, и внутренние переменные «vx» и «vy», в которых вычислялась его текущая скорость, необходимая для расчета координат. Добавим эти переменные в структуру нашего блока, сохранив в ней те, которые являются частью шаблона. В результате этого наш блок будет иметь следующую структуру переменных:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 0 |
| Ready | Сигнал | Выход | 0 | |
| rdsbcppTime0 | double | Внутренняя | 0 | |
| rdsbcppFirstStep | Логический | Внутренняя | 1 | |
| v0 | double | Вход | 0 | |
| Alpha | double | Вход | 0 | |
| x | double | Выход | 0 | |
| y | double | Выход | 0 | |
| vx | double | Внутренняя | 0 | |
| vy | double | Внутренняя | 0 |
Внутрь функции InitVars мы вставим фрагмент старой модели, отвечающий за чтение начальных условий:
if(v0==rdsbcppHugeDouble || Alpha==rdsbcppHugeDouble) vx=vy=rdsbcppHugeDouble; // Ошибка на входе else { double alpha_rad=Alpha*M_PI/180; // В радианы // Вычисление начальных проекций скорости vx=v0*cos(alpha_rad); vy=v0*sin(alpha_rad); }
В нем мы вычисляем начальные значения проекций скорости vx и vy по данным на входах блока v0 и Alpha, если оба входа не содержат признака ошибки rdsbcppHugeDouble. Параметр функции h, в котором передается шаг расчета, для чтения начальных условий нам не нужен (директива препроцессора «#pragma argsused», расположенная перед описанием функции InitVars, подавит вывод предупреждения о неиспользуемом параметре).
Внутрь функции DoStep мы вставим фрагмент, вычислявший в старой модели новые значения x, y и vy (vx в данной задаче не изменяется со временем, поскольку в горизонтальном направлении на тело не действует никаких сил):
double g=1.62; // Ускорение свободного падения на Луне x=x+h*vx; y=y+h*vy; vy=vy-h*g; // Новое vy вычисляем ПОСЛЕ нового y
Таким образом, если убрать поясняющие комментарии, находившиеся внутри функций InitVars и DoStep, содержимое вкладки «» должно теперь выглядеть следующим образом:
//---------------------------------------------------------
// ВНИМАНИЕ! Переменные rdsbcppTime0, rdsbcppFirstStep и
// DynTime нужны для работы блока, их не следует изменять
// или удалять.
//---------------------------------------------------------
#pragma argsused
void InitVars(double h)
{
if(v0==rdsbcppHugeDouble || Alpha==rdsbcppHugeDouble)
vx=vy=rdsbcppHugeDouble; // Ошибка на входе
else
{ double alpha_rad=Alpha*M_PI/180; // В радианы
// Вычисление начальных проекций скорости
vx=v0*cos(alpha_rad);
vy=v0*sin(alpha_rad);
}
}
//---------------------------------------------------------
#pragma argsused
void DoStep(double h)
{ double g=1.62; // Ускорение свободного падения на Луне
x=x+h*vx;
y=y+h*vy;
vy=vy-h*g; // Новое vy вычисляем ПОСЛЕ нового y
}
//---------------------------------------------------------
Если теперь собрать с нашим новым блоком схему, аналогичную изображенной на рис. 406, и запустить расчет, на графике мы увидим ту же самую параболу: новая модель, созданная по шаблону, будет работать точно так же, как и старая, созданная вручную.
Важно понимать, что после создания по какому-либо шаблону, модель, с точки зрения RDS и модуля автокомпиляции, ничем не будет отличаться от модели, созданной вручную «с нуля». Никакой связи между моделью и шаблоном не сохраняется, шаблон – это просто некоторое начальное содержимое модели, которое может быть потом как угодно изменено пользователем. В рассмотренной модели, например, можно было бы стереть все описания в классе, а содержимое функций, которое мы туда вставили, разместить непосредственно на вкладке «» вместо вызовов этих функций. Можно было бы, при желании, переименовать переменные «rdsbcppTime0» и «rdsbcppFirstStep», являющиеся частью шаблона (например, дать им имена «t0» и «Init», как раньше), и в структуре статических переменных блока, и на вкладке «», где они используются. Ни то, ни другое на работоспособность блока не повлияло бы.
Пользователь может в любой момент сохранить любую свою модель в качестве шаблона, выбрав в меню редактора пункт «» (см. §3.5.1). Редактировать, добавлять и удалять шаблоны можно в настройках модуля автокомпиляции, эти действия рассматриваются в §3.8.2.