sarkit-convert 0.1.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/__init__.py +17 -0
- sarkit_convert/_utils.py +253 -0
- sarkit_convert/_version.py +1 -0
- sarkit_convert/csk.py +822 -0
- sarkit_convert/iceye.py +885 -0
- sarkit_convert/sentinel.py +1589 -0
- sarkit_convert/tsx.py +906 -0
- sarkit_convert-0.1.0.dist-info/METADATA +69 -0
- sarkit_convert-0.1.0.dist-info/RECORD +12 -0
- sarkit_convert-0.1.0.dist-info/WHEEL +4 -0
- sarkit_convert-0.1.0.dist-info/entry_points.txt +4 -0
- sarkit_convert-0.1.0.dist-info/licenses/LICENSE +22 -0
sarkit_convert/csk.py
ADDED
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
"""
|
|
2
|
+
===================
|
|
3
|
+
CSK Complex to SICD
|
|
4
|
+
===================
|
|
5
|
+
|
|
6
|
+
Convert a complex image from the Cosmo-SkyMed HD5 SLC into SICD 1.4
|
|
7
|
+
|
|
8
|
+
During development, the following documents were considered:
|
|
9
|
+
|
|
10
|
+
"COSMO-SkyMed Seconda Generazione: System and Products Description", Revision A
|
|
11
|
+
"COSMO-SkyMed Mission and Products Description", Issue 2
|
|
12
|
+
|
|
13
|
+
In addition, SARPy was consulted on how to use the CSK/CSG to compute SICD
|
|
14
|
+
metadata that would predict the complex data characteristics
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import contextlib
|
|
20
|
+
import pathlib
|
|
21
|
+
|
|
22
|
+
import dateutil.parser
|
|
23
|
+
import h5py
|
|
24
|
+
import lxml.builder
|
|
25
|
+
import numpy as np
|
|
26
|
+
import numpy.linalg as npl
|
|
27
|
+
import numpy.polynomial.polynomial as npp
|
|
28
|
+
import sarkit.sicd as sksicd
|
|
29
|
+
import sarkit.verification
|
|
30
|
+
import sarkit.wgs84
|
|
31
|
+
import scipy.constants
|
|
32
|
+
import scipy.optimize
|
|
33
|
+
|
|
34
|
+
from sarkit_convert import _utils as utils
|
|
35
|
+
|
|
36
|
+
NSMAP = {
|
|
37
|
+
"sicd": "urn:SICD:1.4.0",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
MODE_TYPE_MAP = {
|
|
41
|
+
# CSK
|
|
42
|
+
"HIMAGE": "STRIPMAP",
|
|
43
|
+
"PINGPONG": "STRIPMAP",
|
|
44
|
+
"WIDEREGION": "STRIPMAP",
|
|
45
|
+
"HUGEREGION": "STRIPMAP",
|
|
46
|
+
"ENHANCED SPOTLIGHT": "DYNAMIC STRIPMAP",
|
|
47
|
+
"SMART": "DYNAMIC STRIPMAP",
|
|
48
|
+
# CSG
|
|
49
|
+
"STRIPMAP": "STRIPMAP",
|
|
50
|
+
"QUADPOL": "STRIPMAP",
|
|
51
|
+
# KOMPSAT-5 (rebranded CSK)
|
|
52
|
+
"STANDARD": "STRIPMAP",
|
|
53
|
+
"ENHANCED STANDARD": "STRIPMAP",
|
|
54
|
+
"WIDE SWATH": "STRIPMAP",
|
|
55
|
+
"ENHANCED WIDE SWATH": "STRIPMAP",
|
|
56
|
+
"HIGH RESOLUTION": "DYNAMIC STRIPMAP",
|
|
57
|
+
"ENHANCED HIGH RESOLUTION": "DYNAMIC STRIPMAP",
|
|
58
|
+
"ULTRA HIGH RESOLUTION": "DYNAMIC STRIPMAP",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
PIXEL_TYPE_MAP = {
|
|
62
|
+
"float32": "RE32F_IM32F",
|
|
63
|
+
"int16": "RE16I_IM16I",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def try_decode(value):
|
|
68
|
+
"""Try to decode a value.
|
|
69
|
+
|
|
70
|
+
`h5py` attributes are arrays. Strings get returned as `bytes` and need to be decoded.
|
|
71
|
+
"""
|
|
72
|
+
with contextlib.suppress(AttributeError):
|
|
73
|
+
return value.decode()
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def extract_attributes(item, add_dataset_info=False):
|
|
78
|
+
"""Recursively extracts a nested dictionary of attributes from an item and its descendants"""
|
|
79
|
+
retval = dict()
|
|
80
|
+
if add_dataset_info:
|
|
81
|
+
if hasattr(item, "shape"):
|
|
82
|
+
retval["__shape__"] = item.shape
|
|
83
|
+
if hasattr(item, "dtype"):
|
|
84
|
+
retval["__dtype__"] = str(item.dtype)
|
|
85
|
+
if hasattr(item, "items"):
|
|
86
|
+
for name, contents in item.items():
|
|
87
|
+
retval[name] = extract_attributes(contents, add_dataset_info)
|
|
88
|
+
retval.update(sorted((key, try_decode(value)) for key, value in item.attrs.items()))
|
|
89
|
+
return retval
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def compute_apc_poly(h5_attrs, ref_time, start_time, stop_time):
|
|
93
|
+
"""Creates an Aperture Phase Center (APC) poly that orbits the Earth above the equator.
|
|
94
|
+
|
|
95
|
+
Polynomial generates 3D coords in ECF as a function of time from start of collect.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
h5_attrs: dict
|
|
100
|
+
The collection metadata
|
|
101
|
+
ref_time: datetime.datetime
|
|
102
|
+
The time at which the orbit goes through the `apc_pos`.
|
|
103
|
+
start_time: datetime.datetime
|
|
104
|
+
The start time to fit.
|
|
105
|
+
stop_time: datetime.datetime
|
|
106
|
+
The end time to fit.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
`numpy.ndarray`, shape=(6, 3)
|
|
111
|
+
APC poly
|
|
112
|
+
"""
|
|
113
|
+
position = h5_attrs["ECEF Satellite Position"]
|
|
114
|
+
velocity = h5_attrs["ECEF Satellite Velocity"]
|
|
115
|
+
acceleration = h5_attrs["ECEF Satellite Acceleration"]
|
|
116
|
+
times = h5_attrs["State Vectors Times"] - (start_time - ref_time).total_seconds()
|
|
117
|
+
apc_poly = utils.fit_state_vectors(
|
|
118
|
+
(0, (stop_time - start_time).total_seconds()),
|
|
119
|
+
times,
|
|
120
|
+
position,
|
|
121
|
+
velocity,
|
|
122
|
+
acceleration,
|
|
123
|
+
order=5,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return apc_poly
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def hdf5_to_sicd(
|
|
130
|
+
h5_filename,
|
|
131
|
+
sicd_filename,
|
|
132
|
+
classification,
|
|
133
|
+
ostaid,
|
|
134
|
+
img_str,
|
|
135
|
+
chan_index,
|
|
136
|
+
tx_polarizations,
|
|
137
|
+
tx_rcv_pols,
|
|
138
|
+
):
|
|
139
|
+
with h5py.File(h5_filename, "r") as h5file:
|
|
140
|
+
h5_attrs = extract_attributes(h5file)
|
|
141
|
+
mission_id = h5_attrs["Mission ID"]
|
|
142
|
+
if mission_id == "CSG":
|
|
143
|
+
dataset_str = "IMG"
|
|
144
|
+
else:
|
|
145
|
+
dataset_str = "SBI"
|
|
146
|
+
sample_data_h5_path = f"{img_str}/{dataset_str}"
|
|
147
|
+
sample_data_shape = h5file[sample_data_h5_path].shape
|
|
148
|
+
sample_data_dtype = h5file[sample_data_h5_path].dtype
|
|
149
|
+
|
|
150
|
+
# Timeline
|
|
151
|
+
ref_time = dateutil.parser.parse(h5_attrs["Reference UTC"])
|
|
152
|
+
collection_start_time = dateutil.parser.parse(h5_attrs["Scene Sensing Start UTC"])
|
|
153
|
+
collection_stop_time = dateutil.parser.parse(h5_attrs["Scene Sensing Stop UTC"])
|
|
154
|
+
collection_duration = (collection_stop_time - collection_start_time).total_seconds()
|
|
155
|
+
prf = h5_attrs[img_str]["PRF"]
|
|
156
|
+
num_pulses = int(np.ceil(collection_duration * prf))
|
|
157
|
+
look = {"LEFT": 1, "RIGHT": -1}[h5_attrs["Look Side"]]
|
|
158
|
+
|
|
159
|
+
# Collection Info
|
|
160
|
+
collector_name = h5_attrs["Satellite ID"]
|
|
161
|
+
date_str = collection_start_time.strftime("%d%b%y").upper()
|
|
162
|
+
time_str = collection_start_time.strftime("%H%M%S") + "Z"
|
|
163
|
+
core_name = f"{date_str}_{h5_attrs['Satellite ID']}_{time_str}"
|
|
164
|
+
acquisition_mode = h5_attrs["Acquisition Mode"]
|
|
165
|
+
if "scan" in acquisition_mode.lower():
|
|
166
|
+
raise ValueError("ScanSar modes not supported")
|
|
167
|
+
radar_mode_type = MODE_TYPE_MAP.get(acquisition_mode, None)
|
|
168
|
+
if not radar_mode_type:
|
|
169
|
+
radar_mode_type = "DYNAMIC STRIPMAP"
|
|
170
|
+
radar_mode_id = h5_attrs["Multi-Beam ID"]
|
|
171
|
+
|
|
172
|
+
# Creation Info
|
|
173
|
+
creation_time = dateutil.parser.parse(h5_attrs["Product Generation UTC"])
|
|
174
|
+
creation_site = h5_attrs["Processing Centre"]
|
|
175
|
+
l0_ver = h5_attrs.get("L0 Software Version", "NONE")
|
|
176
|
+
l1_ver = h5_attrs.get("L1A Software Version", "NONE")
|
|
177
|
+
creation_application = f"L0: {l0_ver}, L1: {l1_ver}"
|
|
178
|
+
|
|
179
|
+
# Image Data
|
|
180
|
+
pixel_type = PIXEL_TYPE_MAP[sample_data_dtype.name]
|
|
181
|
+
num_rows = sample_data_shape[1]
|
|
182
|
+
num_cols = sample_data_shape[0]
|
|
183
|
+
first_row = 0
|
|
184
|
+
first_col = 0
|
|
185
|
+
scp_pixel = np.array([num_rows // 2, num_cols // 2])
|
|
186
|
+
|
|
187
|
+
# Position
|
|
188
|
+
apc_poly = compute_apc_poly(
|
|
189
|
+
h5_attrs, ref_time, collection_start_time, collection_stop_time
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Radar Collection
|
|
193
|
+
center_frequency = h5_attrs["Radar Frequency"]
|
|
194
|
+
tx_pulse_length = h5_attrs[img_str]["Range Chirp Length"]
|
|
195
|
+
tx_fm_rate = h5_attrs[img_str]["Range Chirp Rate"]
|
|
196
|
+
tx_rf_bw = np.abs(tx_fm_rate * tx_pulse_length)
|
|
197
|
+
tx_freq_min = center_frequency - 0.5 * tx_rf_bw
|
|
198
|
+
tx_freq_max = center_frequency + 0.5 * tx_rf_bw
|
|
199
|
+
tx_freq_start = center_frequency - (tx_pulse_length / 2 * tx_fm_rate)
|
|
200
|
+
adc_sample_rate = h5_attrs[img_str]["Sampling Rate"]
|
|
201
|
+
rcv_window_length = (
|
|
202
|
+
h5_attrs[img_str]["Echo Sampling Window Length"] / adc_sample_rate
|
|
203
|
+
)
|
|
204
|
+
tx_rcv_polarization = tx_rcv_pols[chan_index - 1]
|
|
205
|
+
tx_polarization = tx_rcv_polarization[0]
|
|
206
|
+
|
|
207
|
+
# Grid
|
|
208
|
+
assert h5_attrs["Lines Order"] == "EARLY-LATE"
|
|
209
|
+
assert h5_attrs["Columns Order"] == "NEAR-FAR"
|
|
210
|
+
spacings = np.array(
|
|
211
|
+
[
|
|
212
|
+
h5_attrs[img_str][dataset_str]["Column Spacing"],
|
|
213
|
+
h5_attrs[img_str][dataset_str]["Line Spacing"],
|
|
214
|
+
]
|
|
215
|
+
)
|
|
216
|
+
intervals = np.array(
|
|
217
|
+
[
|
|
218
|
+
h5_attrs[img_str][dataset_str]["Column Time Interval"],
|
|
219
|
+
h5_attrs[img_str][dataset_str]["Line Time Interval"],
|
|
220
|
+
]
|
|
221
|
+
)
|
|
222
|
+
zd_az_0 = h5_attrs[img_str][dataset_str]["Zero Doppler Azimuth First Time"]
|
|
223
|
+
zd_rg_0 = h5_attrs[img_str][dataset_str]["Zero Doppler Range First Time"]
|
|
224
|
+
row_bw = (
|
|
225
|
+
h5_attrs[img_str]["Range Focusing Bandwidth"]
|
|
226
|
+
* 2
|
|
227
|
+
/ scipy.constants.speed_of_light
|
|
228
|
+
)
|
|
229
|
+
row_wid = 1 / row_bw
|
|
230
|
+
col_bw = (
|
|
231
|
+
min(
|
|
232
|
+
h5_attrs[img_str]["Azimuth Focusing Transition Bandwidth"] * intervals[1], 1
|
|
233
|
+
)
|
|
234
|
+
/ spacings[1]
|
|
235
|
+
)
|
|
236
|
+
col_wid = 1 / col_bw
|
|
237
|
+
|
|
238
|
+
# CA and COA times
|
|
239
|
+
num_grid_pts = 51
|
|
240
|
+
grid_indices = np.stack(
|
|
241
|
+
np.meshgrid(
|
|
242
|
+
np.linspace(0, num_rows - 1, num_grid_pts),
|
|
243
|
+
np.linspace(0, num_cols - 1, num_grid_pts),
|
|
244
|
+
indexing="ij",
|
|
245
|
+
),
|
|
246
|
+
axis=-1,
|
|
247
|
+
)
|
|
248
|
+
grid_coords = (grid_indices - scp_pixel) * spacings
|
|
249
|
+
time_coords = grid_indices[:, ::-look] * intervals + np.array([zd_rg_0, zd_az_0])
|
|
250
|
+
start_minus_ref = (collection_start_time - ref_time).total_seconds()
|
|
251
|
+
|
|
252
|
+
if mission_id == "CSG":
|
|
253
|
+
range_ref = h5_attrs[img_str]["Range Polynomial Reference Time"]
|
|
254
|
+
azimuth_ref = h5_attrs[img_str]["Azimuth Polynomial Reference Time"]
|
|
255
|
+
azimuth_ref_zd = h5_attrs[img_str]["Azimuth Polynomial Reference Time - ZD"]
|
|
256
|
+
azimuth_first_time = h5_attrs[img_str]["B0001"]["Azimuth First Time"]
|
|
257
|
+
azimuth_last_time = h5_attrs[img_str]["B0001"]["Azimuth Last Time"]
|
|
258
|
+
raw_times = np.linspace(azimuth_first_time, azimuth_last_time, num_grid_pts)
|
|
259
|
+
|
|
260
|
+
centroid_range_poly = h5_attrs[img_str][
|
|
261
|
+
"Doppler Centroid vs Range Time Polynomial"
|
|
262
|
+
]
|
|
263
|
+
centroid_azimuth_poly = h5_attrs[img_str][
|
|
264
|
+
"Doppler Centroid vs Azimuth Time Polynomial - RAW"
|
|
265
|
+
]
|
|
266
|
+
raw_doppler_centroid = npp.polyval(
|
|
267
|
+
raw_times - azimuth_ref, centroid_azimuth_poly
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
rate_range_poly = h5_attrs[img_str]["Doppler Rate vs Range Time Polynomial"]
|
|
271
|
+
rate_azimuth_poly = h5_attrs[img_str]["Doppler Rate vs Azimuth Time Polynomial"]
|
|
272
|
+
raw_doppler_rate = npp.polyval(raw_times - azimuth_ref, rate_azimuth_poly)
|
|
273
|
+
|
|
274
|
+
zd_times = raw_times - raw_doppler_centroid / raw_doppler_rate
|
|
275
|
+
|
|
276
|
+
zd_to_az_centroid = npp.polyfit(
|
|
277
|
+
zd_times - azimuth_ref_zd, raw_doppler_centroid, 4
|
|
278
|
+
)
|
|
279
|
+
doppler_centroid = (
|
|
280
|
+
npp.polyval(time_coords[..., 0] - range_ref, centroid_range_poly)
|
|
281
|
+
+ npp.polyval(time_coords[..., 1] - azimuth_ref_zd, zd_to_az_centroid)
|
|
282
|
+
- (centroid_range_poly[0] + zd_to_az_centroid[0]) / 2
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
zd_to_az_rate = npp.polyfit(zd_times - azimuth_ref_zd, raw_doppler_rate, 4)
|
|
286
|
+
doppler_rate = (
|
|
287
|
+
npp.polyval(time_coords[..., 0] - range_ref, rate_range_poly)
|
|
288
|
+
+ npp.polyval(time_coords[..., 1] - azimuth_ref_zd, zd_to_az_rate)
|
|
289
|
+
- (rate_range_poly[0] + zd_to_az_rate[0]) / 2
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
range_ref = h5_attrs["Range Polynomial Reference Time"]
|
|
293
|
+
azimuth_ref = h5_attrs["Azimuth Polynomial Reference Time"]
|
|
294
|
+
|
|
295
|
+
centroid_range_poly = h5_attrs["Centroid vs Range Time Polynomial"]
|
|
296
|
+
centroid_azimuth_poly = h5_attrs["Centroid vs Azimuth Time Polynomial"]
|
|
297
|
+
doppler_centroid = (
|
|
298
|
+
npp.polyval(time_coords[..., 0] - range_ref, centroid_range_poly)
|
|
299
|
+
+ npp.polyval(time_coords[..., 1] - azimuth_ref, centroid_azimuth_poly)
|
|
300
|
+
- (centroid_range_poly[0] + centroid_azimuth_poly[0]) / 2
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
rate_range_poly = h5_attrs["Doppler Rate vs Range Time Polynomial"]
|
|
304
|
+
rate_azimuth_poly = h5_attrs["Doppler Rate vs Azimuth Time Polynomial"]
|
|
305
|
+
doppler_rate = (
|
|
306
|
+
npp.polyval(time_coords[..., 0] - range_ref, rate_range_poly)
|
|
307
|
+
+ npp.polyval(time_coords[..., 1] - azimuth_ref, rate_azimuth_poly)
|
|
308
|
+
- (rate_range_poly[0] + rate_azimuth_poly[0]) / 2
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
range_rate_per_hz = -scipy.constants.speed_of_light / (2 * center_frequency)
|
|
312
|
+
range_rate = doppler_centroid * range_rate_per_hz
|
|
313
|
+
range_rate_rate = doppler_rate * range_rate_per_hz
|
|
314
|
+
doppler_centroid_poly = utils.polyfit2d(
|
|
315
|
+
grid_coords[..., 0].flatten(),
|
|
316
|
+
grid_coords[..., 1].flatten(),
|
|
317
|
+
doppler_centroid.flatten(),
|
|
318
|
+
4,
|
|
319
|
+
4,
|
|
320
|
+
)
|
|
321
|
+
doppler_rate_poly = utils.polyfit2d(
|
|
322
|
+
grid_coords[..., 0].flatten(),
|
|
323
|
+
grid_coords[..., 1].flatten(),
|
|
324
|
+
doppler_rate.flatten(),
|
|
325
|
+
4,
|
|
326
|
+
4,
|
|
327
|
+
)
|
|
328
|
+
time_ca_samps = time_coords[..., 1] - start_minus_ref
|
|
329
|
+
time_ca_poly = npp.polyfit(
|
|
330
|
+
grid_coords[..., 1].flatten(), time_ca_samps.flatten(), 1
|
|
331
|
+
)
|
|
332
|
+
time_coa_samps = time_ca_samps + range_rate / range_rate_rate
|
|
333
|
+
time_coa_poly = utils.polyfit2d(
|
|
334
|
+
grid_coords[..., 0].flatten(),
|
|
335
|
+
grid_coords[..., 1].flatten(),
|
|
336
|
+
time_coa_samps.flatten(),
|
|
337
|
+
4,
|
|
338
|
+
4,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
range_ca = time_coords[..., 0] * scipy.constants.speed_of_light / 2
|
|
342
|
+
speed_ca = npl.norm(
|
|
343
|
+
npp.polyval(time_coords[..., 1] - start_minus_ref, npp.polyder(apc_poly)),
|
|
344
|
+
axis=0,
|
|
345
|
+
)
|
|
346
|
+
drsf = range_rate_rate * range_ca / speed_ca**2
|
|
347
|
+
drsf_poly = utils.polyfit2d(
|
|
348
|
+
grid_coords[..., 0].flatten(),
|
|
349
|
+
grid_coords[..., 1].flatten(),
|
|
350
|
+
drsf.flatten(),
|
|
351
|
+
4,
|
|
352
|
+
4,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
llh_ddm = h5_attrs["Scene Centre Geodetic Coordinates"]
|
|
356
|
+
scp_drsf = drsf_poly[0, 0]
|
|
357
|
+
scp_tca = time_ca_poly[0]
|
|
358
|
+
scp_rca = (
|
|
359
|
+
(zd_rg_0 + scp_pixel[0] * intervals[0]) * scipy.constants.speed_of_light / 2
|
|
360
|
+
)
|
|
361
|
+
scp_tcoa = time_coa_poly[0, 0]
|
|
362
|
+
scp_delta_t_coa = scp_tcoa - scp_tca
|
|
363
|
+
scp_varp_ca_mag = npl.norm(npp.polyval(scp_tca, npp.polyder(apc_poly)))
|
|
364
|
+
scp_rcoa = np.sqrt(scp_rca**2 + scp_drsf * scp_varp_ca_mag**2 * scp_delta_t_coa**2)
|
|
365
|
+
scp_rratecoa = scp_drsf / scp_rcoa * scp_varp_ca_mag**2 * scp_delta_t_coa
|
|
366
|
+
|
|
367
|
+
def obj(hae):
|
|
368
|
+
scene_pos = sarkit.wgs84.geodetic_to_cartesian([llh_ddm[0], llh_ddm[1], hae])
|
|
369
|
+
delta_t = np.linspace(-0.01, 0.01)
|
|
370
|
+
arp_pos = npp.polyval(scp_tca + delta_t, apc_poly).T
|
|
371
|
+
arp_speed = npl.norm(npp.polyval(scp_tca, npp.polyder(apc_poly)), axis=0)
|
|
372
|
+
range_ = npl.norm(arp_pos - scene_pos, axis=1)
|
|
373
|
+
range_poly = npp.polyfit(delta_t, range_, len(apc_poly))
|
|
374
|
+
test_drsf = 2 * range_poly[2] * range_poly[0] / arp_speed**2
|
|
375
|
+
return scp_drsf - test_drsf
|
|
376
|
+
|
|
377
|
+
scp_hae = scipy.optimize.brentq(obj, -30e3, 30e3)
|
|
378
|
+
sc_ecf = sarkit.wgs84.geodetic_to_cartesian(llh_ddm)
|
|
379
|
+
scp_set = sksicd.projection.ProjectionSetsMono(
|
|
380
|
+
t_COA=np.array([scp_tcoa]),
|
|
381
|
+
ARP_COA=np.array([npp.polyval(scp_tcoa, apc_poly)]),
|
|
382
|
+
VARP_COA=np.array([npp.polyval(scp_tcoa, npp.polyder(apc_poly))]),
|
|
383
|
+
R_COA=np.array([scp_rcoa]),
|
|
384
|
+
Rdot_COA=np.array([scp_rratecoa]),
|
|
385
|
+
)
|
|
386
|
+
scp_ecf, _, _ = sksicd.projection.r_rdot_to_constant_hae_surface(
|
|
387
|
+
look, sc_ecf, scp_set, scp_hae
|
|
388
|
+
)
|
|
389
|
+
scp_ecf = scp_ecf[0]
|
|
390
|
+
scp_llh = sarkit.wgs84.cartesian_to_geodetic(scp_ecf)
|
|
391
|
+
scp_ca_pos = npp.polyval(scp_tca, apc_poly)
|
|
392
|
+
scp_ca_vel = npp.polyval(scp_tcoa, npp.polyder(apc_poly))
|
|
393
|
+
los = scp_ecf - scp_ca_pos
|
|
394
|
+
u_row = los / npl.norm(los)
|
|
395
|
+
left = np.cross(scp_ca_pos, scp_ca_vel)
|
|
396
|
+
look = np.sign(np.dot(left, u_row))
|
|
397
|
+
spz = -look * np.cross(u_row, scp_ca_vel)
|
|
398
|
+
uspz = spz / npl.norm(spz)
|
|
399
|
+
u_col = np.cross(uspz, u_row)
|
|
400
|
+
|
|
401
|
+
# Build XML
|
|
402
|
+
sicd = lxml.builder.ElementMaker(
|
|
403
|
+
namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]}
|
|
404
|
+
)
|
|
405
|
+
collection_info = sicd.CollectionInfo(
|
|
406
|
+
sicd.CollectorName(collector_name),
|
|
407
|
+
sicd.CoreName(core_name),
|
|
408
|
+
sicd.CollectType("MONOSTATIC"),
|
|
409
|
+
sicd.RadarMode(sicd.ModeType(radar_mode_type), sicd.ModeID(radar_mode_id)),
|
|
410
|
+
sicd.Classification(classification),
|
|
411
|
+
)
|
|
412
|
+
image_creation = sicd.ImageCreation(
|
|
413
|
+
sicd.Application(creation_application),
|
|
414
|
+
sicd.DateTime(creation_time.isoformat() + "Z"),
|
|
415
|
+
sicd.Site(creation_site),
|
|
416
|
+
)
|
|
417
|
+
image_data = sicd.ImageData(
|
|
418
|
+
sicd.PixelType(pixel_type),
|
|
419
|
+
sicd.NumRows(str(num_rows)),
|
|
420
|
+
sicd.NumCols(str(num_cols)),
|
|
421
|
+
sicd.FirstRow(str(first_row)),
|
|
422
|
+
sicd.FirstCol(str(first_col)),
|
|
423
|
+
sicd.FullImage(sicd.NumRows(str(num_rows)), sicd.NumCols(str(num_cols))),
|
|
424
|
+
sicd.SCPPixel(sicd.Row(str(scp_pixel[0])), sicd.Col(str(scp_pixel[1]))),
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
def make_xyz(arr):
|
|
428
|
+
return [sicd.X(str(arr[0])), sicd.Y(str(arr[1])), sicd.Z(str(arr[2]))]
|
|
429
|
+
|
|
430
|
+
def make_llh(arr):
|
|
431
|
+
return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1])), sicd.HAE(str(arr[2]))]
|
|
432
|
+
|
|
433
|
+
def make_ll(arr):
|
|
434
|
+
return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1]))]
|
|
435
|
+
|
|
436
|
+
# Placeholder locations
|
|
437
|
+
geo_data = sicd.GeoData(
|
|
438
|
+
sicd.EarthModel("WGS_84"),
|
|
439
|
+
sicd.SCP(sicd.ECF(*make_xyz(scp_ecf)), sicd.LLH(*make_llh(scp_llh))),
|
|
440
|
+
sicd.ImageCorners(
|
|
441
|
+
sicd.ICP({"index": "1:FRFC"}, *make_ll([0, 0])),
|
|
442
|
+
sicd.ICP({"index": "2:FRLC"}, *make_ll([0, 0])),
|
|
443
|
+
sicd.ICP({"index": "3:LRLC"}, *make_ll([0, 0])),
|
|
444
|
+
sicd.ICP({"index": "4:LRFC"}, *make_ll([0, 0])),
|
|
445
|
+
),
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
dc_sgn = np.sign(-doppler_rate_poly[0, 0])
|
|
449
|
+
col_deltakcoa_poly = (
|
|
450
|
+
-look * dc_sgn * doppler_centroid_poly * intervals[1] / spacings[1]
|
|
451
|
+
)
|
|
452
|
+
vertices = [
|
|
453
|
+
(0, 0),
|
|
454
|
+
(0, num_cols - 1),
|
|
455
|
+
(num_rows - 1, num_cols - 1),
|
|
456
|
+
(num_rows - 1, 0),
|
|
457
|
+
]
|
|
458
|
+
coords = (vertices - scp_pixel) * spacings
|
|
459
|
+
deltaks = npp.polyval2d(coords[:, 0], coords[:, 1], col_deltakcoa_poly)
|
|
460
|
+
dk1 = deltaks.min() - col_bw / 2
|
|
461
|
+
dk2 = deltaks.max() + col_bw / 2
|
|
462
|
+
if dk1 < -0.5 / spacings[1] or dk2 > 0.5 / spacings[1]:
|
|
463
|
+
dk1 = -0.5 / spacings[1]
|
|
464
|
+
dk2 = -dk1
|
|
465
|
+
|
|
466
|
+
row_window_name = h5_attrs["Range Focusing Weighting Function"]
|
|
467
|
+
row_window_coeff = h5_attrs["Range Focusing Weighting Coefficient"]
|
|
468
|
+
col_window_name = h5_attrs["Azimuth Focusing Weighting Function"]
|
|
469
|
+
col_window_coeff = h5_attrs["Azimuth Focusing Weighting Coefficient"]
|
|
470
|
+
|
|
471
|
+
grid = sicd.Grid(
|
|
472
|
+
sicd.ImagePlane("SLANT"),
|
|
473
|
+
sicd.Type("RGZERO"),
|
|
474
|
+
sicd.TimeCOAPoly(),
|
|
475
|
+
sicd.Row(
|
|
476
|
+
sicd.UVectECF(*make_xyz(u_row)),
|
|
477
|
+
sicd.SS(str(spacings[0])),
|
|
478
|
+
sicd.ImpRespWid(str(row_wid)),
|
|
479
|
+
sicd.Sgn("-1"),
|
|
480
|
+
sicd.ImpRespBW(str(row_bw)),
|
|
481
|
+
sicd.KCtr(str(center_frequency / (scipy.constants.speed_of_light / 2))),
|
|
482
|
+
sicd.DeltaK1(str(-row_bw / 2)),
|
|
483
|
+
sicd.DeltaK2(str(row_bw / 2)),
|
|
484
|
+
sicd.DeltaKCOAPoly(),
|
|
485
|
+
sicd.WgtType(
|
|
486
|
+
sicd.WindowName(row_window_name),
|
|
487
|
+
sicd.Parameter({"name": "COEFFICIENT"}, str(row_window_coeff)),
|
|
488
|
+
),
|
|
489
|
+
),
|
|
490
|
+
sicd.Col(
|
|
491
|
+
sicd.UVectECF(*make_xyz(u_col)),
|
|
492
|
+
sicd.SS(str(spacings[1])),
|
|
493
|
+
sicd.ImpRespWid(str(col_wid)),
|
|
494
|
+
sicd.Sgn("-1"),
|
|
495
|
+
sicd.ImpRespBW(str(col_bw)),
|
|
496
|
+
sicd.KCtr("0"),
|
|
497
|
+
sicd.DeltaK1(str(dk1)),
|
|
498
|
+
sicd.DeltaK2(str(dk2)),
|
|
499
|
+
sicd.DeltaKCOAPoly(),
|
|
500
|
+
sicd.WgtType(
|
|
501
|
+
sicd.WindowName(col_window_name),
|
|
502
|
+
sicd.Parameter({"name": "COEFFICIENT"}, str(col_window_coeff)),
|
|
503
|
+
),
|
|
504
|
+
),
|
|
505
|
+
)
|
|
506
|
+
sksicd.Poly2dType().set_elem(grid.find("./{*}TimeCOAPoly"), time_coa_poly)
|
|
507
|
+
sksicd.Poly2dType().set_elem(grid.find("./{*}Row/{*}DeltaKCOAPoly"), [[0]])
|
|
508
|
+
sksicd.Poly2dType().set_elem(
|
|
509
|
+
grid.find("./{*}Col/{*}DeltaKCOAPoly"), col_deltakcoa_poly
|
|
510
|
+
)
|
|
511
|
+
rcs_row_sf = None
|
|
512
|
+
rcs_col_sf = None
|
|
513
|
+
if row_window_name == "HAMMING":
|
|
514
|
+
wgts = scipy.signal.windows.general_hamming(512, row_window_coeff, sym=True)
|
|
515
|
+
wgtfunc = sicd.WgtFunct()
|
|
516
|
+
sksicd.TRANSCODERS["Grid/Row/WgtFunct"].set_elem(wgtfunc, wgts)
|
|
517
|
+
grid.find("./{*}Row").append(wgtfunc)
|
|
518
|
+
row_broadening_factor = utils.broadening_from_amp(wgts)
|
|
519
|
+
row_wid = row_broadening_factor / row_bw
|
|
520
|
+
sksicd.DblType().set_elem(grid.find("./{*}Row/{*}ImpRespWid"), row_wid)
|
|
521
|
+
rcs_row_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
|
|
522
|
+
if col_window_name == "HAMMING":
|
|
523
|
+
wgts = scipy.signal.windows.general_hamming(512, col_window_coeff, sym=True)
|
|
524
|
+
wgtfunc = sicd.WgtFunct()
|
|
525
|
+
sksicd.TRANSCODERS["Grid/Col/WgtFunct"].set_elem(wgtfunc, wgts)
|
|
526
|
+
grid.find("./{*}Col").append(wgtfunc)
|
|
527
|
+
col_broadening_factor = utils.broadening_from_amp(wgts)
|
|
528
|
+
col_wid = col_broadening_factor / col_bw
|
|
529
|
+
sksicd.DblType().set_elem(grid.find("./{*}Col/{*}ImpRespWid"), col_wid)
|
|
530
|
+
rcs_col_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
|
|
531
|
+
|
|
532
|
+
timeline = sicd.Timeline(
|
|
533
|
+
sicd.CollectStart(collection_start_time.isoformat() + "Z"),
|
|
534
|
+
sicd.CollectDuration(str(collection_duration)),
|
|
535
|
+
sicd.IPP(
|
|
536
|
+
{"size": "1"},
|
|
537
|
+
sicd.Set(
|
|
538
|
+
{"index": "1"},
|
|
539
|
+
sicd.TStart(str(0)),
|
|
540
|
+
sicd.TEnd(str(num_pulses / prf)),
|
|
541
|
+
sicd.IPPStart(str(0)),
|
|
542
|
+
sicd.IPPEnd(str(num_pulses - 1)),
|
|
543
|
+
sicd.IPPPoly(),
|
|
544
|
+
),
|
|
545
|
+
),
|
|
546
|
+
)
|
|
547
|
+
sksicd.PolyType().set_elem(timeline.find("./{*}IPP/{*}Set/{*}IPPPoly"), [0, prf])
|
|
548
|
+
|
|
549
|
+
position = sicd.Position(sicd.ARPPoly())
|
|
550
|
+
sksicd.XyzPolyType().set_elem(position.find("./{*}ARPPoly"), apc_poly)
|
|
551
|
+
|
|
552
|
+
rcv_channels = sicd.RcvChannels(
|
|
553
|
+
{"size": str(len(tx_rcv_pols))},
|
|
554
|
+
)
|
|
555
|
+
for ndx, tx_rcv_pol in enumerate(tx_rcv_pols):
|
|
556
|
+
rcv_channels.append(
|
|
557
|
+
sicd.ChanParameters(
|
|
558
|
+
{"index": str(ndx + 1)}, sicd.TxRcvPolarization(tx_rcv_pol)
|
|
559
|
+
)
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
radar_collection = sicd.RadarCollection(
|
|
563
|
+
sicd.TxFrequency(sicd.Min(str(tx_freq_min)), sicd.Max(str(tx_freq_max))),
|
|
564
|
+
sicd.Waveform(
|
|
565
|
+
{"size": "1"},
|
|
566
|
+
sicd.WFParameters(
|
|
567
|
+
{"index": "1"},
|
|
568
|
+
sicd.TxPulseLength(str(tx_pulse_length)),
|
|
569
|
+
sicd.TxRFBandwidth(str(tx_rf_bw)),
|
|
570
|
+
sicd.TxFreqStart(str(tx_freq_start)),
|
|
571
|
+
sicd.TxFMRate(str(tx_fm_rate)),
|
|
572
|
+
sicd.RcvWindowLength(str(rcv_window_length)),
|
|
573
|
+
sicd.ADCSampleRate(str(adc_sample_rate)),
|
|
574
|
+
),
|
|
575
|
+
),
|
|
576
|
+
sicd.TxPolarization(tx_polarization),
|
|
577
|
+
rcv_channels,
|
|
578
|
+
)
|
|
579
|
+
if len(tx_polarizations) > 1:
|
|
580
|
+
radar_collection.find("./{*}TxPolarization").text = "SEQUENCE"
|
|
581
|
+
tx_sequence = sicd.TxSequence({"size": str(len(tx_polarizations))})
|
|
582
|
+
for ndx, tx_pol in enumerate(tx_polarizations):
|
|
583
|
+
tx_sequence.append(
|
|
584
|
+
sicd.TxStep({"index": str(ndx + 1)}, sicd.TxPolarization(tx_pol))
|
|
585
|
+
)
|
|
586
|
+
rcv_channels.addprevious(tx_sequence)
|
|
587
|
+
|
|
588
|
+
image_formation = sicd.ImageFormation(
|
|
589
|
+
sicd.RcvChanProc(sicd.NumChanProc("1"), sicd.ChanIndex(str(chan_index))),
|
|
590
|
+
sicd.TxRcvPolarizationProc(tx_rcv_polarization),
|
|
591
|
+
sicd.TStartProc(str(0)),
|
|
592
|
+
sicd.TEndProc(str(collection_duration)),
|
|
593
|
+
sicd.TxFrequencyProc(
|
|
594
|
+
sicd.MinProc(str(tx_freq_min)), sicd.MaxProc(str(tx_freq_max))
|
|
595
|
+
),
|
|
596
|
+
sicd.ImageFormAlgo("RMA"),
|
|
597
|
+
sicd.STBeamComp("NO"),
|
|
598
|
+
sicd.ImageBeamComp("SV"),
|
|
599
|
+
sicd.AzAutofocus("NO"),
|
|
600
|
+
sicd.RgAutofocus("NO"),
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
rma = sicd.RMA(
|
|
604
|
+
sicd.RMAlgoType("OMEGA_K"),
|
|
605
|
+
sicd.ImageType("INCA"),
|
|
606
|
+
sicd.INCA(
|
|
607
|
+
sicd.TimeCAPoly(),
|
|
608
|
+
sicd.R_CA_SCP(str(scp_rca)),
|
|
609
|
+
sicd.FreqZero(str(center_frequency)),
|
|
610
|
+
sicd.DRateSFPoly(),
|
|
611
|
+
sicd.DopCentroidPoly(),
|
|
612
|
+
),
|
|
613
|
+
)
|
|
614
|
+
sksicd.PolyType().set_elem(rma.find("./{*}INCA/{*}TimeCAPoly"), time_ca_poly)
|
|
615
|
+
sksicd.Poly2dType().set_elem(rma.find("./{*}INCA/{*}DRateSFPoly"), drsf_poly)
|
|
616
|
+
sksicd.Poly2dType().set_elem(
|
|
617
|
+
rma.find("./{*}INCA/{*}DopCentroidPoly"), doppler_centroid_poly
|
|
618
|
+
)
|
|
619
|
+
sicd_xml_obj = sicd.SICD(
|
|
620
|
+
collection_info,
|
|
621
|
+
image_creation,
|
|
622
|
+
image_data,
|
|
623
|
+
geo_data,
|
|
624
|
+
grid,
|
|
625
|
+
timeline,
|
|
626
|
+
position,
|
|
627
|
+
radar_collection,
|
|
628
|
+
image_formation,
|
|
629
|
+
rma,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
image_formation.addnext(sksicd.compute_scp_coa(sicd_xml_obj.getroottree()))
|
|
633
|
+
|
|
634
|
+
# Add Radiometric
|
|
635
|
+
if mission_id == "CSK":
|
|
636
|
+
if h5_attrs["Range Spreading Loss Compensation Geometry"] != "NONE":
|
|
637
|
+
slant_range = h5_attrs["Reference Slant Range"]
|
|
638
|
+
exp = h5_attrs["Reference Slant Range Exponent"]
|
|
639
|
+
scale_factor = slant_range ** (2 * exp)
|
|
640
|
+
rescale_factor = h5_attrs["Rescaling Factor"]
|
|
641
|
+
scale_factor /= rescale_factor * rescale_factor
|
|
642
|
+
if h5_attrs.get("Calibration Constant Compensation Flag", None) == 0:
|
|
643
|
+
cal = h5_attrs[img_str]["Calibration Constant"]
|
|
644
|
+
scale_factor /= cal
|
|
645
|
+
betazero_poly = np.array([[scale_factor]])
|
|
646
|
+
graze = np.deg2rad(float(sicd_xml_obj.findtext("./{*}SCPCOA/{*}GrazeAng")))
|
|
647
|
+
twist = np.deg2rad(float(sicd_xml_obj.findtext("./{*}SCPCOA/{*}TwistAng")))
|
|
648
|
+
sigmazero_poly = betazero_poly * np.cos(graze) * np.cos(twist)
|
|
649
|
+
gammazero_poly = betazero_poly / np.tan(graze) * np.cos(twist)
|
|
650
|
+
|
|
651
|
+
radiometric = sicd.Radiometric(
|
|
652
|
+
sicd.SigmaZeroSFPoly(), sicd.BetaZeroSFPoly(), sicd.GammaZeroSFPoly()
|
|
653
|
+
)
|
|
654
|
+
sksicd.Poly2dType().set_elem(
|
|
655
|
+
radiometric.find("./{*}SigmaZeroSFPoly"), sigmazero_poly
|
|
656
|
+
)
|
|
657
|
+
sksicd.Poly2dType().set_elem(
|
|
658
|
+
radiometric.find("./{*}BetaZeroSFPoly"), betazero_poly
|
|
659
|
+
)
|
|
660
|
+
sksicd.Poly2dType().set_elem(
|
|
661
|
+
radiometric.find("./{*}GammaZeroSFPoly"), gammazero_poly
|
|
662
|
+
)
|
|
663
|
+
if rcs_row_sf and rcs_col_sf:
|
|
664
|
+
rcssf_poly = betazero_poly * (
|
|
665
|
+
rcs_row_sf * rcs_col_sf / (row_bw * col_bw)
|
|
666
|
+
)
|
|
667
|
+
radiometric.find("./{*}SigmaZeroSFPoly").addprevious(sicd.RCSSFPoly())
|
|
668
|
+
sksicd.Poly2dType().set_elem(
|
|
669
|
+
radiometric.find("./{*}RCSSFPoly"), rcssf_poly
|
|
670
|
+
)
|
|
671
|
+
sicd_xml_obj.find("./{*}RMA").addprevious(radiometric)
|
|
672
|
+
|
|
673
|
+
# Add Geodata Corners
|
|
674
|
+
sicd_xmltree = sicd_xml_obj.getroottree()
|
|
675
|
+
image_grid_locations = (
|
|
676
|
+
np.array(
|
|
677
|
+
[[0, 0], [0, num_cols - 1], [num_rows - 1, num_cols - 1], [num_rows - 1, 0]]
|
|
678
|
+
)
|
|
679
|
+
- scp_pixel
|
|
680
|
+
) * spacings
|
|
681
|
+
icp_ecef, _, _ = sksicd.image_to_ground_plane(
|
|
682
|
+
sicd_xmltree,
|
|
683
|
+
image_grid_locations,
|
|
684
|
+
scp_ecf,
|
|
685
|
+
sarkit.wgs84.up(sarkit.wgs84.cartesian_to_geodetic(scp_ecf)),
|
|
686
|
+
)
|
|
687
|
+
icp_llh = sarkit.wgs84.cartesian_to_geodetic(icp_ecef)
|
|
688
|
+
xml_helper = sksicd.XmlHelper(sicd_xmltree)
|
|
689
|
+
xml_helper.set("./{*}GeoData/{*}ImageCorners", icp_llh[:, :2])
|
|
690
|
+
|
|
691
|
+
# Validate XML
|
|
692
|
+
sicd_con = sarkit.verification.SicdConsistency(sicd_xmltree)
|
|
693
|
+
sicd_con.check()
|
|
694
|
+
sicd_con.print_result(fail_detail=True)
|
|
695
|
+
|
|
696
|
+
# Grab the data
|
|
697
|
+
with h5py.File(h5_filename, "r") as h5file:
|
|
698
|
+
data_arr = np.asarray(h5file[sample_data_h5_path])
|
|
699
|
+
dtype = data_arr.dtype
|
|
700
|
+
view_dtype = sksicd.PIXEL_TYPES[pixel_type]["dtype"].newbyteorder(
|
|
701
|
+
dtype.byteorder
|
|
702
|
+
)
|
|
703
|
+
complex_data_arr = np.squeeze(data_arr.view(view_dtype))
|
|
704
|
+
complex_data_arr = np.transpose(complex_data_arr)
|
|
705
|
+
if look > 0:
|
|
706
|
+
complex_data_arr = complex_data_arr[:, ::-1]
|
|
707
|
+
|
|
708
|
+
metadata = sksicd.NitfMetadata(
|
|
709
|
+
xmltree=sicd_xmltree,
|
|
710
|
+
file_header_part={
|
|
711
|
+
"ostaid": ostaid,
|
|
712
|
+
"ftitle": core_name,
|
|
713
|
+
"security": {
|
|
714
|
+
"clas": classification[0].upper(),
|
|
715
|
+
"clsy": "US",
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
im_subheader_part={
|
|
719
|
+
"tgtid": "",
|
|
720
|
+
"iid2": core_name,
|
|
721
|
+
"security": {
|
|
722
|
+
"clas": classification[0].upper(),
|
|
723
|
+
"clsy": "US",
|
|
724
|
+
},
|
|
725
|
+
"isorce": collector_name,
|
|
726
|
+
},
|
|
727
|
+
de_subheader_part={
|
|
728
|
+
"security": {
|
|
729
|
+
"clas": classification[0].upper(),
|
|
730
|
+
"clsy": "US",
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
with sicd_filename.open("wb") as f:
|
|
736
|
+
with sksicd.NitfWriter(f, metadata) as writer:
|
|
737
|
+
writer.write_image(complex_data_arr)
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def main(args=None):
|
|
741
|
+
parser = argparse.ArgumentParser(
|
|
742
|
+
description="Converts a CSK SCS HDF5 file into a SICD.",
|
|
743
|
+
fromfile_prefix_chars="@",
|
|
744
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
parser.add_argument(
|
|
748
|
+
"input_h5_file", type=pathlib.Path, help="path of the input HDF5 file"
|
|
749
|
+
)
|
|
750
|
+
parser.add_argument(
|
|
751
|
+
"classification",
|
|
752
|
+
help="content of the /SICD/CollectionInfo/Classification node in the SICD XML",
|
|
753
|
+
)
|
|
754
|
+
parser.add_argument(
|
|
755
|
+
"output_sicd_file",
|
|
756
|
+
type=pathlib.Path,
|
|
757
|
+
help='path of the output SICD file. The string "{pol}" will be replaced with polarization for multiple images',
|
|
758
|
+
)
|
|
759
|
+
parser.add_argument(
|
|
760
|
+
"--ostaid",
|
|
761
|
+
help="content of the originating station ID (OSTAID) field of the NITF header",
|
|
762
|
+
default="Unknown",
|
|
763
|
+
)
|
|
764
|
+
config = parser.parse_args(args)
|
|
765
|
+
|
|
766
|
+
tx_polarizations = []
|
|
767
|
+
with h5py.File(config.input_h5_file, "r") as h5file:
|
|
768
|
+
acquisition_mode = h5file.attrs["Acquisition Mode"].decode()
|
|
769
|
+
if "scan" in acquisition_mode.lower():
|
|
770
|
+
raise ValueError("ScanSar modes not supported")
|
|
771
|
+
mission_id = h5file.attrs["Mission ID"].decode()
|
|
772
|
+
images = dict()
|
|
773
|
+
if mission_id == "CSG":
|
|
774
|
+
img_str = "S01"
|
|
775
|
+
polarization = h5file.attrs["Polarization"].decode()
|
|
776
|
+
filename = pathlib.Path(
|
|
777
|
+
str(config.output_sicd_file).format(pol=polarization)
|
|
778
|
+
)
|
|
779
|
+
images[img_str] = {
|
|
780
|
+
"polarization": polarization,
|
|
781
|
+
"chan_index": 1,
|
|
782
|
+
"filename": filename,
|
|
783
|
+
}
|
|
784
|
+
tx_polarizations.append(polarization[0])
|
|
785
|
+
tx_rcv_pols = [f"{polarization[0]}:{polarization[1]}"]
|
|
786
|
+
else:
|
|
787
|
+
img_ndx = 1
|
|
788
|
+
images = dict()
|
|
789
|
+
tx_rcv_pols = []
|
|
790
|
+
while (img_str := f"S{img_ndx:02}") in h5file:
|
|
791
|
+
polarization = h5file[img_str].attrs["Polarisation"].decode()
|
|
792
|
+
filename = pathlib.Path(
|
|
793
|
+
str(config.output_sicd_file).format(pol=polarization)
|
|
794
|
+
)
|
|
795
|
+
images[img_str] = {
|
|
796
|
+
"polarization": polarization,
|
|
797
|
+
"chan_index": img_ndx,
|
|
798
|
+
"filename": filename,
|
|
799
|
+
}
|
|
800
|
+
tx_rcv_pols.append(f"{polarization[0]}:{polarization[1]}")
|
|
801
|
+
if (tx_polarization := polarization[0]) not in tx_polarizations:
|
|
802
|
+
tx_polarizations.append(tx_polarization)
|
|
803
|
+
img_ndx += 1
|
|
804
|
+
|
|
805
|
+
if len(images) != len(set([image["filename"] for image in images.values()])):
|
|
806
|
+
raise ValueError("Output filename does not include necessary polarization slug")
|
|
807
|
+
|
|
808
|
+
for img_str, img_info in images.items():
|
|
809
|
+
hdf5_to_sicd(
|
|
810
|
+
h5_filename=config.input_h5_file,
|
|
811
|
+
sicd_filename=img_info["filename"],
|
|
812
|
+
classification=config.classification,
|
|
813
|
+
ostaid=config.ostaid,
|
|
814
|
+
img_str=img_str,
|
|
815
|
+
chan_index=img_info["chan_index"],
|
|
816
|
+
tx_polarizations=tx_polarizations,
|
|
817
|
+
tx_rcv_pols=tx_rcv_pols,
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
if __name__ == "__main__":
|
|
822
|
+
main()
|