Skip to content

Commit 2d84c22

Browse files
bhawkinsGitHub Enterprise
authored andcommitted
Fix multi-channel RSLC processing (#844)
* Fix doppler LUT write when we've already written one band. * Prevent string type promotion on numpy.append. * Whitespace between sentences. * Use better variable name. * Achieve type stability of polarization list in a simpler way. * Two LOC begone!
1 parent 510df3a commit 2d84c22

File tree

1 file changed

+63
-6
lines changed
  • python/packages/nisar/products/writers

1 file changed

+63
-6
lines changed

python/packages/nisar/products/writers/SLC.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import h5py
22
import logging
33
import numpy as np
4+
from numpy.testing import assert_allclose
5+
import os
6+
import isce3
47
from pybind_isce3.core import LUT2d, DateTime, Orbit, Attitude, EulerAngles
58
from pybind_isce3.product import RadarGridParameters
69
from pybind_isce3.geometry import DEMInterpolator
@@ -21,6 +24,61 @@ def time_units(epoch: DateTime) -> str:
2124
return "seconds since " + date + " " + time
2225

2326

27+
def assert_same_lut2d_grid(x: np.ndarray, y: np.ndarray, lut: LUT2d):
28+
assert_allclose(x[0], lut.x_start)
29+
assert_allclose(y[0], lut.y_start)
30+
assert (len(x) > 1) and (len(y) > 1)
31+
assert_allclose(x[1] - x[0], lut.x_spacing)
32+
assert_allclose(y[1] - y[0], lut.y_spacing)
33+
assert lut.width == len(x)
34+
assert lut.length == len(y)
35+
36+
37+
def h5_require_dirname(group: h5py.Group, name: str):
38+
"""Make sure any intermediate paths in `name` exist in `group`.
39+
"""
40+
assert os.sep == '/', "Need to fix HDF5 path manipulation on Windows"
41+
d = os.path.dirname(name)
42+
group.require_group(d)
43+
44+
45+
def add_cal_layer(group: h5py.Group, lut: LUT2d, name: str,
46+
epoch: DateTime, units: str):
47+
"""Add calibration LUT to HDF5 group, making sure that its domain matches
48+
any existing cal LUTs.
49+
50+
Parameters
51+
----------
52+
group : h5py.Group
53+
Group where LUT and its axes will be stored.
54+
lut : isce3.core.LUT2d
55+
Look up table. Axes are assumed to be y=zeroDopplerTime, x=slantRange.
56+
name : str
57+
Name of dataset to store LUT data (can be path relative to `group`).
58+
epoch: isce3.core.DateTime
59+
Reference time associated with y-axis.
60+
units : str
61+
Units of lut.data. Will be stored in `units` attribute of dataset.
62+
"""
63+
# If we've already saved one cal layer then we've already saved its
64+
# x and y axes. Make sure they're the same and just save the new z data.
65+
xname, yname = "slantRange", "zeroDopplerTime"
66+
if (xname in group) and (yname in group):
67+
x, y = group[xname], group[yname]
68+
assert_same_lut2d_grid(x, y, lut)
69+
extant_epoch = isce3.io.get_ref_epoch(y.parent, y.name)
70+
assert extant_epoch == epoch
71+
data = lut.data
72+
z = group.require_dataset(name, data.shape, data.dtype)
73+
z[...] = data
74+
elif (xname not in group) and (yname not in group):
75+
h5_require_dirname(group, name)
76+
lut.save_to_h5(group, name, epoch, units)
77+
else:
78+
raise IOError(f"Found only one of {xname} or {yname}."
79+
" Need both or none.")
80+
81+
2482
class SLC(h5py.File):
2583
def __init__(self, *args, band="LSAR", product="RSLC", **kw):
2684
super().__init__(*args, **kw)
@@ -49,7 +107,7 @@ def set_parameters(self, dop: LUT2d, epoch: DateTime, frequency='A'):
49107
# Actual LUT goes into a subdirectory, not created by serialization.
50108
name = f"frequency{frequency}"
51109
fg = g.require_group(name)
52-
dop.save_to_h5(g, f"{name}/dopplerCentroid", epoch, "Hz")
110+
add_cal_layer(g, dop, f"{name}/dopplerCentroid", epoch, "Hz")
53111
# TODO veff, fmrate not used anywhere afaict except product io.
54112
v = np.zeros_like(dop.data)
55113
g.require_dataset("effectiveVelocity", v.shape, v.dtype, data=v)
@@ -108,15 +166,14 @@ def swath(self, frequency="A") -> h5py.Group:
108166

109167
def add_polarization(self, frequency="A", pol="HH"):
110168
assert len(pol) == 2 and pol[0] in "HVLR" and pol[1] in "HV"
169+
pols = np.string_([pol]) # careful not to promote to unicode below
111170
g = self.swath(frequency)
112171
name = "listOfPolarizations"
113172
if name in g:
114-
pols = np.array(g[name])
115-
assert(pol not in pols)
116-
pols = np.append(pols, [pol])
173+
old_pols = np.array(g[name])
174+
assert pols[0] not in old_pols
175+
pols = np.append(old_pols, pols)
117176
del g[name]
118-
else:
119-
pols = np.array([pol], dtype="S2")
120177
dset = g.create_dataset(name, data=pols)
121178
desc = f"List of polarization layers with frequency{frequency}"
122179
dset.attrs["description"] = np.string_(desc)

0 commit comments

Comments
 (0)