kerbal.ruвики сборки «Оператор»
Оператор / Вики / Автоматика

kOS: библиотека модулей

Как разложить скрипты по файлам-блокам вместо одной простыни: подъём, узлы, наука, телеметрия отдельно, а файл миссии только собирает их и задаёт цифры цели. С готовой структурой и разбором подводных камней.

Первый скрипт вывода на орбиту вырастает до трёхсот строк, и в нём вперемешку: расписание тангажа, политика РСУ, сбор науки, запись лога и цифры конкретного контракта. Следующая миссия — копипаст всей простыни ради двух изменённых чисел.

Лечится разбиением на модули-файлы: механика отдельно, миссия отдельно.

Готовая библиотека лежит в репозитории сборки — папка kos/: девять модулей, шаблон миссии, проверочный прогон и telnet-клиент. Ставится копированием в <KSP>/Ships/Script/. Присылать улучшения приветствуется.

Структура

0:/lib/
   util.ks     помощники: углы, векторы, азимут пуска
   ctrl.ks     политика РСУ и САС, возврат управления
   stage.ks    автостейджер
   orbit.ks    орбитальная механика и исполнение узлов
   ascent.ks   подъём: расписание тангажа, рулевой закон, тяга
   deploy.ks   раскрытие панелей и антенн
   sci.ks      сбор и передача науки
   telem.ks    телеметрия в CSV
   hud.ks      стрелки в полётном виде и надписи
0:/missions/
   dalnyak.ks  конкретная миссия: только цифры и порядок шагов

kOS поддерживает каталоги, путь вида 0:/lib/orbit.ks работает как в обычной системе. Одна оговорка из документации: на macOS и Linux имена файлов только строчными буквами — иначе kOS их не найдёт.

Как это подключается

RUNONCEPATH("0:/lib/util.ks").
RUNONCEPATH("0:/lib/orbit.ks").

RUNONCEPATH отличается от RUNPATH тем, что не выполнит файл повторно, если он уже загружен в этом запуске. Это ровно то, что нужно библиотеке: модуль orbit.ks может сам подключить ctrl.ks, и если миссия уже его подключила — второй раз он не выполнится.

Важное ограничение: RUNONCEPATH работает только внутри программы. Из терминала он выдаст ошибку — там каждый запуск и так компилируется заново.

Три правила, без которых развалится

1. Префиксы в именах. Все функции модуля начинаются с его буквы: u_ для util, o_ для orbit, a_ для ascent, s_ для stage, c_ для ctrl, d_ для deploy, t_ для telem, h_ для hud. В kOS одно глобальное пространство имён, и без префиксов два модуля рано или поздно объявят функцию с одним именем.

2. GLOBAL FUNCTION, а не просто FUNCTION. Иначе функция не будет видна вызывающему файлу.

3. Никаких побочных действий при загрузке. Модуль объявляет функции и значения по умолчанию — и всё. Ничего не печатает, ничем не рулит, ничего не открывает. Иначе порядок подключения начнёт влиять на поведение.

Настройки — лексиконом, а не глобальными переменными

Чтобы модуль подъёма ничего не знал о конкретной миссии, все параметры передаются одним словарём:

GLOBAL FUNCTION a_defaults {
  RETURN LEXICON(
    "park", 80000, "az", 90, "vVert", 60,
    "hDense", 25000, "aoaLo", 5, "aoaHi", 20,
    "hTwr", 22000, "twrMax", 2.3, "qMax", 0.32, "aoaAlarm", 14,
    "onTick", { }
  ).
}

Миссия берёт умолчания и правит только нужное:

SET CFG TO a_defaults().
SET CFG["park"] TO 80000.
SET CFG["az"] TO u_launchAzimuth(8.5).
SET CFG["onTick"] TO missionTick@.

Обратные вызовы: onTick

Модулю подъёма не место в решении, что писать в лог. Поэтому он просто зовёт функцию, которую ему дали:

LOCAL onTick IS cfg["onTick"].
UNTIL SHIP:APOAPSIS > cfg["park"] {
  ...
  onTick().
  WAIT 0.
}

Символ @ после имени функции — ссылка на неё, а не вызов. Так миссия подсовывает свой обработчик, а модуль остаётся независимым. Тот же приём в o_burn(nd, onTick@).

Следующий шаг: миссия одной строкой

Разбиение на модули убирает копипаст механики, но файл миссии всё ещё описывает порядок шагов. А порядок этот выводится из самой цели: если контракт задаёт аргумент перицентра — нужна круговая парковка и ожидание точки витка, если нет — хватит одного совмещённого прожига.

Значит и порядок можно считать. Верхний модуль принимает описание целевой орбиты и собирает профиль сам:

RUNONCEPATH("0:/lib/mission.ks").

SET s TO m_defaults().
SET s["ap"]  TO 7867078.   // апоцентр, м
SET s["pe"]  TO 3331362.   // перицентр, м
SET s["inc"] TO 20.1.      // наклонение, °
SET s["lan"] TO 250.8.     // долгота узла (-1 = не ждать окна)
SET s["aop"] TO 29.6.      // аргумент перицентра (-1 = не важен)

m_orbit(s).

Что решается без участия человека:

Решение На основании чего
Азимут пуска наклонение + поправка на вращение планеты
Ждать ли окно пуска задана ли долгота узла
Нужна ли парковка задан ли аргумент перицентра
Где делать прожиг аргумент перицентра = точка витка, где жжём
Поднимать ли перицентр сравнение с текущим
Что есть ap, а что pe если перепутаны местами — меняются молча

Файл миссии сжимается до шестнадцати строк, половина из которых комментарии. Это и есть предел разумного дробления: человек задаёт цель, скрипт выводит путь.

Как выглядит файл миссии

Ни строчки механики — только цель и порядок:

SET CFG TO a_defaults().
SET CFG["az"] TO u_launchAzimuth(8.5).
SET CFG["onTick"] TO missionTick@.

sciSweep("на столе", FALSE).
IF a_run(CFG) {
  d_all().                                  // панели и антенны
  sciSweep("вышли в космос", TRUE).
  o_burn(o_nodeRaiseOpposite(4757918), missionTick@).
  o_burn(o_nodeRaisePeri(4591510), missionTick@).
  sciSweep("целевая орбита", TRUE).
}
h_off().
c_release().

Новая миссия — копия этого файла с другими числами. Механику трогать не надо.

Что даёт разбиение

Простыня Модули
Новая миссия копипаст 300 строк копия файла на 60 строк
Починил ошибку в стейджере правишь в каждой копии правишь в одном месте
Отладка ищешь в общей куче знаешь, в каком файле смотреть
Проверка синтаксиса только запуском каждый модуль отдельно

Последнее особенно ценно вместе с kOS по telnet: пульт снаружи игры: модуль можно прогнать на площадке отдельно от остальных и увидеть все опечатки до пуска.

Подводные камни

Место на диске. У процессора CX-4181 всего 10 000 байт, у младших моделей 5 000. Библиотека туда не влезет. Но если запускать из архива (0:/…), локальный диск не нужен вовсе — файлы читаются с «земли». Для дальних аппаратов, где архив недоступен без связи, скрипты компилируют в .ksm командой COMPILE: машинный код занимает в разы меньше.

Компиляция не бесплатна. Каждый уникальный файл компилируется один раз за запуск программы и держится в памяти до её конца. Десяток мелких модулей на старте — это заметная пауза перед первой командой. Не дроби ради дробления.

Порядок подключения. Если модуль зовёт функцию из другого, тот должен быть подключён раньше. Проще всего подключать всё скопом в начале файла миссии, а в шапке каждого модуля писать, от чего он зависит.

Одно пространство имён. Глобальная переменная из модуля видна всем и может быть затёрта миссией. Префиксы обязательны и для переменных: A_CMD, T_PATH, C_RCS_UNTIL.

Кто владеет объектом. Самая коварная ошибка разбиения: при переносе кода в модуль теряется строчка, которая в простыне стояла рядом. У нас так пропал ADD nd — фабрика возвращала узел манёвра, а вешать его в план полёта было некому:

Must attach node first
At 0:/lib/orbit.ks, line 46
LOCAL dv0 IS nd:DELTAV.

Узел, не прикреплённый к плану, не отдаёт DELTAV. Компиляция проходит, ошибка вылезает в полёте — на переходе к первому прожигу.

Лечится правилом: у каждого объекта один владелец. Раз o_burn снимает узел через REMOVE, пусть он же его и вешает:

GLOBAL FUNCTION o_burn {
  PARAMETER nd.
  IF NOT HASNODE OR NEXTNODE <> nd { ADD nd. }
  WAIT 0.
  ...
  IF HASNODE { REMOVE nd. }
}

Что происходит, когда скрипт падает. kOS снимает все LOCK, и аппарат остаётся без управления — в вакууме он начинает медленно вращаться. Поэтому между этапами стоит держать SAS ON, а перед своим прожигом модуль сам делает SAS OFF: одновременно рулить kOS и САС не должны.

Что положить в такую библиотеку

Проверенный набор, который окупается с первой же второй миссии:

  • автостейджер — по отсутствию живых двигателей, а не по выгоранию (Скрипт вывода на орбиту: разбор ошибок);
  • исполнение узла — наведение, перемотка, прожиг, снятие узла;
  • подъём — расписание тангажа с ограничением угла атаки;
  • орбитальная математика — круговая скорость, Гоман, подъём апсид;
  • политика РСУ — точечное включение вместо постоянного;
  • наука — сбор и передача с оглядкой на атмосферу;
  • телеметрия — CSV с ограничением частоты записи;
  • визуальная отладка — стрелки потока и команды (kOS: интерфейс, графика и HUD).

Точные сигнатуры всех функций — kOS: справочник библиотеки kerbal.ru. Как это складывается в полёт от команды до орбиты — Автоматизация запуска по шагам.

Связанные статьи
kOS: справочник библиотеки kerbal.ruКаждая функция из lib/*.ks — что принимает, что возвращает, что делает с кораблём. Плюс порядок загрузки, соглашения об именах и настройки, которые задаются перед вызовом.Автоматизация запуска по шагамЧто происходит от строки m_orbit(s) до готовой орбиты: фазы подъёма, выбор схемы выведения, точки отказа и как читать телеметрию. Карта потока управления для отладки и для ИИ-агента.kOS: продвинутоеФункции, списки, библиотека из нескольких файлов, триггеры и их подводные камни, узлы манёвра из кода и защита от «улетел в космос».Скрипт вывода на орбиту: разбор ошибокИнженерный журнал четырёх попыток вывести спутник скриптом kOS: почему ракета кувыркалась, куда делись 1500 м/с, отчего сломались антенны и как чинится каждая из этих бед.kOS: готовые рецептыЧетыре рабочих скрипта kOS с разбором построчно: взлёт, циркуляризация, вывод спутника с заданным наклонением, посадка на Муну.