Last active
August 31, 2024 17:34
-
-
Save werediver/a2610f36f1ce8e13d97bfd2deee970b0 to your computer and use it in GitHub Desktop.
A lunar lander autopilot for lunar.unnecessarymodification.com
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A lunar lander autopilot for https://lunar.unnecessarymodification.com/ | |
function clamp(x, a, b) { | |
return Math.min(Math.max(x, a), b); | |
} | |
function threshold(x, th) { | |
return Math.abs(x) >= th ? x : 0; | |
} | |
function spow(x, p) { | |
return Math.sign(x) * Math.pow(Math.abs(x), p); | |
} | |
function nangle(a) { | |
if (a > 180) { | |
return 360 - a; | |
} else if (a <= -180) { | |
return 360 + a; | |
} | |
return a; | |
} | |
class Vec { | |
constructor(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
static fromAngle(a) { | |
let arad = a / 180 * Math.PI; | |
return new Vec( | |
-Math.sin(arad), | |
-Math.cos(arad), | |
); | |
} | |
norm() { | |
let len = this.abs(); | |
return new Vec( | |
this.x / len, | |
this.y / len, | |
); | |
} | |
dot(v) { | |
return this.x * v.x + this.y * v.y; | |
} | |
angle() { | |
// The reference direction is towards the ground, same as the lander orientation. | |
return nangle(-Math.atan2(this.y, this.x) * 180 / Math.PI - 90); | |
} | |
abs() { | |
return Math.sqrt(this.x * this.x + this.y * this.y) | |
} | |
neg() { | |
return new Vec( | |
-this.x, | |
-this.y, | |
); | |
} | |
add(v) { | |
return new Vec( | |
this.x + v.x, | |
this.y + v.y, | |
); | |
} | |
sub(v) { | |
return new Vec( | |
this.x - v.x, | |
this.y - v.y, | |
); | |
} | |
mul(k) { | |
return new Vec( | |
this.x * k, | |
this.y * k, | |
); | |
} | |
} | |
let { | |
x_position, | |
altitude, | |
angle, | |
userStore, | |
log, | |
plot | |
} = arguments[0]; | |
let pos = new Vec(x_position, altitude); | |
let a = angle; | |
if (!("pos" in userStore)) { | |
userStore.pos = pos; | |
userStore.a = a; | |
return { | |
rotThrust: 0, | |
aftThrust: 0, | |
userStore: userStore, | |
}; | |
} | |
let v = pos.sub(userStore.pos); | |
let w = a - userStore.a; | |
let vset = new Vec( | |
threshold(v.x * 0.8, 0.5), | |
Math.max(v.y, spow(-pos.y / 70, 1.2)), | |
); | |
let verr = vset.sub(v); | |
let aset = clamp(v.add(verr.mul(0.8)).angle(), -90, 90); | |
let aerr = aset - a - spow(w, 1) * 4; | |
let wset = clamp(spow(threshold(aerr / 20, 0.1), 0.5), -4, 4); | |
let werr = wset - w; | |
let thrust = clamp(Vec.fromAngle(a).dot(verr.neg().norm()) * verr.abs() * 2.5, 0, 1); | |
thrust_rot = clamp(werr, -1, 1); | |
plot({ | |
vset: vset.abs(), | |
v: v.abs(), | |
thrust: thrust * 10, | |
}); | |
userStore.pos = pos; | |
userStore.a = a; | |
return { | |
rotThrust: thrust_rot, | |
aftThrust: thrust, | |
userStore: userStore, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment