Skip to content

Commit 2d5b882

Browse files
committed
Fix issue with improper geolocation of IMP products.
Fix antenna gain with IMS mode.
1 parent cb10da4 commit 2d5b882

2 files changed

Lines changed: 91 additions & 53 deletions

File tree

asar_xarray/asar.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,16 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
8989
range_sampling_rate = metadata["records"]["main_processing_params"]["range_samp_rate"]
9090
image_slant_range_time = metadata["direct_parse"]["slant_time_first"] * 1e-9
9191

92-
if metadata["sph_descriptor"] == "Image Mode SLC Image":
92+
range_pixel_spacing = metadata["records"]["main_processing_params"]["range_spacing"]
93+
94+
product_str = metadata["sph_descriptor"]
95+
96+
if product_str == "Image Mode SLC Image":
9397
product_type = "SLC"
98+
elif product_str == "Image Mode Precision Image":
99+
product_type = "GRD"
94100
else:
95-
raise RuntimeError("Only Image mode SLC files(IMS) supported for now")
101+
raise RuntimeError("Only Image mode files supported(IMS & IMP) supported for now")
96102

97103
attrs = {
98104
"family_name": "Envisat",
@@ -106,8 +112,8 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
106112
"product_type": product_type,
107113
"start_time": product_first_line_utc_time,
108114
"stop_time": product_last_line_utc_time,
109-
"range_pixel_spacing" : metadata["range_spacing"],
110-
"azimuth_pixel_spacing" : metadata["azimuth_spacing"],
115+
"range_pixel_spacing": metadata["range_spacing"],
116+
"azimuth_pixel_spacing": metadata["azimuth_spacing"],
111117
"radar_frequency": metadata["records"]["main_processing_params"]["radar_freq"] / 1e9,
112118
"ascending_node_time": "",
113119
"azimuth_pixel_spacing": metadata["records"]["main_processing_params"]["azimuth_spacing"],
@@ -116,15 +122,14 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
116122
"azimuth_time_interval": azimuth_time_interval,
117123
"image_slant_range_time": image_slant_range_time,
118124
"range_sampling_rate": range_sampling_rate,
119-
"incidence_angle_mid_swath": metadata["direct_parse"]["incidence_angle_center"] * 2 * math.pi / 360 ,
125+
"incidence_angle_mid_swath": metadata["direct_parse"]["incidence_angle_center"] * 2 * math.pi / 360,
120126
"metadata": metadata
121127
}
122128

123129
azimuth_time = compute_azimuth_time(
124130
product_first_line_utc_time, product_last_line_utc_time, number_of_lines
125131
)
126132

127-
128133
swap_dims = {"line": "azimuth_time", "pixel": "slant_range_time"}
129134

130135
coords: dict[str, Any] = {
@@ -140,12 +145,34 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
140145
),
141146
}
142147

143-
slant_range_time = np.linspace(
144-
image_slant_range_time,
145-
image_slant_range_time + (number_of_samples - 1) / range_sampling_rate,
146-
number_of_samples,
147-
)
148-
coords["slant_range_time"] = ("pixel", slant_range_time)
148+
if product_type == "SLC":
149+
slant_range_time = np.linspace(
150+
image_slant_range_time,
151+
image_slant_range_time + (number_of_samples - 1) / range_sampling_rate,
152+
number_of_samples,
153+
)
154+
coords["slant_range_time"] = ("pixel", slant_range_time)
155+
else:
156+
157+
# generate slant range times from ground ranges for ground range products
158+
# this is easier due to the Envisat only having GRSR metadata unlike Sentinel1
159+
# however xarray/Sarsen handle GRD conversion differently for S1
160+
ground_range = np.linspace(
161+
0,
162+
range_pixel_spacing * (number_of_samples - 1),
163+
number_of_samples,
164+
)
165+
166+
# numpy polyval expects the polynomial top be highest ranked first
167+
coeffs = list(reversed(metadata["direct_parse"]["grsr_coeffs"]))
168+
169+
slant_ranges = np.polyval(coeffs, ground_range)
170+
slant_ranges *= 2
171+
172+
c = 299792458
173+
slant_range_times = slant_ranges / c
174+
175+
coords["slant_range_time"] = ("pixel", slant_range_times)
149176

150177
data = xr.open_dataarray(filepath, engine='rasterio')
151178
data.encoding.clear()
@@ -180,7 +207,7 @@ def get_chirp_parameters(dataset: gdal.Dataset) -> dict[str, Any]:
180207
new_key = key.replace('CHIRP_PARAMS_ADS_CHIRP_', '').lower()
181208
params['chirp'][new_key] = float(value)
182209

183-
#params['elev_corr_factor'] = float(metadata.get('CHIRP_PARAMS_ADS_ELEV_CORR_FACTOR'))
210+
# params['elev_corr_factor'] = float(metadata.get('CHIRP_PARAMS_ADS_ELEV_CORR_FACTOR'))
184211
params['cal_pulse_info'] = get_chirp_cal_pulse_info(metadata)
185212

186213
return params

asar_xarray/envisat_direct.py

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,10 @@ def parse_direct(path: str, gdal_metadata) -> dict[str, Any]:
175175

176176
r = struct.unpack(">ff5f", srgr_buf[13:41])
177177

178-
178+
# Envisat file specification says it is SR/GR Conversion ADSR,
179+
# however the polynomial is for GR to SR...
179180
srgr_coeffs = list(r[2:])
180-
metadata["srgr_coeffs"] = srgr_coeffs
181+
metadata["grsr_coeffs"] = srgr_coeffs
181182

182183

183184

@@ -187,59 +188,69 @@ def parse_direct(path: str, gdal_metadata) -> dict[str, Any]:
187188
# antenna gain
188189
c = 299792458
189190
n_samp = gdal_metadata["line_length"]
190-
R_first = c * slant_time_first * 1e-9 / 2
191-
range_spacing = c / (2 * gdal_metadata["records"]["main_processing_params"]["range_samp_rate"])
192-
range_ref = gdal_metadata["records"]["main_processing_params"]["range_ref"]
193-
R_first = c * slant_time_first * 1e-9 / 2
194-
195-
osv = gdal_metadata["records"]["main_processing_params"]["orbit_state_vectors"]
196191

197-
sat_x = osv[0]["x_pos_1"] * 1e-2
198-
sat_y = osv[0]["y_pos_1"] * 1e-2
199-
sat_z = osv[0]["z_pos_1"] * 1e-2
200192

201193
gain_arr = []
202-
for n in range(n_samp):
203-
# https://github.com/senbox-org/microwave-toolbox/blob/master/sar-op-calibration/src/main/java/eu/esa/sar/calibration/gpf/calibrators/ASARCalibrator.java
204-
R = R_first + n * range_spacing
205-
n_geogrid = len(lats)
206-
geo_interp_idx = (n / n_samp) * (len(lats) - 1)
207-
idx = int(geo_interp_idx)
208-
fract = geo_interp_idx % 1
209-
#interpolate geogrid to match first line geogrid to first OSV
210-
lat = lats[idx] + (lats[idx+1] - lats[idx]) * fract
211-
lon = lons[idx] + (lons[idx+1] - lons[idx]) * fract
194+
if gdal_metadata["sample_type"] == "DETECTED":
195+
gain_arr = np.ones(n_samp)
196+
spreading_loss = np.ones(n_samp)
197+
else:
198+
# antenna gain
199+
c = 299792458
200+
R_first = c * slant_time_first * 1e-9 / 2
201+
range_spacing = c / (2 * gdal_metadata["records"]["main_processing_params"]["range_samp_rate"])
202+
range_ref = gdal_metadata["records"]["main_processing_params"]["range_ref"]
203+
R_first = c * slant_time_first * 1e-9 / 2
204+
205+
osv = gdal_metadata["records"]["main_processing_params"]["orbit_state_vectors"]
206+
207+
sat_x = osv[0]["x_pos_1"] * 1e-2
208+
sat_y = osv[0]["y_pos_1"] * 1e-2
209+
sat_z = osv[0]["z_pos_1"] * 1e-2
210+
211+
for n in range(n_samp):
212+
# https://github.com/senbox-org/microwave-toolbox/blob/master/sar-op-calibration/src/main/java/eu/esa/sar/calibration/gpf/calibrators/ASARCalibrator.java
213+
R = R_first + n * range_spacing
214+
n_geogrid = len(lats)
215+
geo_interp_idx = (n / n_samp) * (len(lats) - 1)
216+
idx = int(geo_interp_idx)
217+
fract = geo_interp_idx % 1
218+
#interpolate geogrid to match first line geogrid to first OSV
219+
lat = lats[idx] + (lats[idx+1] - lats[idx]) * fract
220+
lon = lons[idx] + (lons[idx+1] - lons[idx]) * fract
221+
222+
sar_dis = math.sqrt(sat_x**2 + sat_y**2 + sat_z**2)
223+
224+
distance = calc_distance(lat, lon)
212225

213-
sar_dis = math.sqrt(sat_x**2 + sat_y**2 + sat_z**2)
226+
# elevation angle, cosine law from three sides, slant range, earth center to satellite, earth center to target
227+
angle_cos = (R * R + sar_dis * sar_dis - distance * distance) / (2 * R * sar_dis)
228+
elev_angle = np.rad2deg(math.acos(angle_cos))
214229

215-
distance = calc_distance(lat, lon)
230+
# find the gain from LUT, with 201 gains against reference elevation angle with 0.05 degree steps
231+
elev_idx = int((elev_angle - metadata["antenna_ref_elev_angle"]) / 0.05)
232+
elev_idx += 100
233+
gain = 1.0
234+
if elev_idx >= 0 and elev_idx <= len(antenna_gains):
235+
# dB -> linear
236+
gain = math.pow(10, antenna_gains[elev_idx] / 10)
216237

217-
# elevation angle, cosine law from three sides, slant range, earth center to satellite, earth center to target
218-
angle_cos = (R * R + sar_dis * sar_dis - distance * distance) / (2 * R * sar_dis)
219-
elev_angle = np.rad2deg(math.acos(angle_cos))
238+
gain_arr.append(1/gain)
220239

221-
# find the gain from LUT, with 201 gains against reference elevation angle with 0.05 degree steps
222-
elev_idx = int((elev_angle - metadata["antenna_ref_elev_angle"]) / 0.05)
223-
elev_idx += 100
224-
gain = 1.0
225-
if elev_idx >= 0 and elev_idx <= len(antenna_gains):
226-
# dB -> linear
227-
gain = math.pow(10, antenna_gains[elev_idx] / 10)
228240

229-
gain_arr.append(gain)
230241

231242

232243

233244

245+
# calculate spreading loss compensation
234246

247+
spreading_loss = []
248+
for n in range(n_samp):
249+
R = R_first + n * range_spacing
250+
factor = math.sqrt((range_ref / R) ** 3)
251+
spreading_loss.append(1/factor)
235252

236-
# calculate spreading loss compensation
237253

238-
spreading_loss = []
239-
for n in range(n_samp):
240-
R = R_first + n * range_spacing
241-
factor = math.sqrt((range_ref / R) ** 3)
242-
spreading_loss.append(1/factor)
243254

244255
cal_factor = gdal_metadata["records"]["main_processing_params"]["calibration_factors"][0]["ext_cal_fact"]
245256

0 commit comments

Comments
 (0)