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
sarkit_convert/sentinel.py
CHANGED
|
@@ -8,11 +8,13 @@ Convert a complex image(s) from the Sentinel SAFE to SICD(s).
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
|
+
import contextlib
|
|
12
|
+
import datetime
|
|
13
|
+
import functools
|
|
11
14
|
import pathlib
|
|
12
15
|
|
|
13
16
|
import dateutil.parser
|
|
14
|
-
import lxml.
|
|
15
|
-
import lxml.etree as et
|
|
17
|
+
import lxml.etree
|
|
16
18
|
import numpy as np
|
|
17
19
|
import numpy.linalg as npl
|
|
18
20
|
import numpy.polynomial.polynomial as npp
|
|
@@ -138,6 +140,20 @@ def _collect_base_info(root_node):
|
|
|
138
140
|
# TOPSAR - closest SICD analog is Dynamic Stripmap
|
|
139
141
|
base_info["mode_type"] = "DYNAMIC STRIPMAP"
|
|
140
142
|
|
|
143
|
+
relative_orbit_num = int(
|
|
144
|
+
root_node.findtext(".//{*}relativeOrbitNumber[@type='start']")
|
|
145
|
+
)
|
|
146
|
+
base_info["parameters"] = {
|
|
147
|
+
"RELATIVE_ORBIT_NUM": relative_orbit_num,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
base_info["start_time_anx"] = float(
|
|
151
|
+
root_node.findtext(".//{*}acquisitionPeriod//{*}startTimeANX")
|
|
152
|
+
)
|
|
153
|
+
base_info["acq_start_str"] = root_node.findtext(
|
|
154
|
+
".//{*}acquisitionPeriod//{*}startTime"
|
|
155
|
+
)
|
|
156
|
+
|
|
141
157
|
# Image Creation
|
|
142
158
|
processing = root_node.find(
|
|
143
159
|
"./metadataSection/metadataObject[@ID='processing']/metadataWrap/xmlData/{*}processing",
|
|
@@ -148,9 +164,6 @@ def _collect_base_info(root_node):
|
|
|
148
164
|
f"{software.attrib['name']} {software.attrib['version']}"
|
|
149
165
|
)
|
|
150
166
|
base_info["creation_date_time"] = dateutil.parser.parse(processing.attrib["stop"])
|
|
151
|
-
base_info["creation_site"] = (
|
|
152
|
-
f"{facility.attrib['name']}, {facility.attrib['site']}, {facility.attrib['country']}"
|
|
153
|
-
)
|
|
154
167
|
|
|
155
168
|
# Radar Collection
|
|
156
169
|
polarizations = root_node.findall(
|
|
@@ -164,6 +177,28 @@ def _collect_base_info(root_node):
|
|
|
164
177
|
return base_info
|
|
165
178
|
|
|
166
179
|
|
|
180
|
+
@functools.lru_cache
|
|
181
|
+
def _window_params(window_name, coefficient):
|
|
182
|
+
window_name = window_name.upper()
|
|
183
|
+
if window_name == "HAMMING":
|
|
184
|
+
wgts = scipy.signal.windows.general_hamming(512, float(coefficient), sym=True)
|
|
185
|
+
elif window_name == "KAISER":
|
|
186
|
+
wgts = scipy.signal.windows.kaiser(512, float(coefficient), sym=True)
|
|
187
|
+
else: # Default to UNIFORM
|
|
188
|
+
window_name = "UNIFORM"
|
|
189
|
+
coefficient = None
|
|
190
|
+
wgts = np.ones(256)
|
|
191
|
+
|
|
192
|
+
broadening_factor = utils.broadening_from_amp(wgts)
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
"window_name": window_name,
|
|
196
|
+
"coefficient": coefficient,
|
|
197
|
+
"wgts": wgts,
|
|
198
|
+
"broadening_factor": broadening_factor,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
167
202
|
def _collect_swath_info(product_root_node):
|
|
168
203
|
swath_info = dict()
|
|
169
204
|
burst_list = product_root_node.findall("./swathTiming/burstList/burst")
|
|
@@ -173,12 +208,25 @@ def _collect_swath_info(product_root_node):
|
|
|
173
208
|
swath_info["mode_id"] = product_root_node.find("./adsHeader/mode").text
|
|
174
209
|
t_slice = _get_slice(product_root_node)
|
|
175
210
|
swath = _get_swath(product_root_node)
|
|
211
|
+
|
|
212
|
+
mission_data_take_id = product_root_node.find("./adsHeader/missionDataTakeId").text
|
|
176
213
|
swath_info["parameters"] = {
|
|
177
214
|
"SLICE": t_slice,
|
|
178
215
|
"SWATH": swath,
|
|
179
216
|
"ORBIT_SOURCE": "SLC_INTERNAL",
|
|
217
|
+
"MISSION_DATA_TAKE_ID": mission_data_take_id,
|
|
180
218
|
}
|
|
181
219
|
|
|
220
|
+
slice_list = product_root_node.findall(
|
|
221
|
+
"./imageAnnotation/imageInformation/sliceList/slice"
|
|
222
|
+
)
|
|
223
|
+
swath_info["sensing_start"] = dateutil.parser.parse(
|
|
224
|
+
slice_list[int(t_slice) - 1].find("./sensingStartTime").text
|
|
225
|
+
)
|
|
226
|
+
swath_info["sensing_stop"] = dateutil.parser.parse(
|
|
227
|
+
slice_list[int(t_slice) - 1].find("./sensingStopTime").text
|
|
228
|
+
)
|
|
229
|
+
|
|
182
230
|
# Radar Collection
|
|
183
231
|
center_frequency = float(
|
|
184
232
|
product_root_node.find(
|
|
@@ -226,8 +274,6 @@ def _collect_swath_info(product_root_node):
|
|
|
226
274
|
"./generalAnnotation/downlinkInformationList/downlinkInformation/prf"
|
|
227
275
|
).text
|
|
228
276
|
)
|
|
229
|
-
swath_info["t_start"] = 0
|
|
230
|
-
swath_info["ipp_start"] = 0
|
|
231
277
|
swath_info["ipp_poly"] = (0, swath_info["prf"])
|
|
232
278
|
|
|
233
279
|
# Image Formation
|
|
@@ -235,7 +281,6 @@ def _collect_swath_info(product_root_node):
|
|
|
235
281
|
f"{swath_info['tx_polarization']}:{swath_info['rcv_polarization']}"
|
|
236
282
|
)
|
|
237
283
|
swath_info["image_form_algo"] = "RMA"
|
|
238
|
-
swath_info["t_start_proc"] = 0
|
|
239
284
|
swath_info["tx_freq_proc"] = swath_info["tx_freq"]
|
|
240
285
|
swath_info["image_beam_comp"] = "SV"
|
|
241
286
|
swath_info["az_autofocus"] = "NO"
|
|
@@ -312,20 +357,15 @@ def _collect_swath_info(product_root_node):
|
|
|
312
357
|
range_proc = product_root_node.find(
|
|
313
358
|
"./imageAnnotation/processingInformation/swathProcParamsList/swathProcParams/rangeProcessing"
|
|
314
359
|
)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
)
|
|
325
|
-
else: # Default to UNIFORM
|
|
326
|
-
swath_info["row_window_name"] = "UNIFORM"
|
|
327
|
-
swath_info["row_params"] = None
|
|
328
|
-
swath_info["row_wgts"] = np.ones(256)
|
|
360
|
+
|
|
361
|
+
window_info = _window_params(
|
|
362
|
+
range_proc.find("./windowType").text,
|
|
363
|
+
range_proc.find("./windowCoefficient").text,
|
|
364
|
+
)
|
|
365
|
+
swath_info["row_window_name"] = window_info["window_name"]
|
|
366
|
+
swath_info["row_params"] = window_info["coefficient"]
|
|
367
|
+
swath_info["row_wgts"] = window_info["wgts"]
|
|
368
|
+
swath_info["row_broadening_factor"] = window_info["broadening_factor"]
|
|
329
369
|
|
|
330
370
|
swath_info["row_ss"] = (_constants.speed_of_light / 2) * delta_tau_s
|
|
331
371
|
swath_info["row_sgn"] = -1
|
|
@@ -352,34 +392,29 @@ def _collect_swath_info(product_root_node):
|
|
|
352
392
|
).text
|
|
353
393
|
)
|
|
354
394
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
swath_info["col_wgts"] = scipy.signal.windows.kaiser(
|
|
363
|
-
512, float(swath_info["col_params"]), sym=True
|
|
364
|
-
)
|
|
365
|
-
else: # Default to UNIFORM
|
|
366
|
-
swath_info["col_window_name"] = "UNIFORM"
|
|
367
|
-
swath_info["col_params"] = None
|
|
368
|
-
swath_info["col_wgts"] = np.ones(256)
|
|
395
|
+
window_info = _window_params(
|
|
396
|
+
az_proc.find("./windowType").text, az_proc.find("./windowCoefficient").text
|
|
397
|
+
)
|
|
398
|
+
swath_info["col_window_name"] = window_info["window_name"]
|
|
399
|
+
swath_info["col_params"] = window_info["coefficient"]
|
|
400
|
+
swath_info["col_wgts"] = window_info["wgts"]
|
|
401
|
+
swath_info["col_broadening_factor"] = window_info["broadening_factor"]
|
|
369
402
|
|
|
370
403
|
swath_info["col_sgn"] = -1
|
|
371
404
|
swath_info["col_kctr"] = 0.0
|
|
372
405
|
swath_info["col_imp_res_bw"] = dop_bw * swath_info["ss_zd_s"] / swath_info["col_ss"]
|
|
373
406
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
swath_info["col_imp_res_wid"] =
|
|
407
|
+
swath_info["row_imp_res_wid"] = (
|
|
408
|
+
swath_info["row_broadening_factor"] / swath_info["row_imp_res_bw"]
|
|
409
|
+
)
|
|
410
|
+
swath_info["col_imp_res_wid"] = (
|
|
411
|
+
swath_info["col_broadening_factor"] / swath_info["col_imp_res_bw"]
|
|
412
|
+
)
|
|
378
413
|
|
|
379
414
|
return swath_info
|
|
380
415
|
|
|
381
416
|
|
|
382
|
-
def _collect_burst_info(product_root_node, swath_info):
|
|
417
|
+
def _collect_burst_info(product_root_node, base_info, swath_info):
|
|
383
418
|
burst_list = product_root_node.findall("./swathTiming/burstList/burst")
|
|
384
419
|
# parse the geolocation information - for SCP calculation
|
|
385
420
|
geo_grid_point_list = product_root_node.findall(
|
|
@@ -549,7 +584,7 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
549
584
|
)
|
|
550
585
|
|
|
551
586
|
# common use for the fitting efforts
|
|
552
|
-
poly_order =
|
|
587
|
+
poly_order = 4
|
|
553
588
|
grid_samples = poly_order + 4
|
|
554
589
|
num_rows = swath_info["num_rows"]
|
|
555
590
|
num_cols = swath_info["num_cols"]
|
|
@@ -584,12 +619,13 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
584
619
|
demod_phase = eta_arg * f_eta_c[:, np.newaxis]
|
|
585
620
|
total_phase = deramp_phase + demod_phase
|
|
586
621
|
|
|
587
|
-
phase = utils.
|
|
622
|
+
phase = utils.polyfit2d_tol(
|
|
588
623
|
coords_rg_2d.flatten(),
|
|
589
624
|
coords_az_2d.flatten(),
|
|
590
625
|
total_phase.flatten(),
|
|
591
626
|
poly_order,
|
|
592
|
-
poly_order,
|
|
627
|
+
poly_order + 1,
|
|
628
|
+
1e-2,
|
|
593
629
|
)
|
|
594
630
|
|
|
595
631
|
# DeltaKCOAPoly is derivative of phase in azimuth/Col direction
|
|
@@ -608,12 +644,13 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
608
644
|
)
|
|
609
645
|
time_coa_sampled = time_ca_sampled + dop_centroid_sampled / doppler_rate_sampled
|
|
610
646
|
|
|
611
|
-
burst_info["time_coa_poly_coefs"] = utils.
|
|
647
|
+
burst_info["time_coa_poly_coefs"] = utils.polyfit2d_tol(
|
|
612
648
|
coords_rg_2d.flatten(),
|
|
613
649
|
coords_az_2d.flatten(),
|
|
614
650
|
time_coa_sampled.flatten(),
|
|
615
651
|
poly_order,
|
|
616
652
|
poly_order,
|
|
653
|
+
1e-3,
|
|
617
654
|
)
|
|
618
655
|
|
|
619
656
|
full_img_verticies = np.array(
|
|
@@ -694,6 +731,28 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
694
731
|
|
|
695
732
|
return row_uvect_ecf, np.cross(uspz, row_uvect_ecf)
|
|
696
733
|
|
|
734
|
+
def _compute_burst_id(base_info, swath_info, burst_info):
|
|
735
|
+
"""Add `burst_id` to each burst info in `swaths_info` based on DI-MPC-IPFDPM, Issue 2.5, Clause 9.25"""
|
|
736
|
+
t_orb = 12 * 24 * 3600 / 175
|
|
737
|
+
t_pre = {"IW": 2.299849, "EW": 2.299970}[swath_info["mode_id"]]
|
|
738
|
+
t_beam = {"IW": 2.758273, "EW": 3.038376}[swath_info["mode_id"]]
|
|
739
|
+
|
|
740
|
+
acq_start_utc = datetime.datetime.fromisoformat(
|
|
741
|
+
base_info["acq_start_str"]
|
|
742
|
+
).replace(tzinfo=datetime.timezone.utc)
|
|
743
|
+
t_anx = acq_start_utc - datetime.timedelta(
|
|
744
|
+
milliseconds=base_info["start_time_anx"]
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
scp_tcoa = burst_info["time_coa_poly_coefs"][0, 0]
|
|
748
|
+
t_b = (
|
|
749
|
+
burst_info["collect_start"] + datetime.timedelta(seconds=scp_tcoa)
|
|
750
|
+
).replace(tzinfo=datetime.timezone.utc)
|
|
751
|
+
delta_t_b = (t_b - t_anx).total_seconds() + (
|
|
752
|
+
base_info["parameters"]["RELATIVE_ORBIT_NUM"] - 1
|
|
753
|
+
) * t_orb
|
|
754
|
+
return int(1 + (delta_t_b - t_pre) // t_beam)
|
|
755
|
+
|
|
697
756
|
def _finalize_stripmap():
|
|
698
757
|
burst_info = dict()
|
|
699
758
|
|
|
@@ -711,38 +770,34 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
711
770
|
(num_rows - 1, 0),
|
|
712
771
|
]
|
|
713
772
|
|
|
714
|
-
|
|
715
|
-
product_root_node.find(
|
|
716
|
-
"./generalAnnotation/downlinkInformationList/downlinkInformation/firstLineSensingTime"
|
|
717
|
-
).text
|
|
718
|
-
)
|
|
719
|
-
stop = dateutil.parser.parse(
|
|
720
|
-
product_root_node.find(
|
|
721
|
-
"./generalAnnotation/downlinkInformationList/downlinkInformation/lastLineSensingTime"
|
|
722
|
-
).text
|
|
723
|
-
)
|
|
724
|
-
slice = int(_get_slice(product_root_node))
|
|
773
|
+
t_slice = int(_get_slice(product_root_node))
|
|
725
774
|
swath = _get_swath(product_root_node)
|
|
726
775
|
burst_info["core_name"] = (
|
|
727
|
-
f"{
|
|
776
|
+
f"{swath_info['sensing_start'].strftime('%d%b%YT%H%M%S').upper()}_{product_root_node.find('./adsHeader/missionId').text}{product_root_node.find('./adsHeader/missionDataTakeId').text}_{t_slice:02d}_{swath}_01"
|
|
728
777
|
)
|
|
729
778
|
|
|
730
|
-
burst_info["parameters"] = {"BURST": f"{1:
|
|
779
|
+
burst_info["parameters"] = {"BURST": f"{1:02d}"}
|
|
780
|
+
|
|
731
781
|
prf = float(
|
|
732
782
|
product_root_node.find(
|
|
733
783
|
"./generalAnnotation/downlinkInformationList/downlinkInformation/prf"
|
|
734
784
|
).text
|
|
735
785
|
)
|
|
736
|
-
duration = (
|
|
786
|
+
duration = (
|
|
787
|
+
swath_info["sensing_stop"] - swath_info["sensing_start"]
|
|
788
|
+
).total_seconds()
|
|
737
789
|
|
|
738
|
-
burst_info["collect_start"] =
|
|
790
|
+
burst_info["collect_start"] = swath_info["sensing_start"]
|
|
739
791
|
burst_info["collect_duration"] = duration
|
|
792
|
+
burst_info["ipp_set_tstart"] = 0
|
|
740
793
|
burst_info["ipp_set_tend"] = duration
|
|
794
|
+
burst_info["ipp_set_ippstart"] = 0
|
|
741
795
|
burst_info["ipp_set_ippend"] = round(duration * prf) - 1
|
|
796
|
+
burst_info["tstart_proc"] = 0
|
|
742
797
|
burst_info["tend_proc"] = duration
|
|
743
798
|
|
|
744
799
|
burst_info["arp_poly_coefs"] = _compute_arp_poly_coefs(
|
|
745
|
-
product_root_node,
|
|
800
|
+
product_root_node, swath_info["sensing_start"]
|
|
746
801
|
).T
|
|
747
802
|
|
|
748
803
|
azimuth_time_first_line = dateutil.parser.parse(
|
|
@@ -750,9 +805,14 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
750
805
|
"./imageAnnotation/imageInformation/productFirstLineUtcTime"
|
|
751
806
|
).text
|
|
752
807
|
)
|
|
753
|
-
first_line_relative_start = (
|
|
808
|
+
first_line_relative_start = (
|
|
809
|
+
azimuth_time_first_line - swath_info["sensing_start"]
|
|
810
|
+
).total_seconds()
|
|
754
811
|
_, _ = _calc_rma_and_grid_info(
|
|
755
|
-
swath_info,
|
|
812
|
+
swath_info,
|
|
813
|
+
burst_info,
|
|
814
|
+
first_line_relative_start,
|
|
815
|
+
swath_info["sensing_start"],
|
|
756
816
|
)
|
|
757
817
|
burst_info["scp_ecf"], burst_info["scp_llh"] = _update_geo_data_info(
|
|
758
818
|
swath_info, burst_info
|
|
@@ -767,6 +827,7 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
767
827
|
burst_info_list = []
|
|
768
828
|
|
|
769
829
|
scps = _get_scps(swath_info, len(burst_list))
|
|
830
|
+
t_slice = int(_get_slice(product_root_node))
|
|
770
831
|
for j, burst in enumerate(burst_list):
|
|
771
832
|
# set preliminary geodata (required for projection)
|
|
772
833
|
burst_info = dict()
|
|
@@ -791,42 +852,53 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
791
852
|
(last_row, first_col),
|
|
792
853
|
]
|
|
793
854
|
|
|
794
|
-
# This is the first and last zero doppler times of the columns in the burst.
|
|
795
|
-
# Not really CollectStart and CollectDuration in SICD (first last pulse time)
|
|
796
|
-
start = dateutil.parser.parse(burst.find("./azimuthTime").text)
|
|
797
|
-
t_slice = int(_get_slice(product_root_node))
|
|
798
855
|
swath = _get_swath(product_root_node)
|
|
856
|
+
# Use burst index for burst_id to maintain SARPy compatibility
|
|
799
857
|
burst_info["core_name"] = (
|
|
800
|
-
f"{
|
|
858
|
+
f"{swath_info['sensing_start'].strftime('%d%b%YT%H%M%S').upper()}_{product_root_node.find('./adsHeader/missionId').text}{product_root_node.find('./adsHeader/missionDataTakeId').text}_{t_slice:02d}_{swath}_{j + 1:02d}"
|
|
859
|
+
)
|
|
860
|
+
arp_poly_coefs = _compute_arp_poly_coefs(
|
|
861
|
+
product_root_node, swath_info["sensing_start"]
|
|
801
862
|
)
|
|
802
|
-
|
|
803
|
-
burst_info["parameters"] = {"BURST": f"{j + 1:d}"}
|
|
804
|
-
arp_poly_coefs = _compute_arp_poly_coefs(product_root_node, start)
|
|
805
863
|
burst_info["arp_poly_coefs"] = arp_poly_coefs.T
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
864
|
+
|
|
865
|
+
az_time = dateutil.parser.parse(burst.find("./azimuthTime").text)
|
|
866
|
+
first_line_relative_start = (
|
|
867
|
+
az_time - swath_info["sensing_start"]
|
|
868
|
+
).total_seconds()
|
|
869
|
+
early, late = _calc_rma_and_grid_info(
|
|
870
|
+
swath_info,
|
|
871
|
+
burst_info,
|
|
872
|
+
first_line_relative_start,
|
|
873
|
+
swath_info["sensing_start"],
|
|
874
|
+
)
|
|
809
875
|
prf = float(
|
|
810
876
|
product_root_node.find(
|
|
811
877
|
"./generalAnnotation/downlinkInformationList/downlinkInformation/prf"
|
|
812
878
|
).text
|
|
813
879
|
)
|
|
814
|
-
burst_info["collect_start"] =
|
|
815
|
-
burst_info["collect_duration"] =
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
burst_info["
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
burst_info["
|
|
822
|
-
burst_info["
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
880
|
+
burst_info["collect_start"] = swath_info["sensing_start"]
|
|
881
|
+
burst_info["collect_duration"] = (
|
|
882
|
+
swath_info["sensing_stop"] - swath_info["sensing_start"]
|
|
883
|
+
).total_seconds()
|
|
884
|
+
burst_info["ipp_set_tstart"] = early
|
|
885
|
+
burst_info["ipp_set_tend"] = late
|
|
886
|
+
burst_info["ipp_set_ippstart"] = round(early * prf)
|
|
887
|
+
burst_info["ipp_set_ippend"] = round(late * prf) - 1
|
|
888
|
+
burst_info["tstart_proc"] = early
|
|
889
|
+
burst_info["tend_proc"] = late
|
|
890
|
+
|
|
891
|
+
if burst.find("./burstId") is not None:
|
|
892
|
+
burst_id = burst.findtext("./burstId")
|
|
893
|
+
else:
|
|
894
|
+
burst_id = _compute_burst_id(base_info, swath_info, burst_info)
|
|
895
|
+
|
|
896
|
+
# Keep BURST as the index to maintain SARPy compatibility
|
|
897
|
+
burst_info["parameters"] = {
|
|
898
|
+
"BURST": f"{j + 1:02d}",
|
|
899
|
+
"BURST_ID": burst_id,
|
|
900
|
+
}
|
|
901
|
+
|
|
830
902
|
burst_info["arp_poly_coefs"] = arp_poly_coefs.T
|
|
831
903
|
|
|
832
904
|
burst_info["scp_ecf"], burst_info["scp_llh"] = _update_geo_data_info(
|
|
@@ -848,7 +920,7 @@ def _collect_burst_info(product_root_node, swath_info):
|
|
|
848
920
|
|
|
849
921
|
def _calc_radiometric_info(cal_file_name, swath_info, burst_info_list):
|
|
850
922
|
"""Compute radiometric polys"""
|
|
851
|
-
cal_root_node =
|
|
923
|
+
cal_root_node = lxml.etree.parse(cal_file_name).getroot()
|
|
852
924
|
cal_vector_list = cal_root_node.findall(
|
|
853
925
|
"./{*}calibrationVectorList/{*}calibrationVector"
|
|
854
926
|
)
|
|
@@ -884,14 +956,15 @@ def _calc_radiometric_info(cal_file_name, swath_info, burst_info_list):
|
|
|
884
956
|
gamma = 1.0 / (gamma * gamma)
|
|
885
957
|
|
|
886
958
|
for idx, burst_info in enumerate(burst_info_list):
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
)
|
|
959
|
+
burst_first_line = idx * lines_per_burst
|
|
960
|
+
burst_last_line = burst_first_line + lines_per_burst
|
|
961
|
+
valid_lines = (line >= burst_first_line) & (line < burst_last_line)
|
|
890
962
|
valid_count = np.sum(valid_lines)
|
|
891
963
|
if valid_count == 0:
|
|
892
964
|
# this burst contained no useful calibration data
|
|
893
965
|
return
|
|
894
966
|
|
|
967
|
+
max_poly_order = 4
|
|
895
968
|
first_row = swath_info["first_row"]
|
|
896
969
|
first_col = swath_info["first_col"]
|
|
897
970
|
scp_row = swath_info["scp_pixel"][0]
|
|
@@ -899,32 +972,45 @@ def _calc_radiometric_info(cal_file_name, swath_info, burst_info_list):
|
|
|
899
972
|
row_ss = swath_info["row_ss"]
|
|
900
973
|
col_ss = swath_info["col_ss"]
|
|
901
974
|
coords_rg = (pixel[valid_lines] + first_row - scp_row) * row_ss
|
|
902
|
-
coords_az = (
|
|
975
|
+
coords_az = (
|
|
976
|
+
line[valid_lines] + first_col - scp_col - burst_first_line
|
|
977
|
+
) * col_ss
|
|
903
978
|
# NB: coords_rg = (valid_count, M) and coords_az = (valid_count, )
|
|
904
979
|
coords_az = np.repeat(coords_az, pixel.shape[1])
|
|
905
980
|
if valid_count > 1:
|
|
906
981
|
coords_az = coords_az.reshape((valid_count, -1))
|
|
982
|
+
max_poly_order_rg = min(max_poly_order, len(coords_rg) - 1)
|
|
983
|
+
max_poly_order_az = min(max_poly_order, len(coords_az) - 1)
|
|
907
984
|
|
|
908
|
-
|
|
985
|
+
sigma_vals = sigma[valid_lines, :].flatten()
|
|
986
|
+
sigma_tol = np.maximum(np.std(sigma_vals) * 1e-1, np.mean(sigma_vals) * 1e-2)
|
|
987
|
+
burst_info["radiometric"]["sigma_zero_poly_coefs"] = utils.polyfit2d_tol(
|
|
909
988
|
coords_rg.flatten(),
|
|
910
989
|
coords_az.flatten(),
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
990
|
+
sigma_vals,
|
|
991
|
+
max_poly_order_rg,
|
|
992
|
+
max_poly_order_az,
|
|
993
|
+
sigma_tol,
|
|
914
994
|
)
|
|
915
|
-
|
|
995
|
+
beta_vals = beta[valid_lines, :].flatten()
|
|
996
|
+
beta_tol = np.maximum(np.std(beta_vals) * 1e-1, np.mean(beta_vals) * 1e-2)
|
|
997
|
+
burst_info["radiometric"]["beta_zero_poly_coefs"] = utils.polyfit2d_tol(
|
|
916
998
|
coords_rg.flatten(),
|
|
917
999
|
coords_az.flatten(),
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1000
|
+
beta_vals,
|
|
1001
|
+
max_poly_order_rg,
|
|
1002
|
+
max_poly_order_az,
|
|
1003
|
+
beta_tol,
|
|
921
1004
|
)
|
|
922
|
-
|
|
1005
|
+
gamma_vals = gamma[valid_lines, :].flatten()
|
|
1006
|
+
gamma_tol = np.maximum(np.std(gamma_vals) * 1e-1, np.mean(gamma_vals) * 1e-2)
|
|
1007
|
+
burst_info["radiometric"]["gamma_zero_poly_coefs"] = utils.polyfit2d_tol(
|
|
923
1008
|
coords_rg.flatten(),
|
|
924
1009
|
coords_az.flatten(),
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1010
|
+
gamma_vals,
|
|
1011
|
+
max_poly_order_rg,
|
|
1012
|
+
max_poly_order_az,
|
|
1013
|
+
gamma_tol,
|
|
928
1014
|
)
|
|
929
1015
|
|
|
930
1016
|
range_weight_f = azimuth_weight_f = 1.0
|
|
@@ -950,7 +1036,7 @@ def _calc_radiometric_info(cal_file_name, swath_info, burst_info_list):
|
|
|
950
1036
|
|
|
951
1037
|
def _calc_noise_level_info(noise_file_name, swath_info, burst_info_list):
|
|
952
1038
|
"""Compute noise poly"""
|
|
953
|
-
noise_root_node =
|
|
1039
|
+
noise_root_node = lxml.etree.parse(noise_file_name).getroot()
|
|
954
1040
|
mode_id = swath_info["mode_id"]
|
|
955
1041
|
lines_per_burst = swath_info["num_cols"]
|
|
956
1042
|
range_size_pixels = swath_info["num_rows"]
|
|
@@ -1017,7 +1103,7 @@ def _calc_noise_level_info(noise_file_name, swath_info, burst_info_list):
|
|
|
1017
1103
|
else:
|
|
1018
1104
|
azimuth_line, azimuth_noise = None, None
|
|
1019
1105
|
|
|
1020
|
-
rg_poly_order = min(
|
|
1106
|
+
rg_poly_order = min(4, range_pixel[0].size - 1)
|
|
1021
1107
|
first_row = swath_info["first_row"]
|
|
1022
1108
|
first_col = swath_info["first_col"]
|
|
1023
1109
|
scp_row = swath_info["scp_pixel"][0]
|
|
@@ -1033,12 +1119,13 @@ def _calc_noise_level_info(noise_file_name, swath_info, burst_info_list):
|
|
|
1033
1119
|
|
|
1034
1120
|
coords_az_2d, coords_rg_2d = np.meshgrid(coords_az, coords_rg)
|
|
1035
1121
|
|
|
1036
|
-
noise_poly = utils.
|
|
1122
|
+
noise_poly = utils.polyfit2d_tol(
|
|
1037
1123
|
coords_rg_2d.flatten(),
|
|
1038
1124
|
coords_az_2d.flatten(),
|
|
1039
1125
|
np.array(range_noise).flatten(),
|
|
1040
1126
|
rg_poly_order,
|
|
1041
1127
|
az_poly_order,
|
|
1128
|
+
1e-2,
|
|
1042
1129
|
)
|
|
1043
1130
|
else:
|
|
1044
1131
|
# TOPSAR has single LUT per burst
|
|
@@ -1083,9 +1170,8 @@ def _calc_noise_level_info(noise_file_name, swath_info, burst_info_list):
|
|
|
1083
1170
|
|
|
1084
1171
|
|
|
1085
1172
|
def _complete_filename(swath_info, burst_info, filename_template):
|
|
1086
|
-
core_name = burst_info["core_name"]
|
|
1087
|
-
burst = core_name[-2:]
|
|
1088
1173
|
swath = swath_info["parameters"]["SWATH"]
|
|
1174
|
+
burst = burst_info["parameters"]["BURST"]
|
|
1089
1175
|
polarization = swath_info["tx_rcv_polarization_proc"].replace(":", "")
|
|
1090
1176
|
formatted_name = filename_template.name.format(
|
|
1091
1177
|
swath=swath, burst=burst, pol=polarization
|
|
@@ -1096,335 +1182,244 @@ def _complete_filename(swath_info, burst_info, filename_template):
|
|
|
1096
1182
|
|
|
1097
1183
|
|
|
1098
1184
|
def _create_sicd_xml(base_info, swath_info, burst_info, classification):
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
# Collection Info
|
|
1102
|
-
collection_info_node = em.CollectionInfo(
|
|
1103
|
-
em.CollectorName(swath_info["collector_name"]),
|
|
1104
|
-
em.CoreName(burst_info["core_name"]),
|
|
1105
|
-
em.CollectType(base_info["collect_type"]),
|
|
1106
|
-
em.RadarMode(
|
|
1107
|
-
em.ModeType(base_info["mode_type"]),
|
|
1108
|
-
em.ModeID(swath_info["mode_id"]),
|
|
1109
|
-
),
|
|
1110
|
-
em.Classification(classification),
|
|
1111
|
-
em.Parameter({"name": "SLICE"}, swath_info["parameters"]["SLICE"]),
|
|
1112
|
-
em.Parameter({"name": "BURST"}, burst_info["parameters"]["BURST"]),
|
|
1113
|
-
em.Parameter({"name": "SWATH"}, swath_info["parameters"]["SWATH"]),
|
|
1114
|
-
em.Parameter(
|
|
1115
|
-
{"name": "ORBIT_SOURCE"}, swath_info["parameters"]["ORBIT_SOURCE"]
|
|
1116
|
-
),
|
|
1117
|
-
)
|
|
1118
|
-
|
|
1119
|
-
# Image Creation
|
|
1120
|
-
image_creation_node = em.ImageCreation(
|
|
1121
|
-
em.Application(base_info["creation_application"]),
|
|
1122
|
-
em.DateTime(base_info["creation_date_time"].strftime("%Y-%m-%dT%H:%M:%SZ")),
|
|
1123
|
-
em.Site(base_info["creation_site"]),
|
|
1124
|
-
em.Profile(f"sarkit-convert {__version__}"),
|
|
1185
|
+
sicd_xml_obj = lxml.etree.Element(
|
|
1186
|
+
f"{{{NSMAP['sicd']}}}SICD", nsmap={None: NSMAP["sicd"]}
|
|
1125
1187
|
)
|
|
1188
|
+
sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
|
|
1126
1189
|
|
|
1127
|
-
#
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
),
|
|
1142
|
-
em.ValidData(
|
|
1143
|
-
{"size": "4"},
|
|
1144
|
-
em.Vertex(
|
|
1145
|
-
{"index": "1"},
|
|
1146
|
-
em.Row(str(burst_info["valid_data"][0][0])),
|
|
1147
|
-
em.Col(str(burst_info["valid_data"][0][1])),
|
|
1148
|
-
),
|
|
1149
|
-
em.Vertex(
|
|
1150
|
-
{"index": "2"},
|
|
1151
|
-
em.Row(str(burst_info["valid_data"][1][0])),
|
|
1152
|
-
em.Col(str(burst_info["valid_data"][1][1])),
|
|
1153
|
-
),
|
|
1154
|
-
em.Vertex(
|
|
1155
|
-
{"index": "3"},
|
|
1156
|
-
em.Row(str(burst_info["valid_data"][2][0])),
|
|
1157
|
-
em.Col(str(burst_info["valid_data"][2][1])),
|
|
1158
|
-
),
|
|
1159
|
-
em.Vertex(
|
|
1160
|
-
{"index": "4"},
|
|
1161
|
-
em.Row(str(burst_info["valid_data"][3][0])),
|
|
1162
|
-
em.Col(str(burst_info["valid_data"][3][1])),
|
|
1163
|
-
),
|
|
1164
|
-
),
|
|
1190
|
+
# Collection Info
|
|
1191
|
+
parameter = [
|
|
1192
|
+
("SLICE", swath_info["parameters"]["SLICE"]),
|
|
1193
|
+
("BURST", burst_info["parameters"]["BURST"]),
|
|
1194
|
+
("SWATH", swath_info["parameters"]["SWATH"]),
|
|
1195
|
+
("ORBIT_SOURCE", swath_info["parameters"]["ORBIT_SOURCE"]),
|
|
1196
|
+
]
|
|
1197
|
+
if burst_info["parameters"].get("BURST_ID") is not None:
|
|
1198
|
+
parameter.append(("BURST_ID", str(burst_info["parameters"]["BURST_ID"])))
|
|
1199
|
+
parameter.extend(
|
|
1200
|
+
[
|
|
1201
|
+
("MISSION_DATA_TAKE_ID", swath_info["parameters"]["MISSION_DATA_TAKE_ID"]),
|
|
1202
|
+
("RELATIVE_ORBIT_NUM", str(base_info["parameters"]["RELATIVE_ORBIT_NUM"])),
|
|
1203
|
+
]
|
|
1165
1204
|
)
|
|
1205
|
+
sicd_ew["CollectionInfo"] = {
|
|
1206
|
+
"CollectorName": swath_info["collector_name"],
|
|
1207
|
+
"CoreName": burst_info["core_name"],
|
|
1208
|
+
"CollectType": base_info["collect_type"],
|
|
1209
|
+
"RadarMode": {
|
|
1210
|
+
"ModeType": base_info["mode_type"],
|
|
1211
|
+
"ModeID": swath_info["mode_id"],
|
|
1212
|
+
},
|
|
1213
|
+
"Classification": classification,
|
|
1214
|
+
"Parameter": parameter,
|
|
1215
|
+
}
|
|
1216
|
+
sicd_ew["ImageCreation"] = {
|
|
1217
|
+
"Application": base_info["creation_application"],
|
|
1218
|
+
"DateTime": base_info["creation_date_time"],
|
|
1219
|
+
}
|
|
1220
|
+
sicd_ew["ImageData"] = {
|
|
1221
|
+
"PixelType": swath_info["pixel_type"],
|
|
1222
|
+
"NumRows": swath_info["num_rows"],
|
|
1223
|
+
"NumCols": swath_info["num_cols"],
|
|
1224
|
+
"FirstRow": swath_info["first_row"],
|
|
1225
|
+
"FirstCol": swath_info["first_col"],
|
|
1226
|
+
"FullImage": {
|
|
1227
|
+
"NumRows": swath_info["num_rows"],
|
|
1228
|
+
"NumCols": swath_info["num_cols"],
|
|
1229
|
+
},
|
|
1230
|
+
"SCPPixel": swath_info["scp_pixel"],
|
|
1231
|
+
"ValidData": burst_info["valid_data"],
|
|
1232
|
+
}
|
|
1166
1233
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
geo_data_node = em.GeoData(
|
|
1175
|
-
em.EarthModel("WGS_84"),
|
|
1176
|
-
em.SCP(
|
|
1177
|
-
em.ECF(*_make_xyz(burst_info["scp_ecf"])),
|
|
1178
|
-
em.LLH(*__make_llh(burst_info["scp_llh"])),
|
|
1179
|
-
),
|
|
1180
|
-
em.ImageCorners(),
|
|
1181
|
-
em.ValidData(),
|
|
1182
|
-
)
|
|
1234
|
+
sicd_ew["GeoData"] = {
|
|
1235
|
+
"EarthModel": "WGS_84",
|
|
1236
|
+
"SCP": {
|
|
1237
|
+
"ECF": burst_info["scp_ecf"],
|
|
1238
|
+
"LLH": burst_info["scp_llh"],
|
|
1239
|
+
},
|
|
1240
|
+
}
|
|
1183
1241
|
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
str(swath_info["col_window_name"]),
|
|
1228
|
-
),
|
|
1229
|
-
*(
|
|
1230
|
-
[
|
|
1231
|
-
em.Parameter(
|
|
1232
|
-
{"name": "COEFFICIENT"},
|
|
1233
|
-
str(swath_info["col_params"]),
|
|
1234
|
-
)
|
|
1235
|
-
]
|
|
1236
|
-
if swath_info["col_params"] is not None
|
|
1237
|
-
else []
|
|
1238
|
-
),
|
|
1239
|
-
),
|
|
1240
|
-
),
|
|
1241
|
-
)
|
|
1242
|
-
sksicd.Poly2dType().set_elem(
|
|
1243
|
-
grid_node.find("./{*}TimeCOAPoly"), burst_info["time_coa_poly_coefs"]
|
|
1244
|
-
)
|
|
1245
|
-
sksicd.Poly2dType().set_elem(
|
|
1246
|
-
grid_node.find("./{*}Row/{*}DeltaKCOAPoly"), swath_info["row_deltak_coa_poly"]
|
|
1247
|
-
)
|
|
1248
|
-
sksicd.Poly2dType().set_elem(
|
|
1249
|
-
grid_node.find("./{*}Col/{*}DeltaKCOAPoly"), burst_info["col_deltak_coa_poly"]
|
|
1250
|
-
)
|
|
1251
|
-
wgtfunc = em.WgtFunct()
|
|
1252
|
-
sksicd.TRANSCODERS["Grid/Row/WgtFunct"].set_elem(wgtfunc, swath_info["row_wgts"])
|
|
1253
|
-
grid_node.find("./{*}Row").append(wgtfunc)
|
|
1254
|
-
wgtfunc = em.WgtFunct()
|
|
1255
|
-
sksicd.TRANSCODERS["Grid/Col/WgtFunct"].set_elem(wgtfunc, swath_info["col_wgts"])
|
|
1256
|
-
grid_node.find("./{*}Col").append(wgtfunc)
|
|
1242
|
+
sicd_ew["Grid"] = {
|
|
1243
|
+
"ImagePlane": swath_info["image_plane"],
|
|
1244
|
+
"Type": swath_info["grid_type"],
|
|
1245
|
+
"TimeCOAPoly": burst_info["time_coa_poly_coefs"],
|
|
1246
|
+
"Row": {
|
|
1247
|
+
"UVectECF": burst_info["row_uvect_ecf"],
|
|
1248
|
+
"SS": swath_info["row_ss"],
|
|
1249
|
+
"ImpRespWid": swath_info["row_imp_res_wid"],
|
|
1250
|
+
"Sgn": swath_info["row_sgn"],
|
|
1251
|
+
"ImpRespBW": swath_info["row_imp_res_bw"],
|
|
1252
|
+
"KCtr": swath_info["row_kctr"],
|
|
1253
|
+
"DeltaK1": burst_info["row_delta_k1"],
|
|
1254
|
+
"DeltaK2": burst_info["row_delta_k2"],
|
|
1255
|
+
"DeltaKCOAPoly": swath_info["row_deltak_coa_poly"],
|
|
1256
|
+
"WgtType": {
|
|
1257
|
+
"WindowName": swath_info["row_window_name"],
|
|
1258
|
+
},
|
|
1259
|
+
"WgtFunct": swath_info["row_wgts"],
|
|
1260
|
+
},
|
|
1261
|
+
"Col": {
|
|
1262
|
+
"UVectECF": burst_info["col_uvect_ecf"],
|
|
1263
|
+
"SS": swath_info["col_ss"],
|
|
1264
|
+
"ImpRespWid": swath_info["col_imp_res_wid"],
|
|
1265
|
+
"Sgn": swath_info["col_sgn"],
|
|
1266
|
+
"ImpRespBW": swath_info["col_imp_res_bw"],
|
|
1267
|
+
"KCtr": swath_info["col_kctr"],
|
|
1268
|
+
"DeltaK1": burst_info["col_delta_k1"],
|
|
1269
|
+
"DeltaK2": burst_info["col_delta_k2"],
|
|
1270
|
+
"DeltaKCOAPoly": burst_info["col_deltak_coa_poly"],
|
|
1271
|
+
"WgtType": {
|
|
1272
|
+
"WindowName": swath_info["col_window_name"],
|
|
1273
|
+
},
|
|
1274
|
+
"WgtFunct": swath_info["col_wgts"],
|
|
1275
|
+
},
|
|
1276
|
+
}
|
|
1277
|
+
if swath_info["row_params"] is not None:
|
|
1278
|
+
sicd_ew["Grid"]["Row"]["WgtType"]["Parameter"] = [
|
|
1279
|
+
("COEFFICIENT", str(swath_info["row_params"]))
|
|
1280
|
+
]
|
|
1281
|
+
if swath_info["col_params"] is not None:
|
|
1282
|
+
sicd_ew["Grid"]["Col"]["WgtType"]["Parameter"] = [
|
|
1283
|
+
("COEFFICIENT", str(swath_info["col_params"]))
|
|
1284
|
+
]
|
|
1257
1285
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
timeline_node.find("./{*}IPP/{*}Set/{*}IPPPoly"), swath_info["ipp_poly"]
|
|
1276
|
-
)
|
|
1286
|
+
sicd_ew["Timeline"] = {
|
|
1287
|
+
"CollectStart": burst_info["collect_start"],
|
|
1288
|
+
"CollectDuration": burst_info["collect_duration"],
|
|
1289
|
+
"IPP": {
|
|
1290
|
+
"@size": 1,
|
|
1291
|
+
"Set": [
|
|
1292
|
+
{
|
|
1293
|
+
"@index": 1,
|
|
1294
|
+
"TStart": burst_info["ipp_set_tstart"],
|
|
1295
|
+
"TEnd": burst_info["ipp_set_tend"],
|
|
1296
|
+
"IPPStart": burst_info["ipp_set_ippstart"],
|
|
1297
|
+
"IPPEnd": burst_info["ipp_set_ippend"],
|
|
1298
|
+
"IPPPoly": swath_info["ipp_poly"],
|
|
1299
|
+
}
|
|
1300
|
+
],
|
|
1301
|
+
},
|
|
1302
|
+
}
|
|
1277
1303
|
|
|
1278
|
-
|
|
1279
|
-
position_node = em.Position(em.ARPPoly())
|
|
1280
|
-
sksicd.XyzPolyType().set_elem(
|
|
1281
|
-
position_node.find("./{*}ARPPoly"), burst_info["arp_poly_coefs"]
|
|
1282
|
-
)
|
|
1304
|
+
sicd_ew["Position"]["ARPPoly"] = burst_info["arp_poly_coefs"]
|
|
1283
1305
|
|
|
1284
1306
|
# Radar Collection
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1307
|
+
sicd_ew["RadarCollection"] = {
|
|
1308
|
+
"TxFrequency": {
|
|
1309
|
+
"Min": swath_info["tx_freq"][0],
|
|
1310
|
+
"Max": swath_info["tx_freq"][1],
|
|
1311
|
+
},
|
|
1312
|
+
"Waveform": {
|
|
1313
|
+
"@size": len(swath_info["rcv_window_length"]),
|
|
1314
|
+
"WFParameters": [
|
|
1315
|
+
{
|
|
1316
|
+
"@index": i,
|
|
1317
|
+
"TxPulseLength": swath_info["tx_pulse_length"],
|
|
1318
|
+
"TxRFBandwidth": swath_info["tx_rf_bw"],
|
|
1319
|
+
"TxFreqStart": swath_info["tx_freq_start"],
|
|
1320
|
+
"TxFMRate": swath_info["tx_fm_rate"],
|
|
1321
|
+
"RcvWindowLength": swl,
|
|
1322
|
+
"ADCSampleRate": swath_info["adc_sample_rate"],
|
|
1323
|
+
}
|
|
1302
1324
|
for i, swl in enumerate(swath_info["rcv_window_length"], start=1)
|
|
1303
1325
|
],
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1326
|
+
},
|
|
1327
|
+
"TxPolarization": swath_info["tx_polarization"],
|
|
1328
|
+
"RcvChannels": {
|
|
1329
|
+
"@size": len(base_info["tx_rcv_polarization"]),
|
|
1330
|
+
"ChanParameters": [
|
|
1331
|
+
{
|
|
1332
|
+
"@index": i,
|
|
1333
|
+
"TxRcvPolarization": entry,
|
|
1334
|
+
}
|
|
1313
1335
|
for i, entry in enumerate(base_info["tx_rcv_polarization"], start=1)
|
|
1314
1336
|
],
|
|
1315
|
-
|
|
1316
|
-
|
|
1337
|
+
},
|
|
1338
|
+
}
|
|
1317
1339
|
|
|
1318
|
-
|
|
1340
|
+
chan_index = None
|
|
1319
1341
|
for i, pol in enumerate(base_info["tx_rcv_polarization"], start=1):
|
|
1320
1342
|
if pol == swath_info["tx_rcv_polarization_proc"]:
|
|
1321
|
-
|
|
1343
|
+
chan_index = str(i)
|
|
1322
1344
|
|
|
1323
1345
|
# Image Formation
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
em.ChanIndex(chan_indices),
|
|
1329
|
-
),
|
|
1330
|
-
em.TxRcvPolarizationProc(swath_info["tx_rcv_polarization_proc"]),
|
|
1331
|
-
em.TStartProc(str(swath_info["t_start_proc"])),
|
|
1332
|
-
em.TEndProc(str(burst_info["tend_proc"])),
|
|
1333
|
-
em.TxFrequencyProc(
|
|
1334
|
-
em.MinProc(str(swath_info["tx_freq_proc"][0])),
|
|
1335
|
-
em.MaxProc(str(swath_info["tx_freq_proc"][1])),
|
|
1336
|
-
),
|
|
1337
|
-
em.ImageFormAlgo(swath_info["image_form_algo"]),
|
|
1338
|
-
em.STBeamComp(swath_info["st_beam_comp"]),
|
|
1339
|
-
em.ImageBeamComp(swath_info["image_beam_comp"]),
|
|
1340
|
-
em.AzAutofocus(swath_info["az_autofocus"]),
|
|
1341
|
-
em.RgAutofocus(swath_info["rg_autofocus"]),
|
|
1346
|
+
now = (
|
|
1347
|
+
datetime.datetime.now(datetime.timezone.utc)
|
|
1348
|
+
.isoformat(timespec="microseconds")
|
|
1349
|
+
.replace("+00:00", "Z")
|
|
1342
1350
|
)
|
|
1351
|
+
sicd_ew["ImageFormation"] = {
|
|
1352
|
+
"RcvChanProc": {
|
|
1353
|
+
"NumChanProc": 1,
|
|
1354
|
+
"PRFScaleFactor": 1,
|
|
1355
|
+
"ChanIndex": [chan_index],
|
|
1356
|
+
},
|
|
1357
|
+
"TxRcvPolarizationProc": swath_info["tx_rcv_polarization_proc"],
|
|
1358
|
+
"TStartProc": burst_info["tstart_proc"],
|
|
1359
|
+
"TEndProc": burst_info["tend_proc"],
|
|
1360
|
+
"TxFrequencyProc": {
|
|
1361
|
+
"MinProc": swath_info["tx_freq_proc"][0],
|
|
1362
|
+
"MaxProc": swath_info["tx_freq_proc"][1],
|
|
1363
|
+
},
|
|
1364
|
+
"ImageFormAlgo": swath_info["image_form_algo"],
|
|
1365
|
+
"STBeamComp": swath_info["st_beam_comp"],
|
|
1366
|
+
"ImageBeamComp": swath_info["image_beam_comp"],
|
|
1367
|
+
"AzAutofocus": swath_info["az_autofocus"],
|
|
1368
|
+
"RgAutofocus": swath_info["rg_autofocus"],
|
|
1369
|
+
"Processing": [
|
|
1370
|
+
{
|
|
1371
|
+
"Type": f"sarkit-convert {__version__} @ {now}",
|
|
1372
|
+
"Applied": True,
|
|
1373
|
+
},
|
|
1374
|
+
],
|
|
1375
|
+
}
|
|
1343
1376
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
)
|
|
1357
|
-
sksicd.PolyType().set_elem(
|
|
1358
|
-
rma_node.find("./{*}INCA/{*}TimeCAPoly"), burst_info["time_ca_poly_coefs"]
|
|
1359
|
-
)
|
|
1360
|
-
sksicd.Poly2dType().set_elem(
|
|
1361
|
-
rma_node.find("./{*}INCA/{*}DRateSFPoly"), burst_info["drsf_poly_coefs"]
|
|
1362
|
-
)
|
|
1363
|
-
sksicd.Poly2dType().set_elem(
|
|
1364
|
-
rma_node.find("./{*}INCA/{*}DopCentroidPoly"),
|
|
1365
|
-
burst_info["doppler_centroid_poly_coefs"],
|
|
1366
|
-
)
|
|
1377
|
+
sicd_ew["RMA"] = {
|
|
1378
|
+
"RMAlgoType": swath_info["rm_algo_type"],
|
|
1379
|
+
"ImageType": swath_info["image_type"],
|
|
1380
|
+
"INCA": {
|
|
1381
|
+
"TimeCAPoly": burst_info["time_ca_poly_coefs"],
|
|
1382
|
+
"R_CA_SCP": swath_info["r_ca_scp"],
|
|
1383
|
+
"FreqZero": swath_info["freq_zero"],
|
|
1384
|
+
"DRateSFPoly": burst_info["drsf_poly_coefs"],
|
|
1385
|
+
"DopCentroidPoly": burst_info["doppler_centroid_poly_coefs"],
|
|
1386
|
+
"DopCentroidCOA": swath_info["dop_centroid_coa"],
|
|
1387
|
+
},
|
|
1388
|
+
}
|
|
1367
1389
|
|
|
1368
|
-
sicd_xml_obj = em.SICD(
|
|
1369
|
-
collection_info_node,
|
|
1370
|
-
image_creation_node,
|
|
1371
|
-
image_data_node,
|
|
1372
|
-
geo_data_node,
|
|
1373
|
-
grid_node,
|
|
1374
|
-
timeline_node,
|
|
1375
|
-
position_node,
|
|
1376
|
-
radar_collection_node,
|
|
1377
|
-
image_formation_node,
|
|
1378
|
-
rma_node,
|
|
1379
|
-
)
|
|
1380
|
-
breakpoint()
|
|
1381
1390
|
# Add Radiometric after Sentinel baseline processing calibration update on 25 Nov 2015.
|
|
1382
1391
|
if "radiometric" in burst_info:
|
|
1383
1392
|
# Radiometric
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
sksicd.Poly2dType().set_elem(
|
|
1403
|
-
radiometric_node.find("./{*}SigmaZeroSFPoly"),
|
|
1404
|
-
burst_info["radiometric"]["sigma_zero_poly_coefs"],
|
|
1405
|
-
)
|
|
1406
|
-
sksicd.Poly2dType().set_elem(
|
|
1407
|
-
radiometric_node.find("./{*}BetaZeroSFPoly"),
|
|
1408
|
-
burst_info["radiometric"]["beta_zero_poly_coefs"],
|
|
1409
|
-
)
|
|
1410
|
-
sksicd.Poly2dType().set_elem(
|
|
1411
|
-
radiometric_node.find("./{*}GammaZeroSFPoly"),
|
|
1412
|
-
burst_info["radiometric"]["gamma_zero_poly_coefs"],
|
|
1413
|
-
)
|
|
1414
|
-
|
|
1415
|
-
sicd_xml_obj.find("{*}RMA").addprevious(radiometric_node)
|
|
1393
|
+
sicd_ew["Radiometric"]["NoiseLevel"]["NoiseLevelType"] = burst_info[
|
|
1394
|
+
"radiometric"
|
|
1395
|
+
]["noise_level_type"]
|
|
1396
|
+
sicd_ew["Radiometric"]["NoiseLevel"]["NoisePoly"] = burst_info["radiometric"][
|
|
1397
|
+
"noise_poly_coefs"
|
|
1398
|
+
]
|
|
1399
|
+
sicd_ew["Radiometric"]["RCSSFPoly"] = burst_info["radiometric"][
|
|
1400
|
+
"rcs_sf_poly_coefs"
|
|
1401
|
+
]
|
|
1402
|
+
sicd_ew["Radiometric"]["SigmaZeroSFPoly"] = burst_info["radiometric"][
|
|
1403
|
+
"sigma_zero_poly_coefs"
|
|
1404
|
+
]
|
|
1405
|
+
sicd_ew["Radiometric"]["BetaZeroSFPoly"] = burst_info["radiometric"][
|
|
1406
|
+
"beta_zero_poly_coefs"
|
|
1407
|
+
]
|
|
1408
|
+
sicd_ew["Radiometric"]["GammaZeroSFPoly"] = burst_info["radiometric"][
|
|
1409
|
+
"gamma_zero_poly_coefs"
|
|
1410
|
+
]
|
|
1416
1411
|
|
|
1417
1412
|
return sicd_xml_obj
|
|
1418
1413
|
|
|
1419
1414
|
|
|
1420
|
-
def _update_geo_data(
|
|
1415
|
+
def _update_geo_data(sicd_ew):
|
|
1421
1416
|
# Update ImageCorners
|
|
1422
|
-
num_rows =
|
|
1423
|
-
num_cols =
|
|
1424
|
-
row_ss =
|
|
1425
|
-
col_ss =
|
|
1426
|
-
scp_pixel =
|
|
1427
|
-
scp_ecf =
|
|
1417
|
+
num_rows = sicd_ew["ImageData"]["NumRows"]
|
|
1418
|
+
num_cols = sicd_ew["ImageData"]["NumCols"]
|
|
1419
|
+
row_ss = sicd_ew["Grid"]["Row"]["SS"]
|
|
1420
|
+
col_ss = sicd_ew["Grid"]["Col"]["SS"]
|
|
1421
|
+
scp_pixel = sicd_ew["ImageData"]["SCPPixel"]
|
|
1422
|
+
scp_ecf = sicd_ew["GeoData"]["SCP"]["ECF"]
|
|
1428
1423
|
image_grid_locations = (
|
|
1429
1424
|
np.array(
|
|
1430
1425
|
[
|
|
@@ -1438,27 +1433,39 @@ def _update_geo_data(xml_helper):
|
|
|
1438
1433
|
) * [row_ss, col_ss]
|
|
1439
1434
|
|
|
1440
1435
|
icp_ecef, _, _ = sksicd.image_to_ground_plane(
|
|
1441
|
-
|
|
1436
|
+
sicd_ew.elem.getroottree(),
|
|
1442
1437
|
image_grid_locations,
|
|
1443
1438
|
scp_ecf,
|
|
1444
1439
|
sarkit.wgs84.up(sarkit.wgs84.cartesian_to_geodetic(scp_ecf)),
|
|
1445
1440
|
)
|
|
1446
1441
|
icp_llh = sarkit.wgs84.cartesian_to_geodetic(icp_ecef)
|
|
1447
|
-
|
|
1448
|
-
|
|
1442
|
+
sicd_ew["GeoData"]["ImageCorners"] = icp_llh[:, :2]
|
|
1443
|
+
sicd_ew["GeoData"]["ValidData"] = icp_llh[:, :2]
|
|
1449
1444
|
|
|
1450
1445
|
|
|
1451
|
-
def _update_rniirs_info(
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1446
|
+
def _update_rniirs_info(sicd_ew):
|
|
1447
|
+
info_density, predicted_rniirs = utils.get_rniirs_estimate(sicd_ew)
|
|
1448
|
+
sicd_ew["CollectionInfo"].add(
|
|
1449
|
+
"Parameter", ("INFORMATION_DENSITY", f"{info_density:.2G}")
|
|
1450
|
+
)
|
|
1451
|
+
sicd_ew["CollectionInfo"].add(
|
|
1452
|
+
"Parameter", ("PREDICTED_RNIIRS", f"{predicted_rniirs:.1f}")
|
|
1453
|
+
)
|
|
1455
1454
|
|
|
1456
|
-
param_node = em.Parameter({"name": "INFORMATION_DENSITY"}, f"{info_density:0.2G}")
|
|
1457
|
-
collection_info_node.append(param_node)
|
|
1458
|
-
param_node = em.Parameter({"name": "PREDICTED_RNIIRS"}, f"{predicted_rniirs:0.1f}")
|
|
1459
|
-
collection_info_node.append(param_node)
|
|
1460
1455
|
|
|
1461
|
-
|
|
1456
|
+
@contextlib.contextmanager
|
|
1457
|
+
def uint_tiff():
|
|
1458
|
+
"""Overwrite SAMPLEFORMAT to force TIFFILE not to upcast from complex int16 to complex64
|
|
1459
|
+
|
|
1460
|
+
Per S1-RS-MDA-52-7441 (Sentinel-1 Product Specification) Table 6-13:
|
|
1461
|
+
"Interpretation of pixel format. Set to 5 (complex signed integer, 'int16') for SLC products"
|
|
1462
|
+
"""
|
|
1463
|
+
oldformat = tifffile.SAMPLEFORMAT
|
|
1464
|
+
tifffile.SAMPLEFORMAT = lambda _: oldformat.UINT # treat as UINT
|
|
1465
|
+
try:
|
|
1466
|
+
yield
|
|
1467
|
+
finally:
|
|
1468
|
+
tifffile.SAMPLEFORMAT = oldformat
|
|
1462
1469
|
|
|
1463
1470
|
|
|
1464
1471
|
def main(args=None):
|
|
@@ -1481,83 +1488,80 @@ def main(args=None):
|
|
|
1481
1488
|
type=pathlib.Path,
|
|
1482
1489
|
help="path of the output SICD file. The strings '{swath}', '{burst}', '{pol}' will be replaced as appropriate for multiple images",
|
|
1483
1490
|
)
|
|
1484
|
-
parser.add_argument(
|
|
1485
|
-
"--ostaid",
|
|
1486
|
-
type=str,
|
|
1487
|
-
help="content of the originating station ID (OSTAID) field of the NITF header",
|
|
1488
|
-
default="Unknown",
|
|
1489
|
-
)
|
|
1490
1491
|
config = parser.parse_args(args)
|
|
1491
1492
|
|
|
1492
1493
|
manifest_filename = config.safe_product_folder / "manifest.safe"
|
|
1493
1494
|
|
|
1494
|
-
manifest_root =
|
|
1495
|
+
manifest_root = lxml.etree.parse(manifest_filename).getroot()
|
|
1495
1496
|
base_info = _collect_base_info(manifest_root)
|
|
1496
1497
|
files = _get_file_sets(config.safe_product_folder, manifest_root)
|
|
1497
1498
|
|
|
1498
1499
|
used_filenames = set()
|
|
1499
1500
|
for entry in files:
|
|
1500
|
-
product_root_node =
|
|
1501
|
+
product_root_node = lxml.etree.parse(entry["product"]).getroot()
|
|
1501
1502
|
swath_info = _collect_swath_info(product_root_node)
|
|
1502
|
-
burst_info_list = _collect_burst_info(product_root_node, swath_info)
|
|
1503
|
-
breakpoint()
|
|
1503
|
+
burst_info_list = _collect_burst_info(product_root_node, base_info, swath_info)
|
|
1504
1504
|
if base_info["creation_date_time"].date() >= np.datetime64("2015-11-25"):
|
|
1505
1505
|
[burst_info.update({"radiometric": {}}) for burst_info in burst_info_list]
|
|
1506
1506
|
_calc_radiometric_info(entry["calibration"], swath_info, burst_info_list)
|
|
1507
1507
|
_calc_noise_level_info(entry["noise"], swath_info, burst_info_list)
|
|
1508
1508
|
|
|
1509
1509
|
# Grab the data and write the files
|
|
1510
|
-
|
|
1510
|
+
# Overwrite SAMPLEFORMAT to force TIFFILE not to upcast from complex int16 to complex64
|
|
1511
|
+
# Treat as uint32 and handle the conversion later
|
|
1512
|
+
assert swath_info["pixel_type"] != "RE16I_IM16I", (
|
|
1513
|
+
"pixel handling assumes 'RE16I_IM16I'"
|
|
1514
|
+
)
|
|
1515
|
+
with uint_tiff(), tifffile.TiffFile(entry["data"]) as tif:
|
|
1511
1516
|
image = tif.asarray().T
|
|
1512
1517
|
image_width = tif.pages[0].tags.values()[0].value
|
|
1513
1518
|
begin_col = 0
|
|
1514
1519
|
for burst_info in burst_info_list:
|
|
1515
|
-
|
|
1520
|
+
sicd_xml_obj = _create_sicd_xml(
|
|
1516
1521
|
base_info, swath_info, burst_info, config.classification.upper()
|
|
1517
1522
|
)
|
|
1523
|
+
sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
|
|
1518
1524
|
# Add SCPCOA node
|
|
1519
|
-
|
|
1520
|
-
sicd.find("./{*}ImageFormation").addnext(scp_coa)
|
|
1521
|
-
xml_helper = sksicd.XmlHelper(et.ElementTree(sicd))
|
|
1525
|
+
sicd_ew["SCPCOA"] = sksicd.compute_scp_coa(sicd_xml_obj.getroottree())
|
|
1522
1526
|
# Update ImageCorners and ValidData
|
|
1523
|
-
_update_geo_data(
|
|
1527
|
+
_update_geo_data(sicd_ew)
|
|
1524
1528
|
|
|
1525
1529
|
# RNIIRS calcs require radiometric info
|
|
1526
1530
|
if "radiometric" in burst_info:
|
|
1527
|
-
_update_rniirs_info(
|
|
1531
|
+
_update_rniirs_info(sicd_ew)
|
|
1528
1532
|
|
|
1529
1533
|
# Check for XML consistency
|
|
1530
|
-
sicd_con = sarkit.verification.SicdConsistency(
|
|
1534
|
+
sicd_con = sarkit.verification.SicdConsistency(sicd_ew.elem)
|
|
1531
1535
|
sicd_con.check()
|
|
1532
1536
|
sicd_con.print_result(fail_detail=True)
|
|
1533
1537
|
|
|
1534
|
-
end_col = begin_col +
|
|
1538
|
+
end_col = begin_col + sicd_ew["ImageData"]["NumCols"]
|
|
1535
1539
|
subset = (slice(0, image_width, 1), slice(begin_col, end_col, 1))
|
|
1536
1540
|
begin_col = end_col
|
|
1537
|
-
image_subset = image[subset]
|
|
1541
|
+
image_subset = np.ascontiguousarray(image[subset])
|
|
1538
1542
|
pixel_type = swath_info["pixel_type"]
|
|
1539
|
-
view_dtype = sksicd.PIXEL_TYPES[pixel_type]["dtype"]
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
complex_data
|
|
1543
|
+
view_dtype = sksicd.PIXEL_TYPES[pixel_type]["dtype"].newbyteorder(
|
|
1544
|
+
image_subset.dtype.byteorder
|
|
1545
|
+
)
|
|
1546
|
+
complex_data = image_subset.view(view_dtype)
|
|
1543
1547
|
|
|
1544
1548
|
metadata = sksicd.NitfMetadata(
|
|
1545
|
-
xmltree=
|
|
1549
|
+
xmltree=sicd_ew.elem.getroottree(),
|
|
1546
1550
|
file_header_part={
|
|
1547
|
-
"ostaid":
|
|
1548
|
-
"ftitle":
|
|
1551
|
+
"ostaid": "ESA",
|
|
1552
|
+
"ftitle": sicd_ew["CollectionInfo"]["CoreName"],
|
|
1549
1553
|
"security": {
|
|
1550
1554
|
"clas": config.classification[0].upper(),
|
|
1551
1555
|
"clsy": "US",
|
|
1552
1556
|
},
|
|
1553
1557
|
},
|
|
1554
1558
|
im_subheader_part={
|
|
1555
|
-
"iid2":
|
|
1559
|
+
"iid2": sicd_ew["CollectionInfo"]["CoreName"],
|
|
1556
1560
|
"security": {
|
|
1557
1561
|
"clas": config.classification[0].upper(),
|
|
1558
1562
|
"clsy": "US",
|
|
1559
1563
|
},
|
|
1560
|
-
"isorce":
|
|
1564
|
+
"isorce": sicd_ew["CollectionInfo"]["CollectorName"],
|
|
1561
1565
|
},
|
|
1562
1566
|
de_subheader_part={
|
|
1563
1567
|
"security": {
|