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

kOS: справочник библиотеки kerbal.ru

Каждая функция из lib/*.ks — что принимает, что возвращает, что делает с кораблём. Плюс порядок загрузки, соглашения об именах и настройки, которые задаются перед вызовом.

Точный справочник по библиотеке, которую установщик кладёт в Ships/Script/. Если kOS: библиотека модулей объясняет почему модули устроены так, то здесь — что именно вызывать: сигнатуры, возвращаемые значения и побочные эффекты.

Написано так, чтобы ответ находился за один взгляд — и человеком, и ИИ-агентом, которому дали доступ к терминалу.

Как это загружается

Модули подключаются через RUNONCEPATH, зависимости тянутся сами:

RUNONCEPATH("0:/lib/mission.ks").   // подтянет все девять модулей
Модуль Зависит от Отвечает за
util.ks углы, векторы, азимут пуска, окно узла
ctrl.ks РСУ и снятие управления
stage.ks отделение ступеней
deploy.ks раскрытие панелей и антенн
telem.ks запись телеметрии в CSV
hud.ks подписи и стрелки на экране
sci.ks сбор и передача науки
orbit.ks ctrl, stage узлы манёвра и их исполнение
ascent.ks util, ctrl, stage подъём с поверхности
mission.ks все миссия целиком
rendezvous.ks ctrl, orbit догнать аппарат на орбите — Рандеву и стыковка
transfer.ks ctrl, orbit, rendezvous перелёт к другому телу той же СВП и захват
land.ks util, ctrl, orbit деорбит и посадка на тело без атмосферы
events.ks одноразовые и сторожевые триггеры вместо линии WAIT
deepspace.ks rendezvous, transfer, deploy, sci дальний вылет одной спецификацией
audit.ks чекеры: есть ли на борту нужное под задачу и под тело

Префикс в имени = модуль. u_ — util, o_ — orbit, a_ — ascent, s_ — stage, c_ — ctrl, d_ — deploy, t_ — telem, h_ — hud, m_ — mission, r_ — rendezvous, x_ — transfer, l_ — land, e_ — events, ds_ — deepspace, q_ — audit. У науки исторически sci. Это не украшение: в kOS все имена глобальны, префикс — единственная защита от столкновений.

mission.ks — миссия целиком

Функция Принимает Возвращает
m_defaults() лексикон настроек со значениями по умолчанию
m_orbit(spec) лексикон TRUE, если вышли на орбиту; FALSE, если подъём прерван
m_report(spec) лексикон печатает «заказано / получилось»
m_tick() один проход: наука, HUD, строка телеметрии

Ключи spec:

Ключ По умолчанию Смысл
name "миссия" имя в логах и на HUD
ap 100000 целевой апоцентр, м
pe -1 целевой перицентр, м; -1 = круговая
inc 0 наклонение, °
lan -1 долгота восходящего узла, °; -1 = не ждать окна
aop -1 аргумент перицентра, °; -1 = не важен
park 80000 высота опорной орбиты, м
log "0:/mission.csv" куда писать телеметрию
hud TRUE рисовать ли подписи на экране

Перепутанные местами ap и pe функция меняет молча — на результат это не влияет.

util.ks — углы, азимут, окно пуска

Функция Принимает Возвращает
u_clamp(x, lo, hi) числа x, зажатый в границы
u_pitchOf(vec) вектор тангаж над горизонтом, °
u_vPitch() тангаж вектора скорости; на скорости < 15 м/с даёт 90
u_aoa() угол атаки, °; на скорости < 15 м/с даёт 0
u_velAzimuth() компасный курс скорости, ° (0 = север)
u_launchAzimuth(inc, vOrb) наклонение, скорость на опорной (по умолчанию 2279) азимут пуска, °
u_limitTo(from, to, lim) два вектора и предел, ° to, повёрнутый к from не дальше lim
u_bodyRot(ut) момент времени поворот планеты, °
u_lanIfLaunchNow() какой узел получится при пуске сейчас, °
u_lanWait(targetLan) узел, ° сколько секунд ждать
u_waitForLan(targetLan, tol) узел, допуск (по умолчанию 2°) ждёт окна, с перемоткой
u_argLat() аргумент широты: угол от восходящего узла до нас, °
u_utAtArgLat(target) угол, ° абсолютный момент, когда мы там окажемся

Две константы подобраны по нашей телеметрии и для другого сейва их надо перемерить:

  • U_AZ_BIAS (−0,45°) — поправка азимута. Расчёт давал 80,80°, полёт дал наклонение 8,02° вместо 8,50°.
  • U_LAN_C (91,80°) — сдвиг в модели LAN = долгота старта + поворот планеты + C. Каждая миссия печатает в конце строку калибровка узла: lon=… UT=… LAN=… — по трём таким числам константа пересчитывается.

u_limitTo — ядро защиты от заваливания: команда рулю никогда не уходит от набегающего потока дальше допустимого угла атаки. Без неё ракета переворачивается на 20 км, мы это проходили.

orbit.ks — узлы манёвра

Функции с o_node… только создают узел и возвращают его. Исполняет o_burn, он же добавляет узел в план и снимает после.

Функция Принимает Возвращает
o_mu() / o_br() гравитационный параметр / радиус тела
o_vCirc(rad) радиус от центра, м круговая скорость, м/с
o_vAt(rad, sma) радиус, большая полуось скорость по вис-виве, м/с
o_nodeCircularize() узел: скруглить в апоцентре
o_nodeCircularizeAtPeri() узел: скруглить в перицентре — для захвата с гиперболы, где апоцентра нет
o_nodeRaiseOpposite(alt) высота, м узел: поднять противоположную апсиду
o_nodeRaisePeri(alt) высота, м узел: поднять перицентр
o_nodeRaiseApoAt(alt, ut) высота, момент узел: поднять апоцентр в заданный момент
o_burn(nd, onTick) узел, необязательная функция исполняет: наводится, перематывает, жжёт, снимает

Что o_burn делает с кораблём: выключает SAS (одновременно с kOS рулить нельзя), даёт импульс РСУ на разворот, ждёт наведения с точностью 2°, перематывает до половины времени прожига, жжёт с затуханием тяги к концу, стейджит при выгорании, затем снимает узел, включает SAS и ждёт полсекунды. onTick вызывается каждый проход — туда передают m_tick@ для логов.

Почему ADD внутри o_burn, а не в фабриках. У неприкреплённого узла нельзя спросить :DELTAV — kOS отвечает «Must attach node first» прямо в полёте. Владелец один, место одно.

ascent.ks — подъём

Функция Принимает Возвращает
a_defaults() лексикон настроек подъёма
a_pitchSchedule(hgt) высота, м нужный тангаж, °
a_steer(cfg) настройки направление для LOCK STEERING
a_throttle(cfg) настройки газ 0…1
a_run(cfg) настройки TRUE при успехе, FALSE при аварии

Настройки cfg: park (апоцентр опорной), az (азимут), vVert (до какой скорости идём строго вверх, 60 м/с), hDense (граница плотного воздуха, 25 км), aoaLo/aoaHi (допуск угла атаки: 5° внизу, 20° выше), hTwr/twrMax (потолок перегрузки 2,3 до 22 км), qMax (потолок напора 0,32 атм), aoaAlarm, onTick.

a_run вешает аварийный триггер: падение со скоростью снижения ниже −10 м/с в диапазоне 3–60 км глушит тягу и возвращает FALSE.

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

transfer.ks — перелёт к другому телу

У CelestialBody в kOS те же суффиксы ORBIT/POSITION/VELOCITY, что у Vessel — поэтому геометрия перелёта к Муну использует те же функции r_nodeMatchPlanes/r_nodeTransfer из rendezvous.ks, что и рандеву с аппаратом. Новое здесь — прицельный перицентр у чужой сферы влияния и захват с гиперболы.

Функция Принимает Возвращает
x_hasEncounter() TRUE, если следующий патч орбиты — уже в чужой СВП
x_encounterPeri() высота перицентра в этой встрече, м; -1 без энкаунтера
x_nodeAimPeri(wantAlt, ut) цель, момент узел: боковой прожиг, знак подобран пробой (как в r_nodeMatchPlanes)
x_aimPeri(wantAlt, tol, tries) цель, допуск (15 км), попыток (6) подгоняет перицентр итеративно, возвращает успех
x_go(body, wantAlt) тело, высота круговой весь перелёт: плоскость → переход → прицел → захват

x_go наклонение к телу не подгоняет — закладывай его на пуске у родителя, как в Рандеву и стыковка: смена плоскости уже у цели дорога.

land.ks — посадка на тело без атмосферы

Функция Принимает Возвращает
l_vsLimit(alt) радарная высота, м предел вертикальной скорости, м/с — интерполяция таблицы из Возврат на Кербин
l_nodeDeorbit(targetAlt) высота прицела (0) узел: опустить перицентр к поверхности
l_descend(onTick, vertAt) колбэк, высота перехода на вертикаль (150 м) садится, держит l_vsLimit
l_go(targetAlt, onTick) высота прицела, колбэк деорбит + посадка одной командой
l_defaults() / l_run(spec) — / лексикон посадка одной спецификацией, ключи targetAlt, vertAt, sci
l_facingBody(refBody) тело TRUE, если корабль сейчас над полушарием, обращённым к нему
l_waitFacing(refBody, maxOrbits) тело, предел витков (3) ждёт витка над этим полушарием

Только для тел без атмосферы: с воздухом сопротивление меняет картину, там нужна связка «парашют → l_descend», а не она одна.

l_facingBody/l_waitFacing дают полушарие, не точку: деорбит стартует над нужной стороной, снос при спуске небольшой относительно размера тела, но не нулевой — точное прицеливание в координаты не решено (см. kos/README.md, список открытого). Вызывать l_facingBody(refBody) имеет смысл только когда SHIP:BODY — не сам refBody: если тело совпадает, направление на него вырождается в нулевой вектор.

events.ks — событийная автоматика

Замена линии WAIT/IF на список «что и когда» — переживает варп и смену фокуса, потому что условия проверяются заново, а не хранятся в счётчиках.

Функция Принимает Возвращает
e_new() пустой список событий
e_once(events, name, cond, fn) список, имя, условие, действие добавляет одноразовый триггер
e_watch(events, name, cond, fn) список, имя, условие, действие добавляет сторожевой триггер (срабатывает каждый тик, пока условие истинно)
e_tick(events) список один проход, TRUE если что-то сработало
e_run(events, stopCond, onTick) список, стоп-условие, колбэк крутит e_tick до стоп-условия
e_done(events) список все ли одноразовые события уже сработали

deepspace.ks — дальний вылет одной спецификацией

Та же идея, что у m_orbit(spec): файл миссии содержит только параметры, решения — здесь.

Функция Принимает Возвращает
ds_defaults() лексикон настроек дальнего вылета
ds_release(tag) метка декаплера ищет деталь с этой меткой и отделяет; TRUE, если нашла
ds_run(spec) лексикон перелёт → развод отделяемых нагрузок → картографическая орбита ИЛИ посадка шины, весь цикл

Ключи spec: name, body (тело, по умолчанию MUN), parkAlt (низкая рабочая орбита захвата), mapAlt (картографическая полярная; 0 — не подниматься), landers (список меток декаплеров, пусто = отделяемых нагрузок нет), selfLand (TRUE — после развода нагрузок шина деорбитит и садится сама вместо подъёма в mapAlt), faceBody (-1 — без разницы; иначе тело, например KERBIN, — ждать витка над обращённым к нему полушарием перед посадкой), sci (снимать/передавать науку на рубежах и при каждом отделении), log.

landers — общее имя: под этой меткой может быть посадочный зонд, а может быть спутник созвездия, который остаётся на орбите — ds_release их не различает, разница только в том, что с ними происходит дальше. ds_run разносит их по равным долям витка, чтобы у них были разные наземные треки/фазы — точный выбор орбиты или биома под каждым остаётся за игроком, автомат этого не гарантирует. Сканеры SCANsat ds_run включает сразу после захвата (d_scan() из lib/deploy.ks) — чем раньше, тем больше витков успеют отснять.

audit.ks — чекеры по задаче и по телу

Проверка не пуском, а чтением деталей на столе: находит ли скрипт нужный прибор по названию, нужный модуль по классу, нужное число меток лендеров. Отдельно от libcheck.ks — тот ловит опечатки в коде, audit.ks смотрит, чем укомплектован конкретно ЭТОТ корабль.

Функция Принимает Возвращает
q_hasPart(substr) подстрока (без учёта регистра) есть ли деталь с таким названием
q_countPart(substr) подстрока сколько таких деталей
q_hasModule(substr) подстрока класса модуля есть ли деталь с таким модулем (ModuleDeployableAntenna, Ablator…)
q_countTag(prefix) префикс метки сколько деталей с меткой, начинающейся так — считает лендеров lander-1/2/3
q_ecCapacity() суммарная ёмкость электрозаряда, ЭЧ
q_report(title, items) заголовок, список LEXICON("label","ok") печатает [OK]/[!] по каждому пункту, возвращает число непройденных

Готовые списки под контракты (образцы, не полная база — паков контрактов десятки, добавляй свои по этому же шаблону):

Функция Что проверяет
q_taskScanAltimetry() высотомер SCAN/R-3B/SAR-* на борту, есть ёмкость под заряд
q_taskScanBiome() мультиспектральный сканер (SCAN/MS-1/MS-R/MS-2A), заряд
q_taskLandBiomes(n) n меток lander-N, n зондовых ядер сверх шины
q_taskConstellation(n, prefix) n меток prefix + N (по умолчанию sat-), антенна на борту
q_taskConstellationByTitle(n, titleSubstr) то же самое без тегов — n деталей с названием, содержащим titleSubstr
q_taskGooOrMaterials() капсула слизи или контейнер материаловедения

И по телу — q_bodyDefaults() / q_bodyChecklist(spec) и два готовых пресета под Мун: q_bodyMun() (профиль «остаться картографом на орбите» — Δv ≥ 1150 м/с, заряд на теневую сторону ≥ 1400 ЭЧ либо РИТЭГ) и q_bodyMunTour() (профиль «развести спутники и сесть без обратного взлёта» — Δv ≥ 2700 м/с от опорной Кербина, заряд не проверяется: шина не сидит месяцами на орбите). spec["dv"] сверяется со стоковым счётчиком дельта-v игры (SHIP:DELTAV:VACUUM, KSP 1.11+) — если в настройках отключён Advanced Tweakables → Delta-V readout, вернётся 0, и это будет читаться как «не хватает», хотя на деле просто выключен показ.

План: телеметрия + чекеры + Δv-бюджет одной спецификацией

Тот же принцип, что у m_orbit/ds_run: логика в библиотеке, файл под конкретный корабль — только параметры.

Функция Принимает Возвращает
q_shipReport() печатает имя, тело, статус, высоту, массу, Δv, заряд, число деталей
q_dvBudget(budget) список LEXICON("label","dv","optional") печатает бегущий итог по обязательным пунктам, сверяет с SHIP:DELTAV:VACUUM, возвращает сумму
q_liveTransfer(dest) тело если уже на стабильной орбите — считает узел перехода к dest через r_nodeTransfer, НЕ ставит его (ADD не вызывается); требует RUNONCEPATH lib/transfer.ks
q_plan(spec) лексикон {"tasks": [...], "budget": [...]} телеметрия + все чекеры из tasks + бюджет, возвращает общее число провалов

Спецификацию под конкретный корабль в файл не сохраняют — собирают tasks/budget из готовых q_task*/q_body* и шлют q_plan(spec) прямо в терминал (через telnet — одной строкой, см. kOS по telnet: пульт снаружи игры). Ни двигателей, ни узлов манёвра — только чтение и арифметика, поэтому это безопасно гонять хоть каждый раз заново.

Чего чекер не видит — и не должен: хватит ли топлива именно этому лендеру, мягко ли он сядет, попадёт ли в нужный биом. Готовый прогон печатает список таких слепых зон отдельной строкой — не молчит о том, что не проверено.

sci.ks — наука

Функция Принимает Возвращает
sciVacuum() TRUE, если выше атмосферы
sciZone() текущая зона: "старт", "атмосфера низко", "космос высоко"
sciModules() список научных модулей на борту
sciSweep(label, mayTransmit) подпись, можно ли передавать снимает показания, при TRUE передаёт
sciTick() сработает только при смене зоны
sciRetry() добор: сколько передано; 0 = больше нечего
sciAudit() на столе, ничего не деплоит: сколько модулей соберёт sciSweep, сколько известны как несовместимые, список незнакомых

Настройки: SCI_MIN_VALUE (0.1) — порог, ниже которого замер не передаётся, а сбрасывается; SCI_ALLOW_ANIM — разрешить выдвижные приборы (включается только в вакууме); SCI_INCOMPATIBLE — модули с «наука» в названии класса, но НЕ с интерфейсом HASDATA/DEPLOY/DATA/TRANSMIT/DUMP (пример — SCANexperiment у SCANsat: своё событие «анализировать данные: тип», HASDATA на нём падает с «Suffix not found» — поймано на живом корабле через telnet); счётчики SCI_SENT, SCI_DUMPED.

Три правила, оплаченные потерянными антеннами, приборами и одним падением скрипта:

  1. В атмосфере науку только собираем. Передача сама раскрывает складную антенну — в набегающем потоке её срывает.
  2. Модули перебираются по индексу, а не через GETMODULE: тот возвращает только первый, а у мачты CA-AMA научных модулей два.
  3. Не добавляй модуль в SCI_PASSIVE/SCI_ANIM по названию класса. «Science»/«Experiment» в имени не гарантирует совместимый интерфейс — проверяй суффиксы на реальном корабле (m:HASDATA и т.п.) до того, как включать в сбор, иначе sciSweep падает прямо во время полёта, а не на столе. sciAudit() для этого и существует.

Остальные модули

Функция Что делает
s_liveEngines() сколько двигателей работает и не выгорело
s_need() нужна ли отсечка: ступень есть, готова, живых двигателей нет
s_tick() отделяет, если нужно; TRUE = отделили
c_rcs(want) включает РСУ, если монотоплива больше резерва (C_MONO_RESERVE, 4)
c_rcsPulse() включить РСУ на C_RCS_HOLD секунд (3)
c_rcsTick() выключить, когда время помощи истекло
c_release() снять всё управление: газ, курс, РСУ, включить SAS
d_vacuum() TRUE выше атмосферы
d_all() раскрыть панели и антенны; в атмосфере не делает ничего
d_scan() включить сканирование на всех сканерах SCANsat на борту
t_init(path, header) начать лог, записать заголовок
t_line(csv) строка в лог, не чаще T_EVERY (0,5 с)
t_elapsed() секунд с начала записи
h_init() / h_off() включить/выключить экранные подписи
h_tick(cmd) обновить стрелки и цифры
h_say(text) подпись на экране

d_all ищет антенны по модулю ModuleDeployableAntenna и жмёт событие раскрытия — по-русски или по-английски, в зависимости от языка игры.

d_scan ищет модуль SCANsat (не путать с SCANexperiment на той же детали — это другой модуль, научный бонус-отчёт, несовместимый с sciSweep, см. SCI_INCOMPATIBLE в sci.ks) и жмёт любое его событие, начинающееся с «запустить сканирование» — у модуля их и есть ровно одно на тип сенсора (radar, multispectral…). У SCANsat-деталей нет отдельного «раскрыть»: сами они не складываются, включать нечего кроме сканирования. Названия событий сверены на живом корабле через telnet, не угаданы по каталогу.

Как проверить, не запуская

На стартовом столе прогоняется весь набор без включения двигателей:

RUNPATH("0:/check/libcheck.ks").

Это ловит опечатки и занятые встроенные имена — то, что иначе выясняется на 20-й секунде полёта. Список запрещённых имён (R, V, ALT, TIME, NODE, STAGE…) — в kOS: отладка и лимиты.


Что происходит от команды до орбиты по шагам — Автоматизация запуска по шагам. Устройство и обоснование решений — kOS: библиотека модулей. Данные корабля — kOS: справочник по данным корабля.

Связанные статьи
kOS: библиотека модулейКак разложить скрипты по файлам-блокам вместо одной простыни: подъём, узлы, наука, телеметрия отдельно, а файл миссии только собирает их и задаёт цифры цели. С готовой структурой и разбором подводных камней.Автоматизация запуска по шагамЧто происходит от строки m_orbit(s) до готовой орбиты: фазы подъёма, выбор схемы выведения, точки отказа и как читать телеметрию. Карта потока управления для отладки и для ИИ-агента.kOS: справочник по данным корабляЧто можно спросить у корабля из скрипта — высота, скорость, орбита, детали, ресурсы, наука, время. Сгруппировано по задачам, с рабочими примерами и граблями, на которые мы наступали.kOS: отладка и лимитыПочему скрипт «висит», ест кадры и не слушается руля: лимит инструкций, WAIT в циклах, блокировки управления, ошибки парсера и рабочий процесс с внешним редактором.Рандеву и стыковкаКак догнать аппарат на орбите: подгонка плоскости, фазовое окно, переход, гашение относительной скорости. Скриптом и руками, с разбором того, где автоматика заканчивается.