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.
@@ -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)