Рассмотрим задачу моделирования вертикального полёта летательного аппарата с реактивным двигателем.
Предположения:
Итак, имеем следующие постоянные:
Введём следующие переменные:
Индексом 0 обозначим момент включения двигателя, индексом 1 — момент выключения двигателя. Тогда Δt = t1 – t0 — время работы двигателя. Аналогичный смысл обозначение Δ имеет и при применении к прочим переменным. Через Δvr обозначено изменение скорости, приобретаемое в результате действия реактивной тяги, вычисляемое по формуле Циолковского. Благодаря предположениям модели изменения скорости под действием реактивной силы и гравитации можно просто сложить:
Подставив вместо Δt переменную 0 ≤ τ ≤ Δt (t = t0 + τ), можно выразить функции изменения массы и скорости аппарата:
Продифференцировав скорость, получим ускорение w(τ). Проинтегрировав скорость, получим высоту x(τ):
Итак, пусть заданы начальные значения t0, m0, v0, x0 и целевые Δt и Δm. Тогда не составит труда вычислить конечные значения переменных:
Для реализации вышеприведённых формул на языке C++ пригодится стандартный заголовочный файл cmath.
Для выполнения моделирования можно определить следующие переменные:
// Параметры модели
double g;
double I;
double empty_m;
// Переменные модели
double t0, t1;
double m0, m1;
double x0, x1;
double v0, v1;
double w0, w1;
Впрочем, здесь уместно заметить, что хотя формально всё это — числа, они имеют различный физический смысл. Для того, чтобы подчеркнуть этот факт, можно воспользоваться средствами системы типов языка программирования и определить для каждой физической величины свой тип. К сожалению, полноценная конструкция такого рода (допускающая осмысленные переходы между типами, например деление расстояния на время, дающее скорость) в C++ слишком сложна. Поэтому в данной программе мы ограничемся “симуляцией” разных типов через введение синонимов типа double
.
// Вспомогательные типы
using Acceleration = double; // м/с2
using Velocity = double; // м/с
using Position = double; // м
using Time = double; // с
using Mass = double; // кг
using Direction = double; // вверх или вниз
const Direction
upwards = +1.0, // Вверх
downwards = -1.0; // Вниз
// Параметры модели
Acceleration g;
Velocity I;
Mass empty_m;
// Переменные модели
Time t0, t1;
Mass m0, m1;
Position x0, x1;
Velocity v0, v1;
Acceleration w0, w1;
Один шаг работы программы состоит в вычислении очередных значений переменных t1
, m1
, x1
, v1
(а также w0
и w1
) для заданных пользователем значений Δm и Δt. Пусть этим занимается функция
void compute_step(Time delta_t, Mass delta_m, Direction dir);
Для следующего шага старые значения t1
, m1
, x1
, v1
должны стать новыми значениями t0
, m0
, x0
, v0
: конечное состояние предыдущего шага — это начальное состояние следующего шага. Для выполнения этого действия добавим вспомогательную функцию
// "Перевернуть страницу"
void finish_step()
{
t0 = t1;
m0 = m1;
x0 = x1;
v0 = v1;
}
Впрочем, в данный момент удобно поместить сюда же вывод текстовой информации о текущем шаге:
// "Перевернуть страницу"
void finish_step()
{
cout << '\n' << w0 << " ~ " << w1 << "m/s2;\n";
cout << t1 << "s, " << x1 << "m: ";
cout << (m1 - empty_m) << "kg; " << v1 << "m/s;\n\n";
t0 = t1;
m0 = m1;
x0 = x1;
v0 = v1;
w0 = w1;
}
Один полёт состоит из цикла, на каждой итерации которого выполняется очередной шаг для очередных значений Δm и Δt, введённых пользователем. При этом для удобства будем считать, что пользователь вводит в качестве Δm неотрицательное значение, если сопло двигателя направлено вниз, и отрицательное, если сопло направлено вверх (“реверс”). Указанный цикл можно реализовать в виде следующей функции:
// Один полёт
void step_loop()
{
cout << "Enter delta-m delta-t for each step\n";
Time delta_t;
Mass delta_m;
while (cin >> delta_m >> delta_t)
{
compute_step(delta_t, -abs(delta_m),
delta_m < 0 ? downwards : upwards);
finish_step();
}
}
Работа консольного приложения состоит из череды симуляций полётов. Каждый полёт характеризуется начальными значениями m0
, x0
и v0
(t0
положим равным 0), а также собственными значениями параметров g
, I
и empty_m
. После установки должных значений вызывается функция step_loop
. После выполняем очистку потока ввода стандартными средствами.
// Консольное приложение
int main()
{
void select_g(); // Выбор g.
void select_I(); // Выбор I.
void step_loop(); // Симуляция полёта.
while (true)
{
select_g();
select_I();
t0 = 0; // Обнуляем время.
cout << "\nEmpty mass = ";
cin >> empty_m; // Масса пустого.
cout << "Propellant mass = ";
cin >> m0; // Масса рабочего тела.
m0 += empty_m; // Полная масса.
cout << "x0 = ";
cin >> x0; // Начальная высота.
cout << "v0 = ";
cin >> v0; // Начальная скорость.
cout << '\n';
step_loop();
// Сброс и очистка потока ввода.
cin.clear();
cin.sync();
cin.ignore(cin.rdbuf()->in_avail());
}
return EXIT_SUCCESS;
}
Функции select_g
и select_I
устанавливают соответственно значения g
и I
, предлагая пользователю выбор из набора известных значений для разных безатмосферных тел и реактивных двигателей.
Итак, всё целиком имеет следующий вид:
#include <cstdlib>
#include <cassert>
#include <iostream>
#include <cmath>
using namespace std;
// Вспомогательные типы
using Acceleration = double; // м/с2
using Velocity = double; // м/с
using Position = double; // м
using Time = double; // с
using Mass = double; // кг
using Direction = double; // вверх или вниз
const Direction
upwards = +1.0, // Вверх
downwards = -1.0; // Вниз
// Параметры модели
Acceleration g;
Velocity I;
Mass empty_m;
// Переменные модели
Time t0, t1;
Mass m0, m1;
Position x0, x1;
Velocity v0, v1;
Acceleration w0, w1;
// Консольное приложение
int main()
{
void select_g(); // Выбор g.
void select_I(); // Выбор I.
void step_loop(); // Симуляция полёта.
while (true)
{
select_g();
select_I();
t0 = 0; // Обнуляем время.
cout << "\nEmpty mass = ";
cin >> empty_m; // Масса пустого.
cout << "Propellant mass = ";
cin >> m0; // Масса рабочего тела.
m0 += empty_m; // Полная масса.
cout << "x0 = ";
cin >> x0; // Начальная высота.
cout << "v0 = ";
cin >> v0; // Начальная скорость.
cout << '\n';
step_loop();
// Сброс и очистка потока ввода.
cin.clear();
cin.sync();
cin.ignore(cin.rdbuf()->in_avail());
}
return EXIT_SUCCESS;
}
// Выбор ускорения свободного падения
void select_g()
{
const Acceleration
g_mercury = 3.7, // Меркурий
g_moon = 1.62, // Луна
g_europa = 1.315, // Европа
g_pluto = 0.617, // Плутон
g_ceris = 0.27; // Церера
cout << "\nSelect location:\n"
"0. No gravity\n"
"1. Mercury\n"
"2. Moon\n"
"3. Europa\n"
"4. Pluto\n"
"5. Ceris\n";
switch (cin.get())
{
case '0': g = 0.0; break;
case '1': g = g_mercury; break;
case '2': g = g_moon; break;
case '3': g = g_europa; break;
case '4': g = g_pluto; break;
case '5': g = g_ceris; break;
default:
cout << " g = ";
cin >> g;
}
cin.ignore();
}
// Выбор удельного импульса
void select_I()
{
const Velocity
I_RD_253 = 3100, // Протон-М 1 ст.; гидразин + азотная кислота
I_NK_33 = 3250, // Союз-М 1 ст.; керосин + кислород
I_J_2 = 4170, // Saturn V, 2/3 ст.; водород + кислород
I_RD_0120 = 4460, // Энергия, ускоритель; водород + кислород
I_arcjet = 16000, // КА, водород, проект
I_SPD_230 = 26500, // КА, ксенон
I_VISTA = 157000; // D-T ТЯРД, водород, проект
cout << "\nSelect engine:\n"
"1. RD-253\n"
"2. NK-33\n"
"3. J-2\n"
"4. RD-0120\n"
"5. Arcjet\n"
"6. SPD-230\n"
"7. VISTA\n";
switch (cin.get())
{
case '1': I = I_RD_253; break;
case '2': I = I_NK_33; break;
case '3': I = I_J_2; break;
case '4': I = I_RD_0120; break;
case '5': I = I_arcjet; break;
case '6': I = I_SPD_230; break;
case '7': I = I_VISTA; break;
default:
cout << " I = ";
cin >> I;
}
cin.ignore();
}
// Один полёт
void step_loop()
{
void compute_step(Time delta_t, Mass delta_m, Direction dir);
void finish_step();
cout << "Enter delta-m delta-t for each step\n";
Time delta_t;
Mass delta_m;
while (cin >> delta_m >> delta_t)
{
compute_step(delta_t, -abs(delta_m),
delta_m < 0 ? downwards : upwards);
finish_step();
}
}
// "Перевернуть страницу"
void finish_step()
{
cout << '\n' << w0 << " ~ " << w1 << "m/s2;\n";
cout << t1 << "s, " << x1 << "m: ";
cout << (m1 - empty_m) << "kg; " << v1 << "m/s;\n\n";
t0 = t1;
m0 = m1;
x0 = x1;
v0 = v1;
w0 = w1;
}
// Вычислить конечные значения переменных на очередном шаге.
void compute_step(Time delta_t, Mass delta_m, Direction dir)
{
// dir используется как множитель I
// t1 = ?
// m1 = ?
// w0 = ?
// w1 = ?
// v1 = ?
// x1 = ?
}
Задача лабораторной работы состоит в написании тела функции compute_step
.
Попробуйте ответить на следующие вопросы:
Кувшинов Д.Р. © 2016