arepytools 1.7.6__py3-none-any.whl
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.
- arepytools/__init__.py +8 -0
- arepytools/constants.py +109 -0
- arepytools/geometry/__init__.py +6 -0
- arepytools/geometry/_geodetic.py +107 -0
- arepytools/geometry/_interpolator.py +210 -0
- arepytools/geometry/anx_time.py +134 -0
- arepytools/geometry/attitude_utils.py +88 -0
- arepytools/geometry/conversions.py +120 -0
- arepytools/geometry/curve.py +344 -0
- arepytools/geometry/curve_protocols.py +113 -0
- arepytools/geometry/direct_geocoding.py +1062 -0
- arepytools/geometry/ellipsoid.py +233 -0
- arepytools/geometry/generalsarattitude.py +528 -0
- arepytools/geometry/generalsarorbit.py +831 -0
- arepytools/geometry/geometric_functions.py +493 -0
- arepytools/geometry/inverse_geocoding.py +315 -0
- arepytools/geometry/inverse_geocoding_core.py +806 -0
- arepytools/geometry/orbit.py +160 -0
- arepytools/geometry/reference_frames.py +289 -0
- arepytools/geometry/rotation.py +144 -0
- arepytools/geometry/wgs84.py +22 -0
- arepytools/io/__init__.py +59 -0
- arepytools/io/channel.py +513 -0
- arepytools/io/channel_iteration.py +147 -0
- arepytools/io/create_orbit.py +35 -0
- arepytools/io/debugio.py +65 -0
- arepytools/io/io_support.py +683 -0
- arepytools/io/manifest.py +100 -0
- arepytools/io/metadata.py +2679 -0
- arepytools/io/parsing/__init__.py +6 -0
- arepytools/io/parsing/metadata_models/__init__.py +93 -0
- arepytools/io/parsing/metadata_models/aresys_generic_metadata.py +222 -0
- arepytools/io/parsing/metadata_models/aresys_types.py +3391 -0
- arepytools/io/parsing/metadata_parsing.py +49 -0
- arepytools/io/parsing/parsing.py +70 -0
- arepytools/io/parsing/translate.py +1701 -0
- arepytools/io/point_target_binary.py +406 -0
- arepytools/io/point_target_file.py +299 -0
- arepytools/io/point_target_models/__init__.py +25 -0
- arepytools/io/point_target_models/aresys_point_target_models.py +181 -0
- arepytools/io/productfolder.py +467 -0
- arepytools/io/productfolder2.py +538 -0
- arepytools/io/productfolder_layout.py +184 -0
- arepytools/math/__init__.py +6 -0
- arepytools/math/axis.py +434 -0
- arepytools/math/genericpoly.py +155 -0
- arepytools/timing/__init__.py +6 -0
- arepytools/timing/conversions.py +54 -0
- arepytools/timing/precisedatetime.py +863 -0
- arepytools-1.7.6.dist-info/METADATA +64 -0
- arepytools-1.7.6.dist-info/RECORD +53 -0
- arepytools-1.7.6.dist-info/WHEEL +4 -0
- arepytools-1.7.6.dist-info/licenses/LICENSE.txt +21 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Coordinate conversions module
|
|
6
|
+
-----------------------------
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from arepytools.geometry.ellipsoid import WGS84
|
|
14
|
+
|
|
15
|
+
_NUMBER_OF_ITERATIONS = 5
|
|
16
|
+
|
|
17
|
+
_A_MAX_SQUARE_MINUS_A_MIN_SQUARE_DIVIDED_BY_A_MAX = (
|
|
18
|
+
WGS84.semi_major_axis**2 - WGS84.semi_minor_axis**2
|
|
19
|
+
) / WGS84.semi_major_axis
|
|
20
|
+
_A_MIN_TIMES_EP2 = WGS84.semi_minor_axis * WGS84.ep2
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def llh2xyz(coordinates: Union[list, np.ndarray]) -> np.ndarray:
|
|
24
|
+
"""Conversion from llh geodetic coordinates to xyz
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
coordinates : Union[list, np.ndarray]
|
|
29
|
+
a numpy ndarray of shape (3,), (3, 1) or (3, N) or a list
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
np.ndarray
|
|
34
|
+
a two dimensional numpy array of shape (3, N) with xyz coordinates
|
|
35
|
+
"""
|
|
36
|
+
coordinates = np.asarray(coordinates)
|
|
37
|
+
if coordinates.shape[0] != 3:
|
|
38
|
+
raise RuntimeError(f"Coordinates has wrong shape: {coordinates.shape} not in (3,), (3, 1) or (3, N)")
|
|
39
|
+
|
|
40
|
+
if coordinates.ndim == 1:
|
|
41
|
+
coordinates = coordinates.copy()
|
|
42
|
+
coordinates.shape = (3, 1)
|
|
43
|
+
|
|
44
|
+
lat = coordinates[0, :]
|
|
45
|
+
lon = coordinates[1, :]
|
|
46
|
+
h = coordinates[2, :]
|
|
47
|
+
|
|
48
|
+
big_n = WGS84.semi_major_axis / np.sqrt(1 - WGS84.eccentricity_square * np.sin(lat) ** 2)
|
|
49
|
+
r = (big_n + h) * np.cos(lat)
|
|
50
|
+
|
|
51
|
+
x = r * np.cos(lon)
|
|
52
|
+
y = r * np.sin(lon)
|
|
53
|
+
z = (big_n * WGS84.semi_axes_ratio_min_max**2 + h) * np.sin(lat)
|
|
54
|
+
|
|
55
|
+
return np.vstack((x, y, z)).astype(float)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def xyz2llh(coordinates: Union[list, np.ndarray]) -> np.ndarray:
|
|
59
|
+
"""Conversion from xyz coordinates to llh geodetic coordinates
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
coordinates : Union[list, np.ndarray]
|
|
64
|
+
a numpy ndarray of shape (3,), (3, 1) or (3, N) or a list
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
np.ndarray
|
|
69
|
+
a two dimensional numpy array of shape (3, N) with llh coordinates
|
|
70
|
+
"""
|
|
71
|
+
coordinates = np.asarray(coordinates)
|
|
72
|
+
if coordinates.shape[0] != 3:
|
|
73
|
+
raise RuntimeError(f"Coordinates has wrong shape: {coordinates.shape} not in (3,), (3, 1) or (3, N)")
|
|
74
|
+
|
|
75
|
+
if coordinates.ndim == 1:
|
|
76
|
+
coordinates = coordinates.copy()
|
|
77
|
+
coordinates.shape = (3, 1)
|
|
78
|
+
|
|
79
|
+
x = coordinates[0, :]
|
|
80
|
+
y = coordinates[1, :]
|
|
81
|
+
z = coordinates[2, :]
|
|
82
|
+
|
|
83
|
+
lon = np.arctan2(y, x)
|
|
84
|
+
|
|
85
|
+
k = np.maximum(np.absolute(x), np.absolute(y))
|
|
86
|
+
k[np.where(k == 0)] = 1.0
|
|
87
|
+
|
|
88
|
+
c1 = x / k
|
|
89
|
+
c2 = y / k
|
|
90
|
+
|
|
91
|
+
r = k * np.sqrt(c1**2 + c2**2)
|
|
92
|
+
|
|
93
|
+
beta = np.arctan2(z, r * WGS84.semi_axes_ratio_min_max)
|
|
94
|
+
sin_beta, cos_beta = np.sin(beta), np.cos(beta)
|
|
95
|
+
|
|
96
|
+
lat = np.arctan2(
|
|
97
|
+
z + _A_MIN_TIMES_EP2 * sin_beta**3,
|
|
98
|
+
r - _A_MAX_SQUARE_MINUS_A_MIN_SQUARE_DIVIDED_BY_A_MAX * cos_beta**3,
|
|
99
|
+
)
|
|
100
|
+
beta_new = np.arctan2(WGS84.semi_axes_ratio_min_max * np.sin(lat), np.cos(lat))
|
|
101
|
+
|
|
102
|
+
for _ in range(_NUMBER_OF_ITERATIONS):
|
|
103
|
+
if np.array_equal(beta, beta_new):
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
beta = beta_new
|
|
107
|
+
sin_beta, cos_beta = np.sin(beta), np.cos(beta)
|
|
108
|
+
|
|
109
|
+
lat = np.arctan2(
|
|
110
|
+
z + _A_MIN_TIMES_EP2 * sin_beta**3,
|
|
111
|
+
r - _A_MAX_SQUARE_MINUS_A_MIN_SQUARE_DIVIDED_BY_A_MAX * cos_beta**3,
|
|
112
|
+
)
|
|
113
|
+
beta_new = np.arctan2(WGS84.semi_axes_ratio_min_max * np.sin(lat), np.cos(lat))
|
|
114
|
+
|
|
115
|
+
# Compute height
|
|
116
|
+
sin_phi = np.sin(lat)
|
|
117
|
+
big_n = WGS84.semi_major_axis / np.sqrt(1 - sin_phi**2 * WGS84.eccentricity_square)
|
|
118
|
+
h = r * np.cos(lat) + (z + WGS84.eccentricity_square * big_n * sin_phi) * sin_phi - big_n
|
|
119
|
+
|
|
120
|
+
return np.vstack((lat, lon, h)).astype(float)
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Curve classes implementation of curve protocols
|
|
6
|
+
-----------------------------------------------
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Tuple, Union
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import numpy.typing as npt
|
|
13
|
+
from scipy.interpolate import CubicSpline
|
|
14
|
+
|
|
15
|
+
from arepytools.geometry.curve_protocols import RealTwiceDifferentiableFunction
|
|
16
|
+
from arepytools.timing.precisedatetime import PreciseDateTime
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class InterpolationOutOfBoundaries(ValueError):
|
|
20
|
+
"""Could not perform interpolation on input value because it's out of validity boundaries"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InvertedTimeBoundaries(RuntimeError):
|
|
24
|
+
"""Inverted time boundaries provided for interpolator domain definition"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PolynomialWrapper:
|
|
28
|
+
"""Polynomial Wrapper implementation"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, coeff: npt.ArrayLike) -> None:
|
|
31
|
+
"""Polynomial wrapper inputs.
|
|
32
|
+
|
|
33
|
+
Polynomial in the form:
|
|
34
|
+
|
|
35
|
+
.. math::
|
|
36
|
+
|
|
37
|
+
a_{n}\\cdot x^{n} + a_{n-1}\\cdot x^{n-1} + ... + a_2\\cdot x^2 + a_1\\cdot x + a_0
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
coeff : npt.ArrayLike
|
|
42
|
+
polynomial coefficients to build the polynomial interpolator,
|
|
43
|
+
coefficients must be provided from the highers order to the lower
|
|
44
|
+
the last coefficient is the constant term
|
|
45
|
+
"""
|
|
46
|
+
self.poly_coefficients: np.ndarray = np.asarray(coeff)
|
|
47
|
+
|
|
48
|
+
def evaluate(self, coords: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
49
|
+
"""Evaluating the input polynomial at the specified variable values.
|
|
50
|
+
|
|
51
|
+
Polynomial in the form:
|
|
52
|
+
|
|
53
|
+
.. math::
|
|
54
|
+
|
|
55
|
+
a_{n}\\cdot x^{n} + a_{n-1}\\cdot x^{n-1} + ... + a_2\\cdot x^2 + a_1\\cdot x + a_0
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
coords : Union[float, npt.ArrayLike]
|
|
60
|
+
values of the variable at which evaluate the polynomial
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
Union[float, npt.ArrayLike]
|
|
65
|
+
polynomial computed at each input variable value
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
coords = np.asarray(coords).astype("float64")
|
|
69
|
+
|
|
70
|
+
# evaluating variable powers for each term of the polynomial at the input coordinate
|
|
71
|
+
variable_values = np.flip([coords**c for c in range(self.poly_coefficients.size)], axis=0)
|
|
72
|
+
|
|
73
|
+
return np.dot(self.poly_coefficients, variable_values)
|
|
74
|
+
|
|
75
|
+
def evaluate_first_derivative(self, coords: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
76
|
+
"""Evaluating the input polynomial first derivative at the specified variable values.
|
|
77
|
+
Polynomial derivative in the form:
|
|
78
|
+
|
|
79
|
+
.. math::
|
|
80
|
+
|
|
81
|
+
n\\cdot a_{n}\\cdot x^{n-1} + (n-1)\\cdot a_{n-1}\\cdot x^{n-2} + ... + 2\\cdot a_2\\cdot x + a_1
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
coords : Union[float, npt.ArrayLike]
|
|
86
|
+
values of the variable at which evaluate the polynomial
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
Union[float, npt.ArrayLike]
|
|
91
|
+
polynomial derivative computed at each input variable value
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
coords = np.asarray(coords).astype("float64")
|
|
95
|
+
# evaluating the derivative polynomial n-coefficients: n*a^(n-1)
|
|
96
|
+
poly_der_coeff = np.flip(range(1, self.poly_coefficients.size))
|
|
97
|
+
coeff = self.poly_coefficients[:-1]
|
|
98
|
+
# multiplying each polynomial variable term by its coefficient
|
|
99
|
+
variable_values = np.flip([coords**c for c in range(self.poly_coefficients.size - 1)], axis=0)
|
|
100
|
+
|
|
101
|
+
# multiplying each polynomial variable term by its derivative coefficient and the polynomial coefficient
|
|
102
|
+
return np.dot(poly_der_coeff * coeff, variable_values)
|
|
103
|
+
|
|
104
|
+
def evaluate_second_derivative(self, coords: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
105
|
+
"""Evaluating the input polynomial second derivative at the specified variable values.
|
|
106
|
+
|
|
107
|
+
Polynomial derivative in the form:
|
|
108
|
+
|
|
109
|
+
.. math::
|
|
110
|
+
|
|
111
|
+
n\\cdot (n-1)\\cdot a_{n}\\cdot x^{n-2} + (n-1)\\cdot (n-2)\\cdot a_{n-1}\\cdot x^{n-3} + ... + 2\\cdot a_2
|
|
112
|
+
|
|
113
|
+
Parameters
|
|
114
|
+
----------
|
|
115
|
+
coords : Union[float, npt.ArrayLike]
|
|
116
|
+
values of the variable at which evaluate the polynomial
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
Union[float, npt.ArrayLike]
|
|
121
|
+
polynomial derivative computed at each input variable value
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
coords = np.asarray(coords).astype("float64")
|
|
125
|
+
# evaluating the derivative polynomial n-coefficients: n*a^(n-1)
|
|
126
|
+
poly_der_coeff = np.flip(range(2, self.poly_coefficients.size))
|
|
127
|
+
# evaluating the derivative polynomial n-coefficients: n-1*a^(n-2)
|
|
128
|
+
poly_der2_coeff = np.flip(range(1, self.poly_coefficients.size - 1))
|
|
129
|
+
coeff = self.poly_coefficients[:-2]
|
|
130
|
+
|
|
131
|
+
# multiplying each polynomial variable term by its coefficient
|
|
132
|
+
# evaluating variable powers for each term of the polynomial evaluated in the point provided
|
|
133
|
+
variable_values = np.flip([coords**c for c in range(self.poly_coefficients.size - 2)], axis=0)
|
|
134
|
+
|
|
135
|
+
# evaluating variable powers for each term of the polynomial evaluated in the point provided
|
|
136
|
+
# multiplying each polynomial variable term by its derivative coefficient and the polynomial coefficient
|
|
137
|
+
return np.dot(poly_der_coeff * poly_der2_coeff * coeff, variable_values)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class SplineWrapper:
|
|
141
|
+
"""Spline wrapper implementation"""
|
|
142
|
+
|
|
143
|
+
def __init__(self, axis: npt.ArrayLike, values: npt.ArrayLike) -> None:
|
|
144
|
+
"""Spline wrapper inputs.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
axis : npt.ArrayLike
|
|
149
|
+
axis array to build the spline interpolator
|
|
150
|
+
values : npt.ArrayLike
|
|
151
|
+
values of the spline at each point of the axis to build the spline interpolator
|
|
152
|
+
"""
|
|
153
|
+
self.spline_interpolator = CubicSpline(np.asarray(axis), np.asarray(values))
|
|
154
|
+
|
|
155
|
+
def evaluate(self, coords: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
156
|
+
"""Evaluating the interpolated spline at the specified variable values.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
coords : Union[float, npt.ArrayLike]
|
|
161
|
+
values of the variable at which evaluate the spline
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
Union[float, npt.ArrayLike]
|
|
166
|
+
spline computed at each input variable value
|
|
167
|
+
"""
|
|
168
|
+
coords = np.asarray(coords).astype("float64")
|
|
169
|
+
|
|
170
|
+
return self.spline_interpolator(coords, 0, extrapolate=False)
|
|
171
|
+
|
|
172
|
+
def evaluate_first_derivative(self, coords: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
173
|
+
"""Evaluating the interpolated spline first derivative at the specified variable values.
|
|
174
|
+
|
|
175
|
+
Parameters
|
|
176
|
+
----------
|
|
177
|
+
coords : Union[float, npt.ArrayLike]
|
|
178
|
+
values of the variable at which evaluate the spline first derivative
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
Union[float, npt.ArrayLike]
|
|
183
|
+
first derivative of interpolated spline computed at each input variable value
|
|
184
|
+
"""
|
|
185
|
+
coords = np.asarray(coords).astype("float64")
|
|
186
|
+
|
|
187
|
+
return self.spline_interpolator(coords, 1, extrapolate=False)
|
|
188
|
+
|
|
189
|
+
def evaluate_second_derivative(self, coords: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
190
|
+
"""Evaluating the interpolated spline second derivative at the specified variable values.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
coords : Union[float, npt.ArrayLike]
|
|
195
|
+
values of the variable at which evaluate the spline second derivative
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
Union[float, npt.ArrayLike]
|
|
200
|
+
second derivative of interpolated spline computed at each input variable value
|
|
201
|
+
"""
|
|
202
|
+
coords = np.asarray(coords).astype("float64")
|
|
203
|
+
|
|
204
|
+
return self.spline_interpolator(coords, 2, extrapolate=False)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class Generic3DCurve:
|
|
208
|
+
"""Implementation of a generic 3D curve from twice differentiable functions"""
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
x_func: RealTwiceDifferentiableFunction,
|
|
213
|
+
y_func: RealTwiceDifferentiableFunction,
|
|
214
|
+
z_func: RealTwiceDifferentiableFunction,
|
|
215
|
+
t_ref: PreciseDateTime,
|
|
216
|
+
time_boundaries: Tuple[PreciseDateTime, PreciseDateTime],
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Generic twice differentiable 3D curve with a given domain of existence/interpolation.
|
|
219
|
+
Must not extrapolate values outside of boundaries.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
x_func : RealTwiceDifferentiableFunction
|
|
224
|
+
twice differentiable function defining the curve behaviour along x, time definition is relative to t_ref
|
|
225
|
+
y_func : RealTwiceDifferentiableFunction
|
|
226
|
+
twice differentiable function defining the curve behaviour along y, time definition is relative to t_ref
|
|
227
|
+
z_func : RealTwiceDifferentiableFunction
|
|
228
|
+
twice differentiable function defining the curve behaviour along z, time definition is relative to t_ref
|
|
229
|
+
t_ref : PreciseDateTime
|
|
230
|
+
reference time of the 3D curve, aka the lower boundary of the maximum domain of definition
|
|
231
|
+
time_boundaries : tuple[PreciseDateTime, PreciseDateTime]
|
|
232
|
+
time boundaries of curve validity (domain)
|
|
233
|
+
"""
|
|
234
|
+
self.x_func = x_func
|
|
235
|
+
self.y_func = y_func
|
|
236
|
+
self.z_func = z_func
|
|
237
|
+
self.time_ref = t_ref
|
|
238
|
+
# evaluating relative time boundaries
|
|
239
|
+
self.time_boundaries = tuple(t - t_ref for t in time_boundaries)
|
|
240
|
+
if not self.time_boundaries[1] > self.time_boundaries[0]:
|
|
241
|
+
raise InvertedTimeBoundaries("Wrong time boundaries order")
|
|
242
|
+
|
|
243
|
+
def evaluate(self, coordinates: Union[PreciseDateTime, npt.ArrayLike]) -> np.ndarray:
|
|
244
|
+
"""Evaluate x, y, z polynomial at given times.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
coordinates : Union[PreciseDateTime, npt.ArrayLike]
|
|
249
|
+
time points
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
np.ndarray
|
|
254
|
+
values of the polynomials at given input times (N, 3)
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
# computing relative times
|
|
258
|
+
times = np.asarray(coordinates)
|
|
259
|
+
times = times - self.time_ref
|
|
260
|
+
self._check_time_validity(relative_times=times)
|
|
261
|
+
|
|
262
|
+
# evaluating polynomials at given times
|
|
263
|
+
return np.stack(
|
|
264
|
+
[
|
|
265
|
+
self.x_func.evaluate(times),
|
|
266
|
+
self.y_func.evaluate(times),
|
|
267
|
+
self.z_func.evaluate(times),
|
|
268
|
+
],
|
|
269
|
+
axis=-1,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def evaluate_first_derivatives(self, coordinates: Union[PreciseDateTime, npt.ArrayLike]) -> np.ndarray:
|
|
273
|
+
"""Evaluate x, y, z polynomial first derivatives at given times.
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
coordinates : Union[PreciseDateTime, npt.ArrayLike]
|
|
278
|
+
time points
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
np.ndarray
|
|
283
|
+
values of the polynomials first derivatives at given input times (N, 3)
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
# computing relative times
|
|
287
|
+
times = np.asarray(coordinates)
|
|
288
|
+
times = times - self.time_ref
|
|
289
|
+
self._check_time_validity(relative_times=times)
|
|
290
|
+
|
|
291
|
+
# evaluating polynomials at given times
|
|
292
|
+
return np.stack(
|
|
293
|
+
[
|
|
294
|
+
self.x_func.evaluate_first_derivative(times),
|
|
295
|
+
self.y_func.evaluate_first_derivative(times),
|
|
296
|
+
self.z_func.evaluate_first_derivative(times),
|
|
297
|
+
],
|
|
298
|
+
axis=-1,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
def evaluate_second_derivatives(self, coordinates: Union[PreciseDateTime, npt.ArrayLike]) -> np.ndarray:
|
|
302
|
+
"""Evaluate x, y, z polynomial second derivatives at given times.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
coordinates : Union[PreciseDateTime, npt.ArrayLike]
|
|
307
|
+
time points
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
np.ndarray
|
|
312
|
+
values of the polynomials second derivatives at given input times (N, 3)
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
# computing relative times
|
|
316
|
+
times = np.asarray(coordinates)
|
|
317
|
+
times = times - self.time_ref
|
|
318
|
+
self._check_time_validity(relative_times=times)
|
|
319
|
+
|
|
320
|
+
# evaluating polynomials at given times
|
|
321
|
+
return np.stack(
|
|
322
|
+
[
|
|
323
|
+
self.x_func.evaluate_second_derivative(times),
|
|
324
|
+
self.y_func.evaluate_second_derivative(times),
|
|
325
|
+
self.z_func.evaluate_second_derivative(times),
|
|
326
|
+
],
|
|
327
|
+
axis=-1,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def _check_time_validity(self, relative_times: Union[float, npt.ArrayLike]) -> None:
|
|
331
|
+
"""Check input times validity with respect to the time validity boundaries.
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
relative_times : Union[float, npt.ArrayLike]
|
|
336
|
+
relative time points at which evaluate the polynomial
|
|
337
|
+
|
|
338
|
+
Raises
|
|
339
|
+
------
|
|
340
|
+
PolynomialInterpolationOutOfBoundaries
|
|
341
|
+
if relative time is out of validity boundaries this error is raised (interpolation cannot be performed)
|
|
342
|
+
"""
|
|
343
|
+
if ~np.any((relative_times > self.time_boundaries[0]) & (relative_times < self.time_boundaries[1])):
|
|
344
|
+
raise InterpolationOutOfBoundaries("Interpolation failed. Times are outside domain")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Aresys S.r.l. <info@aresys.it>
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Curve protocols definition
|
|
6
|
+
--------------------------
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Protocol, Union, runtime_checkable
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import numpy.typing as npt
|
|
13
|
+
|
|
14
|
+
from arepytools.timing.precisedatetime import PreciseDateTime
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class TwiceDifferentiable3DCurve(Protocol):
|
|
19
|
+
"""Custom 3D curve container with evaluation method for curve and first two derivatives.
|
|
20
|
+
f: R | PreciseDateTime -> R^3"""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def domain(self) -> tuple[Any, Any]:
|
|
24
|
+
"""Curve domain boundaries, min and max values"""
|
|
25
|
+
|
|
26
|
+
def evaluate(self, coordinates: Union[PreciseDateTime, float, npt.ArrayLike]) -> np.ndarray:
|
|
27
|
+
"""Evaluate curve value at given input coordinates.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
coordinates : Union[PreciseDateTime, float, npt.ArrayLike]
|
|
32
|
+
coordinates (PreciseDateTime) or float where to evaluate the curve
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
np.ndarray
|
|
37
|
+
values of the curve at given input values (N, 3)
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def evaluate_first_derivatives(self, coordinates: Union[PreciseDateTime, float, npt.ArrayLike]) -> np.ndarray:
|
|
41
|
+
"""Evaluate curve first derivatives values at given input coordinates.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
coordinates : Union[PreciseDateTime, float, npt.ArrayLike]
|
|
46
|
+
coordinates (PreciseDateTime) or float where to evaluate the derivatives
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
np.ndarray
|
|
51
|
+
values of the curve derivatives at given input values (N, 3)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def evaluate_second_derivatives(self, coordinates: Union[PreciseDateTime, float, npt.ArrayLike]) -> np.ndarray:
|
|
55
|
+
"""Evaluate curve second derivatives values at given input coordinates.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
coordinates : Union[PreciseDateTime, float, npt.ArrayLike]
|
|
60
|
+
coordinates (PreciseDateTime) or float where to evaluate the derivatives
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
np.ndarray
|
|
65
|
+
values of the polynomials second derivatives at given input times (N, 3)
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@runtime_checkable
|
|
70
|
+
class RealTwiceDifferentiableFunction(Protocol):
|
|
71
|
+
"""Generic protocol for a f: R -> R function twice differentiable with derivative evaluation methods implemented"""
|
|
72
|
+
|
|
73
|
+
def evaluate(self, coordinates: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
74
|
+
"""Evaluate function value at given coordinates.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
coordinates : Union[float, npt.ArrayLike]
|
|
79
|
+
input coordinates where to evaluate the function
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
Union[float, npt.ArrayLike]
|
|
84
|
+
value of function at each input coordinate
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def evaluate_first_derivative(self, coordinates: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
88
|
+
"""Evaluate function first derivative at given coordinates.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
coordinates : Union[float, npt.ArrayLike]
|
|
93
|
+
input coordinates where to evaluate the function derivative
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
Union[float, npt.ArrayLike]
|
|
98
|
+
values of function first derivative at each input coordinate
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def evaluate_second_derivative(self, coordinates: Union[float, npt.ArrayLike]) -> Union[float, npt.ArrayLike]:
|
|
102
|
+
"""Evaluate function second derivative at given coordinates.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
coordinates : Union[float, npt.ArrayLike]
|
|
107
|
+
input coordinates where to evaluate the function derivative
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
Union[float, npt.ArrayLike]
|
|
112
|
+
values of function second derivative at each input coordinate
|
|
113
|
+
"""
|