arepytools 1.7.6__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 (119) hide show
  1. arepytools-1.7.6/.gitignore +35 -0
  2. arepytools-1.7.6/LICENSE.txt +21 -0
  3. arepytools-1.7.6/PKG-INFO +64 -0
  4. arepytools-1.7.6/README.md +2 -0
  5. arepytools-1.7.6/arepytools/__init__.py +8 -0
  6. arepytools-1.7.6/arepytools/constants.py +109 -0
  7. arepytools-1.7.6/arepytools/geometry/__init__.py +6 -0
  8. arepytools-1.7.6/arepytools/geometry/_geodetic.py +107 -0
  9. arepytools-1.7.6/arepytools/geometry/_interpolator.py +210 -0
  10. arepytools-1.7.6/arepytools/geometry/anx_time.py +134 -0
  11. arepytools-1.7.6/arepytools/geometry/attitude_utils.py +88 -0
  12. arepytools-1.7.6/arepytools/geometry/conversions.py +120 -0
  13. arepytools-1.7.6/arepytools/geometry/curve.py +344 -0
  14. arepytools-1.7.6/arepytools/geometry/curve_protocols.py +113 -0
  15. arepytools-1.7.6/arepytools/geometry/direct_geocoding.py +1062 -0
  16. arepytools-1.7.6/arepytools/geometry/ellipsoid.py +233 -0
  17. arepytools-1.7.6/arepytools/geometry/generalsarattitude.py +528 -0
  18. arepytools-1.7.6/arepytools/geometry/generalsarorbit.py +831 -0
  19. arepytools-1.7.6/arepytools/geometry/geometric_functions.py +493 -0
  20. arepytools-1.7.6/arepytools/geometry/inverse_geocoding.py +315 -0
  21. arepytools-1.7.6/arepytools/geometry/inverse_geocoding_core.py +806 -0
  22. arepytools-1.7.6/arepytools/geometry/orbit.py +160 -0
  23. arepytools-1.7.6/arepytools/geometry/reference_frames.py +289 -0
  24. arepytools-1.7.6/arepytools/geometry/rotation.py +144 -0
  25. arepytools-1.7.6/arepytools/geometry/wgs84.py +22 -0
  26. arepytools-1.7.6/arepytools/io/__init__.py +59 -0
  27. arepytools-1.7.6/arepytools/io/channel.py +513 -0
  28. arepytools-1.7.6/arepytools/io/channel_iteration.py +147 -0
  29. arepytools-1.7.6/arepytools/io/create_orbit.py +35 -0
  30. arepytools-1.7.6/arepytools/io/debugio.py +65 -0
  31. arepytools-1.7.6/arepytools/io/io_support.py +683 -0
  32. arepytools-1.7.6/arepytools/io/manifest.py +100 -0
  33. arepytools-1.7.6/arepytools/io/metadata.py +2679 -0
  34. arepytools-1.7.6/arepytools/io/parsing/__init__.py +6 -0
  35. arepytools-1.7.6/arepytools/io/parsing/metadata_models/__init__.py +93 -0
  36. arepytools-1.7.6/arepytools/io/parsing/metadata_models/aresys_generic_metadata.py +222 -0
  37. arepytools-1.7.6/arepytools/io/parsing/metadata_models/aresys_types.py +3391 -0
  38. arepytools-1.7.6/arepytools/io/parsing/metadata_parsing.py +49 -0
  39. arepytools-1.7.6/arepytools/io/parsing/parsing.py +70 -0
  40. arepytools-1.7.6/arepytools/io/parsing/translate.py +1701 -0
  41. arepytools-1.7.6/arepytools/io/point_target_binary.py +406 -0
  42. arepytools-1.7.6/arepytools/io/point_target_file.py +299 -0
  43. arepytools-1.7.6/arepytools/io/point_target_models/__init__.py +25 -0
  44. arepytools-1.7.6/arepytools/io/point_target_models/aresys_point_target_models.py +181 -0
  45. arepytools-1.7.6/arepytools/io/productfolder.py +467 -0
  46. arepytools-1.7.6/arepytools/io/productfolder2.py +538 -0
  47. arepytools-1.7.6/arepytools/io/productfolder_layout.py +184 -0
  48. arepytools-1.7.6/arepytools/math/__init__.py +6 -0
  49. arepytools-1.7.6/arepytools/math/axis.py +434 -0
  50. arepytools-1.7.6/arepytools/math/genericpoly.py +155 -0
  51. arepytools-1.7.6/arepytools/timing/__init__.py +6 -0
  52. arepytools-1.7.6/arepytools/timing/conversions.py +54 -0
  53. arepytools-1.7.6/arepytools/timing/precisedatetime.py +863 -0
  54. arepytools-1.7.6/docs/source/_static/css/custom.css +9 -0
  55. arepytools-1.7.6/docs/source/_static/icons/aresys_logo.svg +16 -0
  56. arepytools-1.7.6/docs/source/changelog.rst +370 -0
  57. arepytools-1.7.6/docs/source/conf.py +227 -0
  58. arepytools-1.7.6/docs/source/index.rst +26 -0
  59. arepytools-1.7.6/docs/source/install.rst +21 -0
  60. arepytools-1.7.6/docs/source/reference/api/geometry.rst +28 -0
  61. arepytools-1.7.6/docs/source/reference/api/index.rst +11 -0
  62. arepytools-1.7.6/docs/source/reference/api/io.rst +24 -0
  63. arepytools-1.7.6/docs/source/reference/api/math.rst +6 -0
  64. arepytools-1.7.6/docs/source/reference/api/others.rst +4 -0
  65. arepytools-1.7.6/docs/source/reference/api/timing.rst +6 -0
  66. arepytools-1.7.6/docs/source/user_manual/index.rst +14 -0
  67. arepytools-1.7.6/docs/source/user_manual/input_output_module.rst +357 -0
  68. arepytools-1.7.6/docs/source/user_manual/new_code.txt +39 -0
  69. arepytools-1.7.6/docs/source/user_manual/new_code_orb.txt +98 -0
  70. arepytools-1.7.6/docs/source/user_manual/new_orbit_and_anx.rst +129 -0
  71. arepytools-1.7.6/docs/source/user_manual/old_code.txt +90 -0
  72. arepytools-1.7.6/docs/source/user_manual/old_code_orb.txt +73 -0
  73. arepytools-1.7.6/docs/source/user_manual/quickstart/index.rst +14 -0
  74. arepytools-1.7.6/docs/source/user_manual/quickstart/qs_general_sar_orbit.ipynb +240 -0
  75. arepytools-1.7.6/docs/source/user_manual/quickstart/qs_generic_poly.ipynb +147 -0
  76. arepytools-1.7.6/docs/source/user_manual/quickstart/qs_interpolated_orbit.ipynb +284 -0
  77. arepytools-1.7.6/docs/source/user_manual/quickstart/qs_precise_date_time.ipynb +116 -0
  78. arepytools-1.7.6/docs/source/user_manual/quickstart/qs_write_and_read_product_folder.ipynb +226 -0
  79. arepytools-1.7.6/pyproject.toml +100 -0
  80. arepytools-1.7.6/tests/__init__.py +6 -0
  81. arepytools-1.7.6/tests/geometry/__init__.py +6 -0
  82. arepytools-1.7.6/tests/geometry/test_anx_time.py +247 -0
  83. arepytools-1.7.6/tests/geometry/test_attitude_utils.py +171 -0
  84. arepytools-1.7.6/tests/geometry/test_conversions.py +54 -0
  85. arepytools-1.7.6/tests/geometry/test_curve.py +1056 -0
  86. arepytools-1.7.6/tests/geometry/test_direct_geocoding_attitude.py +1170 -0
  87. arepytools-1.7.6/tests/geometry/test_direct_geocoding_bistatic.py +1345 -0
  88. arepytools-1.7.6/tests/geometry/test_direct_geocoding_monostatic.py +1375 -0
  89. arepytools-1.7.6/tests/geometry/test_direct_geocoding_with_looks.py +168 -0
  90. arepytools-1.7.6/tests/geometry/test_ellipsoid.py +179 -0
  91. arepytools-1.7.6/tests/geometry/test_generalsarattitude.py +373 -0
  92. arepytools-1.7.6/tests/geometry/test_generalsarorbit.py +548 -0
  93. arepytools-1.7.6/tests/geometry/test_geodetic.py +67 -0
  94. arepytools-1.7.6/tests/geometry/test_geometric_functions.py +1337 -0
  95. arepytools-1.7.6/tests/geometry/test_inverse_geocoding_attitude.py +1039 -0
  96. arepytools-1.7.6/tests/geometry/test_inverse_geocoding_bistatic.py +2068 -0
  97. arepytools-1.7.6/tests/geometry/test_inverse_geocoding_monostatic.py +1472 -0
  98. arepytools-1.7.6/tests/geometry/test_orbit.py +436 -0
  99. arepytools-1.7.6/tests/geometry/test_reference_frames.py +215 -0
  100. arepytools-1.7.6/tests/geometry/test_rotation.py +264 -0
  101. arepytools-1.7.6/tests/io_/__init__.py +6 -0
  102. arepytools-1.7.6/tests/io_/parsing/__init__.py +6 -0
  103. arepytools-1.7.6/tests/io_/parsing/test_metadata_parsing.py +352 -0
  104. arepytools-1.7.6/tests/io_/parsing/test_translate.py +2114 -0
  105. arepytools-1.7.6/tests/io_/test_channel.py +189 -0
  106. arepytools-1.7.6/tests/io_/test_channel_iteration.py +341 -0
  107. arepytools-1.7.6/tests/io_/test_io_support.py +251 -0
  108. arepytools-1.7.6/tests/io_/test_manifest.py +81 -0
  109. arepytools-1.7.6/tests/io_/test_point_target_binary.py +297 -0
  110. arepytools-1.7.6/tests/io_/test_point_target_file.py +258 -0
  111. arepytools-1.7.6/tests/io_/test_product_folder_layout.py +168 -0
  112. arepytools-1.7.6/tests/io_/test_productfolder.py +382 -0
  113. arepytools-1.7.6/tests/io_/test_productfolder2.py +642 -0
  114. arepytools-1.7.6/tests/math_/__init__.py +6 -0
  115. arepytools-1.7.6/tests/math_/test_axis.py +306 -0
  116. arepytools-1.7.6/tests/math_/test_generic_poly.py +266 -0
  117. arepytools-1.7.6/tests/timing/__init__.py +6 -0
  118. arepytools-1.7.6/tests/timing/test_conversions.py +35 -0
  119. arepytools-1.7.6/tests/timing/test_precisedatetime.py +487 -0
@@ -0,0 +1,35 @@
1
+ # Byte-compiled / optimized
2
+ __pycache__
3
+ *.py[co]
4
+
5
+ # Distribution / packaging
6
+ build/
7
+ _build/
8
+ dist/
9
+ *.egg-info/
10
+ .ipynb_checkpoints/
11
+ arepytools/meta.yaml
12
+ conda_build_dir
13
+
14
+ # Pyenv
15
+ .python-version
16
+
17
+ # VS Code project settings
18
+ .vscode
19
+
20
+ # PyCharm project settings
21
+ .idea
22
+
23
+ # Spyder project settings
24
+ .spyproject
25
+
26
+ # Environments
27
+ venv*
28
+ .venv
29
+
30
+ # Other
31
+ *.bak
32
+ .coverage
33
+ htmlcov
34
+ xsd/*xsd
35
+ .pdm*
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (C) Aresys S.r.l. <info@aresys.it>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: arepytools
3
+ Version: 1.7.6
4
+ Summary: ArePyTools is the Aresys Python toolset for SAR data processing.
5
+ Author-email: "Aresys S.R.L." <info@aresys.it>
6
+ License: MIT License
7
+
8
+ Copyright (C) Aresys S.r.l. <info@aresys.it>
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE.txt
28
+ Classifier: License :: OSI Approved :: MIT License
29
+ Classifier: Operating System :: OS Independent
30
+ Classifier: Programming Language :: Python
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3 :: Only
33
+ Classifier: Programming Language :: Python :: 3.9
34
+ Classifier: Programming Language :: Python :: 3.10
35
+ Classifier: Programming Language :: Python :: 3.11
36
+ Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Topic :: Software Development :: Libraries
38
+ Classifier: Topic :: Utilities
39
+ Requires-Python: >=3.9
40
+ Requires-Dist: lxml
41
+ Requires-Dist: numpy>=1.20
42
+ Requires-Dist: python-dateutil
43
+ Requires-Dist: scipy
44
+ Requires-Dist: xsdata
45
+ Provides-Extra: bench
46
+ Requires-Dist: arepyextras-benchmark; extra == 'bench'
47
+ Provides-Extra: dev
48
+ Requires-Dist: nox; extra == 'dev'
49
+ Requires-Dist: pylint; extra == 'dev'
50
+ Requires-Dist: ruff; extra == 'dev'
51
+ Provides-Extra: doc
52
+ Requires-Dist: ipython; extra == 'doc'
53
+ Requires-Dist: nbsphinx; extra == 'doc'
54
+ Requires-Dist: pydata-sphinx-theme; extra == 'doc'
55
+ Requires-Dist: sphinx-copybutton; extra == 'doc'
56
+ Requires-Dist: sphinx-design; extra == 'doc'
57
+ Requires-Dist: sphinx<8.2; extra == 'doc'
58
+ Provides-Extra: test
59
+ Requires-Dist: coverage; extra == 'test'
60
+ Requires-Dist: unittest-xml-reporting; extra == 'test'
61
+ Description-Content-Type: text/markdown
62
+
63
+ # ArePyTools
64
+
@@ -0,0 +1,2 @@
1
+ # ArePyTools
2
+
@@ -0,0 +1,8 @@
1
+ # SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ ArePyTools: the Python toolkit for SAR data processing
6
+ """
7
+
8
+ __version__ = "1.7.6"
@@ -0,0 +1,109 @@
1
+ # SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
2
+ # SPDX-License-Identifier: MIT
3
+
4
+
5
+ """
6
+ **Deprecated** Constants module
7
+ -------------------------------
8
+
9
+ .. deprecated:: v1.7.0
10
+ Importing constants is deprecated.
11
+ Use scipy.constants.speed_of_light for LIGHT_SPEED.
12
+
13
+ Examples
14
+ --------
15
+ >>> import arepytools.constants as cst
16
+ >>> print(cst.LIGHT_SPEED)
17
+ 299792458.0
18
+ """
19
+ import warnings
20
+
21
+ import scipy
22
+
23
+ warnings.warn(
24
+ "arepytools.constants module is deprecated. Instead of importing LIGHT_SPEED use 'from scipy.constants import speed_of_light'",
25
+ DeprecationWarning,
26
+ stacklevel=2,
27
+ )
28
+
29
+ SECONDS_IN_A_DAY = 86164.09054
30
+ """
31
+ Number of seconds in a day
32
+ """
33
+
34
+ LIGHT_SPEED = scipy.constants.speed_of_light
35
+ """
36
+ Speed of light in vacuum (m/s).
37
+ """
38
+
39
+ # Units of measure
40
+ SECOND_STR = "s"
41
+ """
42
+ Second symbol.
43
+ """
44
+
45
+ HERTZ_STR = "Hz"
46
+ """
47
+ Hertz symbol.
48
+ """
49
+
50
+ JOULE_STR = "j"
51
+ """
52
+ Joule symbol.
53
+ """
54
+
55
+ DEGREE_STR = "deg"
56
+ """
57
+ Degree symbol.
58
+ """
59
+
60
+ RAD_STR = "rad"
61
+ """
62
+ Radian symbol.
63
+ """
64
+
65
+ UTC_STR = "Utc"
66
+ """
67
+ Coordinated Universal Time abbreviation.
68
+ """
69
+
70
+ METER_STR = "m"
71
+ """
72
+ Meter symbol.
73
+ """
74
+
75
+ # Metric prefixes
76
+ KILO = 1e3
77
+ """
78
+ Number of units in one kilounit (kilounits-to-units conversion factor).
79
+ """
80
+
81
+ MEGA = KILO**2
82
+ """
83
+ Number of units in one megaunit (megaunits-to-units conversion factor).
84
+ """
85
+
86
+ GIGA = KILO**3
87
+ """
88
+ Number of units in one gigaunit (gigaunits-to-units conversion factor).
89
+ """
90
+
91
+ MILLI = KILO**-1
92
+ """
93
+ Number of units in one milliunit (milliunits-to-units conversion factor).
94
+ """
95
+
96
+ MICRO = KILO**-2
97
+ """
98
+ Number of units in one microunit (microunits-to-units conversion factor).
99
+ """
100
+
101
+ NANO = KILO**-3
102
+ """
103
+ Number of units in one nanounit (nanounits-to-units conversion factor).
104
+ """
105
+
106
+ PICO = KILO**-4
107
+ """
108
+ Number of units in one picounit (picounits-to-units conversion factor).
109
+ """
@@ -0,0 +1,6 @@
1
+ # SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ Geometry management package
6
+ """
@@ -0,0 +1,107 @@
1
+ # SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ Geodetic module
6
+ ---------------
7
+ """
8
+
9
+ import numpy as np
10
+ import numpy.typing as npt
11
+
12
+ from arepytools.geometry.conversions import llh2xyz, xyz2llh
13
+ from arepytools.geometry.ellipsoid import WGS84
14
+
15
+ _semi_axis_ratio_sqr = WGS84.semi_axes_ratio_min_max**2
16
+ _major_semi_axis_sqr = WGS84.semi_major_axis**2
17
+ _minor_semi_axis_sqr = WGS84.semi_minor_axis**2
18
+
19
+
20
+ def _ze(x: float, y: float) -> float:
21
+ eps = 1.0e-21
22
+
23
+ return np.sqrt(_minor_semi_axis_sqr - _semi_axis_ratio_sqr * (x**2 + y**2)) + eps
24
+
25
+
26
+ def _ze_x(x: float, y: float) -> float:
27
+ return -WGS84.semi_axes_ratio_min_max * x / _ze(x, y)
28
+
29
+
30
+ def _ze_xy(x: float, y: float) -> float:
31
+ # pylint: disable-next=arguments-out-of-order
32
+ return (_semi_axis_ratio_sqr * x) / _ze(x, y) ** 2 * _ze_x(y, x)
33
+
34
+
35
+ def _ze_xx(x: float, y: float) -> float:
36
+ return -_semi_axis_ratio_sqr / _ze(x, y) + (_semi_axis_ratio_sqr * x) / _ze(x, y) ** 2 * _ze_x(x, y)
37
+
38
+
39
+ def _compute_geodetic_jacobian(point: np.ndarray, sensor_position: np.ndarray) -> np.ndarray:
40
+ jac = np.empty(shape=(3, 3), dtype=float)
41
+
42
+ zed_diff = point[2] - sensor_position[2]
43
+
44
+ jac[0][0] = 2.0 * point[0] / _major_semi_axis_sqr
45
+ jac[0][1] = 2.0 * point[1] / _major_semi_axis_sqr
46
+ jac[0][2] = 2.0 * point[2] / _minor_semi_axis_sqr
47
+ jac[1][0] = 1.0 + zed_diff * _ze_xx(point[0], point[1])
48
+ jac[1][1] = zed_diff * _ze_xy(point[0], point[1])
49
+ jac[1][2] = _ze_x(point[0], point[1])
50
+ jac[2][0] = jac[1][1]
51
+ jac[2][1] = 1.0 + zed_diff * _ze_xx(point[1], point[0])
52
+ jac[2][2] = _ze_x(point[1], point[0])
53
+
54
+ return jac
55
+
56
+
57
+ def _compute_geodetic_rhs(point: npt.NDArray[np.floating], sensor_position: npt.NDArray[np.floating]) -> np.ndarray:
58
+ rhs = np.empty(shape=(3,), dtype=float)
59
+ los = point - sensor_position
60
+ rhs[0] = (point[0] ** 2 + point[1] ** 2) / _major_semi_axis_sqr + point[2] ** 2 / _minor_semi_axis_sqr - 1
61
+ rhs[1] = los[0] + los[2] * _ze_x(point[0], point[1])
62
+ rhs[2] = los[1] + los[2] * _ze_x(point[1], point[0])
63
+ return rhs
64
+
65
+
66
+ def compute_geodetic_point(sensor_positions: npt.ArrayLike) -> np.ndarray:
67
+ """Compute the geodetic point that corresponds to a sensor position
68
+
69
+ :param sensor_position: sensor position array like with shape (3,) or (N, 3)
70
+
71
+ :return: geodetic point as a (3,) or (N, 3) numpy array
72
+ """
73
+ sensor_positions = np.asarray(sensor_positions)
74
+
75
+ if sensor_positions.ndim > 2 or sensor_positions.shape[-1] != 3:
76
+ raise ValueError(f"sensor_positions has invalid shape: {sensor_positions.shape}, it should be (3,) or (N, 3)")
77
+
78
+ sensor_positions = sensor_positions.copy()
79
+ change_sign = sensor_positions[..., 2] < 0
80
+ sensor_positions[change_sign, 2] *= -1
81
+
82
+ geodetic_points = xyz2llh(sensor_positions.T)
83
+ geodetic_points[2, ...] = 0
84
+ geodetic_points = llh2xyz(geodetic_points).T.reshape(sensor_positions.shape)
85
+
86
+ for sensor_position, point in zip(
87
+ sensor_positions.reshape((-1, 3)),
88
+ geodetic_points.reshape((-1, 3)),
89
+ ):
90
+ increment_norm_threshold = 1e-9
91
+ max_num_iterations = 10
92
+ for _ in range(max_num_iterations):
93
+ jacobian = _compute_geodetic_jacobian(point[:], sensor_position)
94
+ rhs = _compute_geodetic_rhs(point[:], sensor_position)
95
+
96
+ increment = -np.linalg.solve(jacobian, rhs)
97
+
98
+ increment_norm = np.linalg.norm(increment)
99
+
100
+ point[:] += increment
101
+
102
+ if increment_norm < increment_norm_threshold:
103
+ break
104
+
105
+ geodetic_points[change_sign, 2] *= -1
106
+
107
+ return geodetic_points
@@ -0,0 +1,210 @@
1
+ # SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ """
5
+ Geometry interpolator module
6
+ ----------------------------
7
+ """
8
+
9
+ import numpy as np
10
+
11
+
12
+ def check_eval_function_input(fun):
13
+ """
14
+ Interpolator eval function input checker
15
+ """
16
+
17
+ def decorated_fun(self, *args_f, **kwargs_f):
18
+ if isinstance(args_f[0], (list, np.ndarray)):
19
+ interpolation_axis = np.asarray(args_f[0])
20
+ else:
21
+ interpolation_axis = np.full((1,), args_f[0])
22
+ return fun(self, interpolation_axis, *args_f[1:], **kwargs_f)
23
+
24
+ return decorated_fun
25
+
26
+
27
+ class GeometryInterpolator:
28
+ """
29
+ Internal class for handling interpolation in GeneralSarOrbit and in GeneralSarAttitude
30
+ It interpolates vector-valued functions defined on a given axis
31
+ """
32
+
33
+ _POLYNOMIAL_ORDER = 4
34
+ _DATA_POINTS_PER_POLY = _POLYNOMIAL_ORDER + 1
35
+ _POLY_CENTER_REL_INTERVAL_INDEX = _DATA_POINTS_PER_POLY // 2
36
+
37
+ @classmethod
38
+ def get_min_num_of_data_points(cls):
39
+ """Return the minimum number of data points needed by the interpolator
40
+
41
+ :return: minimum number of data points"""
42
+ return cls._DATA_POINTS_PER_POLY
43
+
44
+ @classmethod
45
+ def get_polynomial_order(cls):
46
+ """Return the order of the interpolation polynomial
47
+
48
+ :return: order of the interpolation polynomial
49
+ """
50
+ return cls._POLYNOMIAL_ORDER
51
+
52
+ def __init__(self, data_axis, data):
53
+ """Initialize the object as interpolator of the input data defined on the specified axis
54
+
55
+ :param data_axis: the axis of length N on which the data is defined
56
+ :param data: data defined as MxN array of M-dimensional values (e.g. state vectors or attitude angles)
57
+ """
58
+ self._data_axis = data_axis
59
+ self._num_of_data_components = data.shape[0]
60
+ self._num_of_polynomials = self._data_axis.size - self._POLYNOMIAL_ORDER
61
+
62
+ # Matrix of polynomials
63
+ self._pos = np.ndarray((self._num_of_polynomials, self._num_of_data_components), dtype=np.poly1d)
64
+ self._vel = np.ndarray((self._num_of_polynomials, self._num_of_data_components), dtype=np.poly1d)
65
+ self._acc = np.ndarray((self._num_of_polynomials, self._num_of_data_components), dtype=np.poly1d)
66
+ # Relative azimuth axis [s]
67
+ relative_axis = self._data_axis.get_relative_array()
68
+ self._data_axis_array = self._data_axis.get_array()
69
+ for poly_index in range(self._num_of_polynomials):
70
+ # Each polynomial domain is defined by the poly_axis
71
+ poly_axis = relative_axis[poly_index : poly_index + self._DATA_POINTS_PER_POLY]
72
+
73
+ # Each polynomial relative axis starts at the start of the interval:
74
+ # P(t) = c0 + c1 * (t-poly_time_axis[0]) + ...
75
+ vandermonde_matrix = np.vander((poly_axis - poly_axis[0]).astype(float))
76
+
77
+ # Computation of polynomial coefficients
78
+ for component_index in range(self._num_of_data_components):
79
+ values = data[
80
+ component_index,
81
+ poly_index : poly_index + self._DATA_POINTS_PER_POLY,
82
+ ]
83
+ coefficients = np.linalg.solve(vandermonde_matrix, values).tolist()
84
+ position = np.poly1d(coefficients)
85
+ self._pos[poly_index, component_index] = position
86
+ self._vel[poly_index, component_index] = np.polyder(position, 1)
87
+ self._acc[poly_index, component_index] = np.polyder(position, 2)
88
+
89
+ @check_eval_function_input
90
+ def eval(self, interpolation_axis, interval_indexes, components_to_interpolate=None):
91
+ """Interpolate the data on the given interpolation axis
92
+
93
+ :param interpolation_axis: interpolation axis
94
+ :param interval_indexes: vector or single scalar or None. Denotes the intervals where the given interpolation
95
+ points are expected
96
+ :param components_to_interpolate: list of the components to interpolate, identified by their
97
+ index [optional]
98
+
99
+ :return: CxP array of interpolated values, where C is the number of interpolated components and P is the length
100
+ of the interpolation axis
101
+ """
102
+ return self._eval(interpolation_axis, interval_indexes, self._pos, components_to_interpolate)
103
+
104
+ @check_eval_function_input
105
+ def eval_first_derivative(self, interpolation_axis, interval_indexes, components_to_interpolate=None):
106
+ """Interpolate the first derivative of the data on the given interpolation axis
107
+
108
+ :param interpolation_axis: interpolation axis
109
+ :param interval_indexes: vector or single scalar or None. Denotes the interval where the given interpolation
110
+ points are expected
111
+ :param components_to_interpolate: list of the components to interpolate, identified by their index [optional]
112
+
113
+ :return: CxP array of interpolated values, where C is the number of interpolated components and P is the length
114
+ of the interpolation axis
115
+ """
116
+ return self._eval(interpolation_axis, interval_indexes, self._vel, components_to_interpolate)
117
+
118
+ @check_eval_function_input
119
+ def eval_second_derivative(self, interpolation_axis, interval_indexes, components_to_interpolate=None):
120
+ """Interpolate the second derivative of the data on the given interpolation axis
121
+
122
+ :param interpolation_axis: interpolation axis
123
+ :param interval_indexes: vector or single scalar or None. Denotes the interval where the given interpolation
124
+ points are expected
125
+ :param components_to_interpolate: list of the components to interpolate, identified by their index [optional]
126
+
127
+ :return: CxP array of interpolated values, where C is the number of interpolated components and P is the length
128
+ of the interpolation axis
129
+ """
130
+
131
+ return self._eval(interpolation_axis, interval_indexes, self._acc, components_to_interpolate)
132
+
133
+ def _eval(
134
+ self,
135
+ interpolation_axis,
136
+ interval_indexes,
137
+ polynomial,
138
+ components_to_interpolate=None,
139
+ ):
140
+ """Interpolate the data on the given interpolation axis by evaluating interpolation
141
+ polynomials (or a their modified version) on the interpolation axis
142
+
143
+ :param interpolation_axis: interpolation axis
144
+ :param interval_indexes: vector or single scalar or None. Denotes the interval where the given time is expected
145
+ :param polynomial: interpolation polynomials to evaluate
146
+ :param components_to_interpolate: list of the components to interpolate, identified by their index [optional]
147
+
148
+ :return: CxP array of interpolated values, where C is the number of interpolated components and P is the length
149
+ of the interpolation axis
150
+ """
151
+
152
+ if components_to_interpolate is None:
153
+ components_to_interpolate = range(self._num_of_data_components)
154
+
155
+ poly_indexes = self.get_polynomial_index(
156
+ interpolation_points=interpolation_axis, interval_indexes=interval_indexes
157
+ )
158
+
159
+ interpolation_results = np.zeros((len(components_to_interpolate), interpolation_axis.size))
160
+
161
+ for interpolation_point_index, (poly_index, interpolation_point) in enumerate(
162
+ zip(poly_indexes, interpolation_axis)
163
+ ):
164
+ interpolation_point_poly_rel = interpolation_point - self._data_axis_array[poly_index]
165
+
166
+ for out_component_index, component_index in enumerate(components_to_interpolate):
167
+ interpolation_results[out_component_index, interpolation_point_index] = polynomial[
168
+ poly_index, component_index
169
+ ](interpolation_point_poly_rel)
170
+
171
+ return interpolation_results
172
+
173
+ def get_interval_index(self, interpolation_points):
174
+ """Get the indexes of the intervals where the interpolation_points lie
175
+
176
+ :param interpolation_points: vector of required interpolation points
177
+
178
+ :return: array of interval indexes
179
+ """
180
+ return self._data_axis.get_interval_id(interpolation_points)
181
+
182
+ def get_polynomial_index(self, interpolation_points=None, interval_indexes=None):
183
+ """Get the indexes of the polynomials to be used for interpolation
184
+
185
+ :param interpolation_points: vector of required interpolation points
186
+ :param interval_indexes: interval indexes of the interpolation points
187
+
188
+ :return: array of polynomial indexes
189
+
190
+ If interpolation points are specified, the positions on the interpolation axis are computed internally to find
191
+ the corresponding polynomial indexes;
192
+ If the interval indexes of the interpolation points on the axis are given, the polynomial indexes are computed
193
+ directly (ignoring the interpolation points).
194
+ """
195
+ if interval_indexes is None:
196
+ if interpolation_points is not None:
197
+ interval_indexes = self._data_axis.get_interval_id(interpolation_points)
198
+ else:
199
+ raise RuntimeError("Specify either interpolation points or interval indexes")
200
+ if isinstance(interval_indexes, int):
201
+ interval_indexes = np.asarray([interval_indexes])
202
+ return np.clip(
203
+ interval_indexes - self._POLY_CENTER_REL_INTERVAL_INDEX,
204
+ 0,
205
+ self._num_of_polynomials - 1,
206
+ )
207
+
208
+ @property
209
+ def num_of_polynomials(self):
210
+ return self._num_of_polynomials