Before full rewrite of orbit calculations
This commit is contained in:
parent
8e55fe863e
commit
400bf42b12
202
index.html
202
index.html
@ -2,12 +2,210 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/png" href="/satellite.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Kerbal calculations</title>
|
<title>Kerbal calculations</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<h1>Kerbal calculations</h1>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td colspan="10"><h3>Current time in game:</h3></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="dateYear">Year:</label></td>
|
||||||
|
<td><input id="dateYear" type="number" min="1" size="2" value="1" /></td>
|
||||||
|
<td><label for="dateDay">Day:</label></td>
|
||||||
|
<td><input id="dateDay" type="number" min="1" max="424" size="1" value="1" /></td>
|
||||||
|
<td><label for="dateHours">Hour:</label></td>
|
||||||
|
<td><input id="dateHours" type="number" min="0" max="5" size="1" value="0" /></td>
|
||||||
|
<td><label for="dateMinutes">Minute:</label></td>
|
||||||
|
<td><input id="dateMinutes" type="number" min="0" max="59" size="1" value="0" /></td>
|
||||||
|
<td><label for="dateSeconds">Second:</label></td>
|
||||||
|
<td><input id="dateSeconds" type="number" min="0" max="59" size="1" value="0" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="10">
|
||||||
|
<h3>Time to periapsis:</h3>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="periapsisYears">Years:</label></td>
|
||||||
|
<td><input id="periapsisYears" type="number" min="0" size="2" value="0" /></td>
|
||||||
|
<td><label for="periapsisDays">Days:</label></td>
|
||||||
|
<td><input id="periapsisDays" type="number" min="0" max="424" size="1" value="0" /></td>
|
||||||
|
<td><label for="periapsisHours">Hours:</label></td>
|
||||||
|
<td><input id="periapsisHours" type="number" min="0" max="5" size="1" value="0" /></td>
|
||||||
|
<td><label for="periapsisMinutes">Minutes:</label></td>
|
||||||
|
<td><input id="periapsisMinutes" type="number" min="0" max="59" size="1" value="0" /></td>
|
||||||
|
<td><label for="periapsisSeconds">Seconds:</label></td>
|
||||||
|
<td><input id="periapsisSeconds" type="number" min="0" max="59" size="1" value="0" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Planet:</h3>
|
||||||
|
<input type="radio" id="kerbol" name="planet" value="kerbol">
|
||||||
|
<label for="kerbol">Kerbol</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="moho" name="planet" value="moho">
|
||||||
|
<label for="moho">Moho</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="eve" name="planet" value="eve">
|
||||||
|
<label for="eve">Eve</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="gilly" name="planet" value="gilly">
|
||||||
|
<label for="gilly">Gilly</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="kerbin" name="planet" value="kerbin" checked>
|
||||||
|
<label for="kerbin">Kerbin</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="mun" name="planet" value="mun">
|
||||||
|
<label for="mun">Mun</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="minmus" name="planet" value="minmus">
|
||||||
|
<label for="minmus">Minmus</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="duna" name="planet" value="duna">
|
||||||
|
<label for="duna">Duna</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="ike" name="planet" value="ike">
|
||||||
|
<label for="ike">Ike</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="dres" name="planet" value="dres">
|
||||||
|
<label for="dres">Dres</label>
|
||||||
|
<br />
|
||||||
|
<input type="radio" id="jool" name="planet" value="jool">
|
||||||
|
<label for="jool">Jool</label>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<h3>Orbital parameters:</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><label for="currentPeriapsis">Periapsis (m):</label></td>
|
||||||
|
<td><input id="currentPeriapsis" type="number" size="10" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="currentApoapsis">Apoapsis (m):</label></td>
|
||||||
|
<td><input id="currentApoapsis" type="number" size="10" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="currentInclination">Inclination (degrees):</label></td>
|
||||||
|
<td><input id="currentInclination" type="number" size="10" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="currentLAN">Longitude of ascending node (degrees):</label></td>
|
||||||
|
<td><input id="currentLAN" type="number" size="10" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="currentAOP">Argument of peripasis (degrees):</label></td>
|
||||||
|
<td><input id="currentAOP" type="number" size="10" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>What to calculate:</h3>
|
||||||
|
<input type="radio" id="coordinates" name="typeOfcalculation" value="coordinates" />
|
||||||
|
<label for="coordinates">Calculate coordinates</label>
|
||||||
|
<input type="radio" id="simplePlaneChange" name="typeOfcalculation" value="simplePlaneChange" />
|
||||||
|
<label for="simplePlaneChange">Simple plane change</label>
|
||||||
|
<input type="radio" id="orbitChange" name="typeOfcalculation" value="orbitChange" />
|
||||||
|
<label for="orbitChange">Orbit change</label>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div id="coordinatesDiv">
|
||||||
|
<button id="calculateCoordinatesButton">Perform calculation</button>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><label for="calculatedMeanAnomaly">Mean anomaly:</label></td><td><input id="calculatedMeanAnomaly" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="calculatedEccentricAnomaly">Eccentric anomaly:</label></td><td><input id="calculatedEccentricAnomaly" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="calculatedX">Position X:</label></td><td><input id="calculatedX" type="number" disabled /></td>
|
||||||
|
<td><label for="calculatedY">Position Y:</label></td><td><input id="calculatedY" type="number" disabled /></td>
|
||||||
|
<td><label for="calculatedZ">Position Z:</label></td><td><input id="calculatedZ" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="calculatedLatitude">Latitude:</label></td><td><input id="calculatedLatitude" type="number" disabled /></td>
|
||||||
|
<td><label for="calculatedLongitude">Longitude:</label></td><td><input id="calculatedLongitude" type="number" disabled /></td>
|
||||||
|
<td><label for="calculatedPlanetLongitude">Longitude over planet:</label></td><td><input id="calculatedPlanetLongitude" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="simplePlaneChangeDiv">
|
||||||
|
<h3>Target plane:</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><label for="targetInclination">Target inclination:</label></td>
|
||||||
|
<td><input id="targetInclination" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="targetLAN">Target longitude of ascending node:</label></td>
|
||||||
|
<td><input id="targetLAN" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<input type="checkbox" id="circularizeOrbit" /><label for="circularizeOrbit">Circularize orbit</label>
|
||||||
|
<br />
|
||||||
|
<button id="simplePlaneChangeButton">Perform calculation</button>
|
||||||
|
|
||||||
|
<h3>Choose one of the following manoeuvres:</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Manoeuvre 1</th><th colspan="2">Manoeuvre 2</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="simpleManoeuvreTime1">Time: </label></td><td><input id="simpleManoeuvreTime1" type="number" disabled /></td>
|
||||||
|
<td><label for="simpleManoeuvreTime2">Time: </label></td><td><input id="simpleManoeuvreTime2" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="simpleManoeuvrePrograde1">Prograde acceleration:</label></td><td><input id="simpleManoeuvrePrograde1" type="number" disabled /></td>
|
||||||
|
<td><label for="simpleManoeuvrePrograde2">Prograde acceleration:</label></td><td><input id="simpleManoeuvrePrograde2" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="simpleManoeuvreNormal1">Normal acceleration:</label></td><td><input id="simpleManoeuvreNormal1" type="number" disabled /></td>
|
||||||
|
<td><label for="simpleManoeuvreNormal2">Normal acceleration:</label></td><td><input id="simpleManoeuvreNormal2" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="simpleManoeuvreRadial1">Radial acceleration:</label></td><td><input id="simpleManoeuvreRadial1" type="number" disabled /></td>
|
||||||
|
<td><label for="simpleManoeuvreRadial2">Radial acceleration:</label></td><td><input id="simpleManoeuvreRadial2" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="simpleManoeuvreTotal1">Total acceleration:</label></td><td><input id="simpleManoeuvreTotal1" type="number" disabled /></td>
|
||||||
|
<td><label for="simpleManoeuvreTotal2">Total acceleration:</label></td><td><input id="simpleManoeuvreTotal2" type="number" disabled /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="orbitChangeDiv">
|
||||||
|
<h3>Target orbit:</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><label for="orbitChangeTargetInclination">Target inclination:</label></td>
|
||||||
|
<td><input id="orbitChangeTargetInclination" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="orbitChangeTargetLAN">Target longitude of ascending node:</label></td>
|
||||||
|
<td><input id="orbitChangeTargetLAN" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="orbitChangeTargetAOP">Target argument of periapsis:</label></td>
|
||||||
|
<td><input id="orbitChangeTargetAOP" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="orbitChangeTargetPeriapsis">Target periapsis:</label></td>
|
||||||
|
<td><input id="orbitChangeTargetPeriapsis" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="orbitChangeTargetApoapsis">Target apoapsis:</label></td>
|
||||||
|
<td><input id="orbitChangeTargetApoapsis" type="number" value="0.0" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<button id="orbitChangeButton">Find best transfer</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
24
package-lock.json
generated
24
package-lock.json
generated
@ -7,11 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "kerbal-calculations",
|
"name": "kerbal-calculations",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
|
||||||
"ts-matrix": "^1.4.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "~5.9.3",
|
|
||||||
"vite": "^7.3.1"
|
"vite": "^7.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1029,26 +1025,6 @@
|
|||||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-matrix": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-matrix/-/ts-matrix-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-huf8zaQF/NackiLsyFiWqX9uyZVGyHCXEmSiWd4/DDuioTlADsO82oA5FOudoKVtZVUDYG4HDzN/jSF2eB7Z7A==",
|
|
||||||
"license": "SEE LICENSE IN LICENSE.md"
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "5.9.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||||
|
|||||||
@ -9,10 +9,6 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "~5.9.3",
|
|
||||||
"vite": "^7.3.1"
|
"vite": "^7.3.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ts-matrix": "^1.4.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/satellite.png
Normal file
BIN
public/satellite.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@ -1,4 +1,4 @@
|
|||||||
interface Planet {
|
export interface Planet {
|
||||||
radius: number,
|
radius: number,
|
||||||
gravitationalParameter: number,
|
gravitationalParameter: number,
|
||||||
rotationPeriod: number,
|
rotationPeriod: number,
|
||||||
@ -6,7 +6,7 @@ interface Planet {
|
|||||||
initialMeridianLongitude: number
|
initialMeridianLongitude: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const Kerbol: Planet = {
|
export const Kerbol: Planet = {
|
||||||
radius: 261600000,
|
radius: 261600000,
|
||||||
gravitationalParameter: 1.1723328e18,
|
gravitationalParameter: 1.1723328e18,
|
||||||
rotationPeriod: 432000,
|
rotationPeriod: 432000,
|
||||||
@ -14,7 +14,7 @@ const Kerbol: Planet = {
|
|||||||
initialMeridianLongitude: 0
|
initialMeridianLongitude: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const Moho: Planet = {
|
export const Moho: Planet = {
|
||||||
radius: 250000,
|
radius: 250000,
|
||||||
gravitationalParameter: 1.6860938e11,
|
gravitationalParameter: 1.6860938e11,
|
||||||
rotationPeriod: 1210000,
|
rotationPeriod: 1210000,
|
||||||
@ -22,7 +22,7 @@ const Moho: Planet = {
|
|||||||
initialMeridianLongitude: 0
|
initialMeridianLongitude: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const Eve: Planet = {
|
export const Eve: Planet = {
|
||||||
radius: 700000,
|
radius: 700000,
|
||||||
gravitationalParameter: 8.1717302e12,
|
gravitationalParameter: 8.1717302e12,
|
||||||
rotationPeriod: 80500,
|
rotationPeriod: 80500,
|
||||||
@ -30,7 +30,7 @@ const Eve: Planet = {
|
|||||||
initialMeridianLongitude: 0
|
initialMeridianLongitude: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const Gilly: Planet = {
|
export const Gilly: Planet = {
|
||||||
radius: 13000,
|
radius: 13000,
|
||||||
gravitationalParameter: 8289449.8,
|
gravitationalParameter: 8289449.8,
|
||||||
rotationPeriod: 28255,
|
rotationPeriod: 28255,
|
||||||
@ -38,7 +38,7 @@ const Gilly: Planet = {
|
|||||||
initialMeridianLongitude: 0.0859373
|
initialMeridianLongitude: 0.0859373
|
||||||
};
|
};
|
||||||
|
|
||||||
const Kerbin: Planet = {
|
export const Kerbin: Planet = {
|
||||||
radius: 600000,
|
radius: 600000,
|
||||||
gravitationalParameter: 3.5316000e12,
|
gravitationalParameter: 3.5316000e12,
|
||||||
rotationPeriod: 21549.425,
|
rotationPeriod: 21549.425,
|
||||||
@ -46,7 +46,7 @@ const Kerbin: Planet = {
|
|||||||
initialMeridianLongitude: 1.571261023
|
initialMeridianLongitude: 1.571261023
|
||||||
};
|
};
|
||||||
|
|
||||||
const Mun: Planet = {
|
export const Mun: Planet = {
|
||||||
radius: 200000,
|
radius: 200000,
|
||||||
gravitationalParameter: 6.5138398e10,
|
gravitationalParameter: 6.5138398e10,
|
||||||
rotationPeriod: 138984.38,
|
rotationPeriod: 138984.38,
|
||||||
@ -54,7 +54,7 @@ const Mun: Planet = {
|
|||||||
initialMeridianLongitude: 4.0145103174219114
|
initialMeridianLongitude: 4.0145103174219114
|
||||||
};
|
};
|
||||||
|
|
||||||
const Minmus: Planet = {
|
export const Minmus: Planet = {
|
||||||
radius: 60000,
|
radius: 60000,
|
||||||
gravitationalParameter: 1.7658000e9,
|
gravitationalParameter: 1.7658000e9,
|
||||||
rotationPeriod: 40400,
|
rotationPeriod: 40400,
|
||||||
|
|||||||
208
src/calculations/mathematics.ts
Normal file
208
src/calculations/mathematics.ts
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
export function checkIfValidMatrix(matrix: number[][]): boolean {
|
||||||
|
if (matrix.length <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just make sure each row has equally many columns
|
||||||
|
var numberOfColumns = -1;
|
||||||
|
matrix.forEach(row => {
|
||||||
|
if (numberOfColumns == -1) {
|
||||||
|
numberOfColumns = row.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row.length != numberOfColumns) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matrixMultiply(matrixOne: number[][], matrixTwo: number[][]): number[][] {
|
||||||
|
if (!checkIfValidMatrix(matrixOne) || !checkIfValidMatrix(matrixTwo)) {
|
||||||
|
throw new TypeError("Two valid matrices are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
var rowsFirstMatrix = matrixOne.length;
|
||||||
|
var colsFirstMatrix = matrixOne[0].length;
|
||||||
|
|
||||||
|
var rowsSecondMatrix = matrixTwo.length;
|
||||||
|
var colsSecondMatrix = matrixTwo[0].length;
|
||||||
|
|
||||||
|
if (colsFirstMatrix != rowsSecondMatrix) {
|
||||||
|
throw new TypeError("The two matrices do not have the correct dimensions to be multiplied together");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < rowsFirstMatrix; i++) {
|
||||||
|
var currentRow = [];
|
||||||
|
for (var j = 0; j < colsSecondMatrix; j++) {
|
||||||
|
var currentResult = 0;
|
||||||
|
for (var k = 0; k < colsFirstMatrix; k++) {
|
||||||
|
currentResult += matrixOne[i][k] * matrixTwo[k][j];
|
||||||
|
}
|
||||||
|
currentRow.push(currentResult);
|
||||||
|
}
|
||||||
|
result.push(currentRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function vectorDotProduct(vectorOne: number[][], vectorTwo: number[][]): number {
|
||||||
|
if (!checkIfValidMatrix(vectorOne) || !checkIfValidMatrix(vectorTwo)) {
|
||||||
|
throw new TypeError("Two valid matrices are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne[0].length != 1 || vectorTwo[0].length != 1) {
|
||||||
|
throw new TypeError("Vectors can only have one column");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne.length != vectorTwo.length) {
|
||||||
|
throw new TypeError("The two vectors need to have the same dimensions");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < vectorOne.length; i++) {
|
||||||
|
result += vectorOne[i][0] * vectorTwo[i][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function vectorCrossProduct(vectorOne: number[][], vectorTwo: number[][]): number[][] {
|
||||||
|
if (!checkIfValidMatrix(vectorOne) || !checkIfValidMatrix(vectorTwo)) {
|
||||||
|
throw new TypeError("Two valid matrices are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne[0].length != 1 || vectorTwo[0].length != 1) {
|
||||||
|
throw new TypeError("Vectors can only have one column");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne.length != vectorTwo.length) {
|
||||||
|
throw new TypeError("The two vectors need to have the same dimensions");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne.length != 3) {
|
||||||
|
throw new TypeError("The vectors need to be three-dimensional");
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
[vectorOne[1][0]*vectorTwo[2][0] - vectorOne[2][0]*vectorTwo[1][0]],
|
||||||
|
[vectorOne[2][0]*vectorTwo[0][0] - vectorOne[0][0]*vectorTwo[2][0]],
|
||||||
|
[vectorOne[0][0]*vectorTwo[1][0] - vectorOne[1][0]*vectorTwo[0][0]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matrixTranspose(matrix: number[][]): number[][] {
|
||||||
|
if (!checkIfValidMatrix(matrix)) {
|
||||||
|
throw new TypeError("A valid matrix is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
for (var j = 0; j < matrix[0].length; j++) {
|
||||||
|
var currentRow = [];
|
||||||
|
for (var i = 0; i < matrix.length; i++) {
|
||||||
|
currentRow.push(matrix[i][j]);
|
||||||
|
}
|
||||||
|
result.push(currentRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addVector(vectorOne: number[][], vectorTwo: number[][]): number[][] {
|
||||||
|
if (!checkIfValidMatrix(vectorOne) || !checkIfValidMatrix(vectorTwo)) {
|
||||||
|
throw new TypeError("Two valid matrices are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne[0].length != 1 || vectorTwo[0].length != 1) {
|
||||||
|
throw new TypeError("Vectors can only have one column");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne.length != vectorTwo.length) {
|
||||||
|
throw new TypeError("The two vectors need to have the same dimensions");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < vectorOne.length; i++) {
|
||||||
|
result.push([vectorOne[i][0] + vectorTwo[i][0]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function subtractVector(vectorOne: number[][], vectorTwo: number[][]): number[][] {
|
||||||
|
if (!checkIfValidMatrix(vectorOne) || !checkIfValidMatrix(vectorTwo)) {
|
||||||
|
throw new TypeError("Two valid matrices are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne[0].length != 1 || vectorTwo[0].length != 1) {
|
||||||
|
throw new TypeError("Vectors can only have one column");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorOne.length != vectorTwo.length) {
|
||||||
|
throw new TypeError("The two vectors need to have the same dimensions");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < vectorOne.length; i++) {
|
||||||
|
result.push([vectorOne[i][0] - vectorTwo[i][0]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getVectorMagnitude(vector: number[][]): number {
|
||||||
|
if (!checkIfValidMatrix(vector)) {
|
||||||
|
throw new TypeError("A valid matrix is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector[0].length != 1) {
|
||||||
|
throw new TypeError("Vectors can only have one column");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < vector.length; i++) {
|
||||||
|
result += vector[i][0]**2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.sqrt(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeVector(vector: number[][]) {
|
||||||
|
if (!checkIfValidMatrix(vector)) {
|
||||||
|
throw new TypeError("A valid matrix is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vector[0].length != 1) {
|
||||||
|
throw new TypeError("Vectors can only have one column");
|
||||||
|
}
|
||||||
|
|
||||||
|
const magnitude = getVectorMagnitude(vector);
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < vector.length; i++) {
|
||||||
|
result.push([vector[i][0] / magnitude]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function multiplyMatrixWithScalar(scalar: number, matrix: number[][]): number[][] {
|
||||||
|
if (!checkIfValidMatrix(matrix)) {
|
||||||
|
throw new TypeError("A valid matrix is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < matrix.length; i++) {
|
||||||
|
var row = [];
|
||||||
|
for (var j = 0; j < matrix[i].length; j++) {
|
||||||
|
row.push(scalar * matrix[i][j]);
|
||||||
|
}
|
||||||
|
result.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@ -0,0 +1,259 @@
|
|||||||
|
import type { Planet } from "./constants";
|
||||||
|
import { getVectorMagnitude, matrixMultiply, matrixTranspose, multiplyMatrixWithScalar, normalizeVector, subtractVector, vectorCrossProduct, vectorDotProduct } from "./mathematics";
|
||||||
|
|
||||||
|
export interface Axes {
|
||||||
|
semiMajor: number,
|
||||||
|
semiMinor: number,
|
||||||
|
linearEccentricity: number,
|
||||||
|
eccentricity: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrbitalElementRotations {
|
||||||
|
argumentOfPeriapsisRotation: number[][],
|
||||||
|
inclinationRotation: number[][],
|
||||||
|
longitudeOfAscendingNodeRotation: number[][],
|
||||||
|
transformationOutOfPlane: number[][],
|
||||||
|
transformationIntoPlane: number[][]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrbitalCoordinates {
|
||||||
|
meanAnomaly: number,
|
||||||
|
orbitalPeriod: number,
|
||||||
|
eccentricAnomaly: number,
|
||||||
|
position: number[][],
|
||||||
|
latitude: number,
|
||||||
|
longitude: number,
|
||||||
|
longitudeOverPlanet: number,
|
||||||
|
axes: Axes,
|
||||||
|
orbitalRotations: OrbitalElementRotations,
|
||||||
|
currentTime: number,
|
||||||
|
planet: Planet
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Manoeuvre {
|
||||||
|
time: number,
|
||||||
|
progradeAcceleration: number,
|
||||||
|
radialAcceleration: number,
|
||||||
|
normalAcceleration: number,
|
||||||
|
totalAcceleration: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const zeroManoeuvre: Manoeuvre = {
|
||||||
|
time: 0,
|
||||||
|
progradeAcceleration: 0,
|
||||||
|
radialAcceleration: 0,
|
||||||
|
normalAcceleration: 0,
|
||||||
|
totalAcceleration: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Transfer {
|
||||||
|
originalPlaneCoordinateAxes: [number[][], number[][], number[][]],
|
||||||
|
transferPlaneCoordinateAxes: [number[][], number[][], number[][]],
|
||||||
|
targetPlaneCoordinateAxes: [number[][], number[][], number[][]],
|
||||||
|
|
||||||
|
firstManoeuvre: Manoeuvre,
|
||||||
|
secondManoeuvre: Manoeuvre
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LambertFunction {
|
||||||
|
(lambda: number): Transfer
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LambertSolver {
|
||||||
|
lambertFunction: LambertFunction,
|
||||||
|
extremeLambda: number,
|
||||||
|
parabolaLambda: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAxes(periapsis: number, apoapsis: number, planet: Planet): Axes {
|
||||||
|
const semiMajor = (periapsis + apoapsis) / 2 + planet.radius;
|
||||||
|
const linearEccentricity = (semiMajor - periapsis - planet.radius);
|
||||||
|
const eccentricity = linearEccentricity / semiMajor;
|
||||||
|
const semiMinor = Math.sqrt(semiMajor**2 - linearEccentricity**2);
|
||||||
|
|
||||||
|
return {
|
||||||
|
semiMajor: semiMajor,
|
||||||
|
semiMinor: semiMinor,
|
||||||
|
linearEccentricity: linearEccentricity,
|
||||||
|
eccentricity: eccentricity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOrbitalPeriod(axes: Axes, gravitationalParameter: number): number {
|
||||||
|
return 2 * Math.PI * Math.sqrt(axes.semiMajor**3 / gravitationalParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMeanAnomalyFromTimeToPeriapsis(timeToPeriapsis: number, periapsis: number, apoapsis: number, planet: Planet): number {
|
||||||
|
const axes = getAxes(periapsis, apoapsis, planet);
|
||||||
|
const orbitalPeriod = getOrbitalPeriod(axes, planet.gravitationalParameter);
|
||||||
|
return (orbitalPeriod - timeToPeriapsis) * 2 * Math.PI / orbitalPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMeanAnomalyFromEccentricAnomaly(eccentricAnomaly: number, eccentricity: number): number {
|
||||||
|
return eccentricAnomaly - eccentricity * Math.sin(eccentricAnomaly);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEccentricAnomalyFromMeanAnomaly(meanAnomaly: number, eccentricity: number) {
|
||||||
|
// Use fixed point iteration
|
||||||
|
var eccentricAnomaly = meanAnomaly;
|
||||||
|
const iterationFunction = (eccentricAnomaly: number): number => {
|
||||||
|
return meanAnomaly + eccentricity * Math.sin(eccentricAnomaly);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (Math.abs(eccentricAnomaly - eccentricity*Math.sin(eccentricAnomaly) - meanAnomaly) > 0.00000001) {
|
||||||
|
eccentricAnomaly = iterationFunction(eccentricAnomaly);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eccentricAnomaly;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOrbitalElementRotations(inclination: number, longitudeOfAscendingNode: number, argumentOfPeriapsis: number): OrbitalElementRotations {
|
||||||
|
const argumentOfPeriapsisRotation =
|
||||||
|
[
|
||||||
|
[Math.cos(argumentOfPeriapsis), -Math.sin(argumentOfPeriapsis), 0],
|
||||||
|
[Math.sin(argumentOfPeriapsis), Math.cos(argumentOfPeriapsis), 0],
|
||||||
|
[0, 0, 1]
|
||||||
|
];
|
||||||
|
|
||||||
|
const inclinationRotation =
|
||||||
|
[
|
||||||
|
[1, 0, 0],
|
||||||
|
[0, Math.cos(inclination), -Math.sin(inclination)],
|
||||||
|
[0, Math.sin(inclination), Math.cos(inclination)]
|
||||||
|
];
|
||||||
|
|
||||||
|
const longitudeOfAscendingNodeRotation =
|
||||||
|
[
|
||||||
|
[Math.cos(longitudeOfAscendingNode), -Math.sin(longitudeOfAscendingNode), 0],
|
||||||
|
[Math.sin(longitudeOfAscendingNode), Math.cos(longitudeOfAscendingNode), 0],
|
||||||
|
[0, 0, 1]
|
||||||
|
];
|
||||||
|
|
||||||
|
const transformationOutOfPlane = matrixMultiply(longitudeOfAscendingNodeRotation, matrixMultiply(inclinationRotation, argumentOfPeriapsisRotation));
|
||||||
|
const transformationIntoPlane = matrixTranspose(transformationOutOfPlane);
|
||||||
|
|
||||||
|
return {
|
||||||
|
argumentOfPeriapsisRotation: argumentOfPeriapsisRotation,
|
||||||
|
inclinationRotation: inclinationRotation,
|
||||||
|
longitudeOfAscendingNodeRotation: longitudeOfAscendingNodeRotation,
|
||||||
|
transformationOutOfPlane: transformationOutOfPlane,
|
||||||
|
transformationIntoPlane: transformationIntoPlane
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOrbitalCoordinates(currentTime: number, timeToPeriapsis: number, periapsis: number, apoapsis: number, inclination: number, longitudeOfAscendingNode: number, argumentOfPeriapsis: number, planet: Planet): OrbitalCoordinates {
|
||||||
|
const axes = getAxes(periapsis, apoapsis, planet);
|
||||||
|
const orbitalPeriod = getOrbitalPeriod(axes, planet.gravitationalParameter);
|
||||||
|
const meanAnomaly = getMeanAnomalyFromTimeToPeriapsis(timeToPeriapsis, periapsis, apoapsis, planet);
|
||||||
|
const eccentricAnomaly = getEccentricAnomalyFromMeanAnomaly(meanAnomaly, axes.eccentricity);
|
||||||
|
const orbitalRotations = getOrbitalElementRotations(inclination, longitudeOfAscendingNode, argumentOfPeriapsis);
|
||||||
|
const localPosition = [
|
||||||
|
[axes.semiMajor * Math.cos(eccentricAnomaly) - axes.linearEccentricity],
|
||||||
|
[axes.semiMinor * Math.sin(eccentricAnomaly)],
|
||||||
|
[0]
|
||||||
|
];
|
||||||
|
|
||||||
|
const globalPosition = matrixMultiply(orbitalRotations.transformationOutOfPlane, localPosition);
|
||||||
|
const longitude = Math.atan2(globalPosition[1][0], globalPosition[0][0]);
|
||||||
|
const latitude = Math.atan2(globalPosition[2][0], Math.sqrt(globalPosition[0][0]**2 + globalPosition[1][0]**2));
|
||||||
|
|
||||||
|
const currentMeridianLongitude = planet.initialMeridianLongitude + currentTime * 2 * Math.PI / planet.rotationPeriod;
|
||||||
|
const longitudeOverPlanet = ((longitude - currentMeridianLongitude) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
||||||
|
|
||||||
|
return {
|
||||||
|
meanAnomaly: meanAnomaly,
|
||||||
|
orbitalPeriod: orbitalPeriod,
|
||||||
|
eccentricAnomaly: eccentricAnomaly,
|
||||||
|
position: globalPosition,
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
longitudeOverPlanet: longitudeOverPlanet,
|
||||||
|
axes: axes,
|
||||||
|
orbitalRotations: orbitalRotations,
|
||||||
|
currentTime: currentTime,
|
||||||
|
planet: planet
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateSimplePlaneChange(coordinates: OrbitalCoordinates, targetInclination: number, targetLongitudeOfAscendingNode: number, circularizeOrbit: boolean): [Manoeuvre, Manoeuvre] {
|
||||||
|
const targetRotations = getOrbitalElementRotations(targetInclination, targetLongitudeOfAscendingNode, 0);
|
||||||
|
const targetPlaneNormalVector = normalizeVector(matrixMultiply(coordinates.orbitalRotations.transformationIntoPlane, matrixMultiply(targetRotations.transformationOutOfPlane, [[0], [0], [1]])));
|
||||||
|
|
||||||
|
// Check if target plane is equal to current plane
|
||||||
|
if (1 - Math.abs(vectorDotProduct(targetPlaneNormalVector, [[0], [0], [1]])) < 0.0001) {
|
||||||
|
return [zeroManoeuvre, zeroManoeuvre];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find vector that is normal to both current plane vector and target plane vector (i.e. lies in both planes)
|
||||||
|
const normalToAll = vectorCrossProduct(targetPlaneNormalVector, [[0], [0], [1]]);
|
||||||
|
|
||||||
|
// Find the true anomaly of this vector
|
||||||
|
const trueAnomaly = Math.atan2(normalToAll[1][0], normalToAll[0][0]);
|
||||||
|
|
||||||
|
// Caclulate the two possible manoeuvres
|
||||||
|
var manoeuvres: Manoeuvre[] = [];
|
||||||
|
const anomalies = [trueAnomaly, trueAnomaly + Math.PI];
|
||||||
|
anomalies.forEach(anomaly => {
|
||||||
|
const eccentricAnomaly = 2 * Math.atan(Math.sqrt((1 - coordinates.axes.eccentricity) / (1 + coordinates.axes.eccentricity)) * Math.tan(anomaly / 2));
|
||||||
|
var meanAnomaly = getMeanAnomalyFromEccentricAnomaly(eccentricAnomaly, coordinates.axes.eccentricity);
|
||||||
|
|
||||||
|
while (meanAnomaly < coordinates.meanAnomaly) {
|
||||||
|
meanAnomaly += 2*Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
const manoeuvreTime = (meanAnomaly - coordinates.meanAnomaly) * coordinates.orbitalPeriod / (2 * Math.PI) + coordinates.currentTime;
|
||||||
|
|
||||||
|
const progradeVector = normalizeVector([
|
||||||
|
[-coordinates.axes.semiMajor * Math.sin(eccentricAnomaly)],
|
||||||
|
[coordinates.axes.semiMinor * Math.cos(eccentricAnomaly)],
|
||||||
|
[0]
|
||||||
|
]);
|
||||||
|
|
||||||
|
const normalVector = [
|
||||||
|
[0],
|
||||||
|
[0],
|
||||||
|
[1]
|
||||||
|
];
|
||||||
|
|
||||||
|
const radialVector = vectorCrossProduct(normalVector, progradeVector);
|
||||||
|
|
||||||
|
const radius = coordinates.axes.semiMajor * (1 - coordinates.axes.eccentricity * Math.cos(eccentricAnomaly));
|
||||||
|
const speed = Math.sqrt(coordinates.planet.gravitationalParameter * (2 / radius - 1 / coordinates.axes.semiMajor));
|
||||||
|
|
||||||
|
const velocity = multiplyMatrixWithScalar(speed, progradeVector);
|
||||||
|
var velocityChange: number[][];
|
||||||
|
var deltaV: number;
|
||||||
|
|
||||||
|
if (!circularizeOrbit) {
|
||||||
|
deltaV = vectorDotProduct(velocity, multiplyMatrixWithScalar(-1, targetPlaneNormalVector));
|
||||||
|
velocityChange = multiplyMatrixWithScalar(deltaV, targetPlaneNormalVector);
|
||||||
|
deltaV = Math.abs(deltaV);
|
||||||
|
} else {
|
||||||
|
const targetSpeed = Math.sqrt(coordinates.planet.gravitationalParameter / radius);
|
||||||
|
const positionVector = [
|
||||||
|
[coordinates.axes.semiMajor * Math.cos(eccentricAnomaly)],
|
||||||
|
[coordinates.axes.semiMinor * Math.sin(eccentricAnomaly)],
|
||||||
|
[0]
|
||||||
|
];
|
||||||
|
|
||||||
|
const targetDirection = normalizeVector(vectorCrossProduct(targetPlaneNormalVector, positionVector));
|
||||||
|
const targetVelocity = multiplyMatrixWithScalar(targetSpeed, targetDirection);
|
||||||
|
velocityChange = subtractVector(targetVelocity, velocity);
|
||||||
|
deltaV = getVectorMagnitude(velocityChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
const progradeChange = vectorDotProduct(velocityChange, progradeVector);
|
||||||
|
const radialChange = -vectorDotProduct(velocityChange, radialVector);
|
||||||
|
const normalChange = vectorDotProduct(velocityChange, normalVector);
|
||||||
|
|
||||||
|
manoeuvres.push({
|
||||||
|
time: manoeuvreTime,
|
||||||
|
progradeAcceleration: progradeChange,
|
||||||
|
radialAcceleration: radialChange,
|
||||||
|
normalAcceleration: normalChange,
|
||||||
|
totalAcceleration: deltaV
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return [manoeuvres[0], manoeuvres[1]];
|
||||||
|
}
|
||||||
237
src/main.ts
237
src/main.ts
@ -1,24 +1,217 @@
|
|||||||
import './style.css'
|
import { Eve, Gilly, Kerbin, Kerbol, Minmus, Moho, Mun, type Planet } from "./calculations/constants";
|
||||||
import typescriptLogo from './typescript.svg'
|
import {calculateSimplePlaneChange, getOrbitalCoordinates} from "./calculations/orbit-calculations";
|
||||||
import viteLogo from '/vite.svg'
|
|
||||||
import { setupCounter } from './counter.ts'
|
|
||||||
|
|
||||||
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
const dateInputYears = document.getElementById("dateYear") as HTMLInputElement;
|
||||||
<div>
|
const dateInputDays = document.getElementById("dateDay") as HTMLInputElement;
|
||||||
<a href="https://vite.dev" target="_blank">
|
const dateInputHours = document.getElementById("dateHours") as HTMLInputElement;
|
||||||
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
const dateInputMinutes = document.getElementById("dateMinutes") as HTMLInputElement;
|
||||||
</a>
|
const dateInputSeconds = document.getElementById("dateSeconds") as HTMLInputElement;
|
||||||
<a href="https://www.typescriptlang.org/" target="_blank">
|
|
||||||
<img src="${typescriptLogo}" class="logo vanilla" alt="TypeScript logo" />
|
|
||||||
</a>
|
|
||||||
<h1>Vite + TypeScript</h1>
|
|
||||||
<div class="card">
|
|
||||||
<button id="counter" type="button"></button>
|
|
||||||
</div>
|
|
||||||
<p class="read-the-docs">
|
|
||||||
Click on the Vite and TypeScript logos to learn more
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
|
|
||||||
setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)
|
const periapsisInputYears = document.getElementById("periapsisYears") as HTMLInputElement;
|
||||||
|
const periapsisInputDays = document.getElementById("periapsisDays") as HTMLInputElement;
|
||||||
|
const periapsisInputHours = document.getElementById("periapsisHours") as HTMLInputElement;
|
||||||
|
const periapsisInputMinutes = document.getElementById("periapsisMinutes") as HTMLInputElement;
|
||||||
|
const periapsisInputSeconds = document.getElementById("periapsisSeconds") as HTMLInputElement;
|
||||||
|
|
||||||
|
const mohoButton = document.getElementById("moho") as HTMLInputElement;
|
||||||
|
const eveButton = document.getElementById("eve") as HTMLInputElement;
|
||||||
|
const gillyButton = document.getElementById("gilly") as HTMLInputElement;
|
||||||
|
const kerbinButton = document.getElementById("kerbin") as HTMLInputElement;
|
||||||
|
const munButton = document.getElementById("mun") as HTMLInputElement;
|
||||||
|
const minmusButton = document.getElementById("minmus") as HTMLInputElement;
|
||||||
|
|
||||||
|
const currentPeriapsisInput = document.getElementById("currentPeriapsis") as HTMLInputElement;
|
||||||
|
const currentApoapsisInput = document.getElementById("currentApoapsis") as HTMLInputElement;
|
||||||
|
const currentInclinationInput = document.getElementById("currentInclination") as HTMLInputElement;
|
||||||
|
const currentLANInput = document.getElementById("currentLAN") as HTMLInputElement;
|
||||||
|
const currentAOPInput = document.getElementById("currentAOP") as HTMLInputElement;
|
||||||
|
|
||||||
|
const coordinatesRadio = document.getElementById("coordinates") as HTMLInputElement;
|
||||||
|
const simplePlaneChangeRadio = document.getElementById("simplePlaneChange") as HTMLInputElement;
|
||||||
|
const orbitChangeRadio = document.getElementById("orbitChange") as HTMLInputElement;
|
||||||
|
|
||||||
|
const coordinatesDiv = document.getElementById("coordinatesDiv");
|
||||||
|
const coordinateCalculationButton = document.getElementById("calculateCoordinatesButton") as HTMLButtonElement;
|
||||||
|
const meanAnomalyInput = document.getElementById("calculatedMeanAnomaly") as HTMLInputElement;
|
||||||
|
const eccentricAnomalyInput = document.getElementById("calculatedEccentricAnomaly") as HTMLInputElement;
|
||||||
|
const positionXInput = document.getElementById("calculatedX") as HTMLInputElement;
|
||||||
|
const positionYInput = document.getElementById("calculatedY") as HTMLInputElement;
|
||||||
|
const positionZInput = document.getElementById("calculatedZ") as HTMLInputElement;
|
||||||
|
const latitudeInput = document.getElementById("calculatedLatitude") as HTMLInputElement;
|
||||||
|
const longitudeInput = document.getElementById("calculatedLongitude") as HTMLInputElement;
|
||||||
|
const longitudeOverPlanetInput = document.getElementById("calculatedPlanetLongitude") as HTMLInputElement;
|
||||||
|
|
||||||
|
const simplePlaneChangeDiv = document.getElementById("simplePlaneChangeDiv");
|
||||||
|
const targetInclinationInput = document.getElementById("targetInclination") as HTMLInputElement;
|
||||||
|
const targetLANInput = document.getElementById("targetLAN") as HTMLInputElement;
|
||||||
|
const circularizeCheckbox = document.getElementById("circularizeOrbit") as HTMLInputElement;
|
||||||
|
const simplePlaneChangeButton = document.getElementById("simplePlaneChangeButton") as HTMLButtonElement;
|
||||||
|
|
||||||
|
const orbitChangeDiv = document.getElementById("orbitChangeDiv");
|
||||||
|
|
||||||
|
const simplePlaneChangeTimes = [
|
||||||
|
document.getElementById("simpleManoeuvreTime1") as HTMLInputElement,
|
||||||
|
document.getElementById("simpleManoeuvreTime2") as HTMLInputElement
|
||||||
|
];
|
||||||
|
const simplePlaneChangeProgrades = [
|
||||||
|
document.getElementById("simpleManoeuvrePrograde1") as HTMLInputElement,
|
||||||
|
document.getElementById("simpleManoeuvrePrograde2") as HTMLInputElement
|
||||||
|
];
|
||||||
|
|
||||||
|
const simplePlaneChangeRadials = [
|
||||||
|
document.getElementById("simpleManoeuvreRadial1") as HTMLInputElement,
|
||||||
|
document.getElementById("simpleManoeuvreRadial2") as HTMLInputElement
|
||||||
|
];
|
||||||
|
|
||||||
|
const simplePlaneChangeNormals = [
|
||||||
|
document.getElementById("simpleManoeuvreNormal1") as HTMLInputElement,
|
||||||
|
document.getElementById("simpleManoeuvreNormal2") as HTMLInputElement
|
||||||
|
];
|
||||||
|
|
||||||
|
const simplePlaneChangeTotals = [
|
||||||
|
document.getElementById("simpleManoeuvreTotal1") as HTMLInputElement,
|
||||||
|
document.getElementById("simpleManoeuvreTotal2") as HTMLInputElement
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
function getDate(): number {
|
||||||
|
const years = parseInt(dateInputYears.value);
|
||||||
|
const days = parseInt(dateInputDays.value);
|
||||||
|
const hours = parseInt(dateInputHours.value);
|
||||||
|
const minutes = parseInt(dateInputMinutes.value);
|
||||||
|
const seconds = parseInt(dateInputSeconds.value);
|
||||||
|
|
||||||
|
return ((((years - 1) * 426 + days - 1) * 6 + hours) * 60 + minutes) * 60 + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeToPeriapsis(): number {
|
||||||
|
const years = parseInt(periapsisInputYears.value);
|
||||||
|
const days = parseInt(periapsisInputDays.value);
|
||||||
|
const hours = parseInt(periapsisInputHours.value);
|
||||||
|
const minutes = parseInt(periapsisInputMinutes.value);
|
||||||
|
const seconds = parseInt(periapsisInputSeconds.value);
|
||||||
|
|
||||||
|
return (((years * 426 + days) * 6 + hours) * 60 + minutes) * 60 + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlanet(): Planet {
|
||||||
|
if (mohoButton.checked) {
|
||||||
|
return Moho;
|
||||||
|
} else if (eveButton.checked) {
|
||||||
|
return Eve;
|
||||||
|
} else if (gillyButton.checked) {
|
||||||
|
return Gilly;
|
||||||
|
} else if (kerbinButton.checked) {
|
||||||
|
return Kerbin;
|
||||||
|
} else if (munButton.checked) {
|
||||||
|
return Mun;
|
||||||
|
} else if (minmusButton.checked) {
|
||||||
|
return Minmus;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Kerbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectCalculationType() {
|
||||||
|
if (coordinatesRadio.checked) {
|
||||||
|
coordinatesDiv?.style.setProperty("display", "block");
|
||||||
|
simplePlaneChangeDiv?.style.setProperty("display", "none");
|
||||||
|
orbitChangeDiv?.style.setProperty("display", "none");
|
||||||
|
} else if (simplePlaneChangeRadio.checked) {
|
||||||
|
coordinatesDiv?.style.setProperty("display", "none");
|
||||||
|
simplePlaneChangeDiv?.style.setProperty("display", "block");
|
||||||
|
orbitChangeDiv?.style.setProperty("display", "none");
|
||||||
|
} else if (orbitChangeRadio.checked) {
|
||||||
|
coordinatesDiv?.style.setProperty("display", "none");
|
||||||
|
simplePlaneChangeDiv?.style.setProperty("display", "none");
|
||||||
|
orbitChangeDiv?.style.setProperty("display", "block");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinatesRadio.onclick = selectCalculationType;
|
||||||
|
simplePlaneChangeRadio.onclick = selectCalculationType;
|
||||||
|
orbitChangeRadio.onclick = selectCalculationType;
|
||||||
|
|
||||||
|
interface CommonInputs {
|
||||||
|
periapsis: number,
|
||||||
|
apoapsis: number,
|
||||||
|
timeToPeriapsis: number,
|
||||||
|
planet: Planet,
|
||||||
|
inclination: number,
|
||||||
|
longitudeOfAscendingNode: number,
|
||||||
|
argumentOfPeriapsis: number,
|
||||||
|
timeElapsed: number
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommonInputs(): CommonInputs {
|
||||||
|
const periapsis = parseFloat(currentPeriapsisInput.value);
|
||||||
|
const apoapsis = parseFloat(currentApoapsisInput.value);
|
||||||
|
const timeToPeriapsis = getTimeToPeriapsis();
|
||||||
|
const planet = getPlanet();
|
||||||
|
const inclination = parseFloat(currentInclinationInput.value) * Math.PI / 180.0;
|
||||||
|
const longitudeOfAscendingNode = parseFloat(currentLANInput.value) * Math.PI / 180.0;
|
||||||
|
const argumentOfPeriapsis = parseFloat(currentAOPInput.value) * Math.PI / 180.0;
|
||||||
|
const timeElapsed = getDate();
|
||||||
|
|
||||||
|
return {
|
||||||
|
periapsis: periapsis,
|
||||||
|
apoapsis: apoapsis,
|
||||||
|
timeToPeriapsis: timeToPeriapsis,
|
||||||
|
planet: planet,
|
||||||
|
inclination: inclination,
|
||||||
|
longitudeOfAscendingNode: longitudeOfAscendingNode,
|
||||||
|
argumentOfPeriapsis: argumentOfPeriapsis,
|
||||||
|
timeElapsed: timeElapsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinateCalculationButton.addEventListener("click", _ => {
|
||||||
|
const commonInputs = getCommonInputs();
|
||||||
|
const orbitalCoordinates = getOrbitalCoordinates(
|
||||||
|
commonInputs.timeElapsed,
|
||||||
|
commonInputs.timeToPeriapsis,
|
||||||
|
commonInputs.periapsis,
|
||||||
|
commonInputs.apoapsis,
|
||||||
|
commonInputs.inclination,
|
||||||
|
commonInputs.longitudeOfAscendingNode,
|
||||||
|
commonInputs.argumentOfPeriapsis,
|
||||||
|
commonInputs.planet
|
||||||
|
);
|
||||||
|
|
||||||
|
meanAnomalyInput.value = orbitalCoordinates.meanAnomaly.toFixed(4);
|
||||||
|
eccentricAnomalyInput.value = orbitalCoordinates.eccentricAnomaly.toFixed(4);
|
||||||
|
positionXInput.value = orbitalCoordinates.position[0][0].toFixed(2);
|
||||||
|
positionYInput.value = orbitalCoordinates.position[1][0].toFixed(2);
|
||||||
|
positionZInput.value = orbitalCoordinates.position[2][0].toFixed(2);
|
||||||
|
latitudeInput.value = (orbitalCoordinates.latitude * 180 / Math.PI).toFixed(6);
|
||||||
|
longitudeInput.value = (orbitalCoordinates.longitude * 180 / Math.PI).toFixed(6);
|
||||||
|
longitudeOverPlanetInput.value = (orbitalCoordinates.longitudeOverPlanet * 180 / Math.PI).toFixed(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
simplePlaneChangeButton.addEventListener("click", _ => {
|
||||||
|
const commonInputs = getCommonInputs();
|
||||||
|
const orbitalCoordinates = getOrbitalCoordinates(
|
||||||
|
commonInputs.timeElapsed,
|
||||||
|
commonInputs.timeToPeriapsis,
|
||||||
|
commonInputs.periapsis,
|
||||||
|
commonInputs.apoapsis,
|
||||||
|
commonInputs.inclination,
|
||||||
|
commonInputs.longitudeOfAscendingNode,
|
||||||
|
commonInputs.argumentOfPeriapsis,
|
||||||
|
commonInputs.planet
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetInclination = parseFloat(targetInclinationInput.value) * Math.PI / 180.0;
|
||||||
|
const targetLongitudeOfAscendingNode = parseFloat(targetLANInput.value) * Math.PI / 180.0;
|
||||||
|
const manoeuvres = calculateSimplePlaneChange(orbitalCoordinates, targetInclination, targetLongitudeOfAscendingNode, circularizeCheckbox.checked);
|
||||||
|
manoeuvres.sort((a, b) => a.totalAcceleration - b.totalAcceleration);
|
||||||
|
manoeuvres.forEach((manoeuvre, index) => {
|
||||||
|
simplePlaneChangeTimes[index].value = manoeuvre.time.toFixed(0);
|
||||||
|
simplePlaneChangeProgrades[index].value = manoeuvre.progradeAcceleration.toFixed(1);
|
||||||
|
simplePlaneChangeRadials[index].value = manoeuvre.radialAcceleration.toFixed(1);
|
||||||
|
simplePlaneChangeNormals[index].value = manoeuvre.normalAcceleration.toFixed(1);
|
||||||
|
simplePlaneChangeTotals[index].value = manoeuvre.totalAcceleration.toFixed(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
selectCalculationType();
|
||||||
Loading…
x
Reference in New Issue
Block a user