sarkit-convert 0.1.0__tar.gz → 0.2.0__tar.gz
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-0.1.0 → sarkit_convert-0.2.0}/PKG-INFO +8 -18
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/pyproject.toml +20 -21
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/sarkit_convert/_utils.py +32 -0
- sarkit_convert-0.2.0/sarkit_convert/_version.py +1 -0
- sarkit_convert-0.1.0/sarkit_convert/csk.py → sarkit_convert-0.2.0/sarkit_convert/cosmo.py +216 -59
- sarkit_convert-0.2.0/sarkit_convert/create_arp_poly.py +166 -0
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/sarkit_convert/iceye.py +25 -14
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/sarkit_convert/sentinel.py +224 -178
- sarkit_convert-0.2.0/sarkit_convert/sidd_metadata.py +201 -0
- sarkit_convert-0.1.0/sarkit_convert/tsx.py → sarkit_convert-0.2.0/sarkit_convert/terrasar.py +165 -62
- sarkit_convert-0.2.0/tests/core/__init__.py +0 -0
- sarkit_convert-0.2.0/tests/core/data/README.md +3 -0
- sarkit_convert-0.2.0/tests/core/data/gdal_results.json +8140 -0
- sarkit_convert-0.2.0/tests/core/data/generate_gdal_results.py +64 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/LICENSE.txt +1 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1A_IW_RAW__0SDV_20250602T135218_20250602T135250_059468_0761DC_3FC1_Channel_IW2_151224.sicd.xml +1081 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1A_IW_RAW__0SDV_20250619T232444_20250619T232516_059722_076A87_E109_Channel_IW1_320291.sicd.xml +1174 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1B_IW_RAW__0SDV_20210709T233156_20210709T233229_027724_034EFB_2B2D_Channel_IW3_101200.sicd.xml +1235 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1B_IW_RAW__0SDV_20210711T135126_20210711T135158_027747_034FBD_708E_Channel_IW2_151224.sicd.xml +1150 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1C_IW_RAW__0SDV_20250527T014921_20250527T014954_002510_0053AA_6A5F_Channel_IW2_135523.sicd.xml +1081 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1C_IW_RAW__0SDV_20250527T135101_20250527T135133_002517_0053E1_EDEB_Channel_IW2_151224.sicd.xml +1082 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1C_IW_RAW__0SDV_20250608T014922_20250608T014954_002685_0058B2_FC36_Channel_IW2_135523.sicd.xml +1081 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1C_IW_RAW__0SDV_20250608T135101_20250608T135134_002692_0058EA_1F12_Channel_IW2_151224.sicd.xml +1082 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1C_IW_RAW__0SDV_20250615T052352_20250615T052425_002789_005BB9_CA6D_Channel_IW2_359460.sicd.xml +1082 -0
- sarkit_convert-0.2.0/tests/core/data/sentinel_sicd_xml/S1C_IW_RAW__0SDV_20250618T233138_20250618T233210_002844_005D47_D58C_Channel_IW3_101200.sicd.xml +1191 -0
- sarkit_convert-0.2.0/tests/core/test_create_arp.py +127 -0
- sarkit_convert-0.2.0/tests/core/test_dependencies.py +19 -0
- sarkit_convert-0.2.0/tests/core/test_sidd_metadata.py +45 -0
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/tests/core/test_utils.py +13 -0
- sarkit_convert-0.2.0/tests/cosmo/__init__.py +0 -0
- sarkit_convert-0.1.0/tests/cosmo/test_csk.py → sarkit_convert-0.2.0/tests/cosmo/test_cosmo.py +1 -1
- sarkit_convert-0.2.0/tests/cosmo/test_dependencies.py +18 -0
- sarkit_convert-0.2.0/tests/iceye/__init__.py +0 -0
- sarkit_convert-0.2.0/tests/iceye/test_dependencies.py +18 -0
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/tests/iceye/test_iceye.py +3 -1
- sarkit_convert-0.2.0/tests/sentinel/__init__.py +0 -0
- sarkit_convert-0.2.0/tests/sentinel/test_dependencies.py +18 -0
- sarkit_convert-0.2.0/tests/terrasar/__init__.py +0 -0
- sarkit_convert-0.2.0/tests/terrasar/test_dependencies.py +18 -0
- sarkit_convert-0.1.0/tests/tsx/test_tsx.py → sarkit_convert-0.2.0/tests/terrasar/test_terrasar.py +4 -4
- sarkit_convert-0.1.0/sarkit_convert/_version.py +0 -1
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/LICENSE +0 -0
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/README.md +0 -0
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/sarkit_convert/__init__.py +0 -0
- {sarkit_convert-0.1.0 → sarkit_convert-0.2.0}/tests/sentinel/test_sentinel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sarkit-convert
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python library for converting SAR data to standard formats.
|
|
5
5
|
Author-Email: Valkyrie Systems Corporation <info@govsco.com>
|
|
6
6
|
License: MIT
|
|
@@ -16,37 +16,27 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Requires-Python: >=3.11
|
|
17
17
|
Requires-Dist: lxml>=5.1.0
|
|
18
18
|
Requires-Dist: numpy>=1.26.3
|
|
19
|
-
Requires-Dist:
|
|
19
|
+
Requires-Dist: pyproj>=3.7.2
|
|
20
|
+
Requires-Dist: sarkit>=1.3.0
|
|
21
|
+
Requires-Dist: scipy>=1.15.1
|
|
20
22
|
Provides-Extra: iceye
|
|
21
23
|
Requires-Dist: h5py>=3.12.1; extra == "iceye"
|
|
22
24
|
Requires-Dist: python-dateutil>=2.9.0; extra == "iceye"
|
|
23
25
|
Provides-Extra: cosmo
|
|
26
|
+
Requires-Dist: astropy>=6.0.0; extra == "cosmo"
|
|
24
27
|
Requires-Dist: h5py>=3.12.1; extra == "cosmo"
|
|
25
28
|
Requires-Dist: python-dateutil>=2.9.0; extra == "cosmo"
|
|
26
|
-
Requires-Dist: scipy>=1.15.1; extra == "cosmo"
|
|
27
29
|
Requires-Dist: shapely>=2.0.2; extra == "cosmo"
|
|
28
|
-
Provides-Extra:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist: python-dateutil>=2.9.0; extra == "tsx"
|
|
31
|
-
Requires-Dist: sarkit[verification]>=0.5.0; extra == "tsx"
|
|
32
|
-
Requires-Dist: scipy>=1.15.1; extra == "tsx"
|
|
30
|
+
Provides-Extra: terrasar
|
|
31
|
+
Requires-Dist: python-dateutil>=2.9.0; extra == "terrasar"
|
|
33
32
|
Provides-Extra: sentinel
|
|
34
33
|
Requires-Dist: python-dateutil>=2.9.0; extra == "sentinel"
|
|
35
|
-
Requires-Dist: scipy>=1.15.1; extra == "sentinel"
|
|
36
34
|
Requires-Dist: tifffile>=2025.5.10; extra == "sentinel"
|
|
37
35
|
Provides-Extra: all
|
|
38
36
|
Requires-Dist: sarkit-convert[iceye]; extra == "all"
|
|
39
37
|
Requires-Dist: sarkit-convert[cosmo]; extra == "all"
|
|
40
|
-
Requires-Dist: sarkit-convert[
|
|
38
|
+
Requires-Dist: sarkit-convert[terrasar]; extra == "all"
|
|
41
39
|
Requires-Dist: sarkit-convert[sentinel]; extra == "all"
|
|
42
|
-
Provides-Extra: dev-lint
|
|
43
|
-
Requires-Dist: ruff>=0.3.0; extra == "dev-lint"
|
|
44
|
-
Requires-Dist: mypy>=1.8.0; extra == "dev-lint"
|
|
45
|
-
Requires-Dist: types-python-dateutil>=2.9.0; extra == "dev-lint"
|
|
46
|
-
Provides-Extra: dev-test
|
|
47
|
-
Requires-Dist: pytest>=7.4.4; extra == "dev-test"
|
|
48
|
-
Provides-Extra: dev
|
|
49
|
-
Requires-Dist: sarkit-convert[dev-lint,dev-test]; extra == "dev"
|
|
50
40
|
Description-Content-Type: text/markdown
|
|
51
41
|
|
|
52
42
|
<div align="center">
|
|
@@ -21,9 +21,11 @@ dynamic = []
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"lxml>=5.1.0",
|
|
23
23
|
"numpy>=1.26.3",
|
|
24
|
-
"
|
|
24
|
+
"pyproj>=3.7.2",
|
|
25
|
+
"sarkit>=1.3.0",
|
|
26
|
+
"scipy>=1.15.1",
|
|
25
27
|
]
|
|
26
|
-
version = "0.
|
|
28
|
+
version = "0.2.0"
|
|
27
29
|
|
|
28
30
|
[project.license]
|
|
29
31
|
text = "MIT"
|
|
@@ -34,43 +36,28 @@ iceye = [
|
|
|
34
36
|
"python-dateutil>=2.9.0",
|
|
35
37
|
]
|
|
36
38
|
cosmo = [
|
|
39
|
+
"astropy>=6.0.0",
|
|
37
40
|
"h5py>=3.12.1",
|
|
38
41
|
"python-dateutil>=2.9.0",
|
|
39
|
-
"scipy>=1.15.1",
|
|
40
42
|
"shapely>=2.0.2",
|
|
41
43
|
]
|
|
42
|
-
|
|
43
|
-
"lxml>=5.1.0",
|
|
44
|
+
terrasar = [
|
|
44
45
|
"python-dateutil>=2.9.0",
|
|
45
|
-
"sarkit[verification]>=0.5.0",
|
|
46
|
-
"scipy>=1.15.1",
|
|
47
46
|
]
|
|
48
47
|
sentinel = [
|
|
49
48
|
"python-dateutil>=2.9.0",
|
|
50
|
-
"scipy>=1.15.1",
|
|
51
49
|
"tifffile>=2025.5.10",
|
|
52
50
|
]
|
|
53
51
|
all = [
|
|
54
52
|
"sarkit-convert[iceye]",
|
|
55
53
|
"sarkit-convert[cosmo]",
|
|
56
|
-
"sarkit-convert[
|
|
54
|
+
"sarkit-convert[terrasar]",
|
|
57
55
|
"sarkit-convert[sentinel]",
|
|
58
56
|
]
|
|
59
|
-
dev-lint = [
|
|
60
|
-
"ruff>=0.3.0",
|
|
61
|
-
"mypy>=1.8.0",
|
|
62
|
-
"types-python-dateutil>=2.9.0",
|
|
63
|
-
]
|
|
64
|
-
dev-test = [
|
|
65
|
-
"pytest>=7.4.4",
|
|
66
|
-
]
|
|
67
|
-
dev = [
|
|
68
|
-
"sarkit-convert[dev-test,dev-lint]",
|
|
69
|
-
]
|
|
70
57
|
|
|
71
58
|
[dependency-groups]
|
|
72
59
|
test = [
|
|
73
|
-
"nox>=
|
|
60
|
+
"nox>=2025.2.9",
|
|
74
61
|
]
|
|
75
62
|
doc = [
|
|
76
63
|
"sphinx>=7.2.6",
|
|
@@ -78,6 +65,16 @@ doc = [
|
|
|
78
65
|
"sphinx-rtd-theme>=2.0.0",
|
|
79
66
|
"sphinxcontrib-autoprogram>=0.1.9",
|
|
80
67
|
]
|
|
68
|
+
dev-lint = [
|
|
69
|
+
"ruff>=0.3.0",
|
|
70
|
+
"mypy>=1.8.0",
|
|
71
|
+
"types-python-dateutil>=2.9.0",
|
|
72
|
+
]
|
|
73
|
+
dev-test = [
|
|
74
|
+
"pytest>=7.4.4",
|
|
75
|
+
"smart-open[http]>=7.5",
|
|
76
|
+
"tifffile>=2025.5.10",
|
|
77
|
+
]
|
|
81
78
|
|
|
82
79
|
[build-system]
|
|
83
80
|
requires = [
|
|
@@ -109,8 +106,10 @@ preview = true
|
|
|
109
106
|
|
|
110
107
|
[[tool.mypy.overrides]]
|
|
111
108
|
module = [
|
|
109
|
+
"astropy.*",
|
|
112
110
|
"h5py.*",
|
|
113
111
|
"lxml.*",
|
|
112
|
+
"pyproj.*",
|
|
114
113
|
"scipy.*",
|
|
115
114
|
"shapely.*",
|
|
116
115
|
]
|
|
@@ -124,6 +124,38 @@ def polyfit2d_tol(x, y, z, max_order_x, max_order_y, tol, strict_tol=False):
|
|
|
124
124
|
return best[0]
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
def polyshift(poly, new_origin):
|
|
128
|
+
"""Returns new polynomial with shifted origin
|
|
129
|
+
|
|
130
|
+
Args
|
|
131
|
+
----
|
|
132
|
+
poly: array-like
|
|
133
|
+
1d polynomial coefficients, with constant term first
|
|
134
|
+
new_origin: float
|
|
135
|
+
location in `poly`'s domain to place new polynomial's origin
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
new_poly
|
|
140
|
+
polynomial of same order as `poly` for which new_poly(0) == poly(new_origin)
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
working_coeffs = np.array(list(reversed(poly)))
|
|
144
|
+
output_coeffs = []
|
|
145
|
+
|
|
146
|
+
for _ in np.arange(len(poly)):
|
|
147
|
+
quot = np.zeros(shape=working_coeffs.shape)
|
|
148
|
+
rem = 0.0
|
|
149
|
+
for ndx, val in enumerate(working_coeffs):
|
|
150
|
+
carry = rem * new_origin
|
|
151
|
+
rem = val + carry
|
|
152
|
+
quot[ndx] = rem
|
|
153
|
+
output_coeffs.append(rem)
|
|
154
|
+
working_coeffs = quot[:-1]
|
|
155
|
+
|
|
156
|
+
return np.array(output_coeffs)
|
|
157
|
+
|
|
158
|
+
|
|
127
159
|
def broadening_from_amp(amp_vals, threshold_db=None):
|
|
128
160
|
"""Compute the broadening factor from amplitudes
|
|
129
161
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.0'
|
|
@@ -17,8 +17,12 @@ metadata that would predict the complex data characteristics
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
19
|
import contextlib
|
|
20
|
+
import datetime
|
|
20
21
|
import pathlib
|
|
21
22
|
|
|
23
|
+
import astropy.coordinates as apcoord
|
|
24
|
+
import astropy.units as apu
|
|
25
|
+
import astropy.utils
|
|
22
26
|
import dateutil.parser
|
|
23
27
|
import h5py
|
|
24
28
|
import lxml.builder
|
|
@@ -30,9 +34,13 @@ import sarkit.verification
|
|
|
30
34
|
import sarkit.wgs84
|
|
31
35
|
import scipy.constants
|
|
32
36
|
import scipy.optimize
|
|
37
|
+
import scipy.spatial.transform
|
|
33
38
|
|
|
39
|
+
from sarkit_convert import __version__
|
|
34
40
|
from sarkit_convert import _utils as utils
|
|
35
41
|
|
|
42
|
+
astropy.utils.iers.conf.autodownload = False
|
|
43
|
+
|
|
36
44
|
NSMAP = {
|
|
37
45
|
"sicd": "urn:SICD:1.4.0",
|
|
38
46
|
}
|
|
@@ -130,7 +138,6 @@ def hdf5_to_sicd(
|
|
|
130
138
|
h5_filename,
|
|
131
139
|
sicd_filename,
|
|
132
140
|
classification,
|
|
133
|
-
ostaid,
|
|
134
141
|
img_str,
|
|
135
142
|
chan_index,
|
|
136
143
|
tx_polarizations,
|
|
@@ -141,8 +148,10 @@ def hdf5_to_sicd(
|
|
|
141
148
|
mission_id = h5_attrs["Mission ID"]
|
|
142
149
|
if mission_id == "CSG":
|
|
143
150
|
dataset_str = "IMG"
|
|
151
|
+
burst_str = "B0001"
|
|
144
152
|
else:
|
|
145
153
|
dataset_str = "SBI"
|
|
154
|
+
burst_str = "B001"
|
|
146
155
|
sample_data_h5_path = f"{img_str}/{dataset_str}"
|
|
147
156
|
sample_data_shape = h5file[sample_data_h5_path].shape
|
|
148
157
|
sample_data_dtype = h5file[sample_data_h5_path].dtype
|
|
@@ -171,7 +180,6 @@ def hdf5_to_sicd(
|
|
|
171
180
|
|
|
172
181
|
# Creation Info
|
|
173
182
|
creation_time = dateutil.parser.parse(h5_attrs["Product Generation UTC"])
|
|
174
|
-
creation_site = h5_attrs["Processing Centre"]
|
|
175
183
|
l0_ver = h5_attrs.get("L0 Software Version", "NONE")
|
|
176
184
|
l1_ver = h5_attrs.get("L1A Software Version", "NONE")
|
|
177
185
|
creation_application = f"L0: {l0_ver}, L1: {l1_ver}"
|
|
@@ -311,31 +319,34 @@ def hdf5_to_sicd(
|
|
|
311
319
|
range_rate_per_hz = -scipy.constants.speed_of_light / (2 * center_frequency)
|
|
312
320
|
range_rate = doppler_centroid * range_rate_per_hz
|
|
313
321
|
range_rate_rate = doppler_rate * range_rate_per_hz
|
|
314
|
-
doppler_centroid_poly = utils.
|
|
322
|
+
doppler_centroid_poly = utils.polyfit2d_tol(
|
|
315
323
|
grid_coords[..., 0].flatten(),
|
|
316
324
|
grid_coords[..., 1].flatten(),
|
|
317
325
|
doppler_centroid.flatten(),
|
|
318
326
|
4,
|
|
319
327
|
4,
|
|
328
|
+
1e-2,
|
|
320
329
|
)
|
|
321
|
-
doppler_rate_poly = utils.
|
|
330
|
+
doppler_rate_poly = utils.polyfit2d_tol(
|
|
322
331
|
grid_coords[..., 0].flatten(),
|
|
323
332
|
grid_coords[..., 1].flatten(),
|
|
324
333
|
doppler_rate.flatten(),
|
|
325
334
|
4,
|
|
326
335
|
4,
|
|
336
|
+
1e-3,
|
|
327
337
|
)
|
|
328
338
|
time_ca_samps = time_coords[..., 1] - start_minus_ref
|
|
329
339
|
time_ca_poly = npp.polyfit(
|
|
330
340
|
grid_coords[..., 1].flatten(), time_ca_samps.flatten(), 1
|
|
331
341
|
)
|
|
332
342
|
time_coa_samps = time_ca_samps + range_rate / range_rate_rate
|
|
333
|
-
time_coa_poly = utils.
|
|
343
|
+
time_coa_poly = utils.polyfit2d_tol(
|
|
334
344
|
grid_coords[..., 0].flatten(),
|
|
335
345
|
grid_coords[..., 1].flatten(),
|
|
336
346
|
time_coa_samps.flatten(),
|
|
337
347
|
4,
|
|
338
348
|
4,
|
|
349
|
+
1e-3,
|
|
339
350
|
)
|
|
340
351
|
|
|
341
352
|
range_ca = time_coords[..., 0] * scipy.constants.speed_of_light / 2
|
|
@@ -344,12 +355,13 @@ def hdf5_to_sicd(
|
|
|
344
355
|
axis=0,
|
|
345
356
|
)
|
|
346
357
|
drsf = range_rate_rate * range_ca / speed_ca**2
|
|
347
|
-
drsf_poly = utils.
|
|
358
|
+
drsf_poly = utils.polyfit2d_tol(
|
|
348
359
|
grid_coords[..., 0].flatten(),
|
|
349
360
|
grid_coords[..., 1].flatten(),
|
|
350
361
|
drsf.flatten(),
|
|
351
362
|
4,
|
|
352
363
|
4,
|
|
364
|
+
1e-6,
|
|
353
365
|
)
|
|
354
366
|
|
|
355
367
|
llh_ddm = h5_attrs["Scene Centre Geodetic Coordinates"]
|
|
@@ -398,23 +410,176 @@ def hdf5_to_sicd(
|
|
|
398
410
|
uspz = spz / npl.norm(spz)
|
|
399
411
|
u_col = np.cross(uspz, u_row)
|
|
400
412
|
|
|
413
|
+
# Antenna
|
|
414
|
+
attitude_quaternion = np.roll(h5_attrs["Attitude Quaternions"], -1, axis=1)
|
|
415
|
+
attitude_times = h5_attrs["Attitude Times"]
|
|
416
|
+
attitude_utcs = [
|
|
417
|
+
ref_time + datetime.timedelta(seconds=attitude_time)
|
|
418
|
+
for attitude_time in attitude_times
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
inertial_position = h5_attrs["Inertial Satellite Position"]
|
|
422
|
+
inertial_velocity = h5_attrs["Inertial Satellite Velocity"]
|
|
423
|
+
inertial_acceleration = h5_attrs["Inertial Satellite Acceleration"]
|
|
424
|
+
eci_apc_poly = utils.fit_state_vectors(
|
|
425
|
+
(0, (collection_stop_time - collection_start_time).total_seconds()),
|
|
426
|
+
h5_attrs["State Vectors Times"]
|
|
427
|
+
- (collection_start_time - ref_time).total_seconds(),
|
|
428
|
+
inertial_position,
|
|
429
|
+
inertial_velocity,
|
|
430
|
+
inertial_acceleration,
|
|
431
|
+
order=5,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
def get_nadir_plane_at_time(time):
|
|
435
|
+
obstime = collection_start_time + datetime.timedelta(seconds=time)
|
|
436
|
+
eci_pos = npp.polyval(time, eci_apc_poly)
|
|
437
|
+
eci_vel = npp.polyval(time, npp.polyder(eci_apc_poly))
|
|
438
|
+
|
|
439
|
+
# Derived from https://adsabs.harvard.edu/full/2006ESASP.606E..35C, Section 3
|
|
440
|
+
z_sc = -eci_pos / np.linalg.norm(eci_pos)
|
|
441
|
+
y_sc_dir = np.cross(z_sc, eci_vel)
|
|
442
|
+
y_sc = y_sc_dir / np.linalg.norm(y_sc_dir)
|
|
443
|
+
x_sc = np.cross(y_sc, z_sc)
|
|
444
|
+
eci_2_ecf = np.array(
|
|
445
|
+
[
|
|
446
|
+
apcoord.GCRS(
|
|
447
|
+
apcoord.CartesianRepresentation(1, 0, 0, unit=apu.m),
|
|
448
|
+
obstime=obstime,
|
|
449
|
+
)
|
|
450
|
+
.transform_to(apcoord.ITRS(obstime=obstime))
|
|
451
|
+
.data.xyz.value,
|
|
452
|
+
apcoord.GCRS(
|
|
453
|
+
apcoord.CartesianRepresentation(0, 1, 0, unit=apu.m),
|
|
454
|
+
obstime=obstime,
|
|
455
|
+
)
|
|
456
|
+
.transform_to(apcoord.ITRS(obstime=obstime))
|
|
457
|
+
.data.xyz.value,
|
|
458
|
+
apcoord.GCRS(
|
|
459
|
+
apcoord.CartesianRepresentation(0, 0, 1, unit=apu.m),
|
|
460
|
+
obstime=obstime,
|
|
461
|
+
)
|
|
462
|
+
.transform_to(apcoord.ITRS(obstime=obstime))
|
|
463
|
+
.data.xyz.value,
|
|
464
|
+
]
|
|
465
|
+
)
|
|
466
|
+
x_sc = x_sc @ eci_2_ecf
|
|
467
|
+
y_sc = y_sc @ eci_2_ecf
|
|
468
|
+
z_sc = z_sc @ eci_2_ecf
|
|
469
|
+
return np.array([x_sc, y_sc, z_sc])
|
|
470
|
+
|
|
471
|
+
rel_att_times = np.array(
|
|
472
|
+
[(att_utc - collection_start_time).total_seconds() for att_utc in attitude_utcs]
|
|
473
|
+
)
|
|
474
|
+
good_indices = np.where(
|
|
475
|
+
np.logical_and(
|
|
476
|
+
np.less(-60, rel_att_times),
|
|
477
|
+
np.less(rel_att_times, 60 + collection_duration),
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
good_times = rel_att_times[good_indices]
|
|
481
|
+
good_att_quat = attitude_quaternion[good_indices]
|
|
482
|
+
nadir_planes = [get_nadir_plane_at_time(time) for time in good_times]
|
|
483
|
+
body_frame = np.array(
|
|
484
|
+
[
|
|
485
|
+
scipy.spatial.transform.Rotation.from_quat(att_quat)
|
|
486
|
+
.inv()
|
|
487
|
+
.apply(nadir_plane.T)
|
|
488
|
+
.T
|
|
489
|
+
for att_quat, nadir_plane in zip(good_att_quat, nadir_planes)
|
|
490
|
+
]
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
ux_rot = body_frame[:, 0, :]
|
|
494
|
+
uy_rot = body_frame[:, 1, :]
|
|
495
|
+
|
|
496
|
+
ant_x_dir_poly = utils.fit_state_vectors(
|
|
497
|
+
(0, (collection_stop_time - collection_start_time).total_seconds()),
|
|
498
|
+
good_times,
|
|
499
|
+
ux_rot,
|
|
500
|
+
None,
|
|
501
|
+
None,
|
|
502
|
+
order=4,
|
|
503
|
+
)
|
|
504
|
+
ant_y_dir_poly = utils.fit_state_vectors(
|
|
505
|
+
(0, (collection_stop_time - collection_start_time).total_seconds()),
|
|
506
|
+
good_times,
|
|
507
|
+
uy_rot,
|
|
508
|
+
None,
|
|
509
|
+
None,
|
|
510
|
+
order=4,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
freq_zero = h5_attrs["Radar Frequency"]
|
|
514
|
+
antenna_beam_elevation = h5_attrs[img_str]["Antenna Beam Elevation"]
|
|
515
|
+
fit_order = 4
|
|
516
|
+
|
|
517
|
+
def fit_steering(code_change_lines, dcs):
|
|
518
|
+
if len(code_change_lines) > 1:
|
|
519
|
+
times = code_change_lines / prf
|
|
520
|
+
return npp.polyfit(times, dcs, fit_order)
|
|
521
|
+
else:
|
|
522
|
+
return np.array(dcs).reshape((1,))
|
|
523
|
+
|
|
524
|
+
if radar_mode_type != "STRIPMAP":
|
|
525
|
+
azimuth_ramp_code_change_lines = h5_attrs[img_str][burst_str][
|
|
526
|
+
"Azimuth Ramp Code Change Lines"
|
|
527
|
+
]
|
|
528
|
+
azimuth_steering = h5_attrs[img_str][burst_str]["Azimuth Steering"]
|
|
529
|
+
elevation_ramp_code_change_lines = h5_attrs[img_str][burst_str][
|
|
530
|
+
"Elevation Ramp Code Change Lines"
|
|
531
|
+
]
|
|
532
|
+
elevation_steering = h5_attrs[img_str][burst_str]["Elevation Steering"]
|
|
533
|
+
eb_dcx = np.sin(np.deg2rad(azimuth_steering))
|
|
534
|
+
eb_dcx_poly = fit_steering(azimuth_ramp_code_change_lines, eb_dcx)
|
|
535
|
+
eb_dcy = -np.sin(np.deg2rad(antenna_beam_elevation + elevation_steering))
|
|
536
|
+
eb_dcy_poly = fit_steering(elevation_ramp_code_change_lines, eb_dcy)
|
|
537
|
+
else:
|
|
538
|
+
eb_dcx_poly = [0.0]
|
|
539
|
+
eb_dcy_poly = [-np.sin(np.deg2rad(antenna_beam_elevation))]
|
|
540
|
+
|
|
541
|
+
antenna_az_gains = h5_attrs[img_str]["Azimuth Antenna Pattern Gains"]
|
|
542
|
+
antenna_az_origin = h5_attrs[img_str]["Azimuth Antenna Pattern Origin"]
|
|
543
|
+
antenna_az_spacing = h5_attrs[img_str]["Azimuth Antenna Pattern Resolution"]
|
|
544
|
+
|
|
545
|
+
antenna_rg_gains = h5_attrs[img_str]["Range Antenna Pattern Gains"]
|
|
546
|
+
antenna_rg_origin = h5_attrs[img_str]["Range Antenna Pattern Origin"]
|
|
547
|
+
antenna_rg_spacing = h5_attrs[img_str]["Range Antenna Pattern Resolution"]
|
|
548
|
+
|
|
549
|
+
def fit_gains(origin, spacing, gains):
|
|
550
|
+
fit_limit = -9
|
|
551
|
+
array_mask = gains > fit_limit
|
|
552
|
+
dcs = np.sin(np.deg2rad(origin + spacing * np.arange(len(gains))))
|
|
553
|
+
return npp.polyfit(dcs[array_mask], gains[array_mask], fit_order)
|
|
554
|
+
|
|
555
|
+
antenna_array_gain = np.zeros((fit_order + 1, fit_order + 1), dtype=float)
|
|
556
|
+
antenna_array_gain[0, :] = fit_gains(
|
|
557
|
+
antenna_rg_origin, antenna_rg_spacing, antenna_rg_gains
|
|
558
|
+
)
|
|
559
|
+
antenna_array_gain[:, 0] = fit_gains(
|
|
560
|
+
antenna_az_origin, antenna_az_spacing, antenna_az_gains
|
|
561
|
+
)
|
|
562
|
+
antenna_array_gain[0, 0] = 0.0
|
|
563
|
+
|
|
401
564
|
# Build XML
|
|
402
565
|
sicd = lxml.builder.ElementMaker(
|
|
403
566
|
namespace=NSMAP["sicd"], nsmap={None: NSMAP["sicd"]}
|
|
404
567
|
)
|
|
405
|
-
|
|
568
|
+
sicd_xml_obj = sicd.SICD()
|
|
569
|
+
sicd_ew = sksicd.ElementWrapper(sicd_xml_obj)
|
|
570
|
+
|
|
571
|
+
sicd_ew["CollectionInfo"] = sicd.CollectionInfo(
|
|
406
572
|
sicd.CollectorName(collector_name),
|
|
407
573
|
sicd.CoreName(core_name),
|
|
408
574
|
sicd.CollectType("MONOSTATIC"),
|
|
409
575
|
sicd.RadarMode(sicd.ModeType(radar_mode_type), sicd.ModeID(radar_mode_id)),
|
|
410
576
|
sicd.Classification(classification),
|
|
411
577
|
)
|
|
412
|
-
|
|
578
|
+
sicd_ew["ImageCreation"] = sicd.ImageCreation(
|
|
413
579
|
sicd.Application(creation_application),
|
|
414
580
|
sicd.DateTime(creation_time.isoformat() + "Z"),
|
|
415
|
-
sicd.Site(creation_site),
|
|
416
581
|
)
|
|
417
|
-
|
|
582
|
+
sicd_ew["ImageData"] = sicd.ImageData(
|
|
418
583
|
sicd.PixelType(pixel_type),
|
|
419
584
|
sicd.NumRows(str(num_rows)),
|
|
420
585
|
sicd.NumCols(str(num_cols)),
|
|
@@ -434,7 +599,7 @@ def hdf5_to_sicd(
|
|
|
434
599
|
return [sicd.Lat(str(arr[0])), sicd.Lon(str(arr[1]))]
|
|
435
600
|
|
|
436
601
|
# Placeholder locations
|
|
437
|
-
|
|
602
|
+
sicd_ew["GeoData"] = sicd.GeoData(
|
|
438
603
|
sicd.EarthModel("WGS_84"),
|
|
439
604
|
sicd.SCP(sicd.ECF(*make_xyz(scp_ecf)), sicd.LLH(*make_llh(scp_llh))),
|
|
440
605
|
sicd.ImageCorners(
|
|
@@ -468,7 +633,7 @@ def hdf5_to_sicd(
|
|
|
468
633
|
col_window_name = h5_attrs["Azimuth Focusing Weighting Function"]
|
|
469
634
|
col_window_coeff = h5_attrs["Azimuth Focusing Weighting Coefficient"]
|
|
470
635
|
|
|
471
|
-
|
|
636
|
+
sicd_ew["Grid"] = sicd.Grid(
|
|
472
637
|
sicd.ImagePlane("SLANT"),
|
|
473
638
|
sicd.Type("RGZERO"),
|
|
474
639
|
sicd.TimeCOAPoly(),
|
|
@@ -503,33 +668,27 @@ def hdf5_to_sicd(
|
|
|
503
668
|
),
|
|
504
669
|
),
|
|
505
670
|
)
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
grid.find("./{*}Col/{*}DeltaKCOAPoly"), col_deltakcoa_poly
|
|
510
|
-
)
|
|
671
|
+
sicd_ew["Grid"]["TimeCOAPoly"] = time_coa_poly
|
|
672
|
+
sicd_ew["Grid"]["Row"]["DeltaKCOAPoly"] = [[0]]
|
|
673
|
+
sicd_ew["Grid"]["Col"]["DeltaKCOAPoly"] = col_deltakcoa_poly
|
|
511
674
|
rcs_row_sf = None
|
|
512
675
|
rcs_col_sf = None
|
|
513
676
|
if row_window_name == "HAMMING":
|
|
514
677
|
wgts = scipy.signal.windows.general_hamming(512, row_window_coeff, sym=True)
|
|
515
|
-
|
|
516
|
-
sksicd.TRANSCODERS["Grid/Row/WgtFunct"].set_elem(wgtfunc, wgts)
|
|
517
|
-
grid.find("./{*}Row").append(wgtfunc)
|
|
678
|
+
sicd_ew["Grid"]["Row"]["WgtFunct"] = wgts
|
|
518
679
|
row_broadening_factor = utils.broadening_from_amp(wgts)
|
|
519
680
|
row_wid = row_broadening_factor / row_bw
|
|
520
|
-
|
|
681
|
+
sicd_ew["Grid"]["Row"]["ImpRespWid"] = row_wid
|
|
521
682
|
rcs_row_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
|
|
522
683
|
if col_window_name == "HAMMING":
|
|
523
684
|
wgts = scipy.signal.windows.general_hamming(512, col_window_coeff, sym=True)
|
|
524
|
-
|
|
525
|
-
sksicd.TRANSCODERS["Grid/Col/WgtFunct"].set_elem(wgtfunc, wgts)
|
|
526
|
-
grid.find("./{*}Col").append(wgtfunc)
|
|
685
|
+
sicd_ew["Grid"]["Col"]["WgtFunct"] = wgts
|
|
527
686
|
col_broadening_factor = utils.broadening_from_amp(wgts)
|
|
528
687
|
col_wid = col_broadening_factor / col_bw
|
|
529
|
-
|
|
688
|
+
sicd_ew["Grid"]["Col"]["ImpRespWid"] = col_wid
|
|
530
689
|
rcs_col_sf = 1 + np.var(wgts) / np.mean(wgts) ** 2
|
|
531
690
|
|
|
532
|
-
|
|
691
|
+
sicd_ew["Timeline"] = sicd.Timeline(
|
|
533
692
|
sicd.CollectStart(collection_start_time.isoformat() + "Z"),
|
|
534
693
|
sicd.CollectDuration(str(collection_duration)),
|
|
535
694
|
sicd.IPP(
|
|
@@ -544,10 +703,9 @@ def hdf5_to_sicd(
|
|
|
544
703
|
),
|
|
545
704
|
),
|
|
546
705
|
)
|
|
547
|
-
|
|
706
|
+
sicd_ew["Timeline"]["IPP"]["Set"][0]["IPPPoly"] = [0, prf]
|
|
548
707
|
|
|
549
|
-
|
|
550
|
-
sksicd.XyzPolyType().set_elem(position.find("./{*}ARPPoly"), apc_poly)
|
|
708
|
+
sicd_ew["Position"]["ARPPoly"] = apc_poly
|
|
551
709
|
|
|
552
710
|
rcv_channels = sicd.RcvChannels(
|
|
553
711
|
{"size": str(len(tx_rcv_pols))},
|
|
@@ -559,7 +717,7 @@ def hdf5_to_sicd(
|
|
|
559
717
|
)
|
|
560
718
|
)
|
|
561
719
|
|
|
562
|
-
|
|
720
|
+
sicd_ew["RadarCollection"] = sicd.RadarCollection(
|
|
563
721
|
sicd.TxFrequency(sicd.Min(str(tx_freq_min)), sicd.Max(str(tx_freq_max))),
|
|
564
722
|
sicd.Waveform(
|
|
565
723
|
{"size": "1"},
|
|
@@ -577,7 +735,7 @@ def hdf5_to_sicd(
|
|
|
577
735
|
rcv_channels,
|
|
578
736
|
)
|
|
579
737
|
if len(tx_polarizations) > 1:
|
|
580
|
-
|
|
738
|
+
sicd_ew["RadarCollection"]["TxPolarization"] = "SEQUENCE"
|
|
581
739
|
tx_sequence = sicd.TxSequence({"size": str(len(tx_polarizations))})
|
|
582
740
|
for ndx, tx_pol in enumerate(tx_polarizations):
|
|
583
741
|
tx_sequence.append(
|
|
@@ -585,7 +743,12 @@ def hdf5_to_sicd(
|
|
|
585
743
|
)
|
|
586
744
|
rcv_channels.addprevious(tx_sequence)
|
|
587
745
|
|
|
588
|
-
|
|
746
|
+
now = (
|
|
747
|
+
datetime.datetime.now(datetime.timezone.utc)
|
|
748
|
+
.isoformat(timespec="microseconds")
|
|
749
|
+
.replace("+00:00", "Z")
|
|
750
|
+
)
|
|
751
|
+
sicd_ew["ImageFormation"] = sicd.ImageFormation(
|
|
589
752
|
sicd.RcvChanProc(sicd.NumChanProc("1"), sicd.ChanIndex(str(chan_index))),
|
|
590
753
|
sicd.TxRcvPolarizationProc(tx_rcv_polarization),
|
|
591
754
|
sicd.TStartProc(str(0)),
|
|
@@ -598,9 +761,23 @@ def hdf5_to_sicd(
|
|
|
598
761
|
sicd.ImageBeamComp("SV"),
|
|
599
762
|
sicd.AzAutofocus("NO"),
|
|
600
763
|
sicd.RgAutofocus("NO"),
|
|
764
|
+
sicd.Processing(
|
|
765
|
+
sicd.Type(f"sarkit-convert {__version__} @ {now}"),
|
|
766
|
+
sicd.Applied("true"),
|
|
767
|
+
),
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
sicd_ew["Antenna"]["TwoWay"]["XAxisPoly"] = ant_x_dir_poly
|
|
771
|
+
sicd_ew["Antenna"]["TwoWay"]["YAxisPoly"] = ant_y_dir_poly
|
|
772
|
+
sicd_ew["Antenna"]["TwoWay"]["FreqZero"] = freq_zero
|
|
773
|
+
sicd_ew["Antenna"]["TwoWay"]["EB"]["DCXPoly"] = eb_dcx_poly
|
|
774
|
+
sicd_ew["Antenna"]["TwoWay"]["EB"]["DCYPoly"] = eb_dcy_poly
|
|
775
|
+
sicd_ew["Antenna"]["TwoWay"]["Array"]["GainPoly"] = antenna_array_gain
|
|
776
|
+
sicd_ew["Antenna"]["TwoWay"]["Array"]["PhasePoly"] = np.zeros(
|
|
777
|
+
dtype=float, shape=(1, 1)
|
|
601
778
|
)
|
|
602
779
|
|
|
603
|
-
|
|
780
|
+
sicd_ew["RMA"] = sicd.RMA(
|
|
604
781
|
sicd.RMAlgoType("OMEGA_K"),
|
|
605
782
|
sicd.ImageType("INCA"),
|
|
606
783
|
sicd.INCA(
|
|
@@ -611,25 +788,11 @@ def hdf5_to_sicd(
|
|
|
611
788
|
sicd.DopCentroidPoly(),
|
|
612
789
|
),
|
|
613
790
|
)
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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
|
-
)
|
|
791
|
+
sicd_ew["RMA"]["INCA"]["TimeCAPoly"] = time_ca_poly
|
|
792
|
+
sicd_ew["RMA"]["INCA"]["DRateSFPoly"] = drsf_poly
|
|
793
|
+
sicd_ew["RMA"]["INCA"]["DopCentroidPoly"] = doppler_centroid_poly
|
|
631
794
|
|
|
632
|
-
|
|
795
|
+
sicd_ew["SCPCOA"] = sksicd.compute_scp_coa(sicd_xml_obj.getroottree())
|
|
633
796
|
|
|
634
797
|
# Add Radiometric
|
|
635
798
|
if mission_id == "CSK":
|
|
@@ -668,7 +831,7 @@ def hdf5_to_sicd(
|
|
|
668
831
|
sksicd.Poly2dType().set_elem(
|
|
669
832
|
radiometric.find("./{*}RCSSFPoly"), rcssf_poly
|
|
670
833
|
)
|
|
671
|
-
sicd_xml_obj.find("./{*}
|
|
834
|
+
sicd_xml_obj.find("./{*}Antenna").addprevious(radiometric)
|
|
672
835
|
|
|
673
836
|
# Add Geodata Corners
|
|
674
837
|
sicd_xmltree = sicd_xml_obj.getroottree()
|
|
@@ -708,7 +871,7 @@ def hdf5_to_sicd(
|
|
|
708
871
|
metadata = sksicd.NitfMetadata(
|
|
709
872
|
xmltree=sicd_xmltree,
|
|
710
873
|
file_header_part={
|
|
711
|
-
"ostaid":
|
|
874
|
+
"ostaid": h5_attrs["Processing Centre"],
|
|
712
875
|
"ftitle": core_name,
|
|
713
876
|
"security": {
|
|
714
877
|
"clas": classification[0].upper(),
|
|
@@ -756,11 +919,6 @@ def main(args=None):
|
|
|
756
919
|
type=pathlib.Path,
|
|
757
920
|
help='path of the output SICD file. The string "{pol}" will be replaced with polarization for multiple images',
|
|
758
921
|
)
|
|
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
922
|
config = parser.parse_args(args)
|
|
765
923
|
|
|
766
924
|
tx_polarizations = []
|
|
@@ -810,7 +968,6 @@ def main(args=None):
|
|
|
810
968
|
h5_filename=config.input_h5_file,
|
|
811
969
|
sicd_filename=img_info["filename"],
|
|
812
970
|
classification=config.classification,
|
|
813
|
-
ostaid=config.ostaid,
|
|
814
971
|
img_str=img_str,
|
|
815
972
|
chan_index=img_info["chan_index"],
|
|
816
973
|
tx_polarizations=tx_polarizations,
|