satkit 0.3.0__tar.gz → 0.3.1__tar.gz

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 (120) hide show
  1. {satkit-0.3.0 → satkit-0.3.1}/Cargo.toml +6 -6
  2. {satkit-0.3.0/python/satkit.egg-info → satkit-0.3.1}/PKG-INFO +1 -1
  3. {satkit-0.3.0 → satkit-0.3.1}/pyproject.toml +1 -1
  4. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/satkit.pyi +1 -1
  5. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/satprop.pyi +3 -5
  6. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/utils.pyi +1 -1
  7. {satkit-0.3.0 → satkit-0.3.1/python/satkit.egg-info}/PKG-INFO +1 -1
  8. {satkit-0.3.0 → satkit-0.3.1}/python/test/orbitprop_gps_fit.py +33 -27
  9. {satkit-0.3.0 → satkit-0.3.1}/python/test/test.py +11 -5
  10. {satkit-0.3.0 → satkit-0.3.1}/src/earthgravity.rs +99 -3
  11. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rk_adaptive.rs +19 -5
  12. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv87.rs +2 -2
  13. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv98.rs +2 -2
  14. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/propagator.rs +9 -4
  15. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pypropagate.rs +0 -1
  16. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pysgp4.rs +18 -12
  17. {satkit-0.3.0 → satkit-0.3.1}/LICENSE +0 -0
  18. {satkit-0.3.0 → satkit-0.3.1}/MANIFEST.in +0 -0
  19. {satkit-0.3.0 → satkit-0.3.1}/README.md +0 -0
  20. {satkit-0.3.0 → satkit-0.3.1}/python/docs/source/conf.py +0 -0
  21. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/__init__.py +0 -0
  22. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/__init__.pyi +0 -0
  23. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/density.pyi +0 -0
  24. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/frametransform.pyi +0 -0
  25. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/jplephem.pyi +0 -0
  26. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/moon.pyi +0 -0
  27. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/planets.pyi +0 -0
  28. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/py.typed +0 -0
  29. {satkit-0.3.0 → satkit-0.3.1}/python/satkit/sun.pyi +0 -0
  30. {satkit-0.3.0 → satkit-0.3.1}/python/satkit.egg-info/SOURCES.txt +0 -0
  31. {satkit-0.3.0 → satkit-0.3.1}/python/satkit.egg-info/dependency_links.txt +0 -0
  32. {satkit-0.3.0 → satkit-0.3.1}/python/satkit.egg-info/requires.txt +0 -0
  33. {satkit-0.3.0 → satkit-0.3.1}/python/satkit.egg-info/top_level.txt +0 -0
  34. {satkit-0.3.0 → satkit-0.3.1}/python/test/download_data.py +0 -0
  35. {satkit-0.3.0 → satkit-0.3.1}/python/test/download_from_json.py +0 -0
  36. {satkit-0.3.0 → satkit-0.3.1}/python/test/download_testvecs.py +0 -0
  37. {satkit-0.3.0 → satkit-0.3.1}/python/test/sp3file.py +0 -0
  38. {satkit-0.3.0 → satkit-0.3.1}/setup.cfg +0 -0
  39. {satkit-0.3.0 → satkit-0.3.1}/src/astrotime.rs +0 -0
  40. {satkit-0.3.0 → satkit-0.3.1}/src/consts.rs +0 -0
  41. {satkit-0.3.0 → satkit-0.3.1}/src/duration.rs +0 -0
  42. {satkit-0.3.0 → satkit-0.3.1}/src/earth_orientation_params.rs +0 -0
  43. {satkit-0.3.0 → satkit-0.3.1}/src/frames.rs +0 -0
  44. {satkit-0.3.0 → satkit-0.3.1}/src/frametransform/ierstable.rs +0 -0
  45. {satkit-0.3.0 → satkit-0.3.1}/src/frametransform/mod.rs +0 -0
  46. {satkit-0.3.0 → satkit-0.3.1}/src/frametransform/qcirs2gcrs.rs +0 -0
  47. {satkit-0.3.0 → satkit-0.3.1}/src/itrfcoord.rs +0 -0
  48. {satkit-0.3.0 → satkit-0.3.1}/src/jplephem.rs +0 -0
  49. {satkit-0.3.0 → satkit-0.3.1}/src/kepler.rs +0 -0
  50. {satkit-0.3.0 → satkit-0.3.1}/src/lib.rs +0 -0
  51. {satkit-0.3.0 → satkit-0.3.1}/src/lpephem/mod.rs +0 -0
  52. {satkit-0.3.0 → satkit-0.3.1}/src/lpephem/moon.rs +0 -0
  53. {satkit-0.3.0 → satkit-0.3.1}/src/lpephem/planets.rs +0 -0
  54. {satkit-0.3.0 → satkit-0.3.1}/src/lpephem/sun.rs +0 -0
  55. {satkit-0.3.0 → satkit-0.3.1}/src/nrlmsise.rs +0 -0
  56. {satkit-0.3.0 → satkit-0.3.1}/src/ode/harmonic_oscillator.rs +0 -0
  57. {satkit-0.3.0 → satkit-0.3.1}/src/ode/mod.rs +0 -0
  58. {satkit-0.3.0 → satkit-0.3.1}/src/ode/nalgebra.rs +0 -0
  59. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rk_adaptive_settings.rs +0 -0
  60. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rk_explicit.rs +0 -0
  61. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkf45.rs +0 -0
  62. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkts54.rs +0 -0
  63. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv65.rs +0 -0
  64. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv65_table.rs +0 -0
  65. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv87_table.rs +0 -0
  66. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv98_nointerp.rs +0 -0
  67. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv98_nointerp_table.rs +0 -0
  68. {satkit-0.3.0 → satkit-0.3.1}/src/ode/rkv98_table.rs +0 -0
  69. {satkit-0.3.0 → satkit-0.3.1}/src/ode/types.rs +0 -0
  70. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/drag.rs +0 -0
  71. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/mod.rs +0 -0
  72. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/point_gravity.rs +0 -0
  73. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/satproperties.rs +0 -0
  74. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/satstate.rs +0 -0
  75. {satkit-0.3.0 → satkit-0.3.1}/src/orbitprop/settings.rs +0 -0
  76. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/mod.rs +0 -0
  77. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/mod_utils.rs +0 -0
  78. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyastrotime.rs +0 -0
  79. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyconsts.rs +0 -0
  80. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pydensity.rs +0 -0
  81. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyduration.rs +0 -0
  82. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyframes.rs +0 -0
  83. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyframetransform.rs +0 -0
  84. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pygravity.rs +0 -0
  85. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyitrfcoord.rs +0 -0
  86. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyjplephem.rs +0 -0
  87. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pykepler.rs +0 -0
  88. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pylpephem_moon.rs +0 -0
  89. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pylpephem_planets.rs +0 -0
  90. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pylpephem_sun.rs +0 -0
  91. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pynrlmsise.rs +0 -0
  92. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pypropresult.rs +0 -0
  93. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pypropsettings.rs +0 -0
  94. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyquaternion.rs +0 -0
  95. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pysatproperties.rs +0 -0
  96. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pysatstate.rs +0 -0
  97. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pysolarsystem.rs +0 -0
  98. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pytle.rs +0 -0
  99. {satkit-0.3.0 → satkit-0.3.1}/src/pybindings/pyutils.rs +0 -0
  100. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/dpper.rs +0 -0
  101. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/dscom.rs +0 -0
  102. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/dsinit.rs +0 -0
  103. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/dspace.rs +0 -0
  104. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/getgravconst.rs +0 -0
  105. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/initl.rs +0 -0
  106. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/mod.rs +0 -0
  107. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/satrec.rs +0 -0
  108. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/sgp4.rs +0 -0
  109. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/sgp4_lowlevel.rs +0 -0
  110. {satkit-0.3.0 → satkit-0.3.1}/src/sgp4/sgp4init.rs +0 -0
  111. {satkit-0.3.0 → satkit-0.3.1}/src/solarsystem.rs +0 -0
  112. {satkit-0.3.0 → satkit-0.3.1}/src/spaceweather.rs +0 -0
  113. {satkit-0.3.0 → satkit-0.3.1}/src/tle.rs +0 -0
  114. {satkit-0.3.0 → satkit-0.3.1}/src/types.rs +0 -0
  115. {satkit-0.3.0 → satkit-0.3.1}/src/utils/datadir.rs +0 -0
  116. {satkit-0.3.0 → satkit-0.3.1}/src/utils/download.rs +0 -0
  117. {satkit-0.3.0 → satkit-0.3.1}/src/utils/mod.rs +0 -0
  118. {satkit-0.3.0 → satkit-0.3.1}/src/utils/skerror.rs +0 -0
  119. {satkit-0.3.0 → satkit-0.3.1}/src/utils/test.rs +0 -0
  120. {satkit-0.3.0 → satkit-0.3.1}/src/utils/update_data.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "satkit"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  edition = "2021"
