mirror of
https://github.com/Krafpy/KSP-MGA-Planner.git
synced 2025-12-12 07:40:41 -08:00
Added ejection angle display
- Added velocity vector calculation in `Orbit` class - Added item for ejection maneuver to display ejection angle
This commit is contained in:
6
dist/main/editor/editor.js
vendored
6
dist/main/editor/editor.js
vendored
@@ -151,6 +151,7 @@ export async function initEditorWithSystem(systems, systemIndex) {
|
||||
progradeDVSpan: getSpan("prograde-delta-v"),
|
||||
normalDVSpan: getSpan("normal-delta-v"),
|
||||
radialDVSpan: getSpan("radial-delta-v"),
|
||||
ejAngleSpan: getSpan("ejection-angle"),
|
||||
depDateSpan: getSpan("result-departure-date"),
|
||||
arrDateSpan: getSpan("result-arrival-date"),
|
||||
totalDVSpan: getSpan("result-total-delta-v"),
|
||||
@@ -160,10 +161,10 @@ export async function initEditorWithSystem(systems, systemIndex) {
|
||||
endDateSpan: getSpan("flyby-end-date"),
|
||||
periAltitudeSpan: getSpan("flyby-periapsis-altitude"),
|
||||
inclinationSpan: getSpan("flyby-inclination"),
|
||||
maneuverDiv: getDiv("maneuvre-details"),
|
||||
flybyDiv: getDiv("flyby-details"),
|
||||
detailsSelector: detailsSelector,
|
||||
stepSlider: stepSlider,
|
||||
maneuverDiv: getDiv("maneuvre-details"),
|
||||
flybyDiv: getDiv("flyby-details")
|
||||
};
|
||||
const resetFoundTrajectory = () => {
|
||||
systemTime.input(updateSystemTime);
|
||||
@@ -243,6 +244,7 @@ export async function initEditorWithSystem(systems, systemIndex) {
|
||||
resultItems.dateSpan.innerHTML = "--";
|
||||
resultItems.normalDVSpan.innerHTML = "--";
|
||||
resultItems.radialDVSpan.innerHTML = "--";
|
||||
resultItems.ejAngleSpan.innerHTML = "--";
|
||||
resultItems.depDateSpan.innerHTML = "--";
|
||||
resultItems.arrDateSpan.innerHTML = "--";
|
||||
resultItems.totalDVSpan.innerHTML = "--";
|
||||
|
||||
25
dist/main/objects/orbit.js
vendored
25
dist/main/objects/orbit.js
vendored
@@ -80,6 +80,31 @@ export class Orbit {
|
||||
pos.multiplyScalar(this.radius(trueAnomaly));
|
||||
return pos;
|
||||
}
|
||||
velocityFromTrueAnomaly(trueAnomaly) {
|
||||
const e = this.eccentricity;
|
||||
const mu = this.attractor.stdGravParam;
|
||||
const nu = trueAnomaly;
|
||||
const a = this.semiMajorAxis;
|
||||
const r = this.radius(nu);
|
||||
const vel = new THREE.Vector3();
|
||||
if (e < 1) {
|
||||
const v = Math.sqrt(mu * a) / r;
|
||||
const E = 2 * Math.atan(Math.tan(nu * 0.5) * Math.sqrt((1 - e) / (1 + e)));
|
||||
vel.set(-v * Math.sin(E), 0, -v * Math.sqrt(1 - e * e) * Math.cos(E));
|
||||
}
|
||||
else {
|
||||
const v = Math.sqrt(-mu * a) / r;
|
||||
const H = 2 * Math.atanh(Math.tan(nu * 0.5) * Math.sqrt((e - 1) / (e + 1)));
|
||||
vel.set(-v * Math.sinh(H), 0, -v * Math.sqrt(e * e - 1) * Math.cosh(H));
|
||||
}
|
||||
const right = new THREE.Vector3(1, 0, 0), up = new THREE.Vector3(0, 1, 0);
|
||||
const ascNodeDir = right.clone();
|
||||
ascNodeDir.applyAxisAngle(up, this.ascNodeLongitude);
|
||||
vel.applyAxisAngle(up, this.ascNodeLongitude);
|
||||
vel.applyAxisAngle(up, this.argOfPeriapsis);
|
||||
vel.applyAxisAngle(ascNodeDir, this.inclination);
|
||||
return vel;
|
||||
}
|
||||
radius(trueAnomaly) {
|
||||
return this.orbitalParam / (1 + this.eccentricity * Math.cos(trueAnomaly));
|
||||
}
|
||||
|
||||
23
dist/main/solvers/trajectory.js
vendored
23
dist/main/solvers/trajectory.js
vendored
@@ -138,12 +138,27 @@ export class Trajectory {
|
||||
const radialDir = progradeDir.clone();
|
||||
radialDir.cross(normalDir);
|
||||
const deltaV = new THREE.Vector3(maneuvre.deltaVToPrevStep.x, maneuvre.deltaVToPrevStep.y, maneuvre.deltaVToPrevStep.z);
|
||||
let ejectAngle = undefined;
|
||||
if (maneuvre.context.type == "ejection") {
|
||||
const nodePos = maneuvre.position;
|
||||
const body = this.system.bodyFromId(step.attractorId);
|
||||
const bodyNu = body.trueAnomalyAtDate(departureDate);
|
||||
const bodyVel = body.orbit.velocityFromTrueAnomaly(bodyNu);
|
||||
const u = new THREE.Vector2(nodePos.x, nodePos.z);
|
||||
const v = new THREE.Vector2(bodyVel.x, bodyVel.z);
|
||||
u.normalize();
|
||||
v.normalize();
|
||||
const cosA = Math.min(Math.max(u.dot(v), -1), 1);
|
||||
ejectAngle = Math.acos(cosA) * 180 / Math.PI;
|
||||
ejectAngle *= Math.sign(u.x * v.y - u.y * v.x);
|
||||
}
|
||||
const details = {
|
||||
stepIndex: i,
|
||||
dateMET: step.dateOfStart - departureDate,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV),
|
||||
ejectAngle: ejectAngle
|
||||
};
|
||||
this._maneuvres.push(details);
|
||||
}
|
||||
@@ -243,6 +258,14 @@ export class Trajectory {
|
||||
resultItems.normalDVSpan.innerHTML = details.normalDV.toFixed(1);
|
||||
resultItems.radialDVSpan.innerHTML = details.radialDV.toFixed(1);
|
||||
resultItems.maneuvreNumber.innerHTML = (option.origin + 1).toString();
|
||||
const ejAngleLI = resultItems.ejAngleSpan.parentElement;
|
||||
if (details.ejectAngle !== undefined) {
|
||||
ejAngleLI.hidden = false;
|
||||
resultItems.ejAngleSpan.innerHTML = details.ejectAngle.toFixed(1);
|
||||
}
|
||||
else {
|
||||
ejAngleLI.hidden = true;
|
||||
}
|
||||
const date = depDate.dateSeconds + dateEMT.dateSeconds;
|
||||
resultItems.dateSpan.onclick = onDateClick(date);
|
||||
resultItems.flybyDiv.hidden = true;
|
||||
|
||||
@@ -280,6 +280,7 @@
|
||||
<li><strong>Prograde ΔV:</strong> <span id="prograde-delta-v">--</span> m/s</li>
|
||||
<li><strong>Normal ΔV:</strong> <span id="normal-delta-v">--</span> m/s</li>
|
||||
<li><strong>Radial ΔV:</strong> <span id="radial-delta-v">--</span> m/s</li>
|
||||
<li hidden><strong>Ejection angle:</strong> <span id="ejection-angle">--</span>° (counter clockwise)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -203,14 +203,15 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
|
||||
detailsSelector.disable();
|
||||
stepSlider.disable();
|
||||
|
||||
const getSpan = (id: string) => document.getElementById(id) as HTMLSpanElement;
|
||||
const getDiv = (id: string) => document.getElementById(id) as HTMLDivElement;
|
||||
const getSpan = (id: string) => document.getElementById(id) as HTMLSpanElement;
|
||||
const getDiv = (id: string) => document.getElementById(id) as HTMLDivElement;
|
||||
|
||||
const resultItems: ResultPannelItems = {
|
||||
dateSpan: getSpan("maneuvre-date"),
|
||||
progradeDVSpan: getSpan("prograde-delta-v"),
|
||||
normalDVSpan: getSpan("normal-delta-v"),
|
||||
radialDVSpan: getSpan("radial-delta-v"),
|
||||
ejAngleSpan: getSpan("ejection-angle"),
|
||||
depDateSpan: getSpan("result-departure-date"),
|
||||
arrDateSpan: getSpan("result-arrival-date"),
|
||||
totalDVSpan: getSpan("result-total-delta-v"),
|
||||
@@ -220,10 +221,10 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
|
||||
endDateSpan: getSpan("flyby-end-date"),
|
||||
periAltitudeSpan: getSpan("flyby-periapsis-altitude"),
|
||||
inclinationSpan: getSpan("flyby-inclination"),
|
||||
maneuverDiv: getDiv("maneuvre-details"),
|
||||
flybyDiv: getDiv("flyby-details"),
|
||||
detailsSelector: detailsSelector,
|
||||
stepSlider: stepSlider,
|
||||
maneuverDiv: getDiv("maneuvre-details"),
|
||||
flybyDiv: getDiv("flyby-details")
|
||||
};
|
||||
|
||||
const resetFoundTrajectory = () => {
|
||||
@@ -322,6 +323,7 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
|
||||
resultItems.dateSpan.innerHTML = "--";
|
||||
resultItems.normalDVSpan.innerHTML = "--";
|
||||
resultItems.radialDVSpan.innerHTML = "--";
|
||||
resultItems.ejAngleSpan.innerHTML = "--";
|
||||
resultItems.depDateSpan.innerHTML = "--";
|
||||
resultItems.arrDateSpan.innerHTML = "--";
|
||||
resultItems.totalDVSpan.innerHTML = "--";
|
||||
|
||||
@@ -128,6 +128,36 @@ export class Orbit implements IOrbit {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public velocityFromTrueAnomaly(trueAnomaly: number){
|
||||
const e = this.eccentricity;
|
||||
const mu = this.attractor.stdGravParam;
|
||||
const nu = trueAnomaly;
|
||||
const a = this.semiMajorAxis;
|
||||
|
||||
const r = this.radius(nu);
|
||||
const vel = new THREE.Vector3();
|
||||
|
||||
if(e < 1) { // Elliptical orbit
|
||||
const v = Math.sqrt(mu * a) / r;
|
||||
const E = 2 * Math.atan( Math.tan(nu*0.5) * Math.sqrt((1-e)/(1+e)) );
|
||||
vel.set(-v * Math.sin(E), 0, -v * Math.sqrt(1 - e*e) * Math.cos(E));
|
||||
} else { // Hyperbolic orbit (the case e = 1, parabolic orbit, will never be reached)
|
||||
const v = Math.sqrt(-mu * a) / r;
|
||||
const H = 2 * Math.atanh( Math.tan(nu*0.5) * Math.sqrt((e-1)/(e+1)) );
|
||||
vel.set(-v * Math.sinh(H), 0, -v * Math.sqrt(e*e - 1) * Math.cosh(H));
|
||||
}
|
||||
|
||||
const right = new THREE.Vector3(1, 0, 0), up = new THREE.Vector3(0, 1, 0);
|
||||
const ascNodeDir = right.clone();
|
||||
ascNodeDir.applyAxisAngle(up, this.ascNodeLongitude);
|
||||
|
||||
vel.applyAxisAngle(up, this.ascNodeLongitude);
|
||||
vel.applyAxisAngle(up, this.argOfPeriapsis);
|
||||
vel.applyAxisAngle(ascNodeDir, this.inclination);
|
||||
|
||||
return vel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trueAnomaly The true anomaly
|
||||
* @returns The real radius of the orbit for the specified anomaly.
|
||||
|
||||
@@ -5,6 +5,7 @@ import { SolarSystem } from "../objects/system.js";
|
||||
import { KSPTime } from "../utilities/time.js";
|
||||
import { CameraController } from "../objects/camera.js";
|
||||
import { SpriteManager } from "../utilities/sprites.js";
|
||||
import { OrbitingBody } from "../objects/body.js";
|
||||
|
||||
export class Trajectory {
|
||||
private _orbitObjects: THREE.Object3D[] = [];
|
||||
@@ -186,7 +187,8 @@ export class Trajectory {
|
||||
const {maneuvre} = step;
|
||||
if(maneuvre){
|
||||
const orbit = this.orbits[i];
|
||||
|
||||
|
||||
// compute the maneuver delta-V vectors
|
||||
const progradeDir = new THREE.Vector3(
|
||||
maneuvre.progradeDir.x,
|
||||
maneuvre.progradeDir.y,
|
||||
@@ -202,12 +204,34 @@ export class Trajectory {
|
||||
maneuvre.deltaVToPrevStep.z,
|
||||
);
|
||||
|
||||
|
||||
// compute the ejection angle if it's the ejection maneuver
|
||||
let ejectAngle: (undefined | number) = undefined;
|
||||
if(maneuvre.context.type == "ejection"){
|
||||
const nodePos = maneuvre.position;
|
||||
|
||||
const body = this.system.bodyFromId(step.attractorId) as OrbitingBody;
|
||||
const bodyNu = body.trueAnomalyAtDate(departureDate);
|
||||
const bodyVel = body.orbit.velocityFromTrueAnomaly(bodyNu);
|
||||
|
||||
// the parking orbit is always in the xz plane
|
||||
const u = new THREE.Vector2(nodePos.x, nodePos.z);
|
||||
const v = new THREE.Vector2(bodyVel.x, bodyVel.z); // project planet velocity in xz plane
|
||||
u.normalize();
|
||||
v.normalize();
|
||||
|
||||
const cosA = Math.min(Math.max(u.dot(v), -1), 1);
|
||||
ejectAngle = Math.acos(cosA) * 180 / Math.PI;
|
||||
ejectAngle *= Math.sign(u.x*v.y - u.y*v.x); // counter clockwise direction of the angle
|
||||
}
|
||||
|
||||
const details = {
|
||||
stepIndex: i,
|
||||
dateMET: step.dateOfStart - departureDate,
|
||||
progradeDV: progradeDir.dot(deltaV),
|
||||
normalDV: normalDir.dot(deltaV),
|
||||
radialDV: radialDir.dot(deltaV),
|
||||
ejectAngle: ejectAngle
|
||||
};
|
||||
this._maneuvres.push(details);
|
||||
}
|
||||
@@ -343,6 +367,14 @@ export class Trajectory {
|
||||
resultItems.radialDVSpan.innerHTML = details.radialDV.toFixed(1);
|
||||
resultItems.maneuvreNumber.innerHTML = (option.origin + 1).toString();
|
||||
|
||||
const ejAngleLI = resultItems.ejAngleSpan.parentElement as HTMLLIElement;
|
||||
if(details.ejectAngle !== undefined){
|
||||
ejAngleLI.hidden = false;
|
||||
resultItems.ejAngleSpan.innerHTML = details.ejectAngle.toFixed(1);
|
||||
} else {
|
||||
ejAngleLI.hidden = true;
|
||||
}
|
||||
|
||||
const date = depDate.dateSeconds + dateEMT.dateSeconds;
|
||||
resultItems.dateSpan.onclick = onDateClick(date);
|
||||
|
||||
|
||||
2
src/types.d.ts
vendored
2
src/types.d.ts
vendored
@@ -286,6 +286,7 @@ type ManeuvreDetails = {
|
||||
progradeDV: number,
|
||||
normalDV: number,
|
||||
radialDV: number,
|
||||
ejectAngle?: number
|
||||
};
|
||||
|
||||
type FlybyDetails = {
|
||||
@@ -309,6 +310,7 @@ type ResultPannelItems = {
|
||||
progradeDVSpan: HTMLSpanElement,
|
||||
normalDVSpan: HTMLSpanElement,
|
||||
radialDVSpan: HTMLSpanElement,
|
||||
ejAngleSpan: HTMLSpanElement,
|
||||
depDateSpan: HTMLSpanElement,
|
||||
arrDateSpan: HTMLSpanElement,
|
||||
totalDVSpan: HTMLSpanElement,
|
||||
|
||||
Reference in New Issue
Block a user