@johnmorrisdotca/uchusen-rockets 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The engine preheat bar before ignition: ten segments and a percent. It fills from 13 seconds before lift-off to 3 seconds before, and is full after.
3
+ * @param missionS Mission seconds from lift-off (negative before).
4
+ * @returns The percent (0 to 100), how many of the ten segments are full, and how full the next one is (0 to 1).
5
+ * @example
6
+ * ```ts
7
+ * import { preheat } from "@johnmorrisdotca/uchusen-rockets/journey";
8
+ *
9
+ * console.log(preheat(-8.5));
10
+ * ```
11
+ */
12
+ export function preheat(missionS) {
13
+ const share = Math.max(0, Math.min(1, (missionS + 13) / 10));
14
+ const percent = Math.round(share * 100);
15
+ const full = Math.min(10, Math.floor(share * 10));
16
+ return { percent, full, partial: full >= 10 ? 0 : share * 10 - full };
17
+ }
18
+ /**
19
+ * The ignition count: IGNITION IN 3, 2, 1, then 0 from lift-off, with how far round the ring dial has gone.
20
+ * @param missionS Mission seconds from lift-off (negative before).
21
+ * @returns The number to show (3, 2, 1 or 0) and the ring's sweep (0 to 1).
22
+ * @example
23
+ * ```ts
24
+ * import { ignition } from "@johnmorrisdotca/uchusen-rockets/journey";
25
+ *
26
+ * console.log(ignition(-2.2), ignition(0));
27
+ * ```
28
+ */
29
+ export function ignition(missionS) {
30
+ if (missionS >= 0)
31
+ return { count: 0, ring: 1 };
32
+ return { count: Math.max(0, Math.min(3, Math.ceil(-missionS))), ring: Math.max(0, Math.min(1, (missionS + 3) / 3)) };
33
+ }
34
+ /**
35
+ * The timer before and during the braking burn: DECELERATION BURN IN until it starts, DECELERATION COMPLETE IN while it runs, nothing after.
36
+ * @param trip A planned trip.
37
+ * @param missionS Mission seconds from lift-off.
38
+ * @returns Which timer, and its seconds; or null when there is none to show.
39
+ * @example
40
+ * ```ts no-run
41
+ * import { burnTimer, type Trip } from "@johnmorrisdotca/uchusen-rockets/journey";
42
+ *
43
+ * declare const trip: Trip;
44
+ * console.log(burnTimer(trip, 10));
45
+ * ```
46
+ */
47
+ export function burnTimer(trip, missionS) {
48
+ const braking = trip.phases.find((phase) => phase.id === "brake" || phase.id === "decelerate" || phase.id === "flyby");
49
+ if (!braking || missionS < 0)
50
+ return null;
51
+ if (missionS < braking.startS)
52
+ return { kind: "burn-in", seconds: braking.startS - missionS };
53
+ if (missionS < braking.endS)
54
+ return { kind: "complete-in", seconds: braking.endS - missionS };
55
+ return null;
56
+ }
@@ -0,0 +1,165 @@
1
+ import type { SourceId } from "../data/sources.ts";
2
+ /**
3
+ * A number with where it is from. Either `source` names a source of this package, or `estimate` says what it is worked from; one of the two is always there.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { CRAFTS } from "@johnmorrisdotca/uchusen-rockets/journey";
8
+ *
9
+ * const saturn = CRAFTS.find((craft) => craft.id === "saturn-v");
10
+ * const tli = saturn?.drive === "ballistic" ? saturn.capacityKg.tli : undefined;
11
+ * console.log(tli?.value, tli?.source);
12
+ * ```
13
+ */
14
+ export interface Quantity {
15
+ value: number;
16
+ source: SourceId | null;
17
+ /** For a number that no source gives: what it is worked from. Shown with "est." wherever the number is. */
18
+ estimate?: string;
19
+ note?: string;
20
+ }
21
+ /** The kinds of orbit a launch vehicle's payload figure is given for, lowest energy first. */
22
+ export type CapacityKind = "leo" | "gto" | "tli" | "mars";
23
+ export interface Payload {
24
+ name: string;
25
+ massKg: Quantity;
26
+ /** The thrust of the engine that brakes it at arrival, newtons. */
27
+ thrustN?: Quantity;
28
+ /** What that engine can change its speed by, metres a second. */
29
+ dvMs?: Quantity;
30
+ /** Used where no thrust is given: the braking acceleration, in g. */
31
+ brakeG?: Quantity;
32
+ /** The specific impulse of the braking engine, seconds: what turns a speed change into propellant. */
33
+ ispS?: Quantity;
34
+ }
35
+ /**
36
+ * What of a rocket is still there at one point of a trip, drawn with the rocket's own parts: the whole rocket, or the slots that are left, of the rocket and variant named.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import type { Stage } from "@johnmorrisdotca/uchusen-rockets/journey";
41
+ *
42
+ * const left: Stage = { rocket: "saturn-v", variant: "saturn-v", slots: ["service-module", "capsule"], name: "Apollo command and service module" };
43
+ * console.log(left.name);
44
+ * ```
45
+ */
46
+ export interface Stage {
47
+ rocket: string;
48
+ variant: string;
49
+ /** The slots that are left, closed up into one stack; null for the whole rocket. */
50
+ slots: readonly string[] | null;
51
+ name: string;
52
+ }
53
+ /**
54
+ * A rocket that flies a Hohmann transfer: its published payloads and the spacecraft it carries.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { CRAFTS } from "@johnmorrisdotca/uchusen-rockets/journey";
59
+ *
60
+ * console.log(CRAFTS.filter((craft) => craft.drive === "ballistic").map((craft) => craft.name));
61
+ * ```
62
+ */
63
+ export interface BallisticCraft {
64
+ id: string;
65
+ name: string;
66
+ kind: "real";
67
+ drive: "ballistic";
68
+ /** The rocket and variant the stage draws. */
69
+ rocket: string;
70
+ variant: string;
71
+ capacityKg: Partial<Record<CapacityKind, Quantity>>;
72
+ payload: Payload;
73
+ /** Seconds from lift-off to the parking orbit. */
74
+ ascentS: Quantity;
75
+ /** Seconds of the departure burn, where a source gives it. */
76
+ departureBurnS?: Quantity;
77
+ /** Seconds the craft takes to turn round before the braking burn. */
78
+ flipS: Quantity;
79
+ /** What is left after each burn: the whole rocket at lift-off, the upper stage with its payload for the departure burn and the coast, the payload alone from the turn round. */
80
+ stages: {
81
+ launch: Stage;
82
+ upper: Stage;
83
+ payload: Stage;
84
+ };
85
+ /** Which way the drawing looks: `front` for a rocket with boosters on its sides, so that all of its cores show. */
86
+ launchView: "side" | "front";
87
+ }
88
+ /**
89
+ * A ship that burns all the way: constant acceleration, a turn round at the halfway point, the same acceleration to stop.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * import { CRAFTS } from "@johnmorrisdotca/uchusen-rockets/journey";
94
+ *
95
+ * const ship = CRAFTS.find((craft) => craft.drive === "continuous");
96
+ * console.log(ship?.name);
97
+ * ```
98
+ */
99
+ export interface ContinuousCraft {
100
+ id: string;
101
+ name: string;
102
+ kind: "fleet";
103
+ drive: "continuous";
104
+ rocket: string;
105
+ variant: string;
106
+ /** Proper acceleration, in g. */
107
+ g: Quantity;
108
+ /** Exhaust speed as a fraction of the speed of light. */
109
+ exhaustOfC: Quantity;
110
+ dryT: Quantity;
111
+ payloadT: Quantity;
112
+ propellantT: Quantity;
113
+ flipS: Quantity;
114
+ }
115
+ /**
116
+ * A craft the journey can fly: a rocket on a Hohmann transfer, or a ship that burns all the way.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * import { CRAFTS, type Craft } from "@johnmorrisdotca/uchusen-rockets/journey";
121
+ *
122
+ * const first: Craft = CRAFTS[0]!;
123
+ * console.log(first.name, first.drive);
124
+ * ```
125
+ */
126
+ export type Craft = BallisticCraft | ContinuousCraft;
127
+ /**
128
+ * Every craft the journey offers: three real rockets and one ship of the SPX fleet. A figure names its source, or is marked as an estimate and says what it is worked from.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * import { CRAFTS } from "@johnmorrisdotca/uchusen-rockets/journey";
133
+ *
134
+ * console.log(CRAFTS.map((craft) => craft.id));
135
+ * ```
136
+ */
137
+ export declare const CRAFTS: readonly Craft[];
138
+ /**
139
+ * What of a craft is left at a phase of its trip, to draw: the whole rocket until the ascent is over, the upper stage with its payload for the departure burn and the coast, and the payload alone from the turn round. A ship that burns all the way is whole throughout.
140
+ *
141
+ * @param craft A craft.
142
+ * @param phase The phase of the trip, such as `coast`.
143
+ * @returns The rocket, variant and slots to draw, and a name for them.
144
+ * @example
145
+ * ```ts
146
+ * import { craftOf, stageOf } from "@johnmorrisdotca/uchusen-rockets/journey";
147
+ *
148
+ * const saturn = craftOf("saturn-v")!;
149
+ * console.log(stageOf(saturn, "ascent").name, stageOf(saturn, "coast").name, stageOf(saturn, "brake").name);
150
+ * ```
151
+ */
152
+ export declare function stageOf(craft: Craft, phase: string): Stage;
153
+ /**
154
+ * A craft by its id.
155
+ *
156
+ * @param id A craft's id.
157
+ * @returns The craft, or undefined when there is none.
158
+ * @example
159
+ * ```ts
160
+ * import { craftOf } from "@johnmorrisdotca/uchusen-rockets/journey";
161
+ *
162
+ * console.log(craftOf("sls")?.name, craftOf("nothing"));
163
+ * ```
164
+ */
165
+ export declare const craftOf: (id: string) => Craft | undefined;
@@ -0,0 +1,142 @@
1
+ const ascentEstimate = (seconds) => ({ value: seconds, source: null, estimate: "A rocket of this class reaches orbit in about eight to nine minutes; this craft's own figure is not in the sources read." });
2
+ /**
3
+ * Every craft the journey offers: three real rockets and one ship of the SPX fleet. A figure names its source, or is marked as an estimate and says what it is worked from.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { CRAFTS } from "@johnmorrisdotca/uchusen-rockets/journey";
8
+ *
9
+ * console.log(CRAFTS.map((craft) => craft.id));
10
+ * ```
11
+ */
12
+ export const CRAFTS = [
13
+ {
14
+ id: "saturn-v",
15
+ name: "Saturn V",
16
+ kind: "real",
17
+ drive: "ballistic",
18
+ rocket: "saturn-v",
19
+ variant: "saturn-v",
20
+ capacityKg: {
21
+ leo: { value: 140_000, source: "wikipedia-saturn-v", note: "Includes the S-IVB stage and the propellant for trans-lunar injection." },
22
+ tli: { value: 43_500, source: "wikipedia-saturn-v" },
23
+ },
24
+ payload: {
25
+ name: "Apollo command and service module with the lunar module",
26
+ massKg: { value: 43_500, source: "wikipedia-saturn-v", note: "The trans-lunar payload." },
27
+ thrustN: { value: 91_189, source: "wikipedia-apollo-csm", note: "The service propulsion engine, 20,500 lbf." },
28
+ ispS: { value: 314, source: "wikipedia-apollo-csm", note: "The service propulsion engine's specific impulse." },
29
+ },
30
+ ascentS: { value: 700, source: "wikipedia-saturn-v", note: "The third stage's first cut-off, about 11 minutes 40 seconds after lift-off." },
31
+ departureBurnS: { value: 350, source: "wikipedia-saturn-v", note: "The article says the third stage burned for almost six minutes for trans-lunar injection." },
32
+ flipS: { value: 120, source: null, estimate: "Transposition, docking and extraction took tens of minutes; the turn before a braking burn is taken as two minutes." },
33
+ launchView: "side",
34
+ stages: {
35
+ launch: { rocket: "saturn-v", variant: "saturn-v", slots: null, name: "Saturn V" },
36
+ upper: { rocket: "saturn-v", variant: "saturn-v", slots: ["third-stage", "adapter", "service-module", "capsule"], name: "S-IVB with the Apollo spacecraft" },
37
+ payload: { rocket: "saturn-v", variant: "saturn-v", slots: ["service-module", "capsule"], name: "Apollo command and service module" },
38
+ },
39
+ },
40
+ {
41
+ id: "sls",
42
+ name: "Space Launch System",
43
+ kind: "real",
44
+ drive: "ballistic",
45
+ rocket: "sls",
46
+ variant: "block-1",
47
+ capacityKg: {
48
+ leo: { value: 95_000, source: "wikipedia-sls" },
49
+ tli: { value: 27_000, source: "wikipedia-sls", note: "59,500 lb." },
50
+ },
51
+ payload: {
52
+ name: "Orion with its service module",
53
+ massKg: { value: 26_375, source: "wikipedia-orion", note: "After separation from the upper stage." },
54
+ thrustN: { value: 26_600, source: "wikipedia-esm", note: "The AJ10 main engine of the European Service Module." },
55
+ dvMs: { value: 1_050, source: "wikipedia-orion", note: "What NASA's interface document says Orion can change its speed by after the upper stage." },
56
+ },
57
+ ascentS: ascentEstimate(480),
58
+ flipS: { value: 120, source: null, estimate: "The turn before a braking burn is taken as two minutes." },
59
+ launchView: "front",
60
+ stages: {
61
+ launch: { rocket: "sls", variant: "block-1", slots: null, name: "Space Launch System" },
62
+ upper: { rocket: "sls", variant: "block-1", slots: ["upper-stage", "service-module", "capsule"], name: "Upper stage with Orion" },
63
+ payload: { rocket: "sls", variant: "block-1", slots: ["service-module", "capsule"], name: "Orion" },
64
+ },
65
+ },
66
+ {
67
+ id: "falcon-heavy",
68
+ name: "Falcon Heavy",
69
+ kind: "real",
70
+ drive: "ballistic",
71
+ rocket: "falcon-heavy",
72
+ variant: "falcon-heavy",
73
+ capacityKg: {
74
+ leo: { value: 63_800, source: "wikipedia-falcon-heavy", note: "With every stage expended." },
75
+ gto: { value: 26_700, source: "wikipedia-falcon-heavy" },
76
+ mars: { value: 16_800, source: "wikipedia-falcon-heavy" },
77
+ },
78
+ payload: {
79
+ name: "A spacecraft of the Mars-transfer payload mass",
80
+ massKg: { value: 16_800, source: "wikipedia-falcon-heavy", note: "The Mars figure." },
81
+ brakeG: { value: 0.2, source: null, estimate: "The article names no spacecraft, so no engine: 0.2 g is a typical figure for a storable-propellant main engine on a craft of this mass." },
82
+ },
83
+ ascentS: ascentEstimate(540),
84
+ flipS: { value: 120, source: null, estimate: "The turn before a braking burn is taken as two minutes." },
85
+ launchView: "front",
86
+ stages: {
87
+ launch: { rocket: "falcon-heavy", variant: "falcon-heavy", slots: null, name: "Falcon Heavy" },
88
+ upper: { rocket: "falcon-9", variant: "block-5", slots: ["second-stage", "fairing"], name: "Upper stage with its payload" },
89
+ payload: { rocket: "falcon-9", variant: "block-5", slots: ["fairing"], name: "The payload in its fairing" },
90
+ },
91
+ },
92
+ {
93
+ id: "kaminari",
94
+ name: "ISV Kaminari, SPX-207",
95
+ kind: "fleet",
96
+ drive: "continuous",
97
+ rocket: "fleet-02",
98
+ variant: "fleet-02",
99
+ g: { value: 0.015, source: "uchusen-fleet-ship-02", note: "Rated proper acceleration." },
100
+ exhaustOfC: { value: 0.4, source: "uchusen-fleet-ship-02", note: "The fusion torch's effective exhaust speed." },
101
+ dryT: { value: 1_100_000, source: "uchusen-fleet-ship-02" },
102
+ payloadT: { value: 1_000_000, source: "uchusen-fleet-ship-02" },
103
+ propellantT: { value: 6_350_000, source: "uchusen-fleet-ship-02" },
104
+ flipS: { value: 600, source: null, estimate: "A ship 1,820 m long turned end for end by its vernier clusters: ten minutes." },
105
+ },
106
+ ];
107
+ /**
108
+ * What of a craft is left at a phase of its trip, to draw: the whole rocket until the ascent is over, the upper stage with its payload for the departure burn and the coast, and the payload alone from the turn round. A ship that burns all the way is whole throughout.
109
+ *
110
+ * @param craft A craft.
111
+ * @param phase The phase of the trip, such as `coast`.
112
+ * @returns The rocket, variant and slots to draw, and a name for them.
113
+ * @example
114
+ * ```ts
115
+ * import { craftOf, stageOf } from "@johnmorrisdotca/uchusen-rockets/journey";
116
+ *
117
+ * const saturn = craftOf("saturn-v")!;
118
+ * console.log(stageOf(saturn, "ascent").name, stageOf(saturn, "coast").name, stageOf(saturn, "brake").name);
119
+ * ```
120
+ */
121
+ export function stageOf(craft, phase) {
122
+ if (craft.drive === "continuous")
123
+ return { rocket: craft.rocket, variant: craft.variant, slots: null, name: craft.name };
124
+ if (phase === "burn" || phase === "coast")
125
+ return craft.stages.upper;
126
+ if (phase === "flip" || phase === "brake" || phase === "flyby" || phase === "arrival")
127
+ return craft.stages.payload;
128
+ return craft.stages.launch;
129
+ }
130
+ /**
131
+ * A craft by its id.
132
+ *
133
+ * @param id A craft's id.
134
+ * @returns The craft, or undefined when there is none.
135
+ * @example
136
+ * ```ts
137
+ * import { craftOf } from "@johnmorrisdotca/uchusen-rockets/journey";
138
+ *
139
+ * console.log(craftOf("sls")?.name, craftOf("nothing"));
140
+ * ```
141
+ */
142
+ export const craftOf = (id) => CRAFTS.find((craft) => craft.id === id);
@@ -0,0 +1,11 @@
1
+ export { AU_KM, AU_M, C, DAY_S, G0, GM_KM3_S2, LY_M, PARKING_ALTITUDE_KM, PERIOD_DAYS, YEAR_S } from "./constants.ts";
2
+ export { circularSpeed, escapeBurn, flipAndBurn, hohmann, kepler, synodicPeriod } from "./physics.ts";
3
+ export type { BodySource, Stop } from "./bodies.ts";
4
+ export { CRAFTS, craftOf, stageOf } from "./crafts.ts";
5
+ export type { BallisticCraft, ContinuousCraft, Craft, Quantity, Stage } from "./crafts.ts";
6
+ export { bestDate, planTrip, windows } from "./trip.ts";
7
+ export type { Phase, Trip, Verdict, Window } from "./trip.ts";
8
+ export { placeAt, playSeconds, stateAt } from "./timeline.ts";
9
+ export type { Place, PlaySpeed, State } from "./timeline.ts";
10
+ export { burnTimer, ignition, preheat } from "./countdown.ts";
11
+ export { astronomicalUnits, duration, gWords, kilometres, lightTime, percentOfLight, speedWords, tonnesWords } from "./words.ts";
@@ -0,0 +1,8 @@
1
+ // Journey mode: the arithmetic and the plan of a trip between places in the solar system. Pure: no DOM, no three.js, no dependency (a page hands it a `BodySource`).
2
+ export { AU_KM, AU_M, C, DAY_S, G0, GM_KM3_S2, LY_M, PARKING_ALTITUDE_KM, PERIOD_DAYS, YEAR_S } from "./constants.js";
3
+ export { circularSpeed, escapeBurn, flipAndBurn, hohmann, kepler, synodicPeriod } from "./physics.js";
4
+ export { CRAFTS, craftOf, stageOf } from "./crafts.js";
5
+ export { bestDate, planTrip, windows } from "./trip.js";
6
+ export { placeAt, playSeconds, stateAt } from "./timeline.js";
7
+ export { burnTimer, ignition, preheat } from "./countdown.js";
8
+ export { astronomicalUnits, duration, gWords, kilometres, lightTime, percentOfLight, speedWords, tonnesWords } from "./words.js";
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Speed of a circular orbit, kilometres a second.
3
+ * @param muKm3s2 The body's gravitational parameter, km^3/s^2.
4
+ * @param rKm The orbit's radius from the body's centre, km.
5
+ * @returns The speed, km/s.
6
+ * @example
7
+ * ```ts
8
+ * import { circularSpeed } from "@johnmorrisdotca/uchusen-rockets/journey";
9
+ *
10
+ * console.log(circularSpeed(398600.4418, 6378.137 + 185).toFixed(2));
11
+ * ```
12
+ */
13
+ export declare const circularSpeed: (muKm3s2: number, rKm: number) => number;
14
+ /**
15
+ * The Hohmann transfer between two circular orbits about one body: its half-ellipse, its time and the two speed changes.
16
+ * @param muKm3s2 The central body's gravitational parameter, km^3/s^2.
17
+ * @param r1Km The starting radius, km.
18
+ * @param r2Km The final radius, km.
19
+ * @returns The semi-major axis (km), the flight time (seconds), the speed change at the start and at the end (km/s, both positive).
20
+ * @example
21
+ * ```ts
22
+ * import { GM_KM3_S2, hohmann } from "@johnmorrisdotca/uchusen-rockets/journey";
23
+ *
24
+ * const mars = hohmann(GM_KM3_S2.sun!, 149_597_870.7, 1.52371034 * 149_597_870.7);
25
+ * console.log((mars.seconds / 86400).toFixed(1), mars.dv1.toFixed(2), mars.dv2.toFixed(2));
26
+ * ```
27
+ */
28
+ export declare function hohmann(muKm3s2: number, r1Km: number, r2Km: number): {
29
+ a: number;
30
+ seconds: number;
31
+ dv1: number;
32
+ dv2: number;
33
+ };
34
+ /**
35
+ * The burn that takes a craft from a circular parking orbit onto a path that leaves with a given speed at infinity, or, run the other way, that captures it into the same circular orbit: `sqrt(v_inf^2 + 2 mu / r) - sqrt(mu / r)`.
36
+ * @param muKm3s2 The body's gravitational parameter, km^3/s^2.
37
+ * @param rKm The parking orbit's radius, km.
38
+ * @param vInfKmS The speed relative to the body once far from it, km/s.
39
+ * @returns The speed change, km/s.
40
+ * @example
41
+ * ```ts
42
+ * import { escapeBurn } from "@johnmorrisdotca/uchusen-rockets/journey";
43
+ *
44
+ * console.log(escapeBurn(398600.4418, 6378.137 + 185, 2.94).toFixed(2));
45
+ * ```
46
+ */
47
+ export declare const escapeBurn: (muKm3s2: number, rKm: number, vInfKmS: number) => number;
48
+ /**
49
+ * The time between two meetings of two bodies in circular orbits, from their periods: `1 / |1/T1 - 1/T2|`.
50
+ * @param period1 The first period, any unit.
51
+ * @param period2 The second period, in the same unit.
52
+ * @returns The synodic period, in that unit.
53
+ * @example
54
+ * ```ts
55
+ * import { synodicPeriod } from "@johnmorrisdotca/uchusen-rockets/journey";
56
+ *
57
+ * console.log(synodicPeriod(365.256, 686.98).toFixed(1));
58
+ * ```
59
+ */
60
+ export declare const synodicPeriod: (period1: number, period2: number) => number;
61
+ /**
62
+ * Kepler's equation `M = E - e sin E`, solved for the eccentric anomaly by Newton's method.
63
+ * @param meanAnomaly The mean anomaly, radians.
64
+ * @param e The eccentricity, 0 to below 1.
65
+ * @returns The eccentric anomaly, radians, in the same turn as the mean anomaly.
66
+ * @example
67
+ * ```ts
68
+ * import { kepler } from "@johnmorrisdotca/uchusen-rockets/journey";
69
+ *
70
+ * console.log(kepler(1, 0.2).toFixed(4));
71
+ * ```
72
+ */
73
+ export declare function kepler(meanAnomaly: number, e: number): number;
74
+ /** Where a craft is on a Hohmann half-ellipse `t` seconds after its burn: its distance from the centre (km), its direction (radians, from the departure direction `theta0`) and its speed (km/s). */
75
+ export declare function transferPoint(muKm3s2: number, r1Km: number, r2Km: number, theta0: number, t: number): {
76
+ r: number;
77
+ theta: number;
78
+ v: number;
79
+ };
80
+ /** The state of a craft that has burned at constant proper acceleration `a` (m/s^2) for `t` seconds of the frame's time, from rest: distance (m), speed (m/s), Lorentz factor and time on board (s). */
81
+ export declare function accelerating(a: number, t: number): {
82
+ x: number;
83
+ v: number;
84
+ gamma: number;
85
+ proper: number;
86
+ };
87
+ /**
88
+ * A trip of constant acceleration with a turn round in the middle: burn to the halfway point, coast while the craft turns end for end, burn the same acceleration to stop. The relativistic constant-proper-acceleration answer, which is `2 sqrt(d / a)` for slow trips.
89
+ * @param distanceM The distance between the two ends, in the frame in which both are at rest, metres.
90
+ * @param a The proper acceleration, m/s^2.
91
+ * @param flipSeconds How long the turn takes, during which the craft coasts at its peak speed. 0 for the textbook flip.
92
+ * @returns How long each burn lasts in the frame's time, the peak speed, the Lorentz factor then, the time on board and in the frame, and the rapidity at the flip.
93
+ * @example
94
+ * ```ts
95
+ * import { flipAndBurn, G0, LY_M } from "@johnmorrisdotca/uchusen-rockets/journey";
96
+ *
97
+ * const trip = flipAndBurn(4.37 * LY_M, G0, 0);
98
+ * console.log((trip.properSeconds / 31_557_600).toFixed(1), (trip.peakSpeed / 299_792_458).toFixed(3));
99
+ * ```
100
+ */
101
+ export declare function flipAndBurn(distanceM: number, a: number, flipSeconds: number): {
102
+ burnSeconds: number;
103
+ peakSpeed: number;
104
+ gamma: number;
105
+ properSeconds: number;
106
+ frameSeconds: number;
107
+ rapidity: number;
108
+ };
109
+ /** The propellant a rocket of exhaust speed `ve` uses to change its speed by `dv` (the Tsiolkovsky equation): the fraction of its starting mass, 0 to 1. Both speeds in the same unit. */
110
+ export declare const propellantFraction: (dv: number, ve: number) => number;
111
+ /** An angle brought into -pi to pi. */
112
+ export declare const wrapPi: (angle: number) => number;
113
+ /** The Lorentz factor of a speed in metres a second. */
114
+ export declare const lorentz: (v: number) => number;
@@ -0,0 +1,154 @@
1
+ // The arithmetic of a trip: circular and escape speeds, the Hohmann transfer, leaving and entering a gravity well, Kepler's equation, the synodic period and constant acceleration with a flip.
2
+ // Newtonian where the speeds are slow, relativistic (constant proper acceleration) for the flip and burn, which reduces to the Newtonian answer in the solar system. Pure: no clock, no DOM, no dependency.
3
+ import { C } from "./constants.js";
4
+ const TAU = 2 * Math.PI;
5
+ /**
6
+ * Speed of a circular orbit, kilometres a second.
7
+ * @param muKm3s2 The body's gravitational parameter, km^3/s^2.
8
+ * @param rKm The orbit's radius from the body's centre, km.
9
+ * @returns The speed, km/s.
10
+ * @example
11
+ * ```ts
12
+ * import { circularSpeed } from "@johnmorrisdotca/uchusen-rockets/journey";
13
+ *
14
+ * console.log(circularSpeed(398600.4418, 6378.137 + 185).toFixed(2));
15
+ * ```
16
+ */
17
+ export const circularSpeed = (muKm3s2, rKm) => Math.sqrt(muKm3s2 / rKm);
18
+ /**
19
+ * The Hohmann transfer between two circular orbits about one body: its half-ellipse, its time and the two speed changes.
20
+ * @param muKm3s2 The central body's gravitational parameter, km^3/s^2.
21
+ * @param r1Km The starting radius, km.
22
+ * @param r2Km The final radius, km.
23
+ * @returns The semi-major axis (km), the flight time (seconds), the speed change at the start and at the end (km/s, both positive).
24
+ * @example
25
+ * ```ts
26
+ * import { GM_KM3_S2, hohmann } from "@johnmorrisdotca/uchusen-rockets/journey";
27
+ *
28
+ * const mars = hohmann(GM_KM3_S2.sun!, 149_597_870.7, 1.52371034 * 149_597_870.7);
29
+ * console.log((mars.seconds / 86400).toFixed(1), mars.dv1.toFixed(2), mars.dv2.toFixed(2));
30
+ * ```
31
+ */
32
+ export function hohmann(muKm3s2, r1Km, r2Km) {
33
+ const a = (r1Km + r2Km) / 2;
34
+ const seconds = Math.PI * Math.sqrt((a * a * a) / muKm3s2);
35
+ const dv1 = Math.abs(Math.sqrt(muKm3s2 / r1Km) * (Math.sqrt((2 * r2Km) / (r1Km + r2Km)) - 1));
36
+ const dv2 = Math.abs(Math.sqrt(muKm3s2 / r2Km) * (1 - Math.sqrt((2 * r1Km) / (r1Km + r2Km))));
37
+ return { a, seconds, dv1, dv2 };
38
+ }
39
+ /**
40
+ * The burn that takes a craft from a circular parking orbit onto a path that leaves with a given speed at infinity, or, run the other way, that captures it into the same circular orbit: `sqrt(v_inf^2 + 2 mu / r) - sqrt(mu / r)`.
41
+ * @param muKm3s2 The body's gravitational parameter, km^3/s^2.
42
+ * @param rKm The parking orbit's radius, km.
43
+ * @param vInfKmS The speed relative to the body once far from it, km/s.
44
+ * @returns The speed change, km/s.
45
+ * @example
46
+ * ```ts
47
+ * import { escapeBurn } from "@johnmorrisdotca/uchusen-rockets/journey";
48
+ *
49
+ * console.log(escapeBurn(398600.4418, 6378.137 + 185, 2.94).toFixed(2));
50
+ * ```
51
+ */
52
+ export const escapeBurn = (muKm3s2, rKm, vInfKmS) => Math.sqrt(vInfKmS * vInfKmS + (2 * muKm3s2) / rKm) - Math.sqrt(muKm3s2 / rKm);
53
+ /**
54
+ * The time between two meetings of two bodies in circular orbits, from their periods: `1 / |1/T1 - 1/T2|`.
55
+ * @param period1 The first period, any unit.
56
+ * @param period2 The second period, in the same unit.
57
+ * @returns The synodic period, in that unit.
58
+ * @example
59
+ * ```ts
60
+ * import { synodicPeriod } from "@johnmorrisdotca/uchusen-rockets/journey";
61
+ *
62
+ * console.log(synodicPeriod(365.256, 686.98).toFixed(1));
63
+ * ```
64
+ */
65
+ export const synodicPeriod = (period1, period2) => 1 / Math.abs(1 / period1 - 1 / period2);
66
+ /**
67
+ * Kepler's equation `M = E - e sin E`, solved for the eccentric anomaly by Newton's method.
68
+ * @param meanAnomaly The mean anomaly, radians.
69
+ * @param e The eccentricity, 0 to below 1.
70
+ * @returns The eccentric anomaly, radians, in the same turn as the mean anomaly.
71
+ * @example
72
+ * ```ts
73
+ * import { kepler } from "@johnmorrisdotca/uchusen-rockets/journey";
74
+ *
75
+ * console.log(kepler(1, 0.2).toFixed(4));
76
+ * ```
77
+ */
78
+ export function kepler(meanAnomaly, e) {
79
+ const turns = Math.floor(meanAnomaly / TAU);
80
+ const m = meanAnomaly - turns * TAU;
81
+ let big = e < 0.8 ? m : Math.PI;
82
+ for (let i = 0; i < 30; i += 1) {
83
+ const step = (big - e * Math.sin(big) - m) / (1 - e * Math.cos(big));
84
+ big -= step;
85
+ if (Math.abs(step) < 1e-13)
86
+ break;
87
+ }
88
+ return big + turns * TAU;
89
+ }
90
+ /** Where a craft is on a Hohmann half-ellipse `t` seconds after its burn: its distance from the centre (km), its direction (radians, from the departure direction `theta0`) and its speed (km/s). */
91
+ export function transferPoint(muKm3s2, r1Km, r2Km, theta0, t) {
92
+ const a = (r1Km + r2Km) / 2;
93
+ const e = Math.abs(r2Km - r1Km) / (r1Km + r2Km);
94
+ const outward = r2Km >= r1Km;
95
+ const n = Math.sqrt(muKm3s2 / (a * a * a));
96
+ const big = kepler((outward ? 0 : Math.PI) + n * t, e);
97
+ const nu = 2 * Math.atan2(Math.sqrt(1 + e) * Math.sin(big / 2), Math.sqrt(1 - e) * Math.cos(big / 2));
98
+ const r = a * (1 - e * Math.cos(big));
99
+ return { r, theta: theta0 + (outward ? nu : nu - Math.PI), v: Math.sqrt(muKm3s2 * (2 / r - 1 / a)) };
100
+ }
101
+ /** The state of a craft that has burned at constant proper acceleration `a` (m/s^2) for `t` seconds of the frame's time, from rest: distance (m), speed (m/s), Lorentz factor and time on board (s). */
102
+ export function accelerating(a, t) {
103
+ const s = (a * t) / C;
104
+ const root = Math.sqrt(1 + s * s);
105
+ return { x: ((C * C) / a) * (root - 1), v: (a * t) / root, gamma: root, proper: (C / a) * Math.asinh(s) };
106
+ }
107
+ /**
108
+ * A trip of constant acceleration with a turn round in the middle: burn to the halfway point, coast while the craft turns end for end, burn the same acceleration to stop. The relativistic constant-proper-acceleration answer, which is `2 sqrt(d / a)` for slow trips.
109
+ * @param distanceM The distance between the two ends, in the frame in which both are at rest, metres.
110
+ * @param a The proper acceleration, m/s^2.
111
+ * @param flipSeconds How long the turn takes, during which the craft coasts at its peak speed. 0 for the textbook flip.
112
+ * @returns How long each burn lasts in the frame's time, the peak speed, the Lorentz factor then, the time on board and in the frame, and the rapidity at the flip.
113
+ * @example
114
+ * ```ts
115
+ * import { flipAndBurn, G0, LY_M } from "@johnmorrisdotca/uchusen-rockets/journey";
116
+ *
117
+ * const trip = flipAndBurn(4.37 * LY_M, G0, 0);
118
+ * console.log((trip.properSeconds / 31_557_600).toFixed(1), (trip.peakSpeed / 299_792_458).toFixed(3));
119
+ * ```
120
+ */
121
+ export function flipAndBurn(distanceM, a, flipSeconds) {
122
+ // distance = 2 x(tb) + v(tb) flip, with x and v the burn's own: increasing in tb, so bisect.
123
+ const reach = (tb) => {
124
+ const s = accelerating(a, tb);
125
+ return 2 * s.x + s.v * flipSeconds;
126
+ };
127
+ let lo = 0;
128
+ let hi = Math.max(1, Math.sqrt(distanceM / a) * 2);
129
+ while (reach(hi) < distanceM)
130
+ hi *= 2;
131
+ for (let i = 0; i < 200; i += 1) {
132
+ const mid = (lo + hi) / 2;
133
+ if (reach(mid) < distanceM)
134
+ lo = mid;
135
+ else
136
+ hi = mid;
137
+ }
138
+ const burnSeconds = (lo + hi) / 2;
139
+ const peak = accelerating(a, burnSeconds);
140
+ return {
141
+ burnSeconds,
142
+ peakSpeed: peak.v,
143
+ gamma: peak.gamma,
144
+ properSeconds: 2 * peak.proper + flipSeconds / peak.gamma,
145
+ frameSeconds: 2 * burnSeconds + flipSeconds,
146
+ rapidity: Math.asinh((a * burnSeconds) / C),
147
+ };
148
+ }
149
+ /** The propellant a rocket of exhaust speed `ve` uses to change its speed by `dv` (the Tsiolkovsky equation): the fraction of its starting mass, 0 to 1. Both speeds in the same unit. */
150
+ export const propellantFraction = (dv, ve) => 1 - Math.exp(-dv / ve);
151
+ /** An angle brought into -pi to pi. */
152
+ export const wrapPi = (angle) => angle - TAU * Math.round(angle / TAU);
153
+ /** The Lorentz factor of a speed in metres a second. */
154
+ export const lorentz = (v) => 1 / Math.sqrt(1 - (v / C) ** 2);