@interstellar-tools/equations 1.1.0 → 1.3.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.
- package/dist/categories/anomalies/asymptote-true-anomaly.d.ts +50 -0
- package/dist/categories/anomalies/asymptote-true-anomaly.d.ts.map +1 -0
- package/dist/categories/anomalies/asymptote-true-anomaly.js +55 -0
- package/dist/categories/anomalies/asymptote-true-anomaly.js.map +1 -0
- package/dist/categories/anomalies/hyperbolic-to-true-anomaly.d.ts +38 -0
- package/dist/categories/anomalies/hyperbolic-to-true-anomaly.d.ts.map +1 -0
- package/dist/categories/anomalies/hyperbolic-to-true-anomaly.js +49 -0
- package/dist/categories/anomalies/hyperbolic-to-true-anomaly.js.map +1 -0
- package/dist/categories/anomalies/true-to-hyperbolic-anomaly.d.ts +40 -0
- package/dist/categories/anomalies/true-to-hyperbolic-anomaly.d.ts.map +1 -0
- package/dist/categories/anomalies/true-to-hyperbolic-anomaly.js +54 -0
- package/dist/categories/anomalies/true-to-hyperbolic-anomaly.js.map +1 -0
- package/dist/categories/kepler/solve-kepler-hyperbolic.d.ts +49 -0
- package/dist/categories/kepler/solve-kepler-hyperbolic.d.ts.map +1 -0
- package/dist/categories/kepler/solve-kepler-hyperbolic.js +90 -0
- package/dist/categories/kepler/solve-kepler-hyperbolic.js.map +1 -0
- package/dist/categories/manoeuvres/cw-two-impulse-transfer.d.ts +58 -0
- package/dist/categories/manoeuvres/cw-two-impulse-transfer.d.ts.map +1 -0
- package/dist/categories/manoeuvres/cw-two-impulse-transfer.js +104 -0
- package/dist/categories/manoeuvres/cw-two-impulse-transfer.js.map +1 -0
- package/dist/categories/manoeuvres/phasing-wait-time.d.ts +55 -0
- package/dist/categories/manoeuvres/phasing-wait-time.d.ts.map +1 -0
- package/dist/categories/manoeuvres/phasing-wait-time.js +77 -0
- package/dist/categories/manoeuvres/phasing-wait-time.js.map +1 -0
- package/dist/categories/orbits/cw-propagate.d.ts +48 -0
- package/dist/categories/orbits/cw-propagate.d.ts.map +1 -0
- package/dist/categories/orbits/cw-propagate.js +68 -0
- package/dist/categories/orbits/cw-propagate.js.map +1 -0
- package/dist/categories/orbits/cw-state-transition.d.ts +65 -0
- package/dist/categories/orbits/cw-state-transition.d.ts.map +1 -0
- package/dist/categories/orbits/cw-state-transition.js +83 -0
- package/dist/categories/orbits/cw-state-transition.js.map +1 -0
- package/dist/categories/orbits/hyperbolic-eccentricity.d.ts +56 -0
- package/dist/categories/orbits/hyperbolic-eccentricity.d.ts.map +1 -0
- package/dist/categories/orbits/hyperbolic-eccentricity.js +72 -0
- package/dist/categories/orbits/hyperbolic-eccentricity.js.map +1 -0
- package/dist/categories/orbits/hyperbolic-semi-major-axis.d.ts +53 -0
- package/dist/categories/orbits/hyperbolic-semi-major-axis.d.ts.map +1 -0
- package/dist/categories/orbits/hyperbolic-semi-major-axis.js +66 -0
- package/dist/categories/orbits/hyperbolic-semi-major-axis.js.map +1 -0
- package/dist/categories/orbits/hyperbolic-time-of-flight.d.ts +64 -0
- package/dist/categories/orbits/hyperbolic-time-of-flight.d.ts.map +1 -0
- package/dist/categories/orbits/hyperbolic-time-of-flight.js +86 -0
- package/dist/categories/orbits/hyperbolic-time-of-flight.js.map +1 -0
- package/dist/categories/orbits/synodic-period.d.ts +59 -0
- package/dist/categories/orbits/synodic-period.d.ts.map +1 -0
- package/dist/categories/orbits/synodic-period.js +72 -0
- package/dist/categories/orbits/synodic-period.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Radians } from '@interstellar-tools/types';
|
|
2
|
+
/**
|
|
3
|
+
* Compute the **true anomaly of a hyperbola's asymptotes**: the limit a body on a hyperbola approaches but never reaches.
|
|
4
|
+
*
|
|
5
|
+
* $$
|
|
6
|
+
* \nu_\infty = \arccos\left(-\frac{1}{e}\right)
|
|
7
|
+
* $$
|
|
8
|
+
*
|
|
9
|
+
* **Units**
|
|
10
|
+
* - Input: `e` dimensionless.
|
|
11
|
+
* - Output: $ \nu_\infty $ in **radians**, in (π/2, π).
|
|
12
|
+
*
|
|
13
|
+
* ::: info Notes
|
|
14
|
+
*
|
|
15
|
+
* - The body comes in from $ -\nu_\infty $ and leaves towards $ +\nu_\infty $.
|
|
16
|
+
* - The trajectory turns through $ 2\nu_\infty - \pi = 2\arcsin(1/e) $, the turning angle of a flyby; see `gravityAssistTurningAngle`.
|
|
17
|
+
* - Just above $ e = 1 $ the asymptotes open to almost π (a near-parabola); for large $ e $ they close towards π/2 (a near-straight line).
|
|
18
|
+
*
|
|
19
|
+
* :::
|
|
20
|
+
*
|
|
21
|
+
* ::: practice Apollo 11, arriving at the Moon
|
|
22
|
+
*
|
|
23
|
+
* **134.0°**
|
|
24
|
+
*
|
|
25
|
+
* _Asymptote of the approach hyperbola, e = 1.44_
|
|
26
|
+
*
|
|
27
|
+
* Without the lunar orbit insertion burn at closest approach, the Moon's gravity would have bent Apollo 11's path through 88° and sent it back out of the Moon's sphere of influence.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* asymptoteTrueAnomaly(1.44); // ≈ 2.338 rad, 134.0°
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* :::
|
|
34
|
+
*
|
|
35
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
36
|
+
* @returns Asymptote true anomaly $ \nu_\infty $ in **radians**.
|
|
37
|
+
* @throws {TypeError} If `e` is not a finite number.
|
|
38
|
+
* @throws {RangeError} If `e <= 1`.
|
|
39
|
+
* @group Anomalies
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* asymptoteTrueAnomaly(2); // 2π/3 (120°)
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @see {@link trueToHyperbolicAnomaly}
|
|
47
|
+
* @see https://en.wikipedia.org/wiki/Hyperbolic_trajectory Hyperbolic trajectory
|
|
48
|
+
*/
|
|
49
|
+
export declare const asymptoteTrueAnomaly: (e: number) => Radians;
|
|
50
|
+
//# sourceMappingURL=asymptote-true-anomaly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asymptote-true-anomaly.d.ts","sourceRoot":"","sources":["../../../src/categories/anomalies/asymptote-true-anomaly.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,oBAAoB,GAAI,GAAG,MAAM,KAAG,OAOhD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the **true anomaly of a hyperbola's asymptotes**: the limit a body on a hyperbola approaches but never reaches.
|
|
3
|
+
*
|
|
4
|
+
* $$
|
|
5
|
+
* \nu_\infty = \arccos\left(-\frac{1}{e}\right)
|
|
6
|
+
* $$
|
|
7
|
+
*
|
|
8
|
+
* **Units**
|
|
9
|
+
* - Input: `e` dimensionless.
|
|
10
|
+
* - Output: $ \nu_\infty $ in **radians**, in (π/2, π).
|
|
11
|
+
*
|
|
12
|
+
* ::: info Notes
|
|
13
|
+
*
|
|
14
|
+
* - The body comes in from $ -\nu_\infty $ and leaves towards $ +\nu_\infty $.
|
|
15
|
+
* - The trajectory turns through $ 2\nu_\infty - \pi = 2\arcsin(1/e) $, the turning angle of a flyby; see `gravityAssistTurningAngle`.
|
|
16
|
+
* - Just above $ e = 1 $ the asymptotes open to almost π (a near-parabola); for large $ e $ they close towards π/2 (a near-straight line).
|
|
17
|
+
*
|
|
18
|
+
* :::
|
|
19
|
+
*
|
|
20
|
+
* ::: practice Apollo 11, arriving at the Moon
|
|
21
|
+
*
|
|
22
|
+
* **134.0°**
|
|
23
|
+
*
|
|
24
|
+
* _Asymptote of the approach hyperbola, e = 1.44_
|
|
25
|
+
*
|
|
26
|
+
* Without the lunar orbit insertion burn at closest approach, the Moon's gravity would have bent Apollo 11's path through 88° and sent it back out of the Moon's sphere of influence.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* asymptoteTrueAnomaly(1.44); // ≈ 2.338 rad, 134.0°
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* :::
|
|
33
|
+
*
|
|
34
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
35
|
+
* @returns Asymptote true anomaly $ \nu_\infty $ in **radians**.
|
|
36
|
+
* @throws {TypeError} If `e` is not a finite number.
|
|
37
|
+
* @throws {RangeError} If `e <= 1`.
|
|
38
|
+
* @group Anomalies
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* asymptoteTrueAnomaly(2); // 2π/3 (120°)
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @see {@link trueToHyperbolicAnomaly}
|
|
46
|
+
* @see https://en.wikipedia.org/wiki/Hyperbolic_trajectory Hyperbolic trajectory
|
|
47
|
+
*/
|
|
48
|
+
export const asymptoteTrueAnomaly = (e) => {
|
|
49
|
+
if (!Number.isFinite(e))
|
|
50
|
+
throw new TypeError(`e must be a finite number. Received: ${e}`);
|
|
51
|
+
if (e <= 1)
|
|
52
|
+
throw new RangeError(`e must be > 1. Received: ${e}`);
|
|
53
|
+
return Math.acos(-1 / e);
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=asymptote-true-anomaly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asymptote-true-anomaly.js","sourceRoot":"","sources":["../../../src/categories/anomalies/asymptote-true-anomaly.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAS,EAAW,EAAE;IACzD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACrB,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,EAAE,CAAC,CAAC;IAEnE,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;IAElE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAY,CAAC;AACtC,CAAC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Radians } from '@interstellar-tools/types';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a **hyperbolic anomaly** $ F $ to the **true anomaly** on a hyperbola.
|
|
4
|
+
*
|
|
5
|
+
* $$
|
|
6
|
+
* \tan\frac{\nu}{2} = \sqrt{\frac{e + 1}{e - 1}}\,\tanh\frac{F}{2}
|
|
7
|
+
* $$
|
|
8
|
+
*
|
|
9
|
+
* **Units**
|
|
10
|
+
* - Inputs: `F` in **radians**, `e` dimensionless.
|
|
11
|
+
* - Output: $ \nu $ in **radians**, in $ (-\nu_\infty, \nu_\infty) $.
|
|
12
|
+
*
|
|
13
|
+
* ::: info Notes
|
|
14
|
+
*
|
|
15
|
+
* - The inverse of {@link trueToHyperbolicAnomaly}.
|
|
16
|
+
* - As $ F \to \pm\infty $, $ \nu $ approaches the asymptotes $ \pm\nu_\infty $ but never reaches them.
|
|
17
|
+
* - Returns a signed angle, negative before periapsis. Use `norm2pi` for [0, 2π).
|
|
18
|
+
*
|
|
19
|
+
* :::
|
|
20
|
+
*
|
|
21
|
+
* @param F - Hyperbolic anomaly $ F $ in **radians** (finite).
|
|
22
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
23
|
+
* @returns True anomaly $ \nu $ in **radians**, in (-π, π).
|
|
24
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
25
|
+
* @throws {RangeError} If `e <= 1`.
|
|
26
|
+
* @group Anomalies
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const nu = hyperbolicToTrueAnomaly(Math.acosh(2), 2);
|
|
31
|
+
* // nu ≈ π/2
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @see {@link trueToHyperbolicAnomaly}
|
|
35
|
+
* @see {@link solveKeplerHyperbolic}
|
|
36
|
+
*/
|
|
37
|
+
export declare const hyperbolicToTrueAnomaly: (F: number, e: number) => Radians;
|
|
38
|
+
//# sourceMappingURL=hyperbolic-to-true-anomaly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hyperbolic-to-true-anomaly.d.ts","sourceRoot":"","sources":["../../../src/categories/anomalies/hyperbolic-to-true-anomaly.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,uBAAuB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,OAa9D,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a **hyperbolic anomaly** $ F $ to the **true anomaly** on a hyperbola.
|
|
3
|
+
*
|
|
4
|
+
* $$
|
|
5
|
+
* \tan\frac{\nu}{2} = \sqrt{\frac{e + 1}{e - 1}}\,\tanh\frac{F}{2}
|
|
6
|
+
* $$
|
|
7
|
+
*
|
|
8
|
+
* **Units**
|
|
9
|
+
* - Inputs: `F` in **radians**, `e` dimensionless.
|
|
10
|
+
* - Output: $ \nu $ in **radians**, in $ (-\nu_\infty, \nu_\infty) $.
|
|
11
|
+
*
|
|
12
|
+
* ::: info Notes
|
|
13
|
+
*
|
|
14
|
+
* - The inverse of {@link trueToHyperbolicAnomaly}.
|
|
15
|
+
* - As $ F \to \pm\infty $, $ \nu $ approaches the asymptotes $ \pm\nu_\infty $ but never reaches them.
|
|
16
|
+
* - Returns a signed angle, negative before periapsis. Use `norm2pi` for [0, 2π).
|
|
17
|
+
*
|
|
18
|
+
* :::
|
|
19
|
+
*
|
|
20
|
+
* @param F - Hyperbolic anomaly $ F $ in **radians** (finite).
|
|
21
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
22
|
+
* @returns True anomaly $ \nu $ in **radians**, in (-π, π).
|
|
23
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
24
|
+
* @throws {RangeError} If `e <= 1`.
|
|
25
|
+
* @group Anomalies
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const nu = hyperbolicToTrueAnomaly(Math.acosh(2), 2);
|
|
30
|
+
* // nu ≈ π/2
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @see {@link trueToHyperbolicAnomaly}
|
|
34
|
+
* @see {@link solveKeplerHyperbolic}
|
|
35
|
+
*/
|
|
36
|
+
export const hyperbolicToTrueAnomaly = (F, e) => {
|
|
37
|
+
for (const [name, x] of [
|
|
38
|
+
['F', F],
|
|
39
|
+
['e', e]
|
|
40
|
+
]) {
|
|
41
|
+
if (!Number.isFinite(x))
|
|
42
|
+
throw new TypeError(`${name} must be a finite number. Received: ${x}`);
|
|
43
|
+
}
|
|
44
|
+
if (e <= 1)
|
|
45
|
+
throw new RangeError(`e must be > 1. Received: ${e}`);
|
|
46
|
+
return (2 *
|
|
47
|
+
Math.atan(Math.sqrt((e + 1) / (e - 1)) * Math.tanh(F / 2)));
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=hyperbolic-to-true-anomaly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hyperbolic-to-true-anomaly.js","sourceRoot":"","sources":["../../../src/categories/anomalies/hyperbolic-to-true-anomaly.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAS,EAAE,CAAS,EAAW,EAAE;IACvE,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI;QACtB,CAAC,GAAG,EAAE,CAAC,CAAC;QACR,CAAC,GAAG,EAAE,CAAC,CAAC;KACA,EAAE,CAAC;QACX,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YACrB,MAAM,IAAI,SAAS,CAAC,GAAG,IAAI,uCAAuC,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;IAElE,OAAO,CAAC,CAAC;QACP,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAY,CAAC;AAC3E,CAAC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Radians } from '@interstellar-tools/types';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a **true anomaly** on a hyperbola to the **hyperbolic anomaly** $ F $.
|
|
4
|
+
*
|
|
5
|
+
* $$
|
|
6
|
+
* \tanh\frac{F}{2} = \sqrt{\frac{e - 1}{e + 1}}\,\tan\frac{\nu}{2}
|
|
7
|
+
* $$
|
|
8
|
+
*
|
|
9
|
+
* **Units**
|
|
10
|
+
* - Inputs: `nu` in **radians**, `e` dimensionless.
|
|
11
|
+
* - Output: $ F $ in **radians** (dimensionless, unbounded).
|
|
12
|
+
*
|
|
13
|
+
* ::: info Notes
|
|
14
|
+
*
|
|
15
|
+
* - The hyperbolic counterpart of the eccentric anomaly. Feed it to $ M_h = e \sinh F - F $ to get a time from periapsis.
|
|
16
|
+
* - `nu` is wrapped to (-π, π] first, so the incoming branch can be given either as a negative angle or just below 2π, as {@link stateToElements} returns it.
|
|
17
|
+
* - Only $ |\nu| < \nu_\infty $ exists on a hyperbola; see {@link asymptoteTrueAnomaly}.
|
|
18
|
+
*
|
|
19
|
+
* :::
|
|
20
|
+
*
|
|
21
|
+
* @param nu - True anomaly $ \nu $ in **radians** (finite, inside the asymptotes).
|
|
22
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
23
|
+
* @returns Hyperbolic anomaly $ F $ in **radians**: negative before periapsis, positive after.
|
|
24
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
25
|
+
* @throws {RangeError} If `e <= 1` or `nu` is at or beyond the asymptotes.
|
|
26
|
+
* @group Anomalies
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import type { Radians } from '@interstellar-tools/types';
|
|
31
|
+
*
|
|
32
|
+
* const F = trueToHyperbolicAnomaly((Math.PI / 2) as Radians, 2);
|
|
33
|
+
* // F ≈ 1.317 (cosh F = e)
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see {@link hyperbolicToTrueAnomaly}
|
|
37
|
+
* @see https://en.wikipedia.org/wiki/Hyperbolic_trajectory#Hyperbolic_anomaly Hyperbolic anomaly
|
|
38
|
+
*/
|
|
39
|
+
export declare const trueToHyperbolicAnomaly: (nu: Radians, e: number) => number;
|
|
40
|
+
//# sourceMappingURL=true-to-hyperbolic-anomaly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"true-to-hyperbolic-anomaly.d.ts","sourceRoot":"","sources":["../../../src/categories/anomalies/true-to-hyperbolic-anomaly.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,uBAAuB,GAAI,IAAI,OAAO,EAAE,GAAG,MAAM,KAAG,MAmBhE,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { asymptoteTrueAnomaly } from './asymptote-true-anomaly.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a **true anomaly** on a hyperbola to the **hyperbolic anomaly** $ F $.
|
|
4
|
+
*
|
|
5
|
+
* $$
|
|
6
|
+
* \tanh\frac{F}{2} = \sqrt{\frac{e - 1}{e + 1}}\,\tan\frac{\nu}{2}
|
|
7
|
+
* $$
|
|
8
|
+
*
|
|
9
|
+
* **Units**
|
|
10
|
+
* - Inputs: `nu` in **radians**, `e` dimensionless.
|
|
11
|
+
* - Output: $ F $ in **radians** (dimensionless, unbounded).
|
|
12
|
+
*
|
|
13
|
+
* ::: info Notes
|
|
14
|
+
*
|
|
15
|
+
* - The hyperbolic counterpart of the eccentric anomaly. Feed it to $ M_h = e \sinh F - F $ to get a time from periapsis.
|
|
16
|
+
* - `nu` is wrapped to (-π, π] first, so the incoming branch can be given either as a negative angle or just below 2π, as {@link stateToElements} returns it.
|
|
17
|
+
* - Only $ |\nu| < \nu_\infty $ exists on a hyperbola; see {@link asymptoteTrueAnomaly}.
|
|
18
|
+
*
|
|
19
|
+
* :::
|
|
20
|
+
*
|
|
21
|
+
* @param nu - True anomaly $ \nu $ in **radians** (finite, inside the asymptotes).
|
|
22
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
23
|
+
* @returns Hyperbolic anomaly $ F $ in **radians**: negative before periapsis, positive after.
|
|
24
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
25
|
+
* @throws {RangeError} If `e <= 1` or `nu` is at or beyond the asymptotes.
|
|
26
|
+
* @group Anomalies
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import type { Radians } from '@interstellar-tools/types';
|
|
31
|
+
*
|
|
32
|
+
* const F = trueToHyperbolicAnomaly((Math.PI / 2) as Radians, 2);
|
|
33
|
+
* // F ≈ 1.317 (cosh F = e)
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see {@link hyperbolicToTrueAnomaly}
|
|
37
|
+
* @see https://en.wikipedia.org/wiki/Hyperbolic_trajectory#Hyperbolic_anomaly Hyperbolic anomaly
|
|
38
|
+
*/
|
|
39
|
+
export const trueToHyperbolicAnomaly = (nu, e) => {
|
|
40
|
+
for (const [name, x] of [
|
|
41
|
+
['nu', nu],
|
|
42
|
+
['e', e]
|
|
43
|
+
]) {
|
|
44
|
+
if (!Number.isFinite(x))
|
|
45
|
+
throw new TypeError(`${name} must be a finite number. Received: ${x}`);
|
|
46
|
+
}
|
|
47
|
+
if (e <= 1)
|
|
48
|
+
throw new RangeError(`e must be > 1. Received: ${e}`);
|
|
49
|
+
const wrapped = Math.atan2(Math.sin(nu), Math.cos(nu));
|
|
50
|
+
if (Math.abs(wrapped) >= asymptoteTrueAnomaly(e))
|
|
51
|
+
throw new RangeError(`nu is at or beyond the asymptotes (|nu| >= acos(-1/e)). Received: nu=${nu}, e=${e}`);
|
|
52
|
+
return 2 * Math.atanh(Math.sqrt((e - 1) / (e + 1)) * Math.tan(wrapped / 2));
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=true-to-hyperbolic-anomaly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"true-to-hyperbolic-anomaly.js","sourceRoot":"","sources":["../../../src/categories/anomalies/true-to-hyperbolic-anomaly.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,EAAW,EAAE,CAAS,EAAU,EAAE;IACxE,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI;QACtB,CAAC,IAAI,EAAE,EAAE,CAAC;QACV,CAAC,GAAG,EAAE,CAAC,CAAC;KACA,EAAE,CAAC;QACX,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YACrB,MAAM,IAAI,SAAS,CAAC,GAAG,IAAI,uCAAuC,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEvD,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,oBAAoB,CAAC,CAAC,CAAC;QAC9C,MAAM,IAAI,UAAU,CAClB,wEAAwE,EAAE,OAAO,CAAC,EAAE,CACrF,CAAC;IAEJ,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;AAC9E,CAAC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solve **Kepler's equation for a hyperbola**: find the hyperbolic anomaly $ F $ from the hyperbolic mean anomaly $ M_h $.
|
|
3
|
+
*
|
|
4
|
+
* $$
|
|
5
|
+
* M_h = e \sinh F - F
|
|
6
|
+
* $$
|
|
7
|
+
*
|
|
8
|
+
* solved with Newton's method:
|
|
9
|
+
*
|
|
10
|
+
* $$
|
|
11
|
+
* F_{n+1} = F_n - \frac{e \sinh F_n - F_n - M_h}{e \cosh F_n - 1}, \qquad F_0 = \min\left(\operatorname{asinh}\frac{|M_h|}{e - 1},\ \sqrt[3]{\frac{6|M_h|}{e}}\right)
|
|
12
|
+
* $$
|
|
13
|
+
*
|
|
14
|
+
* **Units**
|
|
15
|
+
* - Inputs: `Mh` in **radians**, `e` dimensionless.
|
|
16
|
+
* - Output: $ F $ in **radians** (dimensionless, unbounded).
|
|
17
|
+
*
|
|
18
|
+
* ::: info Notes
|
|
19
|
+
*
|
|
20
|
+
* - The equation is odd in $ F $, so the solver works on $ |M_h| $ and restores the sign. Negative values are before periapsis.
|
|
21
|
+
* - $ F_0 $ is an upper bound on the root, since $ e\sinh F - F $ is at least $ (e-1)\sinh F $ and at least $ (e-1)F + eF^3/6 $. The function is increasing and convex for $ F > 0 $, so Newton's method converges monotonically from there, even close to $ e = 1 $ where the derivative $ e\cosh F - 1 $ is small near periapsis.
|
|
22
|
+
* - Unlike an ellipse, a hyperbola isn't periodic: $ M_h $ isn't reduced to one turn.
|
|
23
|
+
* - {@link solveKeplerHighEccentricity} also accepts $ e > 1 $, but this solver throws when it doesn't converge instead of returning its last estimate.
|
|
24
|
+
*
|
|
25
|
+
* :::
|
|
26
|
+
*
|
|
27
|
+
* @param Mh - Hyperbolic mean anomaly $ M_h $ in **radians** (finite).
|
|
28
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
29
|
+
* @param tolerance - Stop when a step is smaller than `tolerance * max(1, |F|)`. Defaults to `1e-14`.
|
|
30
|
+
* @param maxIter - Maximum number of Newton steps. Defaults to `50`.
|
|
31
|
+
* @returns The hyperbolic anomaly $ F $ in **radians**, with the sign of `Mh`.
|
|
32
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
33
|
+
* @throws {RangeError} If `e <= 1`, `tolerance <= 0` or `maxIter < 1`.
|
|
34
|
+
* @throws {Error} If the iteration doesn't converge within `maxIter` steps.
|
|
35
|
+
* @group Kepler Solvers
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import type { Radians } from '@interstellar-tools/types';
|
|
40
|
+
*
|
|
41
|
+
* const F = solveKeplerHyperbolic(12.66 as Radians, 1.44);
|
|
42
|
+
* // F ≈ 3.087, and 1.44 * sinh(F) - F ≈ 12.66
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @see {@link hyperbolicTimeOfFlight}
|
|
46
|
+
* @see https://en.wikipedia.org/wiki/Hyperbolic_trajectory#Hyperbolic_anomaly Hyperbolic anomaly
|
|
47
|
+
*/
|
|
48
|
+
export declare const solveKeplerHyperbolic: (Mh: number, e: number, tolerance?: number, maxIter?: number) => number;
|
|
49
|
+
//# sourceMappingURL=solve-kepler-hyperbolic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solve-kepler-hyperbolic.d.ts","sourceRoot":"","sources":["../../../src/categories/kepler/solve-kepler-hyperbolic.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,eAAO,MAAM,qBAAqB,GAChC,IAAI,MAAM,EACV,GAAG,MAAM,EACT,kBAAiB,EACjB,gBAAY,KACX,MAsCF,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** sinh x - x, from its Taylor series for small x to avoid cancellation. */
|
|
2
|
+
const sinhMinusX = (x) => {
|
|
3
|
+
if (Math.abs(x) > 0.5)
|
|
4
|
+
return Math.sinh(x) - x;
|
|
5
|
+
const x2 = x * x;
|
|
6
|
+
let term = (x * x2) / 6;
|
|
7
|
+
let sum = term;
|
|
8
|
+
for (let k = 5; Math.abs(term) > 1e-17 * Math.abs(sum); k += 2) {
|
|
9
|
+
term *= x2 / ((k - 1) * k);
|
|
10
|
+
sum += term;
|
|
11
|
+
}
|
|
12
|
+
return sum;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Solve **Kepler's equation for a hyperbola**: find the hyperbolic anomaly $ F $ from the hyperbolic mean anomaly $ M_h $.
|
|
16
|
+
*
|
|
17
|
+
* $$
|
|
18
|
+
* M_h = e \sinh F - F
|
|
19
|
+
* $$
|
|
20
|
+
*
|
|
21
|
+
* solved with Newton's method:
|
|
22
|
+
*
|
|
23
|
+
* $$
|
|
24
|
+
* F_{n+1} = F_n - \frac{e \sinh F_n - F_n - M_h}{e \cosh F_n - 1}, \qquad F_0 = \min\left(\operatorname{asinh}\frac{|M_h|}{e - 1},\ \sqrt[3]{\frac{6|M_h|}{e}}\right)
|
|
25
|
+
* $$
|
|
26
|
+
*
|
|
27
|
+
* **Units**
|
|
28
|
+
* - Inputs: `Mh` in **radians**, `e` dimensionless.
|
|
29
|
+
* - Output: $ F $ in **radians** (dimensionless, unbounded).
|
|
30
|
+
*
|
|
31
|
+
* ::: info Notes
|
|
32
|
+
*
|
|
33
|
+
* - The equation is odd in $ F $, so the solver works on $ |M_h| $ and restores the sign. Negative values are before periapsis.
|
|
34
|
+
* - $ F_0 $ is an upper bound on the root, since $ e\sinh F - F $ is at least $ (e-1)\sinh F $ and at least $ (e-1)F + eF^3/6 $. The function is increasing and convex for $ F > 0 $, so Newton's method converges monotonically from there, even close to $ e = 1 $ where the derivative $ e\cosh F - 1 $ is small near periapsis.
|
|
35
|
+
* - Unlike an ellipse, a hyperbola isn't periodic: $ M_h $ isn't reduced to one turn.
|
|
36
|
+
* - {@link solveKeplerHighEccentricity} also accepts $ e > 1 $, but this solver throws when it doesn't converge instead of returning its last estimate.
|
|
37
|
+
*
|
|
38
|
+
* :::
|
|
39
|
+
*
|
|
40
|
+
* @param Mh - Hyperbolic mean anomaly $ M_h $ in **radians** (finite).
|
|
41
|
+
* @param e - Eccentricity $ e $ (finite, > 1).
|
|
42
|
+
* @param tolerance - Stop when a step is smaller than `tolerance * max(1, |F|)`. Defaults to `1e-14`.
|
|
43
|
+
* @param maxIter - Maximum number of Newton steps. Defaults to `50`.
|
|
44
|
+
* @returns The hyperbolic anomaly $ F $ in **radians**, with the sign of `Mh`.
|
|
45
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
46
|
+
* @throws {RangeError} If `e <= 1`, `tolerance <= 0` or `maxIter < 1`.
|
|
47
|
+
* @throws {Error} If the iteration doesn't converge within `maxIter` steps.
|
|
48
|
+
* @group Kepler Solvers
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import type { Radians } from '@interstellar-tools/types';
|
|
53
|
+
*
|
|
54
|
+
* const F = solveKeplerHyperbolic(12.66 as Radians, 1.44);
|
|
55
|
+
* // F ≈ 3.087, and 1.44 * sinh(F) - F ≈ 12.66
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @see {@link hyperbolicTimeOfFlight}
|
|
59
|
+
* @see https://en.wikipedia.org/wiki/Hyperbolic_trajectory#Hyperbolic_anomaly Hyperbolic anomaly
|
|
60
|
+
*/
|
|
61
|
+
export const solveKeplerHyperbolic = (Mh, e, tolerance = 1e-14, maxIter = 50) => {
|
|
62
|
+
for (const [name, x] of [
|
|
63
|
+
['Mh', Mh],
|
|
64
|
+
['e', e],
|
|
65
|
+
['tolerance', tolerance],
|
|
66
|
+
['maxIter', maxIter]
|
|
67
|
+
]) {
|
|
68
|
+
if (!Number.isFinite(x))
|
|
69
|
+
throw new TypeError(`${name} must be a finite number. Received: ${x}`);
|
|
70
|
+
}
|
|
71
|
+
if (e <= 1)
|
|
72
|
+
throw new RangeError(`e must be > 1. Received: ${e}`);
|
|
73
|
+
if (tolerance <= 0)
|
|
74
|
+
throw new RangeError(`tolerance must be > 0. Received: ${tolerance}`);
|
|
75
|
+
if (maxIter < 1)
|
|
76
|
+
throw new RangeError(`maxIter must be >= 1. Received: ${maxIter}`);
|
|
77
|
+
const M = Math.abs(Mh);
|
|
78
|
+
// Start above the root, where Newton's method can't overshoot
|
|
79
|
+
let F = Math.min(Math.asinh(M / (e - 1)), Math.cbrt((6 * M) / e));
|
|
80
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
81
|
+
// e sinh F - F - M, written so nothing cancels when e ≈ 1 and F is small
|
|
82
|
+
const f = (e - 1) * F + e * sinhMinusX(F) - M;
|
|
83
|
+
const step = f / (e * Math.cosh(F) - 1);
|
|
84
|
+
F -= step;
|
|
85
|
+
if (Math.abs(step) <= tolerance * Math.max(1, Math.abs(F)))
|
|
86
|
+
return Math.sign(Mh) * F;
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`solveKeplerHyperbolic did not converge in ${maxIter} steps. Received: Mh=${Mh}, e=${e}`);
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=solve-kepler-hyperbolic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solve-kepler-hyperbolic.js","sourceRoot":"","sources":["../../../src/categories/kepler/solve-kepler-hyperbolic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { CwState, Vector3DTupleType } from '@interstellar-tools/types';
|
|
2
|
+
export interface CwTwoImpulseTransferInterface {
|
|
3
|
+
/** First burn, at the start, in m/s (LVLH). */
|
|
4
|
+
dv1: Vector3DTupleType;
|
|
5
|
+
/** Second burn, on arrival, in m/s (LVLH). */
|
|
6
|
+
dv2: Vector3DTupleType;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Compute the **two burns of a Clohessy–Wiltshire rendezvous**: from a relative state to a target state in a given time.
|
|
10
|
+
*
|
|
11
|
+
* $$
|
|
12
|
+
* \mathbf v_0^+ = \Phi_{rv}^{-1}\left(\mathbf r_f - \Phi_{rr}\,\mathbf r_0\right), \quad
|
|
13
|
+
* \Delta\mathbf v_1 = \mathbf v_0^+ - \mathbf v_0, \quad
|
|
14
|
+
* \Delta\mathbf v_2 = \mathbf v_f - \left(\Phi_{vr}\,\mathbf r_0 + \Phi_{vv}\,\mathbf v_0^+\right)
|
|
15
|
+
* $$
|
|
16
|
+
*
|
|
17
|
+
* with the blocks of $ \Phi(t) $ from {@link cwStateTransition}.
|
|
18
|
+
*
|
|
19
|
+
* **Units**
|
|
20
|
+
* - Inputs: states in **m** and **m/s**, `n` in **rad/s**, `t` in **s**.
|
|
21
|
+
* - Output: both burns in **m/s**, as vectors in the LVLH frame.
|
|
22
|
+
*
|
|
23
|
+
* ::: info Notes
|
|
24
|
+
*
|
|
25
|
+
* - The first burn puts the deputy on a path that reaches the target position after `t`; the second matches the target velocity there. For a rendezvous with the chief, the target state is all zeros.
|
|
26
|
+
* - Some transfer times can't be targeted: in the orbital plane, every whole number of orbits ($ nt = 2\pi k $), and out of plane, every half orbit, where the cross-track motion returns to $ \pm z_0 $ whatever the burn. At a half orbit the cross-track burn is zero if the target is already on that path, and otherwise the function throws.
|
|
27
|
+
* - Valid near a circular reference orbit, for small separations.
|
|
28
|
+
*
|
|
29
|
+
* :::
|
|
30
|
+
*
|
|
31
|
+
* @param state0 - Deputy's relative state before the first burn `[x, y, z, xDot, yDot, zDot]` (finite).
|
|
32
|
+
* @param target - State to arrive at `[x, y, z, xDot, yDot, zDot]` (finite); all zeros to rendezvous with the chief.
|
|
33
|
+
* @param n - Mean motion of the circular reference orbit in **rad/s** (finite, > 0).
|
|
34
|
+
* @param t - Transfer time in **s** (finite, > 0).
|
|
35
|
+
* @returns The two burns `dv1` and `dv2` in **m/s**.
|
|
36
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
37
|
+
* @throws {RangeError} If `n <= 0`, `t <= 0`, or the transfer time can't be targeted.
|
|
38
|
+
* @group Manoeuvres
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import type { CwState } from '@interstellar-tools/types';
|
|
43
|
+
*
|
|
44
|
+
* // 1 km behind the chief, at rest: rendezvous in a quarter of an orbit
|
|
45
|
+
* const n = 0.00113;
|
|
46
|
+
* const { dv1, dv2 } = cwTwoImpulseTransfer(
|
|
47
|
+
* [0, -1000, 0, 0, 0, 0],
|
|
48
|
+
* [0, 0, 0, 0, 0, 0],
|
|
49
|
+
* n,
|
|
50
|
+
* Math.PI / 2 / n
|
|
51
|
+
* );
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @see {@link cwPropagate}
|
|
55
|
+
* @see https://en.wikipedia.org/wiki/Clohessy%E2%80%93Wiltshire_equations Clohessy–Wiltshire equations
|
|
56
|
+
*/
|
|
57
|
+
export declare const cwTwoImpulseTransfer: (state0: CwState, target: CwState, n: number, t: number) => CwTwoImpulseTransferInterface;
|
|
58
|
+
//# sourceMappingURL=cw-two-impulse-transfer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cw-two-impulse-transfer.d.ts","sourceRoot":"","sources":["../../../src/categories/manoeuvres/cw-two-impulse-transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAK5E,MAAM,WAAW,6BAA6B;IAC5C,+CAA+C;IAC/C,GAAG,EAAE,iBAAiB,CAAC;IACvB,8CAA8C;IAC9C,GAAG,EAAE,iBAAiB,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,OAAO,EACf,QAAQ,OAAO,EACf,GAAG,MAAM,EACT,GAAG,MAAM,KACR,6BA+DF,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { applyMatrix3 } from '../helpers/apply-matrix-3.js';
|
|
2
|
+
import { cwStateTransition } from '../orbits/cw-state-transition.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compute the **two burns of a Clohessy–Wiltshire rendezvous**: from a relative state to a target state in a given time.
|
|
5
|
+
*
|
|
6
|
+
* $$
|
|
7
|
+
* \mathbf v_0^+ = \Phi_{rv}^{-1}\left(\mathbf r_f - \Phi_{rr}\,\mathbf r_0\right), \quad
|
|
8
|
+
* \Delta\mathbf v_1 = \mathbf v_0^+ - \mathbf v_0, \quad
|
|
9
|
+
* \Delta\mathbf v_2 = \mathbf v_f - \left(\Phi_{vr}\,\mathbf r_0 + \Phi_{vv}\,\mathbf v_0^+\right)
|
|
10
|
+
* $$
|
|
11
|
+
*
|
|
12
|
+
* with the blocks of $ \Phi(t) $ from {@link cwStateTransition}.
|
|
13
|
+
*
|
|
14
|
+
* **Units**
|
|
15
|
+
* - Inputs: states in **m** and **m/s**, `n` in **rad/s**, `t` in **s**.
|
|
16
|
+
* - Output: both burns in **m/s**, as vectors in the LVLH frame.
|
|
17
|
+
*
|
|
18
|
+
* ::: info Notes
|
|
19
|
+
*
|
|
20
|
+
* - The first burn puts the deputy on a path that reaches the target position after `t`; the second matches the target velocity there. For a rendezvous with the chief, the target state is all zeros.
|
|
21
|
+
* - Some transfer times can't be targeted: in the orbital plane, every whole number of orbits ($ nt = 2\pi k $), and out of plane, every half orbit, where the cross-track motion returns to $ \pm z_0 $ whatever the burn. At a half orbit the cross-track burn is zero if the target is already on that path, and otherwise the function throws.
|
|
22
|
+
* - Valid near a circular reference orbit, for small separations.
|
|
23
|
+
*
|
|
24
|
+
* :::
|
|
25
|
+
*
|
|
26
|
+
* @param state0 - Deputy's relative state before the first burn `[x, y, z, xDot, yDot, zDot]` (finite).
|
|
27
|
+
* @param target - State to arrive at `[x, y, z, xDot, yDot, zDot]` (finite); all zeros to rendezvous with the chief.
|
|
28
|
+
* @param n - Mean motion of the circular reference orbit in **rad/s** (finite, > 0).
|
|
29
|
+
* @param t - Transfer time in **s** (finite, > 0).
|
|
30
|
+
* @returns The two burns `dv1` and `dv2` in **m/s**.
|
|
31
|
+
* @throws {TypeError} If any input is not a finite number.
|
|
32
|
+
* @throws {RangeError} If `n <= 0`, `t <= 0`, or the transfer time can't be targeted.
|
|
33
|
+
* @group Manoeuvres
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import type { CwState } from '@interstellar-tools/types';
|
|
38
|
+
*
|
|
39
|
+
* // 1 km behind the chief, at rest: rendezvous in a quarter of an orbit
|
|
40
|
+
* const n = 0.00113;
|
|
41
|
+
* const { dv1, dv2 } = cwTwoImpulseTransfer(
|
|
42
|
+
* [0, -1000, 0, 0, 0, 0],
|
|
43
|
+
* [0, 0, 0, 0, 0, 0],
|
|
44
|
+
* n,
|
|
45
|
+
* Math.PI / 2 / n
|
|
46
|
+
* );
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @see {@link cwPropagate}
|
|
50
|
+
* @see https://en.wikipedia.org/wiki/Clohessy%E2%80%93Wiltshire_equations Clohessy–Wiltshire equations
|
|
51
|
+
*/
|
|
52
|
+
export const cwTwoImpulseTransfer = (state0, target, n, t) => {
|
|
53
|
+
for (const [name, state] of [
|
|
54
|
+
['state0', state0],
|
|
55
|
+
['target', target]
|
|
56
|
+
]) {
|
|
57
|
+
for (const x of state)
|
|
58
|
+
if (!Number.isFinite(x))
|
|
59
|
+
throw new TypeError(`${name} must have finite components. Received: ${x}`);
|
|
60
|
+
}
|
|
61
|
+
if (!Number.isFinite(t))
|
|
62
|
+
throw new TypeError(`t must be a finite number. Received: ${t}`);
|
|
63
|
+
if (t <= 0)
|
|
64
|
+
throw new RangeError(`t must be > 0. Received: ${t}`);
|
|
65
|
+
const { rr, rv, vr, vv } = cwStateTransition(n, t);
|
|
66
|
+
const r0 = [state0[0], state0[1], state0[2]];
|
|
67
|
+
const v0 = [state0[3], state0[4], state0[5]];
|
|
68
|
+
const fromR0 = applyMatrix3(rr, r0);
|
|
69
|
+
// Position the first burn's velocity has to cover
|
|
70
|
+
const dx = target[0] - fromR0[0];
|
|
71
|
+
const dy = target[1] - fromR0[1];
|
|
72
|
+
const dz = target[2] - fromR0[2];
|
|
73
|
+
// In-plane: invert the 2×2 block of Φrv
|
|
74
|
+
const [[a, b], [c, d]] = rv;
|
|
75
|
+
const det = a * d - b * c;
|
|
76
|
+
if (Math.abs(det) * n * n <= 1e-10)
|
|
77
|
+
throw new RangeError(`The transfer time is a whole number of orbits (nt = 2πk), which can't be targeted in the orbital plane. Received: nt=${n * t}`);
|
|
78
|
+
const xDot = (d * dx - b * dy) / det;
|
|
79
|
+
const yDot = (-c * dx + a * dy) / det;
|
|
80
|
+
// Out of plane: z(t) = c z0 + (s / n) zDot0
|
|
81
|
+
const sOverN = rv[2][2];
|
|
82
|
+
let zDot;
|
|
83
|
+
if (Math.abs(sOverN) * n > 1e-10) {
|
|
84
|
+
zDot = dz / sOverN;
|
|
85
|
+
}
|
|
86
|
+
else if (Math.abs(dz) <= 1e-9 * Math.max(1, Math.abs(target[2]))) {
|
|
87
|
+
zDot = v0[2];
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw new RangeError(`At a half orbit (nt = πk) the cross-track position can't be changed. Received: nt=${n * t}`);
|
|
91
|
+
}
|
|
92
|
+
const v0Plus = [xDot, yDot, zDot];
|
|
93
|
+
const fromR = applyMatrix3(vr, r0);
|
|
94
|
+
const fromV = applyMatrix3(vv, v0Plus);
|
|
95
|
+
return {
|
|
96
|
+
dv1: [xDot - v0[0], yDot - v0[1], zDot - v0[2]],
|
|
97
|
+
dv2: [
|
|
98
|
+
target[3] - (fromR[0] + fromV[0]),
|
|
99
|
+
target[4] - (fromR[1] + fromV[1]),
|
|
100
|
+
target[5] - (fromR[2] + fromV[2])
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=cw-two-impulse-transfer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cw-two-impulse-transfer.js","sourceRoot":"","sources":["../../../src/categories/manoeuvres/cw-two-impulse-transfer.ts"],"names":[],"mappings":"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"}
|