Прогулка по лунолётам. 1 - Пролог

Хорошей программы должно быть много. Математика, вложенная более 25 лет назад в маломощные ПМК Михаилом Пуховым, настолько привлекательна своей простотой и реализмом (за это спасибо консультанту журнала, космонавту, Герою СССР Ю.Н. Глазкову), что несомненно может стать основой игр и симуляторов на любых платформах.

Прежде всего, для повторного использования алгоритм надо привести в понятный человеку и максимально наглядный вид. Код программ для ПМК таковым не является. Тем более, оригинальный код "Лунолетов".

Надо отдать авторам должное: степень достигнутого сжатия так высока, что восстановление алгоритма из кода оказывается делом весьма непростым. Даже для владеющего программированием на ПМК, понимающего использованные трюки, а не просто "знакомого с основами". Очень непростым. После трех часов "раскрутки" я пришел к выводу о нерациональности такого подхода ввиду наличия блок-схемы для "Лунолет-2". Для "Лунолет-1" она практически идентична, лишь следует исключить из рассмотрения ввод угла вектора тяги и расчет горизонтальной скорости.

Повторно выражая через десятилетия авторам "Лунолет-1" и "2" свое уважение, но оставаясь верным долгу профессии вынужден сказать, что программы эти являются своего рода "анти-образцом". То есть примером того, как не надо писать программы. Если, конечно, вы хотите их в дальнейшем изменять, переносить, передавать на сопровождение другим людям. Не надо, за исключением крайних случаев. Например, когда нужно втиснуть алгоритм в злосчастные 98 шагов (байтов) программной памяти и 14 регистров.

При этом само собой подразумевается, что "как надо писать" вы знаете хорошо и на исключение идете осознанно, как Коршунов - к перелету на "Кон-тики". Если будете писать программы для МК-152/161 - никогда не следуйте стилю авторов, если на то нет особых причин. У программистов подобный стиль издавна называется "тарелка спагетти". Как правило, затраты на модификацию такой программы выше, чем на её полное переписывание.

Однако, и блок-схема не блещет структурностью. Блоки начала и конца отсутствуют. Цепочка сравнений, из которых две стрелки в итоге попадают в один блок ввода-вывода - тоже "тяжелое наследие доструктурной эпохи". Но всё-равно, это гораздо лучше "тарелки спагетти".

В итоге, текст программы на FreePascal, объединяющей оба лунолёта, может выглядеть как на листинге ниже.

А мы перейдем к реализации модели на ПМК семейства HP 50/49/48.

program lunar_ship_1;
 
{$mode objfpc}{$H+}
 
uses
  Math;
 
var
  h_i,  // altitude - vertical coordinate, m - current and next
  x_i,  // longitude - horizontal coordinate, m
  v_i,  // horizontal velocity, m/sec
  u_i,  // rate of climb, vertical velocity, m/sec
  mf_i, // mass (amount) of the fuel, kg
  ms,          // mass of the ship without fuel, kg
  a_lim,       // acceleration limit, m/sec2
  // In-flight maneuver:
  d_mf, // fuel consumption, kg
  t,    // time, sec
  ac,   // angle of climb, degrees; is always 0 for vertical flight mode
 
  g,  // acceleration of gravity, m/sec2
  nf, // nozzle flow, m/sec
  ls, // life support, sec
  a,  // current acceleration
  q   // fuel consumption by time
    : real;
  FlightMode          : cardinal = 1;
  WillReverseOfControl: boolean = false;
  WillQuitProgram     : boolean = false;
 
 
procedure DoStep;
var
  v_i1, u_i1: real;
begin
  repeat
    v_i1 := v_i;
    u_i1 := u_i;
    v_i := v_i + a * t * sin(ac);
    x_i := x_i + (v_i1 + v_i) / 2 * t;
    u_i := u_i + (a * cos(ac) - g) * t;
    h_i := h_i + (u_i1 + u_i) / 2 * t;
    mf_i := mf_i - q * t;
 
    ls := ls - t;
 
    if mf_i < 0 then
      t := mf_i / q;
  until mf_i >= 0;
end;
 