5
5
  description = "Satellite Toolkit"
6
6
  readme = "README.md"
@@ -21,22 +21,22 @@ name = "satkit"
21
21
 
22
22
  [dependencies]
23
23
  nalgebra = "0.33.0"
24
- ndarray = "0.15.6"
25
- libc = "0.2"
24
+ ndarray = "0.15.4"
25
+ libc = "0.2.158"
26
26
  chrono = "0.4.19"
27
27
  cty = "0.2.2"
28
28
  num-traits = "0.2"
29
- thiserror = "1.0"
29
+ thiserror = "1.0.63"
30
30
  simba = "0.9"
31
31
  once_cell = "1.6.0"
32
32
  numpy = { version = "0.21.0", optional = true }
33
33
  pyo3 = { version = "0.21", features = ["extension-module"], optional = true }
34
- ureq = "2.9.1"
34
+ ureq = "2.10.1"
35
35
  json = "0.12.4"
36
36
  process_path = "0.1.4"
37
37
 
38
38
  [build-dependencies]
39
- cc = { version = "1.0.74", features = ["parallel"] }
39
+ cc = { version = "1.1.15", features = ["parallel"] }
40
40
  chrono = "0.4.38"
41
41
  pyo3-build-config = { version = "0.21", optional = true }
42
42
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satkit
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Satellite Orbital Dynamics Toolkit
5
5
  Author-email: Steven Michael <ssmichael@gmail.com>
6
6
  Maintainer-email: Steven Michael <ssmichael@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "satkit"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  dependencies = ["numpy>=1.20.0"]
9
9
  requires-python = ">= 3.8"
10
10
  authors = [{ name = "Steven Michael", email = "ssmichael@gmail.com" }]
@@ -1490,7 +1490,7 @@ class satstate:
1490
1490
  Args:
1491
1491
  time (satkit.time|satkit.duration): Time or duration from current time to which to propagate the state
1492
1492
 
1493
- Keyword Arguments:
1493
+ Keyword Args:
1494
1494
  propsettings: satkit.satprop.propsettings object describing settings to use in the propagation. If omitted, default is used
1495
1495
 
1496
1496
  Returns:
@@ -228,17 +228,15 @@ def propagate(
228
228
  vel (npt.ArrayLike[float]): 3-element numpy array representing satellite GCRF velocity in m/s
229
229
  tm (satkit.time): satkit.time object representing instant at which satellite is at "pos" & "vel"
230
230
 
231
- Keyword Arguments:
231
+ Keyword Args:
232
232
  stop_time (satkit.time): satkit.time object representing instant at which new position and velocity will be computed
233
233
  duration_secs (float): duration in seconds from "tm" for at which new position and velocity will be computed.
234
234
  duration_days (float): duration in days from "tm" at which new position and velocity will be computed.
235
235
  duration (satkit.duration): duration from "tm" at which new position & velocity will be computed.
236
236
  output_phi (bool): Output 6x6 state transition matrix between "starttime" and "stoptime" (and at intervals, if specified)
237
237
  propsettings (propsettings): "propsettings" object with input settings for the propagation. if left out, default will be used.
238
- satproperties (satproperties_static): "SatPropertiesStatic" object with drag and radiation pressure succeptibility of satellite.
239
- If left out, drag and radiation pressure are neglected
240
- output_dense: boolean indicting whether or not dense output should be recorded. Default is false. If true, this will allow for calling the "interp" function
241
- to query states at arbitrary times between the start time and the stop time
238
+ satproperties (satproperties_static): "SatPropertiesStatic" object with drag and radiation pressure succeptibility of satellite. If left out, drag and radiation pressure are neglected
239
+ output_dense: boolean indicting whether or not dense output should be recorded. Default is false. If true, this will allow for calling the "interp" function to query states at arbitrary times between the start time and the stop time
242
240
 
243
241
  Returns:
244
242
  (propresult): Propagation result object holding state outputs, statistics, and dense output if requested
@@ -12,7 +12,7 @@ import satkit
12
12
  def update_datafiles(**kwargs):
13
13
  """Download & store data files needed for "satkit" computations
14
14
 
15
- Keyword Arguments:
15
+ Keyword Args:
16
16
 
17
17
  overwrite (bool): Download and overwrite files if they already exist
18
18
  dir(string): Target directory for files. Uses existing data directory if not specified. (see "datadir" function)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satkit
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Satellite Orbital Dynamics Toolkit
5
5
  Author-email: Steven Michael <ssmichael@gmail.com>
6
6
  Maintainer-email: Steven Michael <ssmichael@gmail.com>
@@ -25,26 +25,20 @@ import os
25
25
  basedir = os.getenv(
26
26
  "SATKIT_TESTVEC_ROOT", default="../.." + os.path.sep + "satkit-testvecs"
27
27
  )
28
+ fname = (
29
+ basedir + os.path.sep + "orbitprop" + os.path.sep
30
+ + "ESA0OPSFIN_20233640000_01D_05M_ORB.SP3"
31
+ )
32
+
33
+ [pitrf, timearr] = read_sp3file(fname)
34
+ pgcrf = np.stack(
35
+ np.fromiter(
36
+ (q * p for q, p in zip(sk.frametransform.qitrf2gcrf(timearr), pitrf)),
37
+ list,
38
+ ),
39
+ axis=0,
40
+ )
28
41
 
29
- sp3names = ['mid22630.sp3', 'mid22631.sp3', 'mid22632.sp3',
30
- 'mid22633.sp3', 'mid22634.sp3', 'mid22635.sp3',
31
- 'mid22636.sp3']
32
-
33
- sp3names = ['mid22630.sp3', 'mid22631.sp3', 'mid22632.sp3']
34
- pitrf = np.zeros((0,3), np.float64)
35
- timearr = np.array([])
36
-
37
- for sp3name in sp3names:
38
- fname = (
39
- basedir
40
- + os.path.sep
41
- + "orbitprop"
42
- + os.path.sep
43
- + sp3name
44
- )
45
- [pitrf1, timearray1] = read_sp3file(fname)
46
- pitrf = np.append(pitrf, pitrf1, axis=0)
47
- timearr = np.append(timearr, timearray1)
48
42
 
49
43
  print(pitrf.shape)
50
44
  print(timearr.shape)
@@ -62,14 +56,15 @@ vgcrf = (pgcrf[1, :] - pgcrf[0, :]) / (timearr[1] - timearr[0]).seconds()
62
56
 
63
57
  # Initial state for non-linear least squares is initial velocity
64
58
  # and susceptibility to radiation pressuer : Cr A / m
65
- v0 = np.array([vgcrf[0], vgcrf[1], vgcrf[2], 0.01])
66
-
59
+ #v0 = np.array([vgcrf[0], vgcrf[1], vgcrf[2], 0.02])
60
+ v0 = np.array([2.47130555e03, 2.94682777e03, -5.34171918e02, 2.13018578e-02])
67
61
 
68
62
  def minfunc(v):
69
63
  tstart = timearr[0]
70
64
  tend = timearr[-1]
71
65
  settings = sk.satprop.propsettings()
72
66
  settings.use_jplephem = False
67
+ settings.gravity_order = 10
73
68
  satprops = sk.satprop.satproperties_static()
74
69
  satprops.craoverm = v[3]
75
70
 
@@ -78,12 +73,15 @@ def minfunc(v):
78
73
  v[0:3],
79
74
  tstart,
80
75
  stoptime=tend,
81
- dt=sk.duration.from_minutes(5),
82
76
  propsettings=settings,
83
- satproperties=satprops,
77
+ satproperties=satprops,
78
+ output_dense=True,
84
79
  )
80
+ pest = np.zeros((len(timearr), 3))
81
+ for i in range(len(timearr)):
82
+ pest[i, :] = res.interp(timearr[i])[0:3]
85
83
 
86
- return np.sum(np.sum((res["pos"] - pgcrf) ** 2, axis=1), axis=0)
84
+ return np.sum(np.sum((pest - pgcrf) ** 2, axis=1), axis=0)
87
85
 
88
86
 
89
87
  # Minimize difference between true satellite state and propagated state
@@ -91,6 +89,10 @@ def minfunc(v):
91
89
  r = minimize(minfunc, v0, method="Nelder-Mead")
92
90
  print(r)
93
91
 
92
+ # %%
93
+ print(v0)
94
+ print(r.x)
95
+
94
96
  # %%
95
97
  import plotly.graph_objects as go
96
98
 
@@ -100,11 +102,15 @@ satprops = sk.satprop.satproperties_static()
100
102
  satprops.craoverm = r.x[3]
101
103
  res = sk.satprop.propagate(
102
104
  pgcrf[0,:], r.x[0:3], timearr[0],
103
- stoptime=timearr[-1], dt=sk.duration(minutes=5),
104
- propsettings=settings, satproperties=satprops
105
+ stoptime=timearr[-1],
106
+ propsettings=settings, satproperties=satprops,
107
+ output_dense=True
105
108
  )
106
- perr = res['pos'] - pgcrf
109
+ perr = np.zeros((len(timearr), 3))
110
+ for i in range(len(timearr)):
111
+ perr[i,:] = res.interp(timearr[i])[0:3] - pgcrf[i,:]
107
112
  fig = go.Figure()
113
+
108
114
  fig.add_trace(go.Scatter(x=[t.datetime() for t in timearr], y=perr[:,0], mode='lines', name='X'))
109
115
  fig.add_trace(go.Scatter(x=[t.datetime() for t in timearr], y=perr[:,1], mode='lines', name='Y'))
110
116
  fig.add_trace(go.Scatter(x=[t.datetime() for t in timearr], y=perr[:,2], mode='lines', name='Z'))
@@ -598,13 +598,19 @@ class TestHighPrecisionPropagation:
598
598
  settings = sk.satprop.propsettings()
599
599
  settings.use_jplephem = False
600
600
 
601
+ # Determined by orbitprop_gps_fit.py
602
+ fitparam = np.array(
603
+ [ 2.47130562e+03, 2.94682753e+03, -5.34172176e+02, 2.32565692e-02]
604
+ )
605
+
606
+
601
607
  # Values for craoverm and velocity come from orbitprop_gps_fit.py
602
608
  satprops = sk.satprop.satproperties_static()
603
- satprops.craoverm = 2.13018578e-2
604
- print(satprops)
609
+ satprops.craoverm = fitparam[3]
610
+
605
611
  res = sk.satprop.propagate(
606
612
  pgcrf[0, :],
607
- np.array([2.47130555e03, 2.94682777e03, -5.34171918e02]),
613
+ fitparam[0:3],
608
614
  timearr[0],
609
615
  stoptime=timearr[-1],
610
616
  propsettings=settings,
@@ -613,12 +619,12 @@ class TestHighPrecisionPropagation:
613
619
  )
614
620
  print(res)
615
621
 
616
- # See if propagator is accurate to < 10 meters over 1 day on
622
+ # See if propagator is accurate to < 8 meters over 1 day on
617
623
  # each Cartesian axis
618
624
  for iv in range(pgcrf.shape[0]-5):
619
625
  state = res.interp(timearr[iv])
620
626
  for ix in range(0, 3):
621
- assert m.fabs(state[ix] - pgcrf[iv, ix]) < 10
627
+ assert m.fabs(state[ix] - pgcrf[iv, ix]) < 8
622
628
 
623
629
  class TestSatState:
624
630
  def test_lvlh(self):
@@ -159,7 +159,7 @@ type Mat3 = na::Matrix3<f64>;
159
159
  /// * `pos` - Position as ITRF coordinate (satkit.itrfcoord) or numpy
160
160
  /// 3-vector representing ITRF position in meters
161
161
  ///
162
- /// * `order` - Order of the gravity model
162
+ /// * `order` - Order of the gravity model, up to 40
163
163
  ///
164
164
  /// # References
165
165
  ///
@@ -199,8 +199,56 @@ impl Gravity {
199
199
  self.accel_t::<14, 18>(pos)
200
200
  } else if order == 15 {
201
201
  self.accel_t::<15, 19>(pos)
202
- } else {
202
+ } else if order == 16 {
203
203
  self.accel_t::<16, 20>(pos)
204
+ } else if order == 17 {
205
+ self.accel_t::<17, 21>(pos)
206
+ } else if order == 18 {
207
+ self.accel_t::<18, 22>(pos)
208
+ } else if order == 19 {
209
+ self.accel_t::<19, 23>(pos)
210
+ } else if order == 20 {
211
+ self.accel_t::<20, 24>(pos)
212
+ } else if order == 21 {
213
+ self.accel_t::<21, 25>(pos)
214
+ } else if order == 22 {
215
+ self.accel_t::<22, 26>(pos)
216
+ } else if order == 23 {
217
+ self.accel_t::<23, 27>(pos)
218
+ } else if order == 24 {
219
+ self.accel_t::<24, 28>(pos)
220
+ } else if order == 25 {
221
+ self.accel_t::<25, 29>(pos)
222
+ } else if order == 26 {
223
+ self.accel_t::<26, 30>(pos)
224
+ } else if order == 27 {
225
+ self.accel_t::<27, 31>(pos)
226
+ } else if order == 28 {
227
+ self.accel_t::<28, 32>(pos)
228
+ } else if order == 29 {
229
+ self.accel_t::<29, 33>(pos)
230
+ } else if order == 30 {
231
+ self.accel_t::<30, 34>(pos)
232
+ } else if order == 31 {
233
+ self.accel_t::<31, 35>(pos)
234
+ } else if order == 32 {
235
+ self.accel_t::<32, 36>(pos)
236
+ } else if order == 33 {
237
+ self.accel_t::<33, 37>(pos)
238
+ } else if order == 34 {
239
+ self.accel_t::<34, 38>(pos)
240
+ } else if order == 35 {
241
+ self.accel_t::<35, 39>(pos)
242
+ } else if order == 36 {
243
+ self.accel_t::<36, 40>(pos)
244
+ } else if order == 37 {
245
+ self.accel_t::<37, 41>(pos)
246
+ } else if order == 37 {
247
+ self.accel_t::<38, 42>(pos)
248
+ } else if order == 39 {
249
+ self.accel_t::<39, 43>(pos)
250
+ } else {
251
+ self.accel_t::<40, 44>(pos)
204
252
  }
205
253
  }
206
254
 
@@ -235,8 +283,56 @@ impl Gravity {
235
283
  self.accel_and_partials_t::<14, 18>(pos)
236
284
  } else if order == 15 {
237
285
  self.accel_and_partials_t::<15, 19>(pos)
238
- } else {
286
+ } else if order == 16 {
239
287
  self.accel_and_partials_t::<16, 20>(pos)
288
+ } else if order == 17 {
289
+ self.accel_and_partials_t::<17, 21>(pos)
290
+ } else if order == 18 {
291
+ self.accel_and_partials_t::<18, 22>(pos)
292
+ } else if order == 19 {
293
+ self.accel_and_partials_t::<19, 23>(pos)
294
+ } else if order == 20 {
295
+ self.accel_and_partials_t::<20, 24>(pos)
296
+ } else if order == 21 {
297
+ self.accel_and_partials_t::<21, 25>(pos)
298
+ } else if order == 22 {
299
+ self.accel_and_partials_t::<22, 26>(pos)
300
+ } else if order == 23 {
301
+ self.accel_and_partials_t::<23, 27>(pos)
302
+ } else if order == 24 {
303
+ self.accel_and_partials_t::<24, 28>(pos)
304
+ } else if order == 25 {
305
+ self.accel_and_partials_t::<25, 29>(pos)
306
+ } else if order == 26 {
307
+ self.accel_and_partials_t::<26, 30>(pos)
308
+ } else if order == 27 {
309
+ self.accel_and_partials_t::<27, 31>(pos)
310
+ } else if order == 28 {
311
+ self.accel_and_partials_t::<28, 32>(pos)
312
+ } else if order == 29 {
313
+ self.accel_and_partials_t::<29, 33>(pos)
314
+ } else if order == 30 {
315
+ self.accel_and_partials_t::<30, 34>(pos)
316
+ } else if order == 31 {
317
+ self.accel_and_partials_t::<31, 35>(pos)
318
+ } else if order == 32 {
319
+ self.accel_and_partials_t::<32, 36>(pos)
320
+ } else if order == 33 {
321
+ self.accel_and_partials_t::<33, 37>(pos)
322
+ } else if order == 34 {
323
+ self.accel_and_partials_t::<34, 38>(pos)
324
+ } else if order == 35 {
325
+ self.accel_and_partials_t::<35, 39>(pos)
326
+ } else if order == 36 {
327
+ self.accel_and_partials_t::<36, 40>(pos)
328
+ } else if order == 37 {
329
+ self.accel_and_partials_t::<37, 15>(pos)
330
+ } else if order == 38 {
331
+ self.accel_and_partials_t::<38, 16>(pos)
332
+ } else if order == 39 {
333
+ self.accel_and_partials_t::<39, 17>(pos)
334
+ } else {
335
+ self.accel_and_partials_t::<40, 20>(pos)
240
336
  }
241
337
  }
242
338
 
@@ -18,21 +18,33 @@ pub trait RKAdaptive<const N: usize, const NI: usize> {
18
18
  /// (first compute of next iteration is same as last compute of last iteration)
19
19
  const FSAL: bool;
20
20
 
21
+ /// Interpolate densely calculated solution onto
22
+ /// values that are evenly spaced in "x"
23
+ ///
21
24
  fn interpolate<S: ODEState>(xinterp: f64, sol: &ODESolution<S>) -> ODEResult<S> {
22
25
  if sol.dense.is_none() {
23
26
  return Err(Box::new(ODEError::NoDenseOutputInSolution));
24
27
  }
28
+ if sol.x < xinterp {
29
+ return Err(Box::new(ODEError::InterpExceedsSolutionBounds));
30
+ }
25
31
  let dense = sol.dense.as_ref().unwrap();
26
- if xinterp < dense.x[0] || xinterp > sol.x {
32
+ if sol.x < dense.x[0] {
27
33
  return Err(Box::new(ODEError::InterpExceedsSolutionBounds));
28
34
  }
35
+
36
+ // We know indices are monotonically increasing, so only search from
37
+ // last found position in the array forward
29
38
  let mut idx = match dense.x.iter().position(|x| *x >= xinterp) {
30
39
  Some(v) => v,
31
- None => dense.x.len() - 1,
40
+ None => dense.x.len(),
32
41
  };
33
42
  if idx > 0 {
34
43
  idx -= 1;
35
44
  }
45
+
46
+ // t is fractional distance beween x at idx and idx+1
47
+ // and is in range [0,1]
36
48
  let t = (xinterp - dense.x[idx]) / dense.h[idx];
37
49
 
38
50
  // Compute interpolant coefficient as funciton of t
@@ -47,6 +59,7 @@ pub trait RKAdaptive<const N: usize, const NI: usize> {
47
59
  let bi: Vec<f64> = Self::BI
48
60
  .iter()
49
61
  .map(|biarr| {
62
+ // Coefficients multiply increasing powers of t
50
63
  let mut tj = 1.0;
51
64
  biarr.iter().fold(0.0, |acc, bij| {
52
65
  tj = tj * t;
@@ -60,14 +73,15 @@ pub trait RKAdaptive<const N: usize, const NI: usize> {
60
73
  //
61
74
  // This is equation(5) of:
62
75
  // https://link.springer.com/article/10.1023/A:1021190918665
63
- let yarr = dense.yprime[idx]
76
+ //
77
+ let mut y = dense.yprime[idx]
64
78
  .iter()
65
79
  .enumerate()
66
80
  .fold(dense.y[idx].clone() / dense.h[idx], |acc, (ix, k)| {
67
81
  acc + k.clone() * bi[ix]
68
82
  });
69
-
70
- Ok(yarr * dense.h[idx])
83
+ y = y * dense.h[idx];
84
+ Ok(y)
71
85
  }
72
86
 
73
87
  ///
@@ -77,8 +77,8 @@ mod tests {
77
77
  (0..100).for_each(|idx| {
78
78
  let x = idx as f64 * PI / testcount as f64;
79
79
  let interp = RKV87::interpolate(x, &sol).unwrap();
80
- assert!((interp[0] - x.cos()).abs() < 1e-10);
81
- assert!((interp[1] + x.sin()).abs() < 1e-10);
80
+ assert!((interp[0] - x.cos()).abs() < 1e-12);
81
+ assert!((interp[1] + x.sin()).abs() < 1e-12);
82
82
  });
83
83
 
84
84
  Ok(())
@@ -78,8 +78,8 @@ mod tests {
78
78
  (0..100).for_each(|idx| {
79
79
  let x = idx as f64 * PI / testcount as f64;
80
80
  let interp = RKV98::interpolate(x, &sol).unwrap();
81
- assert!((interp[0] - x.cos()).abs() < 1e-11);
82
- assert!((interp[1] + x.sin()).abs() < 1e-11);
81
+ assert!((interp[0] - x.cos()).abs() < 1e-12);
82
+ assert!((interp[1] + x.sin()).abs() < 1e-12);
83
83
  });
84
84
 
85
85
  Ok(())
@@ -752,10 +752,15 @@ mod tests {
752
752
  })
753
753
  .collect();
754
754
 
755
- let v0 = na::vector![2.47130555e03, 2.94682777e03, -5.34171918e02, 2.13018578e-02];
755
+ let v0 = na::vector![
756
+ 2.47130562e+03,
757
+ 2.94682753e+03,
758
+ -5.34172176e+02,
759
+ 2.32565692e-02
760
+ ];
756
761
 
757
762
  let state0 = na::vector![pgcrf[0][0], pgcrf[0][1], pgcrf[0][2], v0[0], v0[1], v0[2]];
758
- let satprops: SatPropertiesStatic = SatPropertiesStatic::new(0.0, 2.13018578e-2);
763
+ let satprops: SatPropertiesStatic = SatPropertiesStatic::new(0.0, v0[3]);
759
764
  let settings = PropSettings::default();
760
765
 
761
766
  let res = propagate(
@@ -769,10 +774,10 @@ mod tests {
769
774
 
770
775
  // We've propagated over a day; assert that the difference in position on all three coordinate axes
771
776
  // is less than 10 meters for all 5-minute intervals
772
- for iv in 0..(pgcrf.len() - 5) {
777
+ for iv in 0..(pgcrf.len()) {
773
778
  let interp_state = res.interp(&times[iv])?;
774
779
  for ix in 0..3 {
775
- assert!((pgcrf[iv][ix] - interp_state[ix]).abs() < 10.0);
780
+ assert!((pgcrf[iv][ix] - interp_state[ix]).abs() < 8.0);
776
781
  }
777
782
  }
778
783
 
@@ -77,7 +77,6 @@ use pyo3::types::{PyDict, PyString};
77
77
  /// and possibly dense output for interpolation
78
78
  ///
79
79
  ///
80
- ///
81
80
  #[pyfunction(signature=(pos, vel, start, **kwargs))]
82
81
  pub fn propagate(
83
82
  pos: &Bound<'_, np::PyArray1<f64>>,
@@ -4,8 +4,8 @@ use pyo3::types::{PyDict, PyList};
4
4
  use super::pyastrotime::ToTimeVec;
5
5
  use super::pytle::PyTLE;
6
6
  use crate::sgp4 as psgp4;
7
- use numpy::{PyArray, PyArray1};
8
7
  use numpy::PyArrayMethods;
8
+ use numpy::{PyArray, PyArray1};
9
9
 
10
10
  // Thin Python wrapper around SGP4 Error
11
11
  #[allow(non_camel_case_types)]
@@ -71,8 +71,6 @@ impl From<psgp4::SGP4Error> for PySGP4Error {
71
71
  }
72
72
  }
73
73
 
74
-
75
-
76
74
  /// """SGP-4 propagator for TLE
77
75
  ///
78
76
  /// Note:
@@ -95,7 +93,7 @@ impl From<psgp4::SGP4Error> for PySGP4Error {
95
93
  /// Returns:
96
94
  /// tuple[npt.ArrayLike[np.float64], npt.ArrayLike[np.float64]]: position and velocity in meters and meters/second, respectively, in the TEME frame at each of the "Ntime" input times and each of the "Ntle" tles
97
95
  ///
98
- ///
96
+ ///
99
97
  /// Example:
100
98
  ///
101
99
  ///
@@ -117,13 +115,17 @@ impl From<psgp4::SGP4Error> for PySGP4Error {
117
115
  /// >>>
118
116
  /// >>> # convert to ITRF coordinate object
119
117
  /// >>> coord = satkit.itrfcoord.from_vector(pitrf)
120
- /// >>>
118
+ /// >>>
121
119
  /// >>> # Print ITRF coordinate object location
122
120
  /// >>> print(coord)
123
121
  /// ITRFCoord(lat: -0.0363 deg, lon: -2.2438 deg, hae: 35799.51 km)
124
122
  #[pyfunction]
125
123
  #[pyo3(signature=(tle, time, **kwds))]
126
- pub fn sgp4(tle: &Bound<'_, PyAny>, time: &Bound<'_, PyAny>, kwds: Option<&Bound<'_, PyDict>>) -> PyResult<PyObject> {
124
+ pub fn sgp4(
125
+ tle: &Bound<'_, PyAny>,
126
+ time: &Bound<'_, PyAny>,
127
+ kwds: Option<&Bound<'_, PyDict>>,
128
+ ) -> PyResult<PyObject> {
127
129
  let mut output_err = false;
128
130
  let mut opsmode: OpsMode = OpsMode::afspc;
129
131
  let mut gravconst: GravConst = GravConst::wgs72;
@@ -161,11 +163,13 @@ pub fn sgp4(tle: &Bound<'_, PyAny>, time: &Bound<'_, PyAny>, kwds: Option<&Bound
161
163
  // hence the switch
162
164
  if output_err == false {
163
165
  Ok((
164
- PyArray1::from_slice_bound(py, r.data.as_slice()).as_gil_ref()
166
+ PyArray1::from_slice_bound(py, r.data.as_slice())
167
+ .as_gil_ref()
165
168
  .reshape(dims.clone())
166
169
  .unwrap()
167
170
  .to_object(py),
168
- PyArray1::from_slice_bound(py, v.data.as_slice()).as_gil_ref()
171
+ PyArray1::from_slice_bound(py, v.data.as_slice())
172
+ .as_gil_ref()
169
173
  .reshape(dims)
170
174
  .unwrap()
171
175
  .to_object(py),
@@ -173,10 +177,12 @@ pub fn sgp4(tle: &Bound<'_, PyAny>, time: &Bound<'_, PyAny>, kwds: Option<&Bound
173
177
  .to_object(py))
174
178
  } else {
175
179
  Ok((
176
- PyArray1::from_slice_bound(py, r.data.as_slice()).as_gil_ref()
180
+ PyArray1::from_slice_bound(py, r.data.as_slice())
181
+ .as_gil_ref()
177
182
  .reshape(dims.clone())
178
183
  .unwrap(),
179
- PyArray1::from_slice_bound(py, v.data.as_slice()).as_gil_ref()
184
+ PyArray1::from_slice_bound(py, v.data.as_slice())
185
+ .as_gil_ref()
180
186
  .reshape(dims)
181
187
  .unwrap(),
182
188
  PyArray::from_owned_object_array_bound(
@@ -200,9 +206,9 @@ pub fn sgp4(tle: &Bound<'_, PyAny>, time: &Bound<'_, PyAny>, kwds: Option<&Bound
200
206
 
201
207
  pyo3::Python::with_gil(|py| -> PyResult<PyObject> {
202
208
  let n = tles.len() * tmarray.len() * 3;
203
-
209
+
204
210
  let parr = PyArray1::zeros_bound(py, [n], false);
205
- let varr= PyArray1::zeros_bound(py, [n], false);
211
+ let varr = PyArray1::zeros_bound(py, [n], false);
206
212
  let ntimes = tmarray.len();
207
213
 
208
214
  // I'd prefer to create this uninitialized, which would probably be a bit faster,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes