Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: azimuth component calculation of solid earth tide #105

Closed
wants to merge 32 commits into from
Closed
Changes from 4 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ebe5bde
revised rg / az conversion
Mar 13, 2023
2dac619
fix on radar grid raster slicing; revision on `enu2rgaz`
Mar 13, 2023
eedff77
three different versions of `solid_earth_tides` for record
Mar 13, 2023
78e5fb5
clean up version + original `solid_earth_tides`
Mar 13, 2023
bb64cbc
Update src/compass/utils/lut.py
seongsujeong Mar 15, 2023
8d838d8
unused function removed
Mar 15, 2023
13f6630
`enu2rgaz` and `get_enu_vector_ecef` moved to `geometry_utils.py`
Mar 15, 2023
31dc853
codacy issue
Mar 15, 2023
10a33d5
Weather model-based troposphere delay LUTs (#103)
vbrancat Mar 15, 2023
a72e54d
Ionosphere correction (#102)
seongsujeong Mar 16, 2023
8cd13b1
Correct inconsistencies inside CSLC-S1 metadata (#107)
vbrancat Mar 16, 2023
d372678
Populate CSLC-S1 product_version (#109)
vbrancat Mar 16, 2023
b9df71f
Fix s1_geocode_metadata crash (#112)
LiangJYu Mar 17, 2023
4547d5a
Apply static troposphere model when weather model file is not provide…
seongsujeong Mar 21, 2023
d5b1953
CSLC quality assurance (#101)
LiangJYu Mar 21, 2023
39ad0a8
fix orbit path handling (#116)
LiangJYu Mar 21, 2023
a4597bf
Placeholder for geocoded radiometric calibration parameters (#114)
seongsujeong Mar 21, 2023
64743bb
Add browse image standalone and fix bugs (#118)
LiangJYu Mar 22, 2023
d1d7121
Placeholder for geocoded noise LUT (#119)
seongsujeong Mar 22, 2023
033a683
x/y coordinate and spacing datasets added to static layer output (#117)
LiangJYu Mar 22, 2023
71756de
use NETCDF to correctly get geotransform and projection (#121)
LiangJYu Mar 22, 2023
2be64dc
replace hand rolled with h5py visit() (#122)
LiangJYu Mar 22, 2023
908a192
Docker gamma delivery (#123)
seongsujeong Mar 23, 2023
1a89af7
validation script modified to handle both CSLC and static layer HDF5s…
LiangJYu Mar 23, 2023
87a7702
Write CSLC raster and correction stats to HDF5 (#120)
LiangJYu Mar 23, 2023
4f1434d
0.1.3 -> 0.1.4 (#125)
seongsujeong Mar 23, 2023
ec1cfc4
revised rg / az conversion
Mar 13, 2023
a57ba2b
three different versions of `solid_earth_tides` for record
Mar 13, 2023
adc574b
clean up version + original `solid_earth_tides`
Mar 13, 2023
452af59
Update src/compass/utils/lut.py
seongsujeong Mar 15, 2023
2cf9efe
unused function removed
Mar 15, 2023
b046978
`enu2rgaz` and `get_enu_vector_ecef` moved to `geometry_utils.py`
Mar 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 208 additions & 9 deletions src/compass/utils/lut.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def cumulative_correction_luts(burst, dem_path,
and slant range
'''
# Get individual LUTs
geometrical_steer_doppler, bistatic_delay, az_fm_mismatch, [tide_rg, _]= \
geometrical_steer_doppler, bistatic_delay, az_fm_mismatch, [tide_rg, tide_az]= \
compute_geocoding_correction_luts(burst,
dem_path=dem_path,
rg_step=rg_step,
Expand All @@ -57,7 +57,7 @@ def cumulative_correction_luts(burst, dem_path,

# Invert signs to correct for convention
# TO DO: add azimuth SET to LUT
az_lut_data = -(bistatic_delay.data + az_fm_mismatch.data)
az_lut_data = -(bistatic_delay.data + az_fm_mismatch.data) + tide_az

rg_lut = isce3.core.LUT2d(bistatic_delay.x_start,
bistatic_delay.y_start,
Expand All @@ -76,9 +76,9 @@ def cumulative_correction_luts(burst, dem_path,
output_path = f'{scratch_path}/corrections'
os.makedirs(output_path, exist_ok=True)
data_list = [geometry_doppler, bistatic_delay.data, az_fm_mismatch.data,
tide_rg]
tide_rg, tide_az]
descr = ['geometrical doppler', 'bistatic delay', 'azimuth FM rate mismatch',
'slant range Solid Earth tides']
'slant range Solid Earth tides', 'Azimuth time Solid Earth tides']
seongsujeong marked this conversation as resolved.
Show resolved Hide resolved

write_raster(f'{output_path}/corrections', data_list, descr)

Expand Down Expand Up @@ -171,10 +171,11 @@ def compute_geocoding_correction_luts(burst, dem_path,
# compute decimation factor assuming a 5 km spacing along slant range
dec_factor = int(np.round(5000.0 / rg_step))
dec_slice = np.s_[::dec_factor]
rg_set_temp, az_set_temp = solid_earth_tides(burst, lat[dec_slice],
lon[dec_slice],
inc_angle[dec_slice],
head_angle[dec_slice])
rg_set_temp, az_set_temp = solid_earth_tides(burst,
lat[dec_slice, dec_slice],
lon[dec_slice, dec_slice],
height[dec_slice, dec_slice],
Comment on lines +228 to +231
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rg_set_temp, az_set_temp = solid_earth_tides(burst,
lat[dec_slice, dec_slice],
lon[dec_slice, dec_slice],
height[dec_slice, dec_slice],
dec_slice = np.s_[::dec_factor, ::dec_factor]
rg_set_temp, az_set_temp = solid_earth_tides(burst,
lat[dec_slice],
lon[dec_slice],
height[dec_slice],

I think the slice only needs to be altered once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested the original code, and it seems the slicing is happening on the first axis of the array only. In my test case (Rosamond, 10/16/2022, t064_135523_iw1), the radargrid rasters' shapes were (109,418), and dec_factor was 42.
dec_slice was as below:

dec_slice
slice(None, None, 42)

Below are the shapes of the decimated arrays:

lat.shape
(109, 418)
lat[dec_slice].shape
(3, 418)
lat[dec_slice, dec_slice].shape
(3, 10)

So it seems we need to provide the slice instance twice for the 2d arrays. @vbrancat Can you take a look at the information above and confirm that the shape of the decimated array makes sense?

Below is a toy code to show how the slicing works in 2D numpy array:

import numpy as np

arr_original = np.arange(150).reshape((10,15))
dec_slice = np.s_[::5]
print(arr_original.shape)
arr_slice_1 = arr_original[dec_slice]
print(arr_slice_1.shape)

arr_slice_2 = arr_original[dec_slice, dec_slice]
print(arr_slice_2.shape)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I have fixed this in the next commit.

ellipsoid)

# Resize SET to the size of the correction grid
out_shape = bistatic_delay.data.shape
Expand All @@ -187,7 +188,7 @@ def compute_geocoding_correction_luts(burst, dem_path,
rg_set, az_set]


def solid_earth_tides(burst, lat_radar_grid, lon_radar_grid, inc_angle,
def solid_earth_tides_original(burst, lat_radar_grid, lon_radar_grid, inc_angle,
seongsujeong marked this conversation as resolved.
Show resolved Hide resolved
head_angle):
'''
Compute displacement due to Solid Earth Tides (SET)
Expand Down Expand Up @@ -267,6 +268,82 @@ def solid_earth_tides(burst, lat_radar_grid, lon_radar_grid, inc_angle,
return set_rg, set_az




def solid_earth_tides(burst, lat_radar_grid, lon_radar_grid, hgt_radar_grid, ellipsoid):
'''
Compute displacement due to Solid Earth Tides (SET)
in slant range and azimuth directions

Parameters
---------
burst: Sentinel1Slc
S1-A/B burst object
lat_radar_grid: np.ndarray
Latitude array on burst radargrid
lon_radar_grid: np.ndarray
Longitude array on burst radargrid
inc_angle: np.ndarray
Incident angle raster in unit of degrees
head_angle: np.ndaaray
Heading angle raster in unit of degrees

Returns
------
rg_set: np.ndarray
2D array with SET displacement along LOS
az_set: np.ndarray
2D array with SET displacement along azimuth
'''

# Extract top-left coordinates from burst polygon
lon_min, lat_min, _, _ = burst.border[0].bounds

# Generate the atr object to run pySolid. We compute SET on a
# 2.5 km x 2.5 km coarse grid
margin = 0.1
lat_start = lat_min - margin
lon_start = lon_min - margin

atr = {
'LENGTH': 25,
'WIDTH': 100,
'X_FIRST': lon_start,
'Y_FIRST': lat_start,
'X_STEP': 0.023,
'Y_STEP': 0.023
}

# Run pySolid and get SET in ENU coordinate system
(set_e,
set_n,
set_u) = pysolid.calc_solid_earth_tides_grid(burst.sensing_start, atr,
display=False, verbose=True)

# Resample SET from geographical grid to radar grid
# Generate the lat/lon arrays for the SET geogrid
lat_geo_array = np.linspace(atr['Y_FIRST'],
lat_start + atr['Y_STEP'] * atr['LENGTH'],
num=atr['LENGTH'])
lon_geo_array = np.linspace(atr['X_FIRST'],
lon_start + atr['X_STEP'] * atr['WIDTH'],
num=atr['WIDTH'])

# Use scipy RGI to resample SET from geocoded to radar coordinates
pts_src = (np.flipud(lat_geo_array), lon_geo_array)
pts_dst = (lat_radar_grid.flatten(), lon_radar_grid.flatten())

rdr_set_e, rdr_set_n, rdr_set_u = \
[resample_set(set_enu, pts_src, pts_dst).reshape(lat_radar_grid.shape)
for set_enu in [set_e, set_n, set_u]]

rg_set, az_set = enu2rgaz(burst.as_isce3_radargrid(), burst.orbit, ellipsoid,
lon_radar_grid, lat_radar_grid, hgt_radar_grid,
rdr_set_e, rdr_set_n, rdr_set_u)

return rg_set, az_set


def compute_rdr2geo_rasters(burst, dem_raster, output_path,
rg_step, az_step):
'''
Expand Down Expand Up @@ -362,3 +439,125 @@ def resample_set(geo_tide, pts_src, pts_dest):
bounds_error=False, fill_value=0)
rdr_tide = rgi_func(pts_dest)
return rdr_tide


def enu2rgaz(radargrid_ref, orbit, ellipsoid,
seongsujeong marked this conversation as resolved.
Show resolved Hide resolved
lon_arr, lat_arr, hgt_arr,
e_arr, n_arr, u_arr):
'''
Convert ENU displacement into range / azimuth displacement

Parameters
----------
radargrid_ref: isce3.product.RadarGridParameters
Radargrid of the burst
orbit: isce3.core.Orbit
Orbit of the burst
ellipsoid: isce3.core.Ellipsoid
Ellipsoid definition

lon_arr, lat_arr, hgt_arr: np.nadrray
Arrays for lonfigute, latitude, and height.
Units for longitude and latitude are degree; unit for height is meters.

e_arr, n_arr, u_arr: np.ndarray
Displacement in east, north, and up direction in meters

Returns
-------
rg_arr: np.ndarray
Displacement in slant range direction in meters.
az_arr: np.ndarray
Displacement in azimuth direction in seconds.
'''
shape_arr = lon_arr.shape
rg_arr = np.zeros(shape_arr)
az_arr = np.zeros(shape_arr)

# Calculate the ENU vector in ECEF
for i, lon_deg in enumerate(np.nditer(lon_arr)):
index_arr = np.unravel_index(i, lon_arr.shape)
lat_deg = lat_arr[index_arr]
hgt = hgt_arr[index_arr]

vec_e, vec_n, vec_u = get_enu_vector_ecef(lon_deg, lat_deg)

llh_ref = np.array([np.deg2rad(lon_deg),
np.deg2rad(lat_deg),
hgt])

xyz_before = ellipsoid.lon_lat_to_xyz(llh_ref)
xyz_set = (xyz_before + vec_e * e_arr[index_arr]
+ vec_n * n_arr[index_arr]
+ vec_u * u_arr[index_arr])
llh_displaced = ellipsoid.xyz_to_lon_lat(xyz_set)

aztime_ref, slant_range_ref =\
isce3.geometry.geo2rdr(llh_ref,
ellipsoid,
orbit,
isce3.core.LUT2d(),
radargrid_ref.wavelength,
radargrid_ref.lookside,
threshold=1.0e-10, maxiter=50, delta_range=10.0)

aztime_displaced, slant_range_displaced =\
isce3.geometry.geo2rdr(llh_displaced,
ellipsoid,
orbit,
isce3.core.LUT2d(),
radargrid_ref.wavelength,
radargrid_ref.lookside,
threshold=1.0e-10, maxiter=50, delta_range=10.0)

rg_arr[index_arr] = slant_range_displaced - slant_range_ref
az_arr[index_arr] = aztime_displaced - aztime_ref

return rg_arr, az_arr


def get_enu_vector_ecef(lon, lat, unit='degree'):
seongsujeong marked this conversation as resolved.
Show resolved Hide resolved
'''
Calculate the east, north, and up vectors in ECEF for lon / lat provided

Parameters
----------
lon: np.ndarray
Longitude of the points to calculate ENU vectors
lat: np.ndarray
Latitude of the points to calculate ENU vectors
unit: str
Unit of the `lon` and `lat`. Default is `degree`

Return
------
vec_e: np.ndarray
unit vector of "east" direction in ECEF
vec_n: np.ndarray
unit vector of "north" direction in ECEF
vec_u: np.ndarray
unit vector of "up" direction in ECEF


'''
if unit=='degree':
lon_rad = np.deg2rad(lon)
lat_rad = np.deg2rad(lat)
elif unit=='radian':
lon_rad = lon
lat_rad = lat
else:
raise ValueError(f'"{unit}" was provided for "unit", '
'which needs to be either "degree" or "radian"')

vec_u = np.array([np.cos(lon_rad) * np.cos(lat_rad),
np.sin(lon_rad) * np.cos(lat_rad),
np.sin(lat_rad)])

vec_n = np.array([-np.cos(lon_rad) * np.sin(lat_rad),
-np.sin(lon_rad) * np.sin(lat_rad),
np.cos(lat_rad)])

vec_e = np.cross(vec_n, vec_u, axis=0)

return vec_e, vec_n, vec_u