236 lines
10 KiB
TypeScript
236 lines
10 KiB
TypeScript
import { createLabel, createNumberInput, createRadioButton, getCoordinatesFromParameters, type OrbitalParameters } from "./common";
|
|
import { OrbitalParametersGui } from "./orbit";
|
|
import { type Body } from "../calculations/constants";
|
|
import type { FindBestInterceptMessage, ProgressMessage } from "./worker";
|
|
import type { ChangingStorageValue } from "../storage";
|
|
import { ManoeuvresGui } from "./manoeuvres";
|
|
import { extrapolateTrajectory, findOrbitThroughInterpolation, type OrbitalCoordinates } from "../calculations/orbit-calculations";
|
|
import { InterpolateOrbitGui, type InterpolationParameters } from "./interpolate";
|
|
|
|
export type TargetOrbitChoice = "KnownOrbit" | "InterpolateOrbit";
|
|
export function decodeTargetOrbitChoice(s: string): TargetOrbitChoice {
|
|
if (s == "KnownOrbit" || s == "InterpolateOrbit") {
|
|
return s;
|
|
}
|
|
|
|
return "KnownOrbit";
|
|
}
|
|
|
|
export class InterceptTargetGui {
|
|
currentTime: ChangingStorageValue<number>;
|
|
body: ChangingStorageValue<Body>;
|
|
startingOrbitalParameters: ChangingStorageValue<OrbitalParameters>;
|
|
targetOrbitalParameters: ChangingStorageValue<OrbitalParameters>;
|
|
additionalTrueAnomaly: ChangingStorageValue<number>;
|
|
targetOrbitChoice: ChangingStorageValue<TargetOrbitChoice>;
|
|
interpolationParameters: ChangingStorageValue<InterpolationParameters>;
|
|
|
|
parentDiv: HTMLDivElement;
|
|
orbitGui: OrbitalParametersGui;
|
|
interpolateGui: InterpolateOrbitGui;
|
|
manoeuvresGui: ManoeuvresGui;
|
|
|
|
sourceId: string;
|
|
|
|
worker: Worker | null;
|
|
|
|
constructor(currentTime: ChangingStorageValue<number>, body: ChangingStorageValue<Body>, startingOrbitalParameters: ChangingStorageValue<OrbitalParameters>, targetOrbitalParameters: ChangingStorageValue<OrbitalParameters>, additionalTrueAnomaly: ChangingStorageValue<number>, targetOrbitChoice: ChangingStorageValue<TargetOrbitChoice>, interpolationParameters: ChangingStorageValue<InterpolationParameters>) {
|
|
this.currentTime = currentTime;
|
|
this.body = body;
|
|
this.startingOrbitalParameters = startingOrbitalParameters;
|
|
this.targetOrbitalParameters = targetOrbitalParameters;
|
|
this.additionalTrueAnomaly = additionalTrueAnomaly;
|
|
this.targetOrbitChoice = targetOrbitChoice;
|
|
this.interpolationParameters = interpolationParameters;
|
|
|
|
this.orbitGui = new OrbitalParametersGui(targetOrbitalParameters, "orbitWithPosition");
|
|
this.interpolateGui = new InterpolateOrbitGui(this.interpolationParameters);
|
|
this.manoeuvresGui = new ManoeuvresGui(true);
|
|
|
|
this.worker = null;
|
|
|
|
this.parentDiv = document.createElement("div");
|
|
let choiceHeader = document.createElement("h3")
|
|
choiceHeader.appendChild(document.createTextNode("Choose target orbit type"));
|
|
|
|
this.parentDiv.appendChild(choiceHeader);
|
|
|
|
let targetChoiceId = crypto.randomUUID();
|
|
let knownOrbitId = crypto.randomUUID();
|
|
let knownOrbitButton = createRadioButton(targetChoiceId, knownOrbitId);
|
|
let knownOrbitLabel = createLabel(knownOrbitId, "Known orbit");
|
|
this.parentDiv.appendChild(knownOrbitButton);
|
|
this.parentDiv.appendChild(knownOrbitLabel);
|
|
|
|
let interpolateOrbitId = crypto.randomUUID();
|
|
let interpolateOrbitButton = createRadioButton(targetChoiceId, interpolateOrbitId);
|
|
let interpolateOrbitLabel = createLabel(interpolateOrbitId, "Interpolate orbit");
|
|
this.parentDiv.appendChild(interpolateOrbitButton);
|
|
this.parentDiv.appendChild(interpolateOrbitLabel);
|
|
|
|
let orbitContainer = document.createElement("div");
|
|
this.parentDiv.appendChild(orbitContainer);
|
|
|
|
let knownOrbitContainer = document.createElement("div");
|
|
|
|
let parametersHeader = document.createElement("h3");
|
|
parametersHeader.appendChild(document.createTextNode("Target orbit:"));
|
|
knownOrbitContainer.appendChild(parametersHeader);
|
|
knownOrbitContainer.appendChild(this.orbitGui.parentDiv);
|
|
|
|
let interpolateOrbitContainer = document.createElement("div");
|
|
|
|
let interpolateOrbitHeader = document.createElement("h3");
|
|
interpolateOrbitHeader.appendChild(document.createTextNode("Interpolate orbit:"));
|
|
interpolateOrbitContainer.appendChild(interpolateOrbitHeader);
|
|
interpolateOrbitContainer.appendChild(this.interpolateGui.parentDiv);
|
|
|
|
let offsetHeader = document.createElement("h3");
|
|
offsetHeader.appendChild(document.createTextNode("Offset:"));
|
|
this.parentDiv.appendChild(offsetHeader);
|
|
|
|
let additionalTrueAnomalyContainer = document.createElement("div");
|
|
additionalTrueAnomalyContainer.classList.add("orbitalParameter");
|
|
this.parentDiv.appendChild(additionalTrueAnomalyContainer);
|
|
|
|
let additionalTrueAnomalyId = crypto.randomUUID();
|
|
let additionalTrueanomalyLabel = createLabel(additionalTrueAnomalyId, "Additional true anomaly:");
|
|
let additionalTrueAnomalyInput = createNumberInput(additionalTrueAnomalyId, -360, 360);
|
|
[additionalTrueanomalyLabel, additionalTrueAnomalyInput].forEach(child => additionalTrueAnomalyContainer.appendChild(child));
|
|
|
|
let searchButton = document.createElement("button");
|
|
searchButton.appendChild(document.createTextNode("Search for cheapest intercept"));
|
|
this.parentDiv.appendChild(searchButton);
|
|
|
|
let bestInterceptHeader = document.createElement("h3");
|
|
bestInterceptHeader.appendChild(document.createTextNode("Best intercept:"));
|
|
this.parentDiv.appendChild(bestInterceptHeader);
|
|
|
|
this.parentDiv.appendChild(this.manoeuvresGui.parentDiv);
|
|
|
|
searchButton.addEventListener("click", () => {
|
|
if (this.worker !== null) {
|
|
this.worker.terminate();
|
|
this.worker = null;
|
|
searchButton.innerHTML = "Search for cheapest intercept";
|
|
} else {
|
|
searchButton.innerHTML = "Cancel search";
|
|
|
|
let currentTime = this.currentTime.getCurrentValue();
|
|
let body = this.body.getCurrentValue();
|
|
let startingOrbitalParameters = this.startingOrbitalParameters.getCurrentValue();
|
|
let targetOrbitalParameters = this.targetOrbitalParameters.getCurrentValue();
|
|
let additionalTrueAnomaly = this.additionalTrueAnomaly.getCurrentValue();
|
|
let targetOrbitChoice = this.targetOrbitChoice.getCurrentValue();
|
|
let interpolationParameters = this.interpolationParameters.getCurrentValue();
|
|
|
|
interpolationParameters = structuredClone(interpolationParameters);
|
|
interpolationParameters.firstOwnAltitude += body.radius;
|
|
interpolationParameters.firstTargetAltitude += body.radius;
|
|
interpolationParameters.secondOwnAltitude += body.radius;
|
|
interpolationParameters.secondTargetAltitude += body.radius;
|
|
interpolationParameters.targetAltitude += body.radius;
|
|
interpolationParameters.targetPeriapsis += body.radius;
|
|
interpolationParameters.targetApoapsis += body.radius;
|
|
|
|
|
|
let startingCoordinates: OrbitalCoordinates | null = getCoordinatesFromParameters(startingOrbitalParameters, body);
|
|
let startTime: number | null = null;
|
|
|
|
let targetStartTime = 0;
|
|
|
|
let targetCoordinates: OrbitalCoordinates | null = null;
|
|
if (targetOrbitChoice == "InterpolateOrbit") {
|
|
let result = findOrbitThroughInterpolation(startingCoordinates, interpolationParameters, body);
|
|
if (result) {
|
|
targetCoordinates = result[0];
|
|
targetStartTime = startingOrbitalParameters.currentTimeAtReading + result[1];
|
|
}
|
|
} else if (targetOrbitChoice = "KnownOrbit") {
|
|
targetCoordinates = getCoordinatesFromParameters(targetOrbitalParameters, body);
|
|
targetStartTime = targetOrbitalParameters.currentTimeAtReading;
|
|
}
|
|
|
|
if (startingCoordinates && targetCoordinates) {
|
|
startTime = Math.max(currentTime, startingOrbitalParameters.currentTimeAtReading, targetStartTime);
|
|
|
|
if (startTime > startingOrbitalParameters.currentTimeAtReading) {
|
|
let addedTime = startTime - startingOrbitalParameters.currentTimeAtReading;
|
|
startingCoordinates = extrapolateTrajectory(addedTime, startingCoordinates, body);
|
|
}
|
|
|
|
if (startTime > targetStartTime) {
|
|
let addedTime = startTime - targetStartTime;
|
|
targetCoordinates = extrapolateTrajectory(addedTime, targetCoordinates, body);
|
|
}
|
|
}
|
|
|
|
if (startingCoordinates && targetCoordinates && startTime) {
|
|
this.worker = new Worker(new URL('./worker.ts', import.meta.url), {type: 'module'});
|
|
this.worker.addEventListener("message", event => {
|
|
let transferResponse = event.data as ProgressMessage;
|
|
if (transferResponse) {
|
|
if (transferResponse.finished) {
|
|
searchButton.innerHTML = "Search for cheapest intercept";
|
|
this.worker = null;
|
|
}
|
|
|
|
if (transferResponse.bestTransfer) {
|
|
transferResponse.bestTransfer.firstManoeuvre.time += startTime;
|
|
transferResponse.bestTransfer.secondManoeuvre.time += startTime;
|
|
}
|
|
|
|
this.manoeuvresGui.displayProgress(transferResponse);
|
|
}
|
|
});
|
|
|
|
let workerMessage: FindBestInterceptMessage = {
|
|
type: "FindBestIntercept",
|
|
startingSituation: startingCoordinates,
|
|
targetSituation: targetCoordinates,
|
|
body: body,
|
|
additionalTrueAnomaly: additionalTrueAnomaly
|
|
};
|
|
|
|
this.worker.postMessage(workerMessage);
|
|
} else {
|
|
this.manoeuvresGui.displayProgress({
|
|
type: "ProgressMessage",
|
|
finished: true,
|
|
percentDone: 100,
|
|
bestDeltaV: null,
|
|
bestTransfer: null
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
this.sourceId = crypto.randomUUID();
|
|
this.additionalTrueAnomaly.listenToValue((source, value) => {
|
|
if (source == this.sourceId) {
|
|
return;
|
|
}
|
|
|
|
additionalTrueAnomalyInput.value = (value * 180 / Math.PI).toString();
|
|
});
|
|
|
|
additionalTrueAnomalyInput.addEventListener("change", () => {
|
|
let additionalTrueAnomaly = parseFloat(additionalTrueAnomalyInput.value) * Math.PI / 180.0;
|
|
this.additionalTrueAnomaly.set(additionalTrueAnomaly, this.sourceId);
|
|
});
|
|
|
|
knownOrbitButton.addEventListener("change", () => targetOrbitChoice.set("KnownOrbit", this.sourceId));
|
|
interpolateOrbitButton.addEventListener("change", () => targetOrbitChoice.set("InterpolateOrbit", this.sourceId));
|
|
|
|
targetOrbitChoice.listenToValue((_, value) => {
|
|
orbitContainer.innerHTML = "";
|
|
if (value == "KnownOrbit") {
|
|
knownOrbitButton.setAttribute("checked", "");
|
|
orbitContainer.appendChild(knownOrbitContainer);
|
|
} else if (value == "InterpolateOrbit") {
|
|
interpolateOrbitButton.setAttribute("checked", "");
|
|
orbitContainer.appendChild(interpolateOrbitContainer);
|
|
}
|
|
});
|
|
}
|
|
} |