procedure OutInfo;
begin
  writeln('-------------------------------------------');
  writeln('Altitude             : ', h_i:10:2, ' m');
  if FlightMode = 2 then begin
    writeln('Longitude            : ', x_i:10:2, ' m');
    writeln('Velocity (horizontal): ', v_i:10:2, ' m/sec');
  end;
  writeln('Velocity (vertical)  : ', u_i:10:2, ' m/sec');
  writeln('Fuel                 : ', mf_i:10:0, ' kg');
  writeln('Life support         : ', ls:10:0, ' sec');
  writeln('-------------------------------------------');
end;
 
procedure EnterManeuver(
  out WillReverseOfControl: boolean;
  out WillQuitProgram     : boolean);
begin
  repeat
    writeln('Enter in-flight maneuver');
    write('Fuel consumption, kg: '); readln(d_mf);
    WillQuitProgram := d_mf < 0;
    if WillQuitProgram then
      exit;
    write('Time, sec           : '); readln(t);
    if FlightMode <> 2 then
      ac := 0
    else begin
      write('Climb angle, °      : '); readln(ac);
      ac := DegToRad(ac);
    end;
  until t <> 0;
 
  WillReverseOfControl := t < 0;
  t := abs(t);
end;
 
procedure CalcAcceleration(WillReverseOfControl: boolean);
begin
  q := d_mf / t;
  a := q * nf / (ms + mf_i);
  if WillReverseOfControl then begin
    a := -a;
    WillReverseOfControl := false;
  end;
end;
 
begin
  writeln('Lunar ship simple simulator');
  writeln('(c) 1985 "Lunolet-1", "Lunilet-2" by Mikhail PUKHOV, USSR');
  writeln('(c) 2011 Serguei TARASSOV, pmk.arbinada.com');
  writeln;
  writeln('Enter simulator mode:');
  writeln('   1 - "Lunolet-1" - vertical flight only');
  writeln('   2 - "Lunolet-2" - vertical and horizontal flight (2-dimensional)');
  readln(FlightMode);
  writeln('Enter negative fuel consumption value to quit the program');
 
  g := 1.62; // 1,62 m/sec2 for Lune
  ms := 2250;
  nf := 3660;
  a := 0;
  a_lim := 3 * 9.81; // "3g"
  v_i := 0;
  u_i := 0;
  h_i := 0;
  x_i := 0;
  mf_i := 400;
  ls := 3600;
 
  repeat
    if h_i < 0 then begin
      t := 2 * h_i /
        (sqrt(sqr(u_i) + 2 * h_i * (g - a * cos(ac))) - u_i);
    end
    else begin
      if (h_i = 0) or ((a < a_lim) and (mf_i <> 0)) then begin
        OutInfo;
        EnterManeuver(WillReverseOfControl, WillQuitProgram);
        if WillQuitProgram then
          break;
        CalcAcceleration(WillReverseOfControl);
        writeln('Acceleration: ', a:8:2, ' m/sec2');
      end
      else begin
        d_mf := 0;
        if not (a < a_lim) then begin
          writeln('!!! Acceleration exceeded');
          t := a - a_lim;
          writeln('Waiting ', t:5:1, ' sec');
        end;
        if mf_i <= 0 then begin
          writeln('!!! Fuel exceeded');
          t := nf;
        end;
        CalcAcceleration(false);
      end;
    end;
 
    DoStep;
  until WillQuitProgram;
end.

Теперь протестируем полёт Александра Перепёлкина и убедимся, что симулятор работает.

-------------------------------------------
Altitude             :       0.00 m
Velocity (vertical)  :       0.00 m/sec
Fuel                 :        400 kg
Life support         :       3600 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 65
Time, sec           : 3
Acceleration:    29.92 m/sec2
!!! Acceleration exceeded
Waiting   0.5 sec
-------------------------------------------
Altitude             :     169.16 m
Velocity (vertical)  :      84.11 m/sec
Fuel                 :        335 kg
Life support         :       3597 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 0
Time, sec           : 2
Acceleration:     0.00 m/sec2
-------------------------------------------
Altitude             :     334.15 m
Velocity (vertical)  :      80.87 m/sec
Fuel                 :        335 kg
Life support         :       3595 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 65
Time, sec           : 3
Acceleration:    30.68 m/sec2
!!! Acceleration exceeded
Waiting   1.2 sec
-------------------------------------------
Altitude             :     915.81 m
Velocity (vertical)  :     166.02 m/sec
Fuel                 :        270 kg
Life support         :       3590 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 0
Time, sec           : 120
Acceleration:     0.00 m/sec2
-------------------------------------------
Altitude             :    9174.60 m
Velocity (vertical)  :     -28.38 m/sec
Fuel                 :        270 kg
Life support         :       3470 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 25
Time, sec           : 2
Acceleration:    18.15 m/sec2
-------------------------------------------
Altitude             :    9150.92 m
Velocity (vertical)  :       4.69 m/sec
Fuel                 :        245 kg
Life support         :       3468 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 10
Time, sec           : -10
Acceleration:    -1.47 m/sec2
-------------------------------------------
Altitude             :    9043.50 m
Velocity (vertical)  :     -26.18 m/sec
Fuel                 :        235 kg
Life support         :       3458 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 25
Time, sec           : 5
Acceleration:     7.36 m/sec2
-------------------------------------------
Altitude             :    8984.42 m
Velocity (vertical)  :       2.54 m/sec
Fuel                 :        210 kg
Life support         :       3453 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 0
Time, sec           : 90
Acceleration:     0.00 m/sec2
-------------------------------------------
Altitude             :    2652.42 m
Velocity (vertical)  :    -143.26 m/sec
Fuel                 :        210 kg
Life support         :       3363 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 100
Time, sec           : 3
Acceleration:    49.59 m/sec2
!!! Acceleration exceeded
Waiting  20.2 sec
-------------------------------------------
Altitude             :    2122.62 m
Velocity (vertical)  :     -32.00 m/sec
Fuel                 :        110 kg
Life support         :       3340 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 10
Time, sec           : 20
Acceleration:     0.78 m/sec2
-------------------------------------------
Altitude             :    1313.70 m
Velocity (vertical)  :     -48.89 m/sec
Fuel                 :        100 kg
Life support         :       3320 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 10
Time, sec           : 15
Acceleration:     1.04 m/sec2
-------------------------------------------
Altitude             :     514.89 m
Velocity (vertical)  :     -57.62 m/sec
Fuel                 :         90 kg
Life support         :       3305 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 35
Time, sec           : 1.5
Acceleration:    36.50 m/sec2
!!! Acceleration exceeded
Waiting   7.1 sec
-------------------------------------------
Altitude             :     389.79 m
Velocity (vertical)  :     -16.75 m/sec
Fuel                 :         55 kg
Life support         :       3297 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 22
Time, sec           : 22
Acceleration:     1.59 m/sec2
-------------------------------------------
Altitude             :      13.51 m
Velocity (vertical)  :     -17.46 m/sec
Fuel                 :         33 kg
Life support         :       3275 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 22
Time, sec           : 0.7
Acceleration:    50.38 m/sec2
!!! Acceleration exceeded
Waiting  21.0 sec
-------------------------------------------
Altitude             :       7.05 m
Velocity (vertical)  :     -17.27 m/sec
Fuel                 :         11 kg
Life support         :       3253 sec
-------------------------------------------
Enter in-flight maneuver
Fuel consumption, kg: 22
Time, sec           : 0.7
Acceleration:    50.88 m/sec2
!!! Acceleration exceeded
Waiting  21.4 sec
-------------------------------------------
Altitude             :       0.00 m
Velocity (vertical)  :      -3.61 m/sec
Fuel                 :          0 kg
Life support         :       3250 sec
-------------------------------------------

Как видно из протокола, Перепёлкину снова удалось на последних каплях керосина посадить лунолёт, собранный спустя 25 лет по той же схеме, но новыми средствами.

Ну, а читателю можно в третий раз напомнить о квалификации авторов, сумевших сжать (хоть и с потерей сервиса) до 98 байтов и 15 регистров код ста с лишним строк программы на паскале.

Undefined

Комментарии

Лунолёты - это конечно классика, но при наличии более совершенного оборудования можно уточнить и саму математику лунолётов. Например при маневрировании вместо равноускоренного движения использовать движение с постоянной тягой и переменной массой. Скорость рассчитывается по формуле Циолковского, для координаты эту формулу можно проинтегритровать по времени, а для ускорения - продифференцировать. Сложнее с ускорением Кориолиса. В принципе, я это уже делал когда-то под кэсио вот здесь: http://pmk.arbinada.com/node/838

Мои программируемые калькуляторы:
Б3-21, Б3-34, МК-61, МК-52, МК-85
CASIO: cfx-9850GB+, fx-9750G+, fx-9750GII, fx-9860G, Algebra fx-2.0, fx-5800P, fx-7400G+
HP: 50G, 48G, 35s
TI: Nspire-CAS, Voyage-200, 89Titanium
SHARP EL-9600G

Ценное замечание, ведь от уточнения алгоритма внешняя простота симулятора не изменится.

Посмотрим теперь, на что способен User RPL в плане наглядности :) После изучения возможностей SysRPL, стало ясно, что без особых причин лучше его не использовать.

Совпало с точностью до сотых с посадкой на мк-161 :) У меня -3.6111959 касание :) http://msvk40.livejournal.com/19580.html

Предлагаю перейти в динамику, благо там можно и в стиле Перпёлкина вертикальные полёты с посадкой в ту же точку совершать. А то пошаговое прохождение с правом подумать неопределённо долго - неспортивно :)
http://pmk.arbinada.com/node/716

Однако в лунолётах система управления тягой была другая (4-я степень толкинутости - "Нет ребята, всё было не так" :) Плавного управления тягой не было, а было именно три рычага: "угол", "топливо", "время" и кнопка "пуск". При этом настройки нового манёвра можно ввести в ходе исполнения текущего, а по окончании только нажать "пуск". Поэтому придуманный автором этой версии интерфейс не соответствует описанию моделируемого объекта.

Мои программируемые калькуляторы:
Б3-21, Б3-34, МК-61, МК-52, МК-85
CASIO: cfx-9850GB+, fx-9750G+, fx-9750GII, fx-9860G, Algebra fx-2.0, fx-5800P, fx-7400G+
HP: 50G, 48G, 35s
TI: Nspire-CAS, Voyage-200, 89Titanium
SHARP EL-9600G

Так-то, конечно, да. Но вот смог бы реальный пилот на реальном лунолете управляться с таким трехрычажным "пользовательским интерфейсом" в реальном времени?

---------------------------
Истина где-то рядом
aldebaran.ru/author/samurov_vitaliyi/kniga_dozvonitsya_do_devyi/

Вопрос привычки и тренировки. И кроме того - этот интерфейс описан в первоисточнике. В автомобиле ведь на 2 ноги 3 педали приходится, если коробка - не автомат.

Мои программируемые калькуляторы:
Б3-21, Б3-34, МК-61, МК-52, МК-85
CASIO: cfx-9850GB+, fx-9750G+, fx-9750GII, fx-9860G, Algebra fx-2.0, fx-5800P, fx-7400G+
HP: 50G, 48G, 35s
TI: Nspire-CAS, Voyage-200, 89Titanium
SHARP EL-9600G

Модификация управления, когда вместо расхода топлива и времени может задаваться тяга, обсуждалась на разборе полётов в КЭИ (см. "Техника молодежи" №5 за 1986). Там прямо говорится, что, цитирую, "такой ввод может оказаться особенно полезным при постановке игры на персональном компьютере в реальном масштабе времени". Такое управление более близко к реальному.

Подумалось, что такой замечательный материал можно было выложить в "eReader-friendly" форматах, одним файлом. Например, в .PDF, .FB2, .MOBI(.PRC) и .EPUB
Чтобы можно было бы скачивать и читать в оффлайнах.

---------------------------
Истина где-то рядом
aldebaran.ru/author/samurov_vitaliyi/kniga_dozvonitsya_do_devyi/

А какой конкретно материал и в какой компоновке имеет смысл выкладывать? Экспорт отдельных страниц в printer-friendly форматах можно прикрутить без проблем.

Ну, хотя-бы "Прогулка по лунолётам, части 1 - 4". Я сейчас руками скопипастил себе в один файл и в буклет-книжечку на цветном лазере :)

---------------------------
Истина где-то рядом
aldebaran.ru/author/samurov_vitaliyi/kniga_dozvonitsya_do_devyi/

Хорошо, надо попробовать сделать то же в опенофисном формате и экспортировать в FB2.ZIP

В тексте программы на Pascal ошибка.
При работе с программой по варианту "Лунолет-2" значение угла не будет обсчитываться в градусах.
Функция sin(x) в Pascal принимает аргумент в радианах. Т.е. для получения корректных результатов необходимо преобразовывать введенное значение градсной меры угла в радианы.
Правильный фрагмент процедуры EnterManeuver:

write('Climb angle, ° : '); readln(ac);
ac := ac * (3.1415926535 * 180); // <== перевод в радианы !!

Да, нужен перевод в градусы (в Math есть функция), спасибо за вычитку!