sarkit-convert 0.1.0__py3-none-any.whl → 0.3.0__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.
- sarkit_convert/_utils.py +47 -18
- sarkit_convert/_version.py +1 -1
- sarkit_convert/{csk.py → cosmo.py} +369 -218
- sarkit_convert/create_arp_poly.py +166 -0
- sarkit_convert/iceye.py +217 -301
- sarkit_convert/sentinel.py +457 -453
- sarkit_convert/sidd_metadata.py +201 -0
- sarkit_convert/{tsx.py → terrasar.py} +318 -215
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.3.0.dist-info}/METADATA +26 -26
- sarkit_convert-0.3.0.dist-info/RECORD +14 -0
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.3.0.dist-info}/WHEEL +1 -1
- sarkit_convert-0.1.0.dist-info/RECORD +0 -12
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.3.0.dist-info}/entry_points.txt +0 -0
- {sarkit_convert-0.1.0.dist-info → sarkit_convert-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sarkit.wgs84
|
|
3
|
+
import scipy.optimize
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def eci_uv(pos, z_sgn, inclination_deg):
|
|
7
|
+
upos = pos / np.linalg.norm(pos, axis=-1, keepdims=True)
|
|
8
|
+
east = np.cross([0, 0, 1], upos)
|
|
9
|
+
east /= np.linalg.norm(east, axis=-1, keepdims=True)
|
|
10
|
+
north = np.cross(upos, east)
|
|
11
|
+
cos_gclat = (1 - upos[..., 2] ** 2) ** 0.5
|
|
12
|
+
cos_inc = np.cos(np.deg2rad(inclination_deg))
|
|
13
|
+
if abs(cos_inc) > cos_gclat:
|
|
14
|
+
raise ValueError(
|
|
15
|
+
f"Position {pos} is inconsistent with an orbital"
|
|
16
|
+
f" inclination of {inclination_deg} degrees"
|
|
17
|
+
)
|
|
18
|
+
cos_k = cos_inc / cos_gclat
|
|
19
|
+
sin_k = np.sign(z_sgn) * (1 - cos_k**2) ** 0.5
|
|
20
|
+
return north * sin_k + east * cos_k
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def create_inclination_ring(scp_pos_ecef, incidence_deg, orbit_height_m):
|
|
24
|
+
inc_rad = np.deg2rad(incidence_deg)
|
|
25
|
+
scp_pos_geo = sarkit.wgs84.cartesian_to_geodetic(scp_pos_ecef)
|
|
26
|
+
scp_up = sarkit.wgs84.up(scp_pos_geo)
|
|
27
|
+
scp_north = sarkit.wgs84.north(scp_pos_geo)
|
|
28
|
+
scp_east = sarkit.wgs84.east(scp_pos_geo)
|
|
29
|
+
|
|
30
|
+
cos_inc = np.cos(inc_rad)
|
|
31
|
+
sin_inc = np.sin(inc_rad)
|
|
32
|
+
to_sat = scp_up * cos_inc + scp_north * sin_inc
|
|
33
|
+
|
|
34
|
+
def orbit_error(dist):
|
|
35
|
+
sat_pos = scp_pos_ecef + to_sat * dist
|
|
36
|
+
sat_height = sarkit.wgs84.cartesian_to_geodetic(sat_pos)[2]
|
|
37
|
+
return sat_height - orbit_height_m
|
|
38
|
+
|
|
39
|
+
dist_radial = scipy.optimize.brentq(
|
|
40
|
+
orbit_error, orbit_height_m / cos_inc / 2, orbit_height_m / cos_inc
|
|
41
|
+
)
|
|
42
|
+
dist_up = dist_radial * cos_inc
|
|
43
|
+
dist_out = dist_radial * sin_inc
|
|
44
|
+
ring_center = scp_pos_ecef + scp_up * dist_up
|
|
45
|
+
return ring_center, dist_out, scp_east, scp_north
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_arp_poly(
|
|
49
|
+
scp_pos_ecef,
|
|
50
|
+
incidence_deg,
|
|
51
|
+
look_direction,
|
|
52
|
+
orbit_height_m,
|
|
53
|
+
orbit_inclination_deg,
|
|
54
|
+
*,
|
|
55
|
+
angle_to_north_deg=None,
|
|
56
|
+
orbit_direction=None,
|
|
57
|
+
doppler_cone_angle_deg=None,
|
|
58
|
+
):
|
|
59
|
+
"""Create an aperture reference position polynomial from a subset of metadata
|
|
60
|
+
and orbital parameters
|
|
61
|
+
|
|
62
|
+
Args
|
|
63
|
+
----
|
|
64
|
+
scp_pos_ecef: ndarray, shape=(3, )
|
|
65
|
+
Scene Center Point position in ecef cartesian coordinates
|
|
66
|
+
incidence_deg: float
|
|
67
|
+
Incidence Angle to SCP during imaging
|
|
68
|
+
look_direction: float
|
|
69
|
+
SICD SideOfTrack conveyed as float (LEFT -> 1, RIGHT -> -1)
|
|
70
|
+
orbit_height_m: float
|
|
71
|
+
Orbital height in meters. Assumed to be constant
|
|
72
|
+
orbit_inclination_deg: float
|
|
73
|
+
Orbital inclination in degrees
|
|
74
|
+
angle_to_north_deg: float|None
|
|
75
|
+
Imaging angle clockwise from north in degrees. Equivalent to SICD/SCPCOA/AzimAng
|
|
76
|
+
orbit_direction: float|None
|
|
77
|
+
Indicates whether the arp velocity should be ascending or descending.
|
|
78
|
+
Sign of this value will determine the sign of the velocity z component
|
|
79
|
+
doppler_cone_angle_deg: float|None
|
|
80
|
+
Doppler cone angle at SCP COA in degrees. Equivalent to SICD/SCPCOA/DopplerConeAng
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
ndarray, shape=(3, 3)
|
|
85
|
+
Aperture reference position polynomial in ecef cartesian coordinates.
|
|
86
|
+
The origin of this polynomial is the SCP center of aperture
|
|
87
|
+
|
|
88
|
+
Notes
|
|
89
|
+
-----
|
|
90
|
+
Either angle_to_north_deg or both orbit_direction and doppler_cone_angle_deg
|
|
91
|
+
must be specified.
|
|
92
|
+
If all three are present, only angle_to_north_deg will be used as it provides the
|
|
93
|
+
most accurate result.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
ring_center, dist_out, scp_east, scp_north = create_inclination_ring(
|
|
97
|
+
scp_pos_ecef, incidence_deg, orbit_height_m
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
gm = (
|
|
101
|
+
3.986004418e14 # https://en.wikipedia.org/wiki/Standard_gravitational_parameter
|
|
102
|
+
)
|
|
103
|
+
omega_3 = sarkit.wgs84.NOMINAL_MEAN_ANGULAR_VELOCITY
|
|
104
|
+
|
|
105
|
+
def eciuvel_to_ecefvel(ecefpos, eciuvel):
|
|
106
|
+
eciuvel = eciuvel / np.linalg.norm(eciuvel)
|
|
107
|
+
vmag = (gm / np.linalg.norm(ecefpos, axis=-1)) ** 0.5
|
|
108
|
+
eci_vel = vmag * eciuvel
|
|
109
|
+
ecefvel = eci_vel - np.cross(np.array([0, 0, omega_3]), ecefpos)
|
|
110
|
+
return ecefvel
|
|
111
|
+
|
|
112
|
+
if angle_to_north_deg:
|
|
113
|
+
angle_to_north_rad = np.deg2rad(angle_to_north_deg)
|
|
114
|
+
arp_pos = ring_center + dist_out * (
|
|
115
|
+
scp_east * np.sin(angle_to_north_rad)
|
|
116
|
+
+ scp_north * np.cos(angle_to_north_rad)
|
|
117
|
+
)
|
|
118
|
+
if angle_to_north_deg % 360 < 180:
|
|
119
|
+
eastness = +1
|
|
120
|
+
else:
|
|
121
|
+
eastness = -1
|
|
122
|
+
|
|
123
|
+
orbit_direction = look_direction * eastness
|
|
124
|
+
elif orbit_direction and doppler_cone_angle_deg:
|
|
125
|
+
doppler_cone_angle_rad = np.deg2rad(doppler_cone_angle_deg)
|
|
126
|
+
|
|
127
|
+
def dca_err(atn):
|
|
128
|
+
arp_pos = ring_center + dist_out * (
|
|
129
|
+
scp_east * np.sin(atn) + scp_north * np.cos(atn)
|
|
130
|
+
)
|
|
131
|
+
arp_vel_eci = eci_uv(arp_pos, orbit_direction, orbit_inclination_deg)
|
|
132
|
+
arp_vel_ecef = eciuvel_to_ecefvel(arp_pos, arp_vel_eci)
|
|
133
|
+
uvel = arp_vel_ecef / np.linalg.norm(arp_vel_ecef)
|
|
134
|
+
los = scp_pos_ecef - arp_pos
|
|
135
|
+
ulos = los / np.linalg.norm(los)
|
|
136
|
+
dca = np.arccos(np.dot(uvel, ulos))
|
|
137
|
+
return doppler_cone_angle_rad - dca
|
|
138
|
+
|
|
139
|
+
eastness = orbit_direction * look_direction
|
|
140
|
+
if eastness > 0:
|
|
141
|
+
min_na = 0
|
|
142
|
+
max_na = np.pi
|
|
143
|
+
else:
|
|
144
|
+
min_na = np.pi
|
|
145
|
+
max_na = 2 * np.pi
|
|
146
|
+
|
|
147
|
+
angle_to_north_rad = scipy.optimize.brentq(dca_err, min_na, max_na)
|
|
148
|
+
|
|
149
|
+
arp_pos = ring_center + dist_out * (
|
|
150
|
+
scp_east * np.sin(angle_to_north_rad)
|
|
151
|
+
+ scp_north * np.cos(angle_to_north_rad)
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
"Either angle_to_north_deg or both orbit_direction"
|
|
156
|
+
" and doppler_cone_angle_deg must be specified."
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
arp_vel_eci = eci_uv(arp_pos, orbit_direction, orbit_inclination_deg)
|
|
160
|
+
arp_vel = eciuvel_to_ecefvel(arp_pos, arp_vel_eci)
|
|
161
|
+
arp_acc_mag = gm / np.linalg.norm(arp_pos) ** 2
|
|
162
|
+
arp_acc_dir = -arp_pos / np.linalg.norm(arp_pos)
|
|
163
|
+
arp_acc = arp_acc_dir * arp_acc_mag - np.cross(
|
|
164
|
+
[0, 0, omega_3], 2 * arp_vel + np.cross([0, 0, omega_3], arp_pos)
|
|
165
|
+
)
|
|
166
|
+
return np.stack([arp_pos, arp_vel, arp_acc / 2], axis=-1)
|