Constant offset for GRD calibration

I finally understood the issue, which came from the EOPF product itself. The ground_range coordinates of the LUTs contain wrong values and the workaround consist in interpolating using the two other corresponding dimensions called line and pixel.

I reported the issue on the EOPF GitLab here: S1 L1 mapping quality group (#887) · Issues · EOPF CPM / EOPF Core Python Modules · GitLab

The working code can be found below:

import xarray as xr
import numpy as np
import rioxarray
import matplotlib.pyplot as plt

grd_path = "https://objects.eodc.eu/e05ab01a9d56408d82ac32d69a5aae2a:notebook-data/tutorial_data/cpm_v262/S1B_IW_GRDH_1SDV_20170503T173207_20170503T173232_005437_00987B_1A41.zarr"

dt = xr.open_datatree(grd_path, engine="zarr", chunks="auto")

group_VH = [x for x in dt.children if "VH" in x][0]
grd_vh = dt[group_VH].measurements.to_dataset().rename({"grd": "vh"})
sigma_lut = dt[group_VH].quality.calibration.sigma_nought

sigma_lut_line_pixel = xr.DataArray(
    data=sigma_lut.data,
    dims=["line", "pixel"],
    coords=dict(
        line=(["line"], sigma_lut.line.values),
        pixel=(["pixel"], sigma_lut.pixel.values),
    ),
)
grd_vh_line_pixel = xr.DataArray(
    data=grd_vh.vh.data,
    dims=["line", "pixel"],
    coords=dict(
        line=(["line"], grd_vh.vh.line.values),
        pixel=(["pixel"], grd_vh.vh.pixel.values),
    ),
)
sigma_lut_interp_line_pixel = sigma_lut_line_pixel.interp_like(grd_vh_line_pixel,method="linear")

sigma_lut_interp = xr.DataArray(
    data=sigma_lut_interp_line_pixel.data,
    dims=["azimuth_time", "ground_range"],
    coords=dict(
        azimuth_time=(["azimuth_time"], grd_vh.vh.azimuth_time.values),
        ground_range=(["ground_range"], grd_vh.vh.ground_range.values),
    ),
)
eopf_sigma_0_vh = ((abs(grd_vh.astype(np.float32)) ** 2) / (sigma_lut_interp**2)).vh.compute()

SNAP_sigma_0_path = "./S1B_IW_GRDH_1SDV_20170503T173207_20170503T173232_005437_00987B_1A41_Cal.tif"
SNAP_sigma_0_vh = rioxarray.open_rasterio(SNAP_sigma_0_path)[0]

SNAP_mean = SNAP_sigma_0_vh.mean().data
SNAP_median = SNAP_sigma_0_vh.median().data

EOPF_mean = eopf_sigma_0_vh.mean().data
EOPF_median = eopf_sigma_0_vh.median().data

print(f"SNAP sigma 0 vh mean: {SNAP_mean} median: {SNAP_median}")
print(f"EOPF sigma 0 vh mean: {EOPF_mean} median: {EOPF_median}")

fig, ax = plt.subplots(1,2,figsize=(16, 8))

eopf_sigma_0_vh.plot.hist(bins=300,xlim=(0,0.2),range=(0,0.2),ylim=(0,10000000),ax=ax[0])
ax[0].set_title("EOPF sigma 0")
SNAP_sigma_0_vh.plot.hist(bins=300,xlim=(0,0.2),range=(0,0.2),ylim=(0,10000000),ax=ax[1])
ax[1].set_title("SNAP sigma 0")

fig.tight_layout()

plt.show()
SNAP sigma 0 vh mean: 0.01803111843764782 median: 0.006793084088712931
EOPF sigma 0 vh mean: 0.01803111845179507 median: 0.006793084177165347