Skip to content

Instantly share code, notes, and snippets.

@werediver
Last active August 31, 2024 17:34
Show Gist options
  • Save werediver/a2610f36f1ce8e13d97bfd2deee970b0 to your computer and use it in GitHub Desktop.
Save werediver/a2610f36f1ce8e13d97bfd2deee970b0 to your computer and use it in GitHub Desktop.
A lunar lander autopilot for lunar.unnecessarymodification.com
// 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