Автор: Pand5461 Категория: Kerbal Space Program » Гайды

KerboScript в примерах и задачах. Часть 2.5. Триггеры. Завершение спутниковой сети

Часть 0. Базовые функции
Часть 1. Выход на орбиту Кербина
Часть 2. Гомановский переход и запуск первого ретранслятора
В предыдущей части обещалась сеть из четырёх спутников, из которых был запущен один. Здесь описан запуск остальных. В качестве знакомства с новой функцией KerboScript рассмотрим триггеры.
Запуск оставшихся трёх спутников, по сути, повторяет запуск первого с единственным нюансом: на целевую орбиту они должны выходить не абы куда, а со сдвигом на 90, 180 и 270 градусов относительно первого спутника. Это требует точного расчёта времени прожига для создания переходной орбиты. Расчёт, впрочем, не слишком сложен.
Переход между орбитами

Рисунок 1. К расчёту выхода на переходную орбиту. Положительное направление отсчёта углов показано зелёным, отрицательное - красным.

Смотрим рисунок 1. Сумма угла φ1 между радиус-векторами аппарата и цели из центра Кербина в апоцентре и угла поворота цели φt за время трансфера за вычетом угла между аппаратом и целью в перицентре φ0 (углы считаются с учётом направления поворота, т.е. φ0 и φ1 на рисунке имеют разные знаки) равна 180o, или φ0 = φ1 + |Δφt| - 180o.
Рассчитав угол φ0 по желаемому φ1, рассчитываем время манёвра. Если наш аппарат и цель движутся по круговым орбитам на восток, то, принимая во внимание положительное направление углов в KSP по часовой стрелке,
φsat = φ0sat - t/Tsat×360o
φtgt = φ0tgt - t/Ttgt×360o
φsat-tgt = φtgt - φsat = φ' + (t/Tsat - t/Ttgt)×360o
где Tsat и Ttgt - периоды орбит аппарата и цели, соответственно.
В нашем случае необходимо по желаемому углу φsat-tgt в конце трансфера найти угол φ', под которым начинать манёвр. В качестве времени t берётся половина периода трансферной орбиты. Период вычиcляется по известной величине большой полуоси:
T = 2πA3/21/2
Большая полуось A переходной орбиты, очевидно, равна полусумме радиусов начальной и финальной орбит.
Используя эту математику, слегка переписываем функцию расчёта переходного манёвра. Теперь она будет брать в качестве аргументов желаемый объект и желаемый угол в апоцентре между направлениями на аппарат и на цель.

function transfernode {
  parameter tgt is Mun.
  parameter phitotgt is 0. // при запуске без аргументов выводим на траекторию столкновения с Муном
  
  // расчёт dV
  local newsma to tgt:orbit:semimajoraxis.
  local r0 to orbit:semimajoraxis.
  local v0 to velocity:orbit:mag.
  
  local a1 to (newsma + r0)/2. // большая полуось переходной орбиты
  local Vpe to sqrt( body:mu * ( 2/r0 - 1/a1 ) ).
  local deltav to Vpe - v0.
  
  // расчёт точки манёвра
  local t12 to constant:pi * a1^1.5 / body:mu^0.5. // время прохождения от перицентра до апоцентра
  local tomega to 360/tgt:orbit:period.
  local phitrans to phitotgt + tomega*t12 - 180. // под этим углом нужно начинать манёвр
  
  local phinow to 180 - vang( body:position, tgt:position - body:position ).
  if vcrs( body:position, tgt:position ):y > 0 { set phinow to 360 - phinow. }
  local omegaeff to 360/orbit:period - tomega.  // угловая скорость, с которой мы движемся относительно цели
  local etatrans to ( phitrans - phinow ) / omegaeff. // когда должен быть манёвр
  // если нет времени на манёвр, он переносится на следующий период
  if etatrans < (deltav * mass / ship:availablethrust + 30) { set etatrans to etatrans + 360/abs(omegaeff). }
  
  print "Transfer burn: " + round(v0) + " -> " + round(Vpe) + "m/s".
  set nd to node(time:seconds + etatrans, 0, 0, deltav).
  add nd.
}

Для расчёта времени до манёвра вычисляется начальный угол φ' до цели. Для получения знака используется векторное произведение. С учётом того, что аппарат находится в начале координат, то для того, чтобы угол φ' был положителен, нужно условие сонаправленности [-RKerbin × (Rtgt - RKerbin)] с осью Y (если обе орбиты экваториальны). Свойство векторного произведения [RKerbin × RKerbin] = 0 позволяет немного сократить условие положительности угла до [RKerbin × Rtgt]y < 0, что и сделано в скрипте. Расчёт потребной дельты аналогичен тому, что было для запуска первого спутника.
Теперь с этой новой функцией расчёта переходной орбиты можно запустить три новых крафта, аналогичных KommSat из первой части, указав им выходить на одну орбиту с первым спутником с разницей фаз 90o, 90o и 270o - и сеть будет завершена. Но я в рамках гайда предлагаю пойти немного другим путём и запустить все три спутника на одном носителе в проекте Konstellation. Именно ради унификации с этим проектом в прошлой части программа работы носителя и не включала довывод с суборбитальной траектории силами спутника - при выводе трёх сразу доразгон силами лишь одного мне кажется нечестным.
Кто купил КоммСатов пачку...

Рисунок 2. Носитель на три спутника. Аэродинамика, бессердечная ты сволочь, центр масс заставляешь бустерами поднимать. 

Добавление к носителю от KommSat баков пообъёмнее и бустеров помощнее вполне позволяет без изменения скрипта вывести три спутника разом, оставив последние 20 м/с на сход с орбиты. А вот логику скрипта для спутников нужно несколько доработать.
Теперь у нас есть три спутника, для каждого из которых нужно сделать свой загрузочный скрипт. Рабочий скрипт желательно максимально унифицировать. Программа работы предлагается следующая:
  • Режим 1
    Ждать отделения. Для первого спутника сигнал передаёт ракета, для остальных - предыдущий спутник.
  • Режим 2
    По условному сигналу отстрелиться от пачки. Не забыть перед этим передать сигнал следующему, что дальше его очередь.
  • Режимы 3-8
    Аналогично предыдущей части гайда - выйти на целевую орбиту, только теперь с более конкретно заданными параметрами.
  • Режим 9
    Аналогично предыдущей части гайда - сидеть на целевой орбите, сориентировавшись на Солнце.

Поскольку режимы 1 и 3-9 полностью аналогичны тому, что в предыдущем гайде, нужно переписать только режим 2.
Как организовать отделение по условному сигналу? Рассмотрим такой вариант, как использование триггера. Триггер - это специальный тип условной конструкции, который организует постоянную фоновую проверку некоторого условия. При выполнении условия выполняется прерывание и выполняется код триггера. Пример:

if ship:availablethrust = 0 { stage. } // запустит следующую ступень, если в момент вызова нет доступной тяги
when ship:availablethrust = 0 then { stage. } // на каждом цикле обсчёта физики проверяет, есть ли доступная тяга
// как только тяги не станет, запускает следующую ступень

Триггеры есть двух типов: when ... then и on. Первый проверяет выполнение условия, указанного после when, второй - выполняет код, если выражение после on поменяло значение. Примеры использования:

when altitude > body:atm:height then print "We have escaped atmosphere".
lock steering to up.
stage.
wait until false.
// если в ступени достаточно топлива, чтобы при вертикальном подъёме выбросить за границу атмосферы,
// триггер сработает при пересечении границы атмосферы и выведет в консоль сообщение
// даже несмотря на вечный wait
on body:radius print "SOI has changed".
// триггер сработает, когда поменялось название центрального тела

На основе триггера on можно писать очень сложные последовательности для групп механизации, чем, по существу, мы сейчас и займёмся.
Итак, в скрипт спутника KommSat вносим следующие изменения:

function satprogram {
  if satmode = 0 {
    print "Waiting for separation.".
    wait until exists("released.txt").
    print "Separation confirmed. ".
    wait 20.
    nextmode().
  }
  if satmode = 1 {
    print "Aligning for optimal solar panel performance.".
    AlignToSun().
    wait 15.
    on rcs { nextmode(). }
  }
  until satmode > 1 {
    wait 1.
  }
  if satmode = 2 {
    confirmsep(nextsat).
    decouple(nextdecoupler).
    log "set nextsat to " + char(34) + "NULL" + char(34) + "." to "mode.ks".
    log "set nextdecoupler to " + char(34) + "NULL" + char(34) + "." to "mode.ks".
    // char(34) - это кавычка, другим способом её не залогировать
    list engines in el.
    for e in el { if not e:ignition e:activate. } // stage может не работать, если отделённый спутник окажется неактивным
    on rcs { nextmode(). }
  }
  until satmode > 2 {
    wait 1.
  }
  if satmode = 3 {
    log "set target to vessel(" + char(34) + target:name + char(34) + ")." to "mode.ks".
    transfernode(target, 90).
    nextmode().
  }
  if satmode = 4 {
    exenode().
    nextmode().
  }
  if satmode = 5 {
    wait 10.
    aponode(apoapsis).
    nextmode().
  }
  if satmode = 6 {
    exenode().
    nextmode().
  }
  if satmode = 7 {
    TrimPeriod(target:orbit:period).
    nextmode().
  }
  if satmode = 8 {
    set dish to ship:partsnamed("HighGainAntenna5")[0].
    set d to dish:getmodule("ModuleRTAntenna").
    d:doevent("activate").
    d:setfield("target", Mun).
    print "Satellite deployed to operational orbit.".
    nextmode().
  }  
  until false AlignToSun().
}

Здесь введено два триггера, которые срабатывают при изменении статуса RCS (т.е. когда мы нажимаем R на клавиатуре). Первый начинает работу в режиме 1 - после того, как спутник отделился от отработавшей свою программу части ракеты. При срабатывании этого триггера спутник переходит в режим 2 - отделяется от последующих и инициализирует следующий триггер. Далее нужно выбрать цель и снова нажать R. По второму триггеру запускается программа перехода на рабочую орбиту на 90o впереди перед целью.
Здесь нужно отметить вот что. Движок игры не позволяет всем спутникам сразу проводить манёвры в разных частях орбиты, т.е. выход на рабочую орбиту они должны производить по очереди. Это значит, что когда активный аппарат уходит далеко от остальной части, программа на них останавливается, а при переключении на них происходит перезагрузка. Перезагрузка происходит с нуля, т.е. предыдущее состояние забывается, в том числе и триггеры. В прошлой части объяснено, как во вспомогательный файл записывать режим работы, на котором программа остановилась, и потом восстановить его. Здесь же дополнительной задачей стоит то, что триггеры в коде нужно расставить так, чтобы при перезагрузке они правильно реинициализировались. Приведу пример:

runpath("mode.ks").
function nextmode {
  parameter newmode is satmode+1.
  set satmode to newmode.
  log "set satmode to " + newmode + "." to "mode.ks".
  AlignToSun().
}
// Текущий код
  if satmode = 1 {
    print "Aligning for optimal solar panel performance.".
    AlignToSun().
    wait 15.
    on rcs { nextmode(). }
  }
  until satmode > 1 {
    wait 1.
  }
  if satmode = 2 ...
// Нерабочий код
  if satmode = 1 {
    print "Aligning for optimal solar panel performance.".
    AlignToSun().
    wait 15.
    on rcs { nextmode(). }
    nextmode().
  }
  until satmode > 2 {
    wait 1.
  }
  if satmode = 3 ...

И работающий код, и нерабочий сделают одно и то же, если аппарат в промежутке ожидания триггера не будет перезагружен. Однако в случае перезагрузки они работают по-разному. В рабочем коде после перезагрузки аппарат переходит в satmode 1 и перезапускает триггер. В нерабочем же после перезагрузки аппарат оказывается в satmode 2, триггер заново не создаётся, и ожидание длится вечно.
Для обеспечения корректности работы после перезагрузки предназначено и странное логирование в satmode 2 - после того, как аппарат уже отделился, по штатным значениям ни nextsat, ни nextdecoupler найдены не будут.
Теперь поймём, что делают необъявленные пока функции confirmsep и decouple. Первая передаёт оставшейся связке, что спутник отделился, а вторая отстреливает его.
function decouple {
  parameter dcname.
  if dcname <> "NULL" {
    ship:partstagged(dcname)[0]:getmodule("ModuleDecouple"):doevent("Decouple").
  }
}
function confirmsep {
  parameter nextdrive.
  if nextdrive <> "NULL" {
    local path to nextdrive + ":/released.txt".
    create(path).
  }
}

Функция decouple сделана потому, что при разделении крафта на несколько порядок срабатывания ступеней может измениться непредсказуемым образом. Чтобы избежать срабатывания неправильного разделителя, в редакторе щёлкаем по ним ПКМ и выбираем "Change Name Tag" (в карьере доступно только после прокачки ЦВС до 2 уровня; впрочем, в крафте больше 30 частей, так что без ЦВС 2 уровня его всё равно не запустить), где называем каждый разделитель индивидуальным именем. Заодно ставим Disable Staging, чтобы исключить случайное срабатывание.
Функция confirmsep просто пишет на следующий спутник файл, который сигнализирует тому, что его очередь следующая. Проверка на NULL вводится потому, что последнему оставшемуся не надо ни отделяться, ни кому-то что-то писать.
Таким образом, основные функции для работы всех спутников одинаковы. Различаются только начальные параметры, которые записываются в boot-файлы. Пример для верхнего спутника (он первый, поскольку с него начиналась сборка):

set volume(1):name to "sat1".
if status="PRELAUNCH" { 
  copypath("0:/Konstellation.ks","satprogram.ks").
  log "local satmode to 0." to "mode.ks".
}
local nextsat to "sat2".
local nextdecoupler to "Decoupler1".
runpath("mode.ks").
runpath("satprogram.ks").
satprogram().

Как можно видеть, первой строчкой мы задаём новое имя для volume(1), что позволяет в программах, выполняемых на других модулях, не угадывать номер данного раздела, а использовать текстовый алиас. Для второго и третьего спутников, естественно, алиасы будут sat2 и sat3.
Также, по сравнению с предыдущим гайдом, несколько оптимизирован профиль запуска и подправлена функция TrimPeriod. Скачать всё вместе с крафтом можно внизу под катом.
Итак, всё готово к запуску. Пускаем ракету Konstellation, дожидаемся выхода на опорную орбиту и схода носителя. Если сверху не пролетает первый запущенный KommSat, то нужно ещё дождаться восстановления связи с KSC. При наличии связи "включение" RCS отделит первый спутник. Затем выбираем для него цель и "выключаем" RCS (если запустили триггер, не выбрав цель - программа упадёт, но не беда, достаточно перезагрузиться, набрав в консоли reboot, и сделать уже всё как надо), после чего он выходит на орбиту на 90o впереди от цели. Переключаемся на то, что осталось на опорной орбите, повторяем RCS - указать цель - RCS. После выхода второго спутника повторяем процедуру с третьим. В итоге имеем красивую группировку спутников с синхронизованными до миллисекунды периодами. Сеть должна работать как для низкоорбитальных аппаратов, так и для программы "Мун".
KerboScript в примерах и задачах. Часть 2.5. Триггеры. Завершение спутниковой сети

Рисунок 3. Quadratisch. Praktisch. Gut.

В следующей части - как попасть в Мун с опорной орбиты и как сделать это прямым пуском.

Крафт (kOS, RemoteTech), скрипты:

- + +29
У Вас НЕТ прав на выставление оценки для этой новости.
Для выставлени оценки необходимо пройти регистрацию на сайте.
Если Вы уже зарегистрировались, то войдите на сайт.
  1. 46
    Это MrKerbMan MrKerbMan - #10 0
    +3
    Вау. Вот ведь запарился человек. Давно таких годных гайдов не читал.
    »
    Написано:
    Группа: Редактор, Публ/Комм: 29/1 063

    GLORY TO KOLOBKI!!!
     
    1. 9
      Это Pand5461 Pand5461 - #20 0
      0
      Спасибо. Мне и самому польза есть - спагетти-код свой немного почистил.
      »
      Написано:
      Группа: Модераторы, Публ/Комм: 13/73
      Мои kOS скрипты: https://github.com/pand5461/kOS
       
  2. 1
    Это Airtra Airtra - #30 0
    0
    Молодец! И спасибо за интересную информацию! Ждем продолжения беспилотных миссий...
    »
    Написано:
    Группа: Посетители, Публ/Комм: 2/174
    Когда от музыки в ответ в глазах забегают картинки, послушай мой любимый сэт на 3й стороне пластинке...
     
  3. 0
    Это StanislavSay StanislavSay - #40 0
    0
    Хорошо изложено, жду продолжения)
    »
    Написано:
    Группа: Посетители, Публ/Комм: 0/2
     
  4. 0
    Это nefirma nefirma - #50 0
    +2
    Спасибо большое за цикл статей! Все хорошо разложено и объяснено, мне было полезно ;-)

    Жду продолжения!

    З.Ы. Специально ради "+" автору наконец-то зарегистрировался на этом сайте ;-)
    »
    Написано:
    Группа: Посетители, Публ/Комм: 0/1
     
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
Чат
  • Опрос
  • Стримы
    Хотите ли Вы сотрудничества между проектами SpaceDock.ru и Ангар Джеба?
Последние комментарии
  • Kuzminattor Kuzminattor написал в
    Проект "Покорение" (всего 4 комм.)
    Я бы с тобой не согласился, прогресс есть и он значительный. Просто чтобы делать всё идеально надо долго сидеть подбирать варианты для выполнения конкретной задачи, а в карьере, за что я ее и люблю, ты это еще делаешь в условиях ограниченных вариантов крафта (у меня на момент постройки этой ракеты было открыто сильно меньше половины возможных исследований ). Лично у меня просто нет времени долго сидеть и думать как сделать еще лучше, играю в своё удовольствие, так сказать работа, учеба, личная да и диплом говорить: "начинай меня делать"

    Я собственно и строю начиная с полезной нагрузки, чтобы потом было понятно какой нужен РН для вывода, то что надо массу уменьшать тоже понятно, это все же пассивный груз.

    Как там всё работает при полет я тоже знаю (во всяком случае в реальной жизни), так сказать обучение на кафедре ракетостроения не прошло даром, я бы даже сказал, что из-за этой игры я туда и поступил))
  • Kuzminattor Kuzminattor написал в
    Проект "Покорение" (всего 4 комм.)
    Ссылку пофиксил, как-то не заметил вчера.
    По скринам учту на будущие, сейчас ничего менять не буду, да и исходников уже нет.
    По избыточности - скорее всего ты прав, на конец полета у меня осталось очень много топлива, а значит можно было делать меньше, но это уже я не рассчитал, когда смотрел общую дельту (мне даже казалось, что хватит впритык)
  • Slv Slv написал в
    Проект "Покорение" (всего 4 комм.)
    По сравнению с тем "чудовищем" 4 года назад прогресс конечно есть, но что бы выбраться в плюса такими же темпами тебе еще лет 100 надо прогрессировать, ибо прогресс хоть и есть но ОЧЕНЬ маленький как йух в ледяной воде. %-)

    ЗЫ совет -- начинай строить с полезной нагрузки (последней ступени) делай ее как можно БОЛЕЕ ЛЕГКОЙ. Ставь цель -- долететь или приземлиться или собрать науку с приземления или собрать ВСЮ науку, и уже от цели начинаешь плясать....
  • Marschig Marschig написал в
    Проект "Покорение" (всего 4 комм.)
    Ну, оно может на Дюну с возвратом. И текста немало написано. С плюсами всё.

    Далее к минусам.
    Вложения сделаны неправильно. Ссылка на крафт вообще не работает, ссылки на часть скринов тоже.
    "Резать" скрины не надо. Надо пересохранять в *.jpg. 1920x1080 jpg умещается в ограничение.

    Конструкция сделана с дополнением Making History и в стоке не откроется. О чём следовало бы упомянуть. Так что даже с исправленным вложением детальных замечаний по конструкции не будет. На скринах в ряде мест выглядит избыточной.
  • Kostya88 Kostya88 написал в
    Космолет + Kerbal Operating Sy ... (всего 13 комм.)
    А, так ты в таком смысле :)
    Хм, надо попробовать, сначала проверю в какой точке радар определяет высоту, и пропишу угол тангажа под рельеф поверхности.
  • Slv Slv написал в
    Космолет + Kerbal Operating Sy ... (всего 13 комм.)
    Не в этом смысле что ты понял. %-)
    При снижении, если поверхность внизу не ровная, чтоб автопилот сравнивал реальную скорость снижения (относительно поверхности внизу) и скорость снижения например относительно моря, чтоб определять поверхность снизу ровная или с уклоном/подьемом и соответственно тангажом регулировал снижение. Ведь если это не проверять можно получить заметный +к вертикальной скорости если на горку (или предгорье, где поверхности с уклонами) какую-нибудь сесть и сломаться.
Все комментарии
Обновления на форуме
175 Всего
1 Польз.
174 Гостей
Яндекс, Google, Marschig
Онлайн список
Новостей на страницу:
Наверх