Files
kos-scripts/lib/moonmath.ks
2025-10-29 15:40:14 -07:00

111 lines
4.7 KiB
Plaintext

@lazyglobal off.
global moonmath is lex(
"calculate_launch", calculate_launch@,
"position", moon_position_at_time@
).
local function calculate_launch {
parameter target_inclination, azimuth, target_alt.
local time_fudge is 0.
local wait_time is 0.
local orbit_time is 0.
local burn_ra is 0.
local tt is hohmann_transfer_time(earth, target_alt, moon:obt:semimajoraxis).
print "Initial moon distance: " + moon:obt:semimajoraxis.
print "Initial travel time: " + tt.
local moon_vec is moon_position_at_time(time:seconds + tt + time_fudge).
local iterations is 0.
until iterations > 10 {
local next_tt is hohmann_transfer_time(earth, target_alt, moon_vec:mag - earth:radius).
print "Updated moon distance: " + (moon_vec:mag - earth:radius).
print "Updated travel time: " + next_tt.
local next_moon_vec is moon_position_at_time(time:seconds + next_tt + wait_time + orbit_time+time_fudge).
local err is abs((next_moon_vec - moon_vec):mag).
print "Position Error " + iterations + ": " + err.
set moon_vec to next_moon_vec.
set tt to next_tt.
set iterations to iterations + 1.
local rm is moon_vec:normalized.
local rt is(-1) * rm.
set burn_ra to mod(arctan2(rt:y,rt:x) + 360, 360).
// `w` is the unit vector normal to the launch orbital plane
local w_z is cos(target_inclination).
local w_y_1 is (0 - w_z*rm:y*rm:z + rm:x*sqrt(1 - rm:z*rm:z - w_z*w_z)) / (rm:x*rm:x + rm:y*rm:y).
local w_y_2 is (0 - w_z*rm:y*rm:z - rm:x*sqrt(1 - rm:z*rm:z - w_z*w_z)) / (rm:x*rm:x + rm:y*rm:y).
local w_x_1 is (0 - w_y_1*rm:y - w_z*rm:z)/rm:x.
local w_x_2 is (0 - w_y_2*rm:y - w_z*rm:z)/rm:x.
local launch_ra_1_cos is (w_z*w_x_1*sin(ship:latitude) + w_y_1*cos(ship:latitude)*cos(azimuth)) / (w_z*w_z-1)*cos(ship:latitude).
local launch_ra_2_cos is (w_z*w_x_2*sin(ship:latitude) + w_y_2*cos(ship:latitude)*cos(azimuth)) / (w_z*w_z-1)*cos(ship:latitude).
local launch_ra_1_sin is (w_z*w_y_1*sin(ship:latitude) - w_x_1*cos(ship:latitude)*cos(azimuth)) / (w_z*w_z-1)*cos(ship:latitude).
local launch_ra_2_sin is (w_z*w_y_2*sin(ship:latitude) - w_x_1*cos(ship:latitude)*cos(azimuth)) / (w_z*w_z-1)*cos(ship:latitude).
local launch_right_ascension_1 is arctan2(launch_ra_1_sin, launch_ra_1_cos).
local launch_right_ascension_2 is arctan2(launch_ra_2_sin, launch_ra_2_cos).
local current_right_ascension is earth:rotationangle + ship:longitude.
local wait_angle_1 is mod(360 + launch_right_ascension_1 - current_right_ascension, 360).
local wait_angle_2 is mod(360 + launch_right_ascension_2 - current_right_ascension, 360).
print "Need to rotate " + wait_angle_1 + " or " + wait_angle_2 + " degrees.".
local wait_angle is min(wait_angle_1, wait_angle_2).
print "Choosing " + wait_angle.
set wait_time to body:rotationperiod * wait_angle / 360.
print "That will take " + wait_time + " seconds.".
local launch_ra is wait_angle + earth:rotationangle + ship:longitude.
local orbit_angle is mod(burn_ra - launch_ra + 360, 360).
local orbit_frac is orbit_angle / 360.
set orbit_time to orbit_frac * 90 * 60.
if err < (moon:radius / 100) {
break.
}
}
return lex(
"launch_time", time:seconds + wait_time,
"moon_altitude", moon_vec:mag - earth:radius,
"burn_longitude", burn_ra
).
}
local function moon_position_at_time {
parameter t.
return position_at_time(moon, t).
}
global function position_at_time {
parameter bod, t.
local bod_orbit is bod:obt.
local delta_t is t - bod_orbit:epoch.
local revolutions is delta_t / bod_orbit:period.
local mean_anomaly is mod(revolutions * 360 + bod_orbit:meananomalyatepoch, 360).
local e is bod_orbit:eccentricity.
local e2 is e*e.
local e3 is e2*e.
local true_anomaly is mean_anomaly + (2*e - e3/4)*sin(mean_anomaly) + 5/4*e2*sin(2*mean_anomaly)+13/12*e3*sin(3*mean_anomaly).
local radius is bod_orbit:semimajoraxis * (1 - e2) / (1 + e*cos(true_anomaly)).
local sin_lan is sin(bod_orbit:longitudeofascendingnode).
local cos_lan is cos(bod_orbit:longitudeofascendingnode).
local sin_inc is sin(bod_orbit:inclination).
local cos_inc is cos(bod_orbit:inclination).
local sin_anom is sin(bod_orbit:argumentofperiapsis + true_anomaly).
local cos_anom is cos(bod_orbit:argumentofperiapsis + true_anomaly).
local x is radius * (cos_lan*cos_anom - sin_lan*sin_anom*cos_inc).
local y is radius * (sin_lan*cos_anom + cos_lan*sin_anom*cos_inc).
local z is radius * sin_inc * sin_anom.
return V(x, y, z).
}