@interstellar-tools/equations 0.1.2 → 0.2.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.
Files changed (61) hide show
  1. package/dist/__tests__/gravitational-acceleration-on1-by2.int.spec.d.ts +2 -0
  2. package/dist/__tests__/gravitational-acceleration-on1-by2.int.spec.d.ts.map +1 -0
  3. package/dist/__tests__/gravitational-acceleration-on1-by2.int.spec.js +86 -0
  4. package/dist/__tests__/gravitational-acceleration-on1-by2.int.spec.js.map +1 -0
  5. package/dist/__tests__/gravitational-force-on1-by2.int.spec.d.ts +2 -0
  6. package/dist/__tests__/gravitational-force-on1-by2.int.spec.d.ts.map +1 -0
  7. package/dist/__tests__/gravitational-force-on1-by2.int.spec.js +106 -0
  8. package/dist/__tests__/gravitational-force-on1-by2.int.spec.js.map +1 -0
  9. package/dist/__tests__/gravitational-force.spec.d.ts +2 -0
  10. package/dist/__tests__/gravitational-force.spec.d.ts.map +1 -0
  11. package/dist/__tests__/gravitational-force.spec.js +105 -0
  12. package/dist/__tests__/gravitational-force.spec.js.map +1 -0
  13. package/dist/__tests__/gravitational-parameters.spec.d.ts +2 -0
  14. package/dist/__tests__/gravitational-parameters.spec.d.ts.map +1 -0
  15. package/dist/__tests__/gravitational-parameters.spec.js +75 -0
  16. package/dist/__tests__/gravitational-parameters.spec.js.map +1 -0
  17. package/dist/__tests__/helpers/index.d.ts +8 -0
  18. package/dist/__tests__/helpers/index.d.ts.map +1 -0
  19. package/dist/__tests__/helpers/index.js +25 -0
  20. package/dist/__tests__/helpers/index.js.map +1 -0
  21. package/dist/__tests__/kepler-period.spec.d.ts +2 -0
  22. package/dist/__tests__/kepler-period.spec.d.ts.map +1 -0
  23. package/dist/__tests__/kepler-period.spec.js +72 -0
  24. package/dist/__tests__/kepler-period.spec.js.map +1 -0
  25. package/dist/__tests__/specific-mechanical-energy.spec.d.ts +2 -0
  26. package/dist/__tests__/specific-mechanical-energy.spec.d.ts.map +1 -0
  27. package/dist/__tests__/specific-mechanical-energy.spec.js +81 -0
  28. package/dist/__tests__/specific-mechanical-energy.spec.js.map +1 -0
  29. package/dist/__tests__/vis-viva-speed.spec.d.ts +2 -0
  30. package/dist/__tests__/vis-viva-speed.spec.d.ts.map +1 -0
  31. package/dist/__tests__/vis-viva-speed.spec.js +80 -0
  32. package/dist/__tests__/vis-viva-speed.spec.js.map +1 -0
  33. package/dist/compute-mean-anomaly.d.ts +1 -1
  34. package/dist/compute-mean-anomaly.js +1 -1
  35. package/dist/eccentric-to-true-anomaly.d.ts +1 -1
  36. package/dist/eccentric-to-true-anomaly.js +1 -1
  37. package/dist/gravitational-parameter.d.ts +49 -0
  38. package/dist/gravitational-parameter.d.ts.map +1 -0
  39. package/dist/gravitational-parameter.js +61 -0
  40. package/dist/gravitational-parameter.js.map +1 -0
  41. package/dist/index.d.ts +5 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +5 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/kepler-period.d.ts +45 -0
  46. package/dist/kepler-period.d.ts.map +1 -0
  47. package/dist/kepler-period.js +54 -0
  48. package/dist/kepler-period.js.map +1 -0
  49. package/dist/law-of-gravitation.d.ts +243 -0
  50. package/dist/law-of-gravitation.d.ts.map +1 -0
  51. package/dist/law-of-gravitation.js +284 -0
  52. package/dist/law-of-gravitation.js.map +1 -0
  53. package/dist/specific-mechanical-energy.d.ts +49 -0
  54. package/dist/specific-mechanical-energy.d.ts.map +1 -0
  55. package/dist/specific-mechanical-energy.js +57 -0
  56. package/dist/specific-mechanical-energy.js.map +1 -0
  57. package/dist/vis-viva-speed.d.ts +42 -0
  58. package/dist/vis-viva-speed.d.ts.map +1 -0
  59. package/dist/vis-viva-speed.js +70 -0
  60. package/dist/vis-viva-speed.js.map +1 -0
  61. package/package.json +76 -4
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gravitational-acceleration-on1-by2.int.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-acceleration-on1-by2.int.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/gravitational-acceleration-on1-by2.int.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,86 @@
1
+ import assert from 'node:assert/strict';
2
+ import test, { describe } from 'node:test';
3
+ import { gravitationalAccelerationOn1By2 } from '../law-of-gravitation';
4
+ import { norm, relClose, scale, sub, vecRelClose } from './helpers';
5
+ import { G_SI } from '@interstellar-tools/constants';
6
+ describe('gravitationalAccelerationOn1By2', () => {
7
+ test('1D sanity (+x): ||a|| = G*m2/r^2 and points +x', () => {
8
+ const G = 1;
9
+ const m2 = 3;
10
+ const r1 = [0, 0, 0];
11
+ const r2 = [10, 0, 0];
12
+ const a = gravitationalAccelerationOn1By2(m2, r1, r2, G);
13
+ // Expected magnitude: 1*3/10^2 = 0.03, direction +x
14
+ relClose(norm(a), 0.03, 1e-14);
15
+ vecRelClose(a, [0.03, 0, 0], 1e-14);
16
+ });
17
+ test('1D sanity (-x): points toward body 2 at negative x', () => {
18
+ const G = 1;
19
+ const m2 = 3;
20
+ const r1 = [0, 0, 0];
21
+ const r2 = [-10, 0, 0];
22
+ const a = gravitationalAccelerationOn1By2(m2, r1, r2, G);
23
+ relClose(norm(a), 0.03, 1e-14);
24
+ vecRelClose(a, [-0.03, 0, 0], 1e-14);
25
+ });
26
+ test('direction equals r-hat for arbitrary 3D positions', () => {
27
+ const G = 2.5;
28
+ const m2 = 11;
29
+ const r1 = [0.2, -3, 5.5];
30
+ const r2 = [4.2, 1, -1];
31
+ const a = gravitationalAccelerationOn1By2(m2, r1, r2, G);
32
+ const dr = sub(r2, r1);
33
+ const r = Math.hypot(...dr);
34
+ const rhat = [dr[0] / r, dr[1] / r, dr[2] / r];
35
+ const ahat = scale(a, 1 / norm(a));
36
+ vecRelClose(ahat, rhat, 1e-12);
37
+ });
38
+ test('inverse-square scaling: doubling r → quarter acceleration', () => {
39
+ const G = 10;
40
+ const m2 = 5;
41
+ const r1 = [0, 0, 0];
42
+ const r2a = [2, 0, 0]; // r = 2
43
+ const r2b = [4, 0, 0]; // r = 4 (double → quarter a)
44
+ const aA = gravitationalAccelerationOn1By2(m2, r1, r2a, G);
45
+ const aB = gravitationalAccelerationOn1By2(m2, r1, r2b, G);
46
+ relClose(norm(aB), norm(aA) / 4, 1e-14);
47
+ });
48
+ test('linearity: scaling m2 or G scales a accordingly', () => {
49
+ const G = 3;
50
+ const m2 = 5;
51
+ const r1 = [1, 2, 3];
52
+ const r2 = [4, -1, 0];
53
+ const a = gravitationalAccelerationOn1By2(m2, r1, r2, G);
54
+ const a_m2x2 = gravitationalAccelerationOn1By2(2 * m2, r1, r2, G);
55
+ const a_Gx2 = gravitationalAccelerationOn1By2(m2, r1, r2, 2 * G);
56
+ vecRelClose(a_m2x2, scale(a, 2));
57
+ vecRelClose(a_Gx2, scale(a, 2));
58
+ });
59
+ test('real-world sanity: Earth due to Sun at 1 AU (~5.93e-3 m/s^2)', () => {
60
+ const mSun = 1.9885e30; // kg
61
+ const rEarth = [0, 0, 0];
62
+ const rSun = [1.495978707e11, 0, 0]; // m
63
+ const a = gravitationalAccelerationOn1By2(mSun, rEarth, rSun, G_SI);
64
+ // Closed-form expectation GM_sun / r^2
65
+ const dr = sub(rSun, rEarth);
66
+ const r = Math.hypot(...dr);
67
+ const expected = (G_SI * mSun) / (r * r);
68
+ relClose(norm(a), expected, 1e-12);
69
+ // Also within ~0.5% of commonly cited value
70
+ const ref = 5.93e-3;
71
+ assert.ok(Math.abs(norm(a) - ref) / ref < 5e-3, `|a| ${norm(a)} differs from ~${ref}`);
72
+ });
73
+ test('error propagation: invalid inputs cause throws', () => {
74
+ const r1 = [0, 0, 0];
75
+ const r2 = [1, 0, 0];
76
+ // negative / non-finite m2
77
+ assert.throws(() => gravitationalAccelerationOn1By2(-1, r1, r2, 1), /non-negative|finite/);
78
+ assert.throws(() => gravitationalAccelerationOn1By2(Number.NaN, r1, r2, 1), /finite/);
79
+ // coincident positions
80
+ assert.throws(() => gravitationalAccelerationOn1By2(1, r1, r1, 1), /singular \(r = 0\)/);
81
+ // invalid G
82
+ assert.throws(() => gravitationalAccelerationOn1By2(1, r1, r2, 0), /positive/);
83
+ assert.throws(() => gravitationalAccelerationOn1By2(1, r1, r2, Number.NaN), /finite/);
84
+ });
85
+ });
86
+ //# sourceMappingURL=gravitational-acceleration-on1-by2.int.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-acceleration-on1-by2.int.spec.js","sourceRoot":"","sources":["../../src/__tests__/gravitational-acceleration-on1-by2.int.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gravitational-force-on1-by2.int.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-force-on1-by2.int.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/gravitational-force-on1-by2.int.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,106 @@
1
+ import assert from 'node:assert/strict';
2
+ import test, { describe } from 'node:test';
3
+ import { gravitationalForceOn1By2 } from '../law-of-gravitation';
4
+ import { norm, relClose, scale, sub, vecRelClose } from './helpers';
5
+ import { G_SI } from '@interstellar-tools/constants';
6
+ describe('gravitationalForceOn1By2', () => {
7
+ test('1D sanity (+x): |F| = G*m1*m2/r^2 and points +x', () => {
8
+ const G = 1;
9
+ const m1 = 2;
10
+ const m2 = 3;
11
+ const r1 = [0, 0, 0];
12
+ const r2 = [10, 0, 0];
13
+ const F = gravitationalForceOn1By2(m1, m2, r1, r2, G);
14
+ // Expected magnitude and direction
15
+ const expectedMag = (G * m1 * m2) / 10 ** 2; // 0.06
16
+ relClose(norm(F), expectedMag, 1e-14);
17
+ vecRelClose(F, [expectedMag, 0, 0], 1e-14);
18
+ });
19
+ test('1D sanity (-x): points toward body 2 (at -x)', () => {
20
+ const G = 1;
21
+ const m1 = 2;
22
+ const m2 = 3;
23
+ const r1 = [0, 0, 0];
24
+ const r2 = [-10, 0, 0];
25
+ const F = gravitationalForceOn1By2(m1, m2, r1, r2, G);
26
+ relClose(norm(F), 0.06, 1e-14);
27
+ vecRelClose(F, [-0.06, 0, 0], 1e-14);
28
+ });
29
+ test('direction equals r-hat for arbitrary 3D positions', () => {
30
+ const G = 2.5;
31
+ const m1 = 7;
32
+ const m2 = 11;
33
+ const r1 = [0.2, -3, 5.5];
34
+ const r2 = [4.2, 1, -1];
35
+ const F = gravitationalForceOn1By2(m1, m2, r1, r2, G);
36
+ const dr = sub(r2, r1);
37
+ const r = Math.hypot(...dr);
38
+ const rhat = [dr[0] / r, dr[1] / r, dr[2] / r];
39
+ // Normalize F and compare to rhat
40
+ const Fhat = scale(F, 1 / norm(F));
41
+ vecRelClose(Fhat, rhat, 1e-12);
42
+ });
43
+ test('Newton’s 3rd law: F_1<-2 = - F_2<-1', () => {
44
+ const G = 9.87;
45
+ const m1 = 7;
46
+ const m2 = 11;
47
+ const r1 = [0, -2, 5];
48
+ const r2 = [3, 1, -1];
49
+ const F12 = gravitationalForceOn1By2(m1, m2, r1, r2, G);
50
+ const F21 = gravitationalForceOn1By2(m2, m1, r2, r1, G);
51
+ vecRelClose(F12, [-F21[0], -F21[1], -F21[2]]);
52
+ });
53
+ test('inverse-square scaling: doubling r → quarter force', () => {
54
+ const G = 10;
55
+ const m1 = 2;
56
+ const m2 = 5;
57
+ const r1 = [0, 0, 0];
58
+ const r2a = [2, 0, 0]; // r = 2
59
+ const r2b = [4, 0, 0]; // r = 4 (double → quarter force)
60
+ const Fa = gravitationalForceOn1By2(m1, m2, r1, r2a, G);
61
+ const Fb = gravitationalForceOn1By2(m1, m2, r1, r2b, G);
62
+ relClose(norm(Fb), norm(Fa) / 4, 1e-14);
63
+ });
64
+ test('linearity: scaling m1, m2, or G scales F accordingly', () => {
65
+ const G = 3;
66
+ const m1 = 2;
67
+ const m2 = 5;
68
+ const r1 = [1, 2, 3];
69
+ const r2 = [4, -1, 0];
70
+ const F = gravitationalForceOn1By2(m1, m2, r1, r2, G);
71
+ const F_m1x2 = gravitationalForceOn1By2(2 * m1, m2, r1, r2, G);
72
+ const F_m2x2 = gravitationalForceOn1By2(m1, 2 * m2, r1, r2, G);
73
+ const F_Gx2 = gravitationalForceOn1By2(m1, m2, r1, r2, 2 * G);
74
+ // Doubling any of m1, m2, or G doubles the force vector
75
+ vecRelClose(F_m1x2, scale(F, 2));
76
+ vecRelClose(F_m2x2, scale(F, 2));
77
+ vecRelClose(F_Gx2, scale(F, 2));
78
+ });
79
+ test('real-world sanity: Earth–Sun at ~1 AU (~3.54e22 N)', () => {
80
+ const mEarth = 5.972e24; // kg
81
+ const mSun = 1.9885e30; // kg
82
+ const rEarth = [0, 0, 0];
83
+ const rSun = [1.495978707e11, 0, 0]; // m
84
+ const F = gravitationalForceOn1By2(mEarth, mSun, rEarth, rSun, G_SI);
85
+ const r = Math.hypot(rSun[0] - rEarth[0], rSun[1] - rEarth[1], rSun[2] - rEarth[2]);
86
+ const expectedMag = (G_SI * mEarth * mSun) / (r * r);
87
+ relClose(norm(F), expectedMag, 1e-12);
88
+ // also within ~0.5% of the commonly quoted magnitude
89
+ const ref = 3.542e22;
90
+ assert.ok(Math.abs(norm(F) - ref) / ref < 5e-3, `|F| ${norm(F)} differs from ~${ref}`);
91
+ });
92
+ test('error propagation: invalid inputs cause throws', () => {
93
+ const r1 = [0, 0, 0];
94
+ const r2 = [1, 0, 0];
95
+ // negative / non-finite masses
96
+ assert.throws(() => gravitationalForceOn1By2(-1, 1, r1, r2, 1), /non-negative|finite/);
97
+ assert.throws(() => gravitationalForceOn1By2(1, -1, r1, r2, 1), /non-negative|finite/);
98
+ assert.throws(() => gravitationalForceOn1By2(Number.NaN, 1, r1, r2, 1), /finite/);
99
+ // coincident positions
100
+ assert.throws(() => gravitationalForceOn1By2(1, 1, r1, r1, 1), /singular \(r = 0\)/);
101
+ // invalid G
102
+ assert.throws(() => gravitationalForceOn1By2(1, 1, r1, r2, 0), /positive/);
103
+ assert.throws(() => gravitationalForceOn1By2(1, 1, r1, r2, Number.NaN), /finite/);
104
+ });
105
+ });
106
+ //# sourceMappingURL=gravitational-force-on1-by2.int.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-force-on1-by2.int.spec.js","sourceRoot":"","sources":["../../src/__tests__/gravitational-force-on1-by2.int.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gravitational-force.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-force.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/gravitational-force.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ import assert from 'node:assert/strict';
2
+ import test, { describe } from 'node:test';
3
+ import { gravitationalForce } from '../law-of-gravitation';
4
+ import { G_SI } from '@interstellar-tools/constants';
5
+ import { norm, relClose, vecRelClose } from './helpers';
6
+ describe('gravitationalForce', () => {
7
+ test('computes simple 1D case (+x) with custom G', () => {
8
+ const G = 1; // simple numbers to check algebra
9
+ const m1 = 2;
10
+ const m2 = 3;
11
+ const r1 = [0, 0, 0];
12
+ const r2 = [10, 0, 0];
13
+ const res = gravitationalForce(m1, m2, r1, r2, G);
14
+ // Expected: |F| = G m1 m2 / r^2 = 6 / 100 = 0.06; direction = +x
15
+ relClose(res.magnitude, 0.06);
16
+ vecRelClose(res.vector, [0.06, 0, 0]);
17
+ // invariants
18
+ relClose(norm(res.vector), res.magnitude);
19
+ relClose(norm(res.direction), 1);
20
+ vecRelClose(res.vector, [
21
+ res.magnitude * res.direction[0],
22
+ res.magnitude * res.direction[1],
23
+ res.magnitude * res.direction[2]
24
+ ]);
25
+ });
26
+ test('direction points from body 1 toward body 2 (arbitrary 3D)', () => {
27
+ const G = 1.23;
28
+ const m1 = 1;
29
+ const m2 = 4;
30
+ const r1 = [1, 2, 3];
31
+ const r2 = [-2, 6, 3]; // displacement = (-3, 4, 0)
32
+ const res = gravitationalForce(m1, m2, r1, r2, G);
33
+ // direction should align with normalized (r2 - r1)
34
+ const dr = [
35
+ r2[0] - r1[0],
36
+ r2[1] - r1[1],
37
+ r2[2] - r1[2]
38
+ ];
39
+ const r = Math.hypot(...dr);
40
+ const rhat = [dr[0] / r, dr[1] / r, dr[2] / r];
41
+ vecRelClose(res.direction, rhat, 1e-12);
42
+ // vector == magnitude * rhat
43
+ vecRelClose(res.vector, [
44
+ res.magnitude * rhat[0],
45
+ res.magnitude * rhat[1],
46
+ res.magnitude * rhat[2]
47
+ ]);
48
+ });
49
+ test("Newton's 3rd law: F_1<-2 = - F_2<-1", () => {
50
+ const G = 9.87;
51
+ const m1 = 7;
52
+ const m2 = 11;
53
+ const r1 = [0, -2, 5];
54
+ const r2 = [3, 1, -1];
55
+ const F12 = gravitationalForce(m1, m2, r1, r2, G).vector;
56
+ const F21 = gravitationalForce(m2, m1, r2, r1, G).vector;
57
+ vecRelClose(F12, [-F21[0], -F21[1], -F21[2]]);
58
+ });
59
+ test('real-world sanity: Earth-Sun at ~1 AU (matches known ~3.54e22 N)', () => {
60
+ const mEarth = 5.972e24; // kg
61
+ const mSun = 1.9885e30; // kg
62
+ const r1 = [0, 0, 0];
63
+ const r2 = [1.495978707e11, 0, 0]; // m
64
+ const res = gravitationalForce(mEarth, mSun, r1, r2, G_SI);
65
+ // Compare to closed-form expectation (not using the function's internal dir)
66
+ const r = Math.hypot(r2[0] - r1[0], r2[1] - r1[1], r2[2] - r1[2]);
67
+ const expectedMag = (G_SI * mEarth * mSun) / (r * r);
68
+ // exact equivalence (up to FP)
69
+ relClose(res.magnitude, expectedMag, 1e-15);
70
+ // also compare to the commonly quoted magnitude ~3.542e22 N within 0.5%
71
+ const ref = 3.542e22;
72
+ assert.ok(Math.abs(res.magnitude - ref) / ref < 5e-3, `|F| ${res.magnitude} differs from ~${ref}`);
73
+ });
74
+ test('error: negative or non-finite masses', () => {
75
+ const r1 = [0, 0, 0];
76
+ const r2 = [1, 0, 0];
77
+ assert.throws(() => gravitationalForce(-1, 1, r1, r2, 1), /non-negative/);
78
+ assert.throws(() => gravitationalForce(1, -1, r1, r2, 1), /non-negative/);
79
+ assert.throws(() => gravitationalForce(Number.NaN, 1, r1, r2, 1), /finite/);
80
+ assert.throws(() => gravitationalForce(1, Number.POSITIVE_INFINITY, r1, r2, 1), /finite/);
81
+ });
82
+ test('error: coincident positions (r = 0)', () => {
83
+ const r = [3, -4, 5];
84
+ assert.throws(() => gravitationalForce(1, 1, r, r, 1), /singular \(r = 0\)/);
85
+ });
86
+ test('error: invalid G (non-finite or <= 0)', () => {
87
+ const r1 = [0, 0, 0];
88
+ const r2 = [1, 0, 0];
89
+ assert.throws(() => gravitationalForce(1, 1, r1, r2, 0), /positive/);
90
+ assert.throws(() => gravitationalForce(1, 1, r1, r2, Number.NaN), /finite/);
91
+ assert.throws(() => gravitationalForce(1, 1, r1, r2, -1), /positive/);
92
+ });
93
+ test('scales with 1/r^2', () => {
94
+ const G = 10;
95
+ const m1 = 2;
96
+ const m2 = 5;
97
+ const r1 = [0, 0, 0];
98
+ const r2a = [2, 0, 0]; // r = 2
99
+ const r2b = [4, 0, 0]; // r = 4 (double -> quarter force)
100
+ const Fa = gravitationalForce(m1, m2, r1, r2a, G).magnitude;
101
+ const Fb = gravitationalForce(m1, m2, r1, r2b, G).magnitude;
102
+ relClose(Fb, Fa / 4);
103
+ });
104
+ });
105
+ //# sourceMappingURL=gravitational-force.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-force.spec.js","sourceRoot":"","sources":["../../src/__tests__/gravitational-force.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gravitational-parameters.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-parameters.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/gravitational-parameters.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,75 @@
1
+ import assert from 'node:assert/strict';
2
+ import test, { describe } from 'node:test';
3
+ import { relClose } from './helpers';
4
+ import { gravitationalParameter } from '../gravitational-parameter';
5
+ describe('gravitationalParameter', () => {
6
+ test('computes μ = G(M + m) for simple numbers', () => {
7
+ const G = 10;
8
+ const M = 2;
9
+ const m = 3;
10
+ const mu = gravitationalParameter(M, m, G);
11
+ assert.equal(mu, 10 * (2 + 3)); // 50
12
+ });
13
+ test('matches canonical GM_Earth when M = GM/G', () => {
14
+ const GM_canonical = 3.986004418e14; // m^3/s^2
15
+ const M_from_GM = GM_canonical / 6.6743e-11;
16
+ const mu = gravitationalParameter(M_from_GM); // m=0, G=G_SI
17
+ relClose(mu, GM_canonical, 1e-12);
18
+ });
19
+ test('M = 0 returns μ = G * m', () => {
20
+ const G = 6.7;
21
+ const M = 0;
22
+ const m = 123;
23
+ assert.equal(gravitationalParameter(M, m, G), G * m);
24
+ });
25
+ test('commutative in masses: μ(M,m) = μ(m,M)', () => {
26
+ const G = 3.21;
27
+ const M = 7;
28
+ const m = 11;
29
+ const mu1 = gravitationalParameter(M, m, G);
30
+ const mu2 = gravitationalParameter(m, M, G);
31
+ assert.equal(mu1, mu2);
32
+ });
33
+ test('approximation: when m ≪ M, μ ≈ G M within ~m/M relative error', () => {
34
+ const G = 2;
35
+ const M = 1e8;
36
+ const m = 1e2; // m/M = 1e-6
37
+ const exact = gravitationalParameter(M, m, G);
38
+ const approx = G * M;
39
+ // Relative error should be about m/M
40
+ const relErr = Math.abs(exact - approx) / approx;
41
+ assert.ok(relErr < 2e-6, `relative error ${relErr} not < 2e-6`);
42
+ });
43
+ test('scales linearly with G and with (M + m)', () => {
44
+ const G = 5;
45
+ const M = 10;
46
+ const m = 4;
47
+ const mu = gravitationalParameter(M, m, G);
48
+ const mu_doubleG = gravitationalParameter(M, m, 2 * G);
49
+ const mu_doubleMasses = gravitationalParameter(2 * M, 2 * m, G);
50
+ // Doubling G doubles μ
51
+ relClose(mu_doubleG, 2 * mu, 1e-15, 'doubling G should double μ');
52
+ // Doubling both masses doubles (M + m) and thus doubles μ
53
+ relClose(mu_doubleMasses, 2 * mu, 1e-15, 'doubling masses should double μ');
54
+ });
55
+ test('throws on invalid M (negative or non-finite)', () => {
56
+ const G = 6.7;
57
+ const m = 1;
58
+ assert.throws(() => gravitationalParameter(-1, m, G), /finite|positive/);
59
+ assert.throws(() => gravitationalParameter(Number.NaN, m, G), /finite/);
60
+ });
61
+ test('throws on invalid m (negative or non-finite)', () => {
62
+ const G = 6.7;
63
+ const M = 1;
64
+ assert.throws(() => gravitationalParameter(M, -1, G), /finite|positive/);
65
+ assert.throws(() => gravitationalParameter(M, Number.NaN, G), /finite/);
66
+ });
67
+ test('throws on invalid G (non-finite or ≤ 0)', () => {
68
+ const M = 1;
69
+ const m = 1;
70
+ assert.throws(() => gravitationalParameter(M, m, 0), /positive/);
71
+ assert.throws(() => gravitationalParameter(M, m, -1), /positive/);
72
+ assert.throws(() => gravitationalParameter(M, m, Number.NaN), /finite/);
73
+ });
74
+ });
75
+ //# sourceMappingURL=gravitational-parameters.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gravitational-parameters.spec.js","sourceRoot":"","sources":["../../src/__tests__/gravitational-parameters.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Vector3DTupleType } from '@interstellar-tools/types';
2
+ export declare const relClose: (a: number, b: number, eps?: number, msg?: string) => void;
3
+ export declare const vecRelClose: (a: Vector3DTupleType, b: Vector3DTupleType, eps?: number, msg?: string) => void;
4
+ export declare const norm: (v: Vector3DTupleType) => number;
5
+ export declare const scale: (v: Vector3DTupleType, s: number) => Vector3DTupleType;
6
+ export declare const sub: (a: Vector3DTupleType, b: Vector3DTupleType) => Vector3DTupleType;
7
+ export declare const absClose: (a: number, b: number, eps?: number, msg?: string) => void;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAG9D,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,YAAW,EAAE,MAAM,MAAM,SAMvE,CAAC;AAEF,eAAO,MAAM,WAAW,GACtB,GAAG,iBAAiB,EACpB,GAAG,iBAAiB,EACpB,YAAW,EACX,MAAM,MAAM,SAOb,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,GAAG,iBAAiB,WAExC,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,GAAG,iBAAiB,EAAE,GAAG,MAAM,KAAG,iBAEvD,CAAC;AAEF,eAAO,MAAM,GAAG,GACd,GAAG,iBAAiB,EACpB,GAAG,iBAAiB,KACnB,iBAEF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,YAAW,EAAE,MAAM,MAAM,SAEvE,CAAC"}
@@ -0,0 +1,25 @@
1
+ import assert from 'node:assert/strict';
2
+ export const relClose = (a, b, eps = 1e-12, msg) => {
3
+ const scale = Math.max(1, Math.abs(a), Math.abs(b));
4
+ assert.ok(Math.abs(a - b) <= eps * scale, msg ?? `|${a} - ${b}| > ${eps} * ${scale}`);
5
+ };
6
+ export const vecRelClose = (a, b, eps = 1e-12, msg) => {
7
+ assert.equal(a.length, 3);
8
+ assert.equal(b.length, 3);
9
+ for (let i = 0; i < 3; i++) {
10
+ relClose(a[i], b[i], eps, msg);
11
+ }
12
+ };
13
+ export const norm = (v) => {
14
+ return Math.hypot(v[0], v[1], v[2]);
15
+ };
16
+ export const scale = (v, s) => {
17
+ return [v[0] * s, v[1] * s, v[2] * s];
18
+ };
19
+ export const sub = (a, b) => {
20
+ return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
21
+ };
22
+ export const absClose = (a, b, eps = 1e-12, msg) => {
23
+ assert.ok(Math.abs(a - b) <= eps, msg ?? `|${a} - ${b}| > ${eps}`);
24
+ };
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/__tests__/helpers/index.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,GAAG,GAAG,KAAK,EAAE,GAAY,EAAE,EAAE;IAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,CACP,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,GAAG,KAAK,EAC9B,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,EAAE,CAC3C,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,CAAoB,EACpB,CAAoB,EACpB,GAAG,GAAG,KAAK,EACX,GAAY,EACZ,EAAE;IACF,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAoB,EAAE,EAAE;IAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,CAAoB,EAAE,CAAS,EAAqB,EAAE;IAC1E,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,GAAG,GAAG,CACjB,CAAoB,EACpB,CAAoB,EACD,EAAE;IACrB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,GAAG,GAAG,KAAK,EAAE,GAAY,EAAE,EAAE;IAC1E,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;AACrE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=kepler-period.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kepler-period.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/kepler-period.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ import assert from 'node:assert/strict';
2
+ import test, { describe } from 'node:test';
3
+ import { relClose } from './helpers';
4
+ import { keplerPeriod } from '../kepler-period';
5
+ describe('keplerPeriod', () => {
6
+ test('GEO sanity: a ≈ 42,164 km around Earth → T ≈ 86,164 s', () => {
7
+ const a = 42164e3; // m
8
+ const muEarth = 3.986004418e14; // m^3/s^2
9
+ const T = keplerPeriod(a, muEarth);
10
+ const ref = 86164.0905; // sidereal day (s)
11
+ // Keep tolerance modest: orbit isn't exactly Keplerian in reality; our formula is ideal
12
+ relClose(T, ref, 1e-3); // 0.1% relative tolerance
13
+ });
14
+ test('LEO circular (~400 km): a = Re + 400 km → T ~ 5550 s', () => {
15
+ const a = 6378e3 + 400e3; // m
16
+ const muEarth = 3.986004418e14; // m^3/s^2
17
+ const T = keplerPeriod(a, muEarth);
18
+ const ref = 5550; // ~92.5 min
19
+ relClose(T, ref, 5e-2); // 5% slack; depends on precise constants
20
+ });
21
+ test('Scaling with semi-major axis: T ∝ a^(3/2) (same μ)', () => {
22
+ const mu = 3.986004418e14;
23
+ const a1 = 7000e3;
24
+ const a2 = 2 * a1;
25
+ const T1 = keplerPeriod(a1, mu);
26
+ const T2 = keplerPeriod(a2, mu);
27
+ const expectedRatio = Math.pow(2, 1.5); // 2^(3/2)
28
+ relClose(T2 / T1, expectedRatio, 1e-12);
29
+ });
30
+ test('Scaling with μ: doubling μ reduces T by 1/√2 (same a)', () => {
31
+ const a = 10000e3;
32
+ const mu = 3.986004418e14;
33
+ const T1 = keplerPeriod(a, mu);
34
+ const T2 = keplerPeriod(a, 2 * mu);
35
+ const expectedRatio = 1 / Math.SQRT2;
36
+ relClose(T2 / T1, expectedRatio, 1e-12);
37
+ });
38
+ test('Heliocentric: a = 1 AU around Sun → T ≈ 365.256 days', () => {
39
+ const AU = 149597870700; // m (IAU 2012)
40
+ const muSun = 1.32712440018e20; // m^3/s^2
41
+ const T = keplerPeriod(AU, muSun);
42
+ const siderealYear = 365.256363004 * 86400; // s
43
+ relClose(T, siderealYear, 1e-3); // 0.1% tolerance
44
+ });
45
+ test('Input validation: a <= 0, μ <= 0, or non-finite throws', () => {
46
+ const a = 7000e3;
47
+ const mu = 3.986004418e14;
48
+ // a invalid
49
+ assert.throws(() => keplerPeriod(0, mu), /positive/);
50
+ assert.throws(() => keplerPeriod(-1, mu), /positive/);
51
+ assert.throws(() => keplerPeriod(Number.NaN, mu), /finite/);
52
+ // μ invalid
53
+ assert.throws(() => keplerPeriod(a, 0), /positive/);
54
+ assert.throws(() => keplerPeriod(a, -1), /positive/);
55
+ assert.throws(() => keplerPeriod(a, Number.NaN), /finite/);
56
+ });
57
+ test('Numerical stability: very large a still yields finite result', () => {
58
+ const a = 1e9; // 1,000,000 km
59
+ const muEarth = 3.986004418e14; // m^3/s^2
60
+ const T = keplerPeriod(a, muEarth);
61
+ assert.ok(Number.isFinite(T) && T > 0);
62
+ });
63
+ // Optional tight identity test against the formula directly
64
+ test('Definition check: T = 2π * sqrt(a^3/μ)', () => {
65
+ const a = 12345678; // m
66
+ const mu = 7.654321e13; // m^3/s^2
67
+ const T = keplerPeriod(a, mu);
68
+ const expected = 2 * Math.PI * Math.sqrt((a * a * a) / mu);
69
+ relClose(T, expected, 1e-15);
70
+ });
71
+ });
72
+ //# sourceMappingURL=kepler-period.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kepler-period.spec.js","sourceRoot":"","sources":["../../src/__tests__/kepler-period.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=specific-mechanical-energy.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specific-mechanical-energy.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/specific-mechanical-energy.spec.ts"],"names":[],"mappings":""}