Added insertion burn checkbox.

This commit is contained in:
Krafpy
2022-08-20 01:34:02 +02:00
parent e9f1ad8d81
commit c75ba94cf8
13 changed files with 156 additions and 95 deletions

View File

@@ -29,11 +29,12 @@ class TrajectoryCalculator {
addPrecomputedOrbits(bodiesOrbits) {
this._bodiesOrbits = bodiesOrbits;
}
setParameters(depAltitude, destAltitude, startDateMin, startDateMax, params) {
this._depAltitude = depAltitude;
this._destAltitude = destAltitude;
this._startDateMin = startDateMin;
this._startDateMax = startDateMax;
setParameters(settings, params) {
this._depAltitude = settings.depAltitude;
this._destAltitude = settings.destAltitude;
this._startDateMin = settings.startDate;
this._startDateMax = settings.endDate;
this._noInsertion = settings.noInsertion;
this._params = params;
this._getDepartureSettings();
this._getLegsSettings();
@@ -93,7 +94,9 @@ class TrajectoryCalculator {
this._computeFirstLegArc(last);
this._computeLegSecondArcSimple(last);
this._computeInsertion();
this._computeCircularization();
if (!this._noInsertion) {
this._computeCircularization();
}
}
get totalDeltaV() {
let total = 0;
@@ -128,7 +131,8 @@ class TrajectoryCalculator {
infos.duration = Math.max(minLegDuration, infos.duration);
}
computeStartingMeanAnomalies() {
for (let i = 1; i < this.steps.length - 1; i++) {
const k = this._noInsertion ? 0 : 1;
for (let i = 1; i < this.steps.length - k; i++) {
const step = this.steps[i];
const { orbitElts, angles } = step;
const e = orbitElts.eccentricity;
@@ -189,7 +193,7 @@ class TrajectoryCalculator {
const t_periapsisState = { pos: t_periPos, vel: t_periVel };
const t_flybyOrbit = Physics3D.stateToOrbitElements(t_periapsisState, body);
const enterAngle = Physics3D.trueAnomalyAtRadius(t_flybyOrbit, body.soi);
const angles = { begin: -enterAngle, end: 0 };
const angles = { begin: -enterAngle, end: this._noInsertion ? enterAngle : 0 };
const drawAngles = { begin: angles.begin, end: angles.end };
const t_incomingVel = Physics3D.orbitElementsToState(t_flybyOrbit, body, angles.begin).vel;
const t_incomingVelDir = normalize3(t_incomingVel);
@@ -201,6 +205,13 @@ class TrajectoryCalculator {
const periapsisState = { pos: periPos, vel: periVel };
const insertionOrbit = Physics3D.stateToOrbitElements(periapsisState, body);
const tof = Physics3D.tofBetweenAnomalies(insertionOrbit, body, angles.begin, angles.end);
const flybyDetails = {
bodyId: body.id,
soiEnterDate: this._lastStepEndDate,
soiExitDate: this._lastStepEndDate + tof,
periRadius: periRadius,
inclination: insertionOrbit.inclination,
};
this.steps.push({
orbitElts: insertionOrbit,
attractorId: body.id,
@@ -208,7 +219,8 @@ class TrajectoryCalculator {
drawAngles: drawAngles,
duration: tof,
dateOfStart: this._lastStepEndDate,
startM: 0
startM: 0,
flyby: this._noInsertion ? flybyDetails : undefined
});
this._vesselState = periapsisState;
this._secondArcsData.push({

View File

@@ -17,11 +17,8 @@ class TrajectoryOptimizer extends WorkerEnvironment {
}
}
onWorkerDataPass(data) {
this._depAltitude = data.depAltitude;
this._destAltitude = data.destAltitude;
this._sequence = data.sequence;
this._startDateMin = data.startDateMin;
this._startDateMax = data.startDateMax;
this._settings = data.settings;
}
onWorkerRun(input) {
this._newDeltaVs = [];
@@ -35,11 +32,20 @@ class TrajectoryOptimizer extends WorkerEnvironment {
this._bestTrajectory = trajectory;
}
const lastIdx = trajectory.steps.length - 1;
const finalOrbit = trajectory.steps[lastIdx].orbitElts;
const lastStep = trajectory.steps[lastIdx];
const finalOrbit = lastStep.orbitElts;
const totDV = trajectory.totalDeltaV;
this._newDeltaVs.push(totDV);
const lastInc = Math.abs(finalOrbit.inclination);
return totDV + totDV * lastInc * 0.1;
let periVelCost = 0;
if (this._settings.noInsertion) {
const finalBody = this._system[lastStep.attractorId];
const periapsis = this._settings.destAltitude + finalBody.radius;
const periVel = Physics3D.velocityAtRadius(finalOrbit, finalBody, periapsis);
const circDV = periVel - Physics3D.circularVelocity(finalBody, periapsis);
periVelCost = circDV;
}
return totDV + totDV * lastInc * 0.1 + periVelCost;
};
const trajConfig = this._config.trajectorySearch;
const { diffWeight } = trajConfig;
@@ -82,7 +88,7 @@ class TrajectoryOptimizer extends WorkerEnvironment {
trajectory.addPrecomputedOrbits(this._bodiesOrbits);
let attempts = 0;
while (attempts < maxAttempts) {
trajectory.setParameters(this._depAltitude, this._destAltitude, this._startDateMin, this._startDateMax, agent);
trajectory.setParameters(this._settings, agent);
let failed = false;
try {
trajectory.compute();

View File

@@ -132,6 +132,8 @@ export async function initEditorWithSystem(systems, systemIndex) {
};
depAltitude.value = config.editor.defaultAltitude;
destAltitude.value = config.editor.defaultAltitude;
const noInsertionBox = document.getElementById("insertion-checkbox");
noInsertionBox.checked = false;
const customSequence = document.getElementById("custom-sequence");
const deltaVPlot = new EvolutionPlot("evolution-plot");
deltaVPlot.hide();
@@ -195,8 +197,8 @@ export async function initEditorWithSystem(systems, systemIndex) {
else
sequence = sequenceSelector.sequence;
updateAltitudeRange(depAltitude, sequence.bodies[0]);
const seqLen = sequence.length;
updateAltitudeRange(destAltitude, sequence.bodies[seqLen - 1]);
const slen = sequence.length;
updateAltitudeRange(destAltitude, sequence.bodies[slen - 1]);
const startDate = timeRangeStart.dateSeconds;
const endDate = timeRangeEnd.dateSeconds;
if (endDate < startDate)
@@ -204,8 +206,15 @@ export async function initEditorWithSystem(systems, systemIndex) {
const depAltitudeVal = depAltitude.value * 1000;
const destAltitudeVal = destAltitude.value * 1000;
resetFoundTrajectory();
const userSettings = {
startDate: startDate,
endDate: endDate,
depAltitude: depAltitudeVal,
destAltitude: destAltitudeVal,
noInsertion: noInsertionBox.checked
};
const perfStart = performance.now();
await solver.searchOptimalTrajectory(sequence, startDate, endDate, depAltitudeVal, destAltitudeVal);
await solver.searchOptimalTrajectory(sequence, userSettings);
console.log(`Search time: ${performance.now() - perfStart} ms`);
displayFoundTrajectory();
}

View File

@@ -30,12 +30,12 @@ export class TrajectorySolver {
if (this._running)
this._cancelled = true;
}
async searchOptimalTrajectory(sequence, startDateMin, startDateMax, depAltitude, destAltitude) {
async searchOptimalTrajectory(sequence, settings) {
this._running = true;
this.plot.clearPlot();
this._calculatePopulationSize(sequence);
this._calculatePopulationChunks();
await this._passSettingsData(sequence.ids, startDateMin, startDateMax, depAltitude, destAltitude);
await this._passSettingsData(sequence, settings);
await this._createStartPopulation();
this._updatePlot(0);
const { maxGenerations } = this.config.trajectorySearch;
@@ -50,10 +50,8 @@ export class TrajectorySolver {
}
this._running = false;
}
async _passSettingsData(sequence, startDateMin, startDateMax, depAltitude, destAltitude) {
return this._workerPool.passData({
depAltitude, destAltitude, sequence, startDateMin, startDateMax
});
async _passSettingsData(sequence, settings) {
return this._workerPool.passData({ sequence: sequence.ids, settings });
}
_calculatePopulationChunks() {
const { splitLimit } = this.config.trajectorySearch;

View File

@@ -284,13 +284,14 @@ export class Trajectory {
date = missionStart;
else if (date > missionEnd)
date = missionEnd;
const k = lastStep.flyby ? 0 : 1;
let i = 1;
for (; i < this.steps.length - 1; i++) {
for (; i < this.steps.length - k; i++) {
const { dateOfStart, duration } = this.steps[i];
if (date >= dateOfStart && date < dateOfStart + duration)
break;
}
i = Math.min(i, this.steps.length - 2);
i = Math.min(i, this.steps.length - k - 1);
const step = this.steps[i];
const pod = this._spriteObjects[this._podSpriteIndex].pop();
const curAttrId = this.steps[this._podSpriteIndex].attractorId;
@@ -302,9 +303,8 @@ export class Trajectory {
const newGroup = this.system.objectsOfBody(newAttrId);
newGroup.add(pod);
const orbit = this.orbits[i];
const relDate = date - step.dateOfStart;
const { startM } = step;
const trueAnom = orbit.solveTrueAnomalyAtDate(startM, 0, relDate);
const trueAnom = orbit.solveTrueAnomalyAtDate(startM, step.dateOfStart, date);
const pos = orbit.positionFromTrueAnomaly(trueAnom);
const { scale } = this.config.rendering;
pod.position.set(pos.x, pos.y, pos.z);

View File

@@ -171,6 +171,13 @@
km (above surface level)
</div>
</div>
<div id="insertion-checkbox-container">
<div class="controls">
<input name="insertion-checkbox" id="insertion-checkbox" type="checkbox">
<label for="insertion-checkbox">No insertion burn</label>
</div>
</div>
<!-- Sequence optimization buttons and messages -->
<div class="form-actions">
<button class="submit-btn" id="search-btn">Search trajectory</button>

View File

@@ -11,6 +11,7 @@ class TrajectoryCalculator {
private _destAltitude!: number;
private _startDateMin!: number;
private _startDateMax!: number;
private _noInsertion!: boolean;
private _params!: Agent;
private _bodiesOrbits!: OrbitalElements3D[];
@@ -49,17 +50,12 @@ class TrajectoryCalculator {
this._bodiesOrbits = bodiesOrbits;
}
public setParameters(
depAltitude: number,
destAltitude: number,
startDateMin: number,
startDateMax: number,
params: Agent
){
this._depAltitude = depAltitude;
this._destAltitude = destAltitude;
this._startDateMin = startDateMin;
this._startDateMax = startDateMax;
public setParameters(settings: TrajectoryUserSettings, params: Agent){
this._depAltitude = settings.depAltitude;
this._destAltitude = settings.destAltitude;
this._startDateMin = settings.startDate;
this._startDateMax = settings.endDate;
this._noInsertion = settings.noInsertion;
this._params = params;
this._getDepartureSettings();
@@ -146,7 +142,9 @@ class TrajectoryCalculator {
// Compute the insertion and circularization around
// the destination body
this._computeInsertion();
this._computeCircularization();
if(!this._noInsertion) {
this._computeCircularization();
}
}
public get totalDeltaV(){
@@ -195,7 +193,8 @@ class TrajectoryCalculator {
}
public computeStartingMeanAnomalies(){
for(let i = 1; i < this.steps.length-1; i++){ // ignore begin and end circular orbits
const k = this._noInsertion ? 0 : 1;
for(let i = 1; i < this.steps.length-k; i++){ // ignore begin and end circular (if insertion burn) orbits
const step = this.steps[i];
const {orbitElts, angles} = step;
const e = orbitElts.eccentricity;
@@ -296,7 +295,7 @@ class TrajectoryCalculator {
// Compute the SOI enter and exit angles
const enterAngle = Physics3D.trueAnomalyAtRadius(t_flybyOrbit, body.soi);
const angles = {begin: -enterAngle, end: 0};
const angles = {begin: -enterAngle, end: this._noInsertion ? enterAngle : 0};
const drawAngles = {begin: angles.begin, end: angles.end};
// Compute the angle between the incoming velocity vector and the velocity vector
@@ -319,6 +318,15 @@ class TrajectoryCalculator {
// Compute the duration of the flyby
const tof = Physics3D.tofBetweenAnomalies(insertionOrbit, body, angles.begin, angles.end);
// Flyby details in the case of no circularization
const flybyDetails: FlybyInfo = {
bodyId: body.id,
soiEnterDate: this._lastStepEndDate,
soiExitDate: this._lastStepEndDate + tof,
periRadius: periRadius,
inclination: insertionOrbit.inclination,
};
// Append the insertion orbit
this.steps.push({
@@ -328,7 +336,8 @@ class TrajectoryCalculator {
drawAngles: drawAngles,
duration: tof,
dateOfStart: this._lastStepEndDate,
startM: 0
startM: 0,
flyby: this._noInsertion ? flybyDetails : undefined
});
// Store the state of the vessel at periapsis for further

View File

@@ -13,11 +13,8 @@ class TrajectoryOptimizer extends WorkerEnvironment {
private _system!: IOrbitingBody[];
private _bodiesOrbits!: OrbitalElements3D[];
private _depAltitude!: number;
private _destAltitude!: number;
private _sequence!: number[];
private _startDateMin!: number;
private _startDateMax!: number;
private _settings!: TrajectoryUserSettings;
private _bestTrajectory!: TrajectoryCalculator;
private _bestDeltaV!: number;
@@ -41,11 +38,8 @@ class TrajectoryOptimizer extends WorkerEnvironment {
}
override onWorkerDataPass(data: any){
this._depAltitude = data.depAltitude;
this._destAltitude = data.destAltitude;
this._sequence = data.sequence;
this._startDateMin = data.startDateMin;
this._startDateMax = data.startDateMax;
this._settings = data.settings;
}
override onWorkerRun(input: any){
@@ -67,17 +61,31 @@ class TrajectoryOptimizer extends WorkerEnvironment {
// Get the circular final orbit
const lastIdx = trajectory.steps.length-1;
const finalOrbit = trajectory.steps[lastIdx].orbitElts;
const lastStep = trajectory.steps[lastIdx];
const finalOrbit = lastStep.orbitElts;
const totDV = trajectory.totalDeltaV;
this._newDeltaVs.push(totDV);
const lastInc = Math.abs(finalOrbit.inclination);
// If there is no circularization burn, try to minimize the velocity at the periapsis
// of the arrival body by actually considering a circularization
let periVelCost = 0;
if(this._settings.noInsertion) {
const finalBody = this._system[lastStep.attractorId];
const periapsis = this._settings.destAltitude + finalBody.radius;
const periVel = Physics3D.velocityAtRadius(finalOrbit, finalBody, periapsis);
const circDV = periVel - Physics3D.circularVelocity(finalBody, periapsis);
periVelCost = circDV;
}
// Attempt to force a minimal inclination of the
// circular orbit around the destination body
// FIX : doesn't work so well...
return totDV + totDV*lastInc*0.1;
return totDV + totDV*lastInc*0.1 + periVelCost;
};
const trajConfig = this._config.trajectorySearch;
const {diffWeight} = trajConfig;
const {minCrossProba, maxCrossProba} = trajConfig;
@@ -138,13 +146,7 @@ class TrajectoryOptimizer extends WorkerEnvironment {
// If an error occurs, the agent is randomized.
let attempts = 0;
while(attempts < maxAttempts){
trajectory.setParameters(
this._depAltitude,
this._destAltitude,
this._startDateMin,
this._startDateMax,
agent
);
trajectory.setParameters(this._settings, agent);
let failed = false;
// FIX: "This radius is never reached" error thrown... why ?
try {

View File

@@ -180,6 +180,10 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
depAltitude.value = config.editor.defaultAltitude;
destAltitude.value = config.editor.defaultAltitude;
// No insertion burn checkbox
const noInsertionBox = document.getElementById("insertion-checkbox") as HTMLInputElement;
noInsertionBox.checked = false;
// Custom sequence input
const customSequence = document.getElementById("custom-sequence") as HTMLInputElement;
@@ -200,7 +204,7 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
stepSlider.disable();
const getSpan = (id: string) => document.getElementById(id) as HTMLSpanElement;
const getDiv = (id: string) => document.getElementById(id) as HTMLDivElement;
const getDiv = (id: string) => document.getElementById(id) as HTMLDivElement;
const resultItems = {
dateSpan: getSpan("maneuvre-date"),
@@ -259,8 +263,8 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
sequence = sequenceSelector.sequence;
updateAltitudeRange(depAltitude, sequence.bodies[0]);
const seqLen = sequence.length;
updateAltitudeRange(destAltitude, sequence.bodies[seqLen-1]);
const slen = sequence.length;
updateAltitudeRange(destAltitude, sequence.bodies[slen-1]);
const startDate = timeRangeStart.dateSeconds;
const endDate = timeRangeEnd.dateSeconds;
@@ -272,10 +276,16 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
resetFoundTrajectory();
const userSettings: TrajectoryUserSettings = {
startDate: startDate,
endDate: endDate,
depAltitude: depAltitudeVal,
destAltitude: destAltitudeVal,
noInsertion: noInsertionBox.checked
};
const perfStart = performance.now();
await solver.searchOptimalTrajectory(
sequence, startDate, endDate, depAltitudeVal, destAltitudeVal
);
await solver.searchOptimalTrajectory(sequence, userSettings);
console.log(`Search time: ${performance.now() - perfStart} ms`);
displayFoundTrajectory();
@@ -284,6 +294,7 @@ export async function initEditorWithSystem(systems: SolarSystemData[], systemInd
if(err instanceof Error && err.message != "TRAJECTORY FINDER CANCELLED")
paramsErr.show(err);
console.error(err);
} finally {
systemSelector.enable();

View File

@@ -38,13 +38,7 @@ export class TrajectorySolver {
if(this._running) this._cancelled = true;
}
public async searchOptimalTrajectory(
sequence: FlybySequence,
startDateMin: number,
startDateMax: number,
depAltitude: number,
destAltitude: number
){
public async searchOptimalTrajectory(sequence: FlybySequence, settings: TrajectoryUserSettings){
this._running = true;
this.plot.clearPlot()
@@ -52,10 +46,7 @@ export class TrajectorySolver {
this._calculatePopulationSize(sequence);
this._calculatePopulationChunks();
await this._passSettingsData(
sequence.ids, startDateMin, startDateMax, depAltitude, destAltitude
);
await this._passSettingsData(sequence, settings);
await this._createStartPopulation();
this._updatePlot(0);
@@ -73,16 +64,8 @@ export class TrajectorySolver {
this._running = false;
}
private async _passSettingsData(
sequence: number[],
startDateMin: number,
startDateMax: number,
depAltitude: number,
destAltitude: number
){
return this._workerPool.passData({
depAltitude, destAltitude, sequence, startDateMin, startDateMax
});
private async _passSettingsData(sequence: FlybySequence, settings: TrajectoryUserSettings){
return this._workerPool.passData({sequence: sequence.ids, settings});
}
private _calculatePopulationChunks(){

View File

@@ -404,13 +404,14 @@ export class Trajectory {
else if(date > missionEnd) date = missionEnd;
// Get the step where the pod is at this date
const k = lastStep.flyby ? 0 : 1; // ignore circular orbit if insertion burn occured
let i = 1;
for(; i < this.steps.length-1; i++){
for(; i < this.steps.length-k; i++){
const {dateOfStart, duration} = this.steps[i];
if(date >= dateOfStart && date < dateOfStart + duration)
break;
}
i = Math.min(i, this.steps.length-2);
i = Math.min(i, this.steps.length-k-1);
const step = this.steps[i];
@@ -427,11 +428,10 @@ export class Trajectory {
const newGroup = this.system.objectsOfBody(newAttrId);
newGroup.add(pod);
// Compute its position on the leg arc where the pod is
// Compute the position on the arc where the pod is
const orbit = this.orbits[i];
const relDate = date - step.dateOfStart;
const {startM} = step;
const trueAnom = orbit.solveTrueAnomalyAtDate(startM, 0, relDate);
const trueAnom = orbit.solveTrueAnomalyAtDate(startM, step.dateOfStart, date);
const pos = orbit.positionFromTrueAnomaly(trueAnom);
const {scale} = this.config.rendering;

8
src/types.d.ts vendored
View File

@@ -296,6 +296,14 @@ type FlybyDetails = {
inclinationDeg: number
};
type TrajectoryUserSettings = {
startDate: number,
endDate: number,
depAltitude: number,
destAltitude: number,
noInsertion: boolean
};
type ResultPannelItems = {
dateSpan: HTMLSpanElement,
progradeDVSpan: HTMLSpanElement,

View File

@@ -206,7 +206,7 @@ input[type="text"]::placeholder
.control-group .control-label
{
width: 190px;
width: 140px;
text-align: right;
padding-right: 17px;
}
@@ -223,7 +223,7 @@ input[type="text"]::placeholder
.controls
{
width: 310px;
width: auto;
}
.error-msg, .progress-msg
@@ -451,6 +451,22 @@ input[type="range"]
width: 413px;
}
#insertion-checkbox-container
{
margin-top: 15px;
}
#insertion-checkbox-container .controls
{
margin-left: 157px;
}
#insertion-checkbox
{
margin: 0;
padding: 0;
}
/* Paragraphs */
p