Skip to content

Commit 4cdb87f

Browse files
bhawkinsgmgunter
authored andcommitted
Focus using external ephemeris (#948)
* Read orbit and attitude data files if provided. * Add python interface to update Attitude reference epoch. * Add python interface to update Orbit reference epoch. * Bind copy constructors for Orbit and Attitude * Update SLC attitude writer (epoch added in b119fa2) * Use a consistent time epoch for all objects. * Use external orbit and attitude in focus.py unit test. * Remove unnecessary raw string delimiters. Co-authored-by: Geoffrey M Gunter <[email protected]> * Add copy dunder methods. * Add contains() method to Attitude, Orbit, and their python bindings. * Require orbit covers raw data interval and load earlier so available for output grid init. * Improve default output grid. * Do the reskew calculations in a rigorous way and kludge short orbits. * Move useful junk into isce3 API * Remove superfluous approx reskew calculation. * Remove extraneous return parameters. * Note assumption * Remove superfluous raw string delimiters. Co-authored-by: Geoffrey M Gunter <[email protected]> * Remove shebang Co-authored-by: Geoffrey M Gunter <[email protected]> * Remove sh Co-authored-by: Geoffrey M Gunter <[email protected]> * Note optional arguments. Co-authored-by: Geoffrey M Gunter <[email protected]> * Add blank line Co-authored-by: Geoffrey M Gunter <[email protected]> * Add blank line Co-authored-by: Geoffrey M Gunter <[email protected]> * Use Orbit.contains method in test. Co-authored-by: Geoffrey M Gunter <[email protected]> * Only copy orbit and attitude if necessary. Co-authored-by: Geoffrey M Gunter <[email protected]> * Go Vader on ob1 Co-authored-by: Geoffrey M Gunter <[email protected]> * Add missing newline * use typing.Optional annotations * Add type annotations to sar_duration * Use clearer units for chirp duration. * More appropriate log levels for grid truncation messages. * Maintain identity in trivial rdr2rdr case. * Fix up commit d6c74a8 * Prefer builtin round() Co-authored-by: Geoffrey M Gunter <[email protected]>
1 parent 80fcb0e commit 4cdb87f

File tree

18 files changed

+1442
-56
lines changed

18 files changed

+1442
-56
lines changed

cxx/isce3/core/Attitude.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ class Attitude {
5959
/** Time of last measurement relative to reference epoch (s) */
6060
double endTime() const { return _time[size() - 1]; }
6161

62+
/** Check if time falls in the valid interpolation domain. */
63+
bool contains(double time) const {
64+
return (startTime() <= time) && (time <= endTime());
65+
}
66+
6267
/** UTC time of first measurement */
6368
DateTime startDateTime() const
6469
{

cxx/isce3/core/Orbit.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,12 @@ class Orbit {
7676
/** Reference epoch (UTC) */
7777
const DateTime & referenceEpoch() const { return _reference_epoch; }
7878

79-
/** Set reference epoch (UTC) */
79+
/** Set reference epoch (UTC)
80+
*
81+
* Also updates relative time tags so that
82+
* referenceEpoch() + TimeDelta(time()[i])
83+
* results in the same absolute time tags before and after this call.
84+
*/
8085
void referenceEpoch(const DateTime &);
8186

8287
/** Interpolation method */
@@ -103,6 +108,11 @@ class Orbit {
103108
/** UTC time of last state vector */
104109
DateTime endDateTime() const { return _reference_epoch + TimeDelta(endTime()); }
105110

111+
/** Check if time falls in the valid interpolation domain. */
112+
bool contains(double time) const {
113+
return (startTime() <= time) && (time <= endTime());
114+
}
115+
106116
/** Time interval between state vectors (s) */
107117
double spacing() const { return _time.spacing(); }
108118

python/extensions/pybind_isce3/core/Attitude.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ void addbinding(pybind11::class_<Attitude>& pyAttitude)
3535
"Serialize Attitude to h5py.Group object.",
3636
py::arg("h5py_group"))
3737

38+
// add a function rather than just a settable property since
39+
// self.time is also modified
40+
.def("update_reference_epoch",
41+
py::overload_cast<const DateTime&>(&Attitude::referenceEpoch),
42+
R"(
43+
Set reference epoch and modify time tags so that
44+
self.reference_epoch + TimeDelta(self.time[i])
45+
remains invariant for all i in range(len(self.time)).)",
46+
py::arg("reference_epoch"))
47+
48+
.def("copy", [](const Attitude& self) { return Attitude(self);})
49+
.def("__copy__", [](const Attitude& self) { return Attitude(self);})
50+
.def("__deepcopy__", [](const Attitude& self, py::dict) {
51+
return Attitude(self);
52+
}, py::arg("memo"))
53+
3854
.def_property_readonly("size", &Attitude::size)
3955
.def_property_readonly("time", &Attitude::time)
4056
.def_property_readonly("quaternions", &Attitude::quaternions)
@@ -44,5 +60,8 @@ void addbinding(pybind11::class_<Attitude>& pyAttitude)
4460
.def_property_readonly("end_datetime", &Attitude::endDateTime)
4561
.def_property_readonly("reference_epoch",
4662
py::overload_cast<>(&Attitude::referenceEpoch, py::const_))
63+
.def("contains", &Attitude::contains,
64+
"Check if time falls in the valid interpolation domain.",
65+
py::arg("time"))
4766
;
4867
}

python/extensions/pybind_isce3/core/Orbit.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,20 @@ void addbinding(py::class_<Orbit> & pyOrbit)
3434
.def(py::init<std::vector<StateVector>, isce3::core::DateTime>())
3535
.def_property_readonly("reference_epoch",
3636
py::overload_cast<>(&Orbit::referenceEpoch, py::const_))
37+
// add a function rather than just a settable property since
38+
// self.time is also modified
39+
.def("update_reference_epoch",
40+
py::overload_cast<const DateTime&>(&Orbit::referenceEpoch),
41+
R"(
42+
Set reference epoch and modify time tags so that
43+
self.reference_epoch + TimeDelta(self.time[i])
44+
remains invariant for all i in range(len(self.time)).)",
45+
py::arg("reference_epoch"))
46+
.def("copy", [](const Orbit& self) { return Orbit(self); })
47+
.def("__copy__", [](const Orbit& self) { return Orbit(self); })
48+
.def("__deepcopy__", [](const Orbit& self, py::dict) {
49+
return Orbit(self);
50+
}, py::arg("memo"))
3751
.def_property_readonly("time", py::overload_cast<>(&Orbit::time, py::const_))
3852
.def_property_readonly("position", [](const Orbit & self) {
3953
return py::roarray(toBuffer(self.position()), py::cast(self));
@@ -79,5 +93,9 @@ void addbinding(py::class_<Orbit> & pyOrbit)
7993
},
8094
"Interpolate platform position and velocity",
8195
py::arg("t"))
96+
97+
.def("contains", &Orbit::contains,
98+
"Check if time falls in the valid interpolation domain.",
99+
py::arg("time"))
82100
;
83101
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from pybind_isce3.focus import *
2+
from .sar_duration import get_sar_duration
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import isce3
2+
from isce3.core import Orbit, Ellipsoid
3+
import numpy as np
4+
5+
6+
def get_sar_duration(t: float, r: float, orbit: Orbit, ellipsoid: Ellipsoid,
7+
azres: float, wavelength: float):
8+
"""
9+
Get approximate synthetic aperture duration (coherent processing interval).
10+
11+
Parameters
12+
---------
13+
t : float
14+
Azimuth time, in seconds since orbit epoch
15+
r : float
16+
Range, in meters
17+
orbit : isce3.core.Orbit
18+
ellipsoid : isce3.core.Ellipsoid
19+
azres : float
20+
Desired azimuth resolution, in meters
21+
22+
Returns
23+
-------
24+
cpi : float
25+
Synthetic aperture duration (approximate), s
26+
"""
27+
pos, vel = orbit.interpolate(t)
28+
vs = np.linalg.norm(vel)
29+
# assume radar and target are close enough that r_dir() is constant
30+
lon, lat, h = ellipsoid.xyz_to_lon_lat(pos)
31+
hdg = isce3.geometry.heading(lon, lat, vel)
32+
a = ellipsoid.r_dir(hdg, lat)
33+
# assume circular orbit (no nadir component of velocity)
34+
vg = vs / (1 + h / a)
35+
# small angle approximations and ignore window factor
36+
return wavelength / (2 * azres) * r / vg
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from pybind_isce3.geometry import *
2+
from .rdr2rdr import rdr2rdr
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from isce3.core import Ellipsoid, LookSide, LUT2d, Orbit
2+
from isce3.geometry import DEMInterpolator, rdr2geo, geo2rdr
3+
from typing import Union, Optional
4+
5+
6+
def rdr2rdr(t: float,
7+
r: float,
8+
orbit: Orbit,
9+
side: Union[str, LookSide],
10+
doppler: LUT2d,
11+
wavelength: float,
12+
dem: DEMInterpolator = DEMInterpolator(),
13+
ellipsoid: Ellipsoid = Ellipsoid(),
14+
doppler_out: Optional[LUT2d] = None,
15+
orbit_out: Optional[Orbit] = None,
16+
rdr2geo_params=dict(),
17+
geo2rdr_params=dict()):
18+
"""
19+
Convert coordinates from one radar geometry to another with a different
20+
Doppler (reskew) or orbit (motion compensation).
21+
22+
Parameters
23+
----------
24+
t : float
25+
Azimuth time, seconds past orbit epoch
26+
r : float
27+
Range, m
28+
orbit : isce3.core.Orbit
29+
Orbit defining radar motion on input path
30+
side : {"left", "right", isce3.core.LookSide.Left, isce3.core.LookSide.Right}
31+
Flag desribing which side the radar is looking.
32+
doppler : isce3.core.LUT2d
33+
Doppler look up table vs input range and azimuth time, Hz
34+
wavelength : float
35+
Wavelength associated with Doppler, m
36+
dem : isce3.geometry.DEMInterpolator, optional
37+
Digital elevation model, m above ellipsoid. Defaults to h=0.
38+
ellipsoid : isce3.core.Ellipsoid, optional
39+
Ellipsoid describing surface. Defaults to WGS-84.
40+
doppler_out : isce3.core.LUT2d, optional
41+
Doppler look up table vs output range and azimuth time, Hz
42+
Defaults to input `doppler`.
43+
orbit_out : isce3.core.Orbit, optional
44+
Orbit defining radar motion on output path. Defaults to input `orbit`.
45+
rdr2geo_params : dict, optional
46+
Dictionary specifying convergence paramters for rdr2geo solver.
47+
Keys among {"threshold", "maxiter", "extraiter"}
48+
See isce3.geometry.rdr2geo
49+
geo2rdr_params: dict, optional
50+
Dictionary specifying convergence paramters for geo2rdr solver.
51+
Keys among {"threshold", "maxiter", "delta_range"}
52+
See isce3.geometry.geo2rdr
53+
54+
Returns
55+
-------
56+
t_out : float
57+
Azimuth time in output geometry, seconds past orbit epoch
58+
r_out : float
59+
Range in output geometry, m
60+
"""
61+
if (orbit_out is None) and (doppler_out is None):
62+
return t, r
63+
if orbit_out is None:
64+
orbit_out = orbit
65+
if doppler_out is None:
66+
doppler_out = doppler
67+
doppler_in = doppler.eval(t, r)
68+
llh = rdr2geo(t, r, orbit, side, doppler_in, wavelength, dem, ellipsoid,
69+
**rdr2geo_params)
70+
tout, rout = geo2rdr(llh, ellipsoid, orbit_out, doppler_out, wavelength,
71+
side, **geo2rdr_params)
72+
return tout, rout

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ def set_orbit(self, orbit: Orbit, accel=None, type="Custom"):
268268
" WGS84 G1762 reference frame")
269269
d.attrs["units"] = np.string_("meters per second squared")
270270

271-
def set_attitude(self, attitude: Attitude, epoch: DateTime, type="Custom"):
271+
def set_attitude(self, attitude: Attitude, type="Custom"):
272272
log.info("Writing attitude to SLC")
273273
g = self.root.require_group("metadata/attitude")
274274
d = g.require_dataset("attitudeType", (), "S10", data=np.string_(type))
@@ -279,7 +279,7 @@ def set_attitude(self, attitude: Attitude, epoch: DateTime, type="Custom"):
279279
d.attrs["description"] = np.string_("Time vector record. This record"
280280
" contains the time corresponding to attitude and quaternion"
281281
" records")
282-
d.attrs["units"] = np.string_(time_units(epoch))
282+
d.attrs["units"] = np.string_(time_units(attitude.reference_epoch))
283283
# TODO attitude rates
284284
n = len(attitude.time)
285285
qdot = np.zeros((n, 3))

0 commit comments

Comments
 (0)