-
Notifications
You must be signed in to change notification settings - Fork 1
/
stager.ks
218 lines (196 loc) · 7.46 KB
/
stager.ks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//KOS
// A function that will detect if there's a need to stage right now.
// Takes no arguments, but returns a boolean:
//
// false = Staging did not occur.
// true = Staged because of a flameout.
//
// Call repeatedly in a program's main loop as you are burning.
@LAZYGLOBAL OFF.
local g0 is 9.81.
function stager {
parameter stg_eList is 0. // IN/OUT: list of engins on ship. Edited when staging to remove engines no longer attached.
parameter zeroThrot is false. // set to true if you want ot zero throttle on staging because RO with ullage.
local did_stage is false.
local want_stage is false.
local new_engs is stg_eList.
local unused_engs_exist is false.
local reason is "".
local ignited_before is get_ignited_set(stg_eList).
set g0 to body:mu/((altitude+body:radius)^2).
// Maxthrust falsely reads zero sometimes when on rails, making
// this check stage unnecesarily. Skip check if on rails:
if (kuniverse:timewarp:mode = "RAILS" and kuniverse:timewarp:warp > 0) or
not(ship:unpacked) {
return false.
}
if new_engs:istype("scalar") {
local elist is 0.
list engines in elist.
for eng in elist {
new_engs:add(eng).
}
}
// simple dumb - check if nothing active,
// then stage:
if ship:availablethrust = 0 {
set want_stage to true.
if throttle > 0 {
if defined(LIB_RO) and LIB_RO and engines_more_ignitions() {
print "Stager found current engine has more ignitions. Trying again before giving up on this stage.".
set want_stage to false. // don't try to stage after all if we can re-ignite this engine.
// these are from lib/ro.ks
push_rcs_until_ullage_ok(new_engs, 10, true).
attempt_reignition(new_engs).
}
}
set reason to "Staged because availablethrust = 0 when throttle=" + round(throttle,3).
if zeroThrot lock throttle to 0. wait 0.
}
// Visited_engs is a string used like an array where each char is a bool.
// (space ' ' = not visited, and 'V' = visted.)
local visited_engs is uniqueset().
for stg_eng in new_engs {
if not(visited_engs:contains(stg_eng)) {
if stg_eng:ship = ship { // skip parts in the list no longer attached, if there are any
if not(probably_sepratron(stg_eng)) {
if (stg_eng:ignition and is_flameout(stg_eng)) {
local all_out is true.
// It doesn't count as flamed out if it has symmetrical partners still going:
// (Needed for RealFuels where there is a second or two of random variance
// in booster duration so some will flameout while others are still going.)
for idx in range(1, stg_eng:SYMMETRYCOUNT) {
local eng is stg_eng:SYMMETRYPARTNER(idx).
if not(eng:ignition) or not(is_flameout(eng)) {
set all_out to false.
visited_engs:add(eng). // don't need to check again in the above loop.
}
}
if all_out {
set reason to "Staged because engine '" + stg_eng:title + "' (and its symmetrical partners) flamed out when throttle=" + throttle.
wait 0.
// If NO engines, kill throttle while transitioning.
// If *some* thrust (i.e. we staged a side booster but the core is still going), don't
// zero throttle as that would kill the still working engine:
if zeroThrot and ship:maxthrust = 0 {
lock throttle to 0.
wait 0.
}
set want_stage to true.
}
} else { // At least 1 engine exists that is either still running or hasn't been started:
set unused_engs_exist to true.
}
}
}
}
}
if want_stage and unused_engs_exist {
wait until stage:ready.
if defined(LIB_RO) and LIB_RO {
push_rcs_until_ullage_ok(new_engs, 10, true).
}
stage.
print reason.
wait 0. // make decoupled engines go away before making list again.
list engines in new_engs.
local new_igniteds is get_new_igniteds(new_engs, ignited_before).
if new_igniteds > 0 {
print "Stager just ignited " + new_igniteds + " new engine(s).".
print " |-- Waiting for new engine(s) to spool some thrust up.".
local startwait is time:seconds.
until availablethrust > 0 {
if (time:seconds > startwait + 6) {
print " |-- ERROR - after 6 seconds still no thrust at all.".
break.
}
}
print " `-- Done Waiting.".
}
set did_stage to true.
}
// If we edited the list, edit the caller's list too:
if did_stage and not stg_eList:istype("scalar") {
stg_eList:CLEAR().
for eng in new_engs {
stg_eList:ADD(eng).
}
}
return did_stage.
// How many engines in the list are ignited that weren't in the old list?
function get_ignited_set {
parameter eng_list.
local eng_set is uniqueset().
for eng in eng_list {
if eng:ignition
eng_set:add(eng:UID).
}
return eng_set.
}
// How many engines in the new list are ignited now that weren't in the old list?
function get_new_igniteds {
parameter eng_list, old_ign_set.
local num is 0.
for eng in eng_list {
if eng:ignition and not(old_ign_set:contains(eng:UID)) {
set num to num + 1.
}
}
return num.
}
// Is this flamed-out engine likely just a tiny sepratron and is
// not actualy a booster in need of decoupling? This check
// had to be more complex to handle RP-1 since RP-1 has many
// little parts that can serve as sepratrons:
function probably_sepratron {
parameter eng.
if eng:name = "sepMotor1" or eng:tag = "flameout no stage"
return true.
// If its direct parent is a decoupler, it has to be a decouple-able
// booster. It can't be a sepratron:
if eng:hasparent and eng:parent:istype("decoupler")
return false.
// The above checks should get a trustable answer in most cases.
// From here down it gets a bit guess-y and heuristic:
// If it's small AND disobeys the throttle, it's probably(?) a sepratron:
if eng:throttlelock and eng:mass < 0.2
return true.
return false.
}
// This check now has to be more complex because RP-1 can make
// engines flameout just shy of using up all the fuel, which means
// they're flamed out even though the :flameout suffix won't report
// "true". (it appears as if the suffix is set based on fuel left.)
function is_flameout {
parameter eng.
// Thrust to weight of the engine itself (can it push its own mass):
function self_TWR {
return eng:thrust/(eng:mass*g0).
}
// Something in RP-1 is making a newly throttled-up engine not
// start returning a nonzero value for several ticks. So this needs to
// continue being true over a few ticsk to assume it really is flamed out:
local twr is 0.
local prev_twr is -1.
for tries in range(0,2) {
set twr to self_TWR().
if eng:flameout or
(
// Engine thrust is pathetic..
eng:ignition and throttle > 0 and twr < 0.3
and
// .. and it's getting worse (rather than still spooling up):
twr < prev_twr
) {
// Try again after a moment. Verify this is true several
// times in a row before believing it:
wait 0.2.
} else {
return false.
}
set prev_twr to twr.
}
// Only if it appeared to be flamed out two tries in a row are we sure of this:
return true.
}
}