Download this notebook from github.
Sentinel-1 Level-1B IFREMER variables explanation
Although some variables need extra explanation about why there are present, how they are computed and how to properly use them.
A dedicated Python library is available to use IFREMER Sentinel-1 Level-1B products: https://slcl1butils.readthedocs.io
[1]:
from xsarslc.get_test_files import get_test_file
import xarray as xr
# https://cyclobs.ifremer.fr/static/sarwing_datarmor/xsardata/S1B_IW_XSP__1SDV_20200925T173737_20200925T173804_023535_02CB5D_E473.SAFE/s1b-iw1-slc-vv-20200925t173739-20200925t173804-023535-02cb5d-004_L1B_xspec_IFR_1.4k.nc.zip
#localpath = get_test_file('S1B_IW_XSP__1SDV_20200925T173737_20200925T173804_023535_02CB5D_E473.SAFE/s1b-iw1-slc-vv-20200925t173739-20200925t173804-023535-02cb5d-004_L1B_xspec_IFR_1.4k.nc')
localpath = get_test_file('s1b-iw1-slc-vv-20200925t173739-20200925t173804-023535-02cb5d-004_L1B_xspec_IFR_1.4k.nc')
localpath
[1]:
'/tmp/s1b-iw1-slc-vv-20200925t173739-20200925t173804-023535-02cb5d-004_L1B_xspec_IFR_1.4k.nc'
[2]:
import os
#localpath_tweaked = os.path.join(os.path.dirname(os.path.dirname(localpath)),os.path.basename(localpath))
#print(localpath_tweaked,os.path.exists(localpath_tweaked))
print(os.path.exists(localpath))
ds = xr.open_dataset(localpath,group='intraburst')
ds
True
[2]:
<xarray.Dataset> Dimensions: (burst: 9, tile_line: 1, tile_sample: 4, freq_sample: 401, freq_line: 51, 0tau: 3, 1tau: 2, 2tau: 1, c_sample: 2, c_line: 2) Coordinates: pol <U2 ... * burst (burst) int16 0 1 2 3 4 5 6 7 8 k_rg (burst, tile_sample, freq_sample) float32 ... k_az (freq_line) float32 ... line (burst, tile_line) int16 ... sample (burst, tile_sample) int16 ... longitude (burst, tile_line, tile_sample) float32 ... latitude (burst, tile_line, tile_sample) float32 ... Dimensions without coordinates: tile_line, tile_sample, freq_sample, freq_line, 0tau, 1tau, 2tau, c_sample, c_line Data variables: (12/25) incidence (burst, tile_line, tile_sample) float32 ... normalized_variance (burst, tile_line, tile_sample) float32 ... sigma0 (burst, tile_line, tile_sample) float32 ... nesz (burst, tile_line, tile_sample) float32 ... ground_heading (burst, tile_line, tile_sample) float32 ... doppler_centroid (burst, tile_line, tile_sample) float32 ... ... ... xspectra_0tau_Re (burst, tile_line, tile_sample, freq_line, freq_sample, 0tau) float32 ... xspectra_0tau_Im (burst, tile_line, tile_sample, freq_line, freq_sample, 0tau) float32 ... xspectra_1tau_Re (burst, tile_line, tile_sample, freq_line, freq_sample, 1tau) float32 ... xspectra_1tau_Im (burst, tile_line, tile_sample, freq_line, freq_sample, 1tau) float32 ... xspectra_2tau_Re (burst, tile_line, tile_sample, freq_line, freq_sample, 2tau) float32 ... xspectra_2tau_Im (burst, tile_line, tile_sample, freq_line, freq_sample, 2tau) float32 ... Attributes: (12/27) name: SENTINEL1_DS:/home/datawork-cersat-public/projec... short_name: SENTINEL1_DS:S1B_IW_SLC__1SDV_20200925T173737_20... product: SLC safe: S1B_IW_SLC__1SDV_20200925T173737_20200925T173804... swath: IW multidataset: False ... ... tile_overlap_sample: 0 tile_overlap_line: 0 periodo_width_sample: 3540 periodo_width_line: 3540 periodo_overlap_sample: 1770 periodo_overlap_line: 1770
wavenumbers in range axis: “k_rg”
“k_rg” variable slightly depends on each tile because the pixel spacing is not the same in the IW sub-swath.
We made the choice to have a constant dk (in range and azimuth) for all the tiles/cross-spectrum, and we achieved a 10e-3 consistency as a maximum point-to-point difference wavenumbers .
This strategy allows to compare a spectra to another.
[3]:
k_rg1 = ds['k_rg'].isel(burst=2,tile_sample=1)
k_rg2 = ds['k_rg'].isel(burst=2,tile_sample=3)
k_rg1
[3]:
<xarray.DataArray 'k_rg' (freq_sample: 401)> [401 values with dtype=float32] Coordinates: pol <U2 ... burst int16 2 k_rg (freq_sample) float32 ... sample int16 ... Dimensions without coordinates: freq_sample Attributes: long_name: wavenumber in range direction units: rad/m
[4]:
from matplotlib import pyplot as plt
k_rg1.plot(label="burst=2,tile_sample=1",linestyle='--',lw=5)
k_rg2.plot(label="burst=2,tile_sample=2")
plt.legend()
[4]:
<matplotlib.legend.Legend at 0x7f405d8ba380>
[5]:
(k_rg1-k_rg2).plot()
plt.title('difference between 2 different k_rg')
[5]:
Text(0.5, 1.0, 'difference between 2 different k_rg')
The difference between the 2 vectors of wave numbers is super small but for users who might want to stack the different cross spectrum, a first step would be to assign a single k_rg to all the cross spectrum.
SAR image complexe cartesian cross spectrum
The “xpsectra_{0-1-2}tau_*” is split in real and imaginary part because netCDF4 file format does not allow to store complex values.
Also only half of the spectrum along the range wavenumbers is stored in the netCDF files to save space.
[6]:
# recompose complexe SAr image cross spectrum from real and imaginary part
for tautau in range(3):
ds['xspectra_%stau'%tautau] = ds['xspectra_%stau_Re'%tautau] + 1j*ds['xspectra_%stau_Im'%tautau]
#ds = ds.drop(['xspectra_%stau_Re'%tautau,'xspectra_%stau_Im'%tautau])
one_cartesian_xsp = ds['xspectra_2tau'].isel({'burst':6,'tile_line':0,'tile_sample':2,'2tau':0})
one_cartesian_xsp = one_cartesian_xsp.assign_coords({'freq_line':one_cartesian_xsp.k_az,'freq_sample':one_cartesian_xsp.k_rg})
one_cartesian_xsp
[6]:
<xarray.DataArray 'xspectra_2tau' (freq_line: 51, freq_sample: 401)> array([[-1.2477797 +0.5443525j , 1.5725 -0.01272768j, 0.4407456 -0.2429612j , ..., 0.00830453-0.3919062j , 0.1216751 +0.09071319j, -0.05311767+0.49395174j], [ 2.060735 +0.5237646j , 0.2005456 +0.29838213j, -0.02560596-0.18570837j, ..., 0.05239271+0.30355382j, 0.54826015-0.08569147j, 0.02144801-0.19576392j], [-1.4342278 +1.4327244j , -0.30036288+0.18255554j, 0.6784638 -0.17505352j, ..., -0.04385818+0.16508722j, -0.375506 -0.16829075j, -0.24914488+0.21455552j], ..., [-1.4342278 -1.4327244j , -0.53528905+0.7241161j , -1.539189 +0.64669746j, ..., 0.03286539+0.10321924j, -0.2948199 -0.2361605j , -0.47778353-0.54428077j], [ 2.060735 -0.5237646j , -0.2049799 -1.0952276j , 0.42247522+1.1954173j , ..., 0.15609382-0.17570984j, 0.36818713-0.3684804j , -0.01811259-0.32531738j], [-1.2477797 -0.5443525j , -0.39482182+1.2361256j , -0.9189708 +0.479853j , ..., 0.360627 +0.5266922j , 0.07324556+0.2173959j , 0.4194936 +0.03121182j]], dtype=complex64) Coordinates: pol <U2 'VV' burst int16 6 k_rg (freq_sample) float32 ... k_az (freq_line) float32 ... line int16 9642 sample int16 12215 longitude float32 3.652 latitude float32 40.42 * freq_line (freq_line) float32 -0.04414 -0.04238 ... 0.04238 0.04414 * freq_sample (freq_sample) float32 0.0 0.001775 0.00355 ... 0.7082 0.71
[7]:
from matplotlib import colors as mcolors
cmap = mcolors.LinearSegmentedColormap.from_list("", ["white","violet","mediumpurple","cyan","springgreen","yellow","red"])
abs(one_cartesian_xsp.real).plot(cmap=cmap)
plt.grid(True)
landmask
The “land_flag” annotated in the sample of L1B product comes from cartopy
10 m resolution polygons.
cartopy/shapefiles/natural_earth/physical/: https://www.naturalearthdata.com/
[8]:
ds['land_flag'].plot()
[8]:
<matplotlib.collections.QuadMesh at 0x7f405cdb0310>
variance of cross spectrum
“var_xspectra_{0-1-2}tau” is the variance of the different periodograms while “xpsectra_{0-1-2}tau” is the mean of the periodograms computed within a given tile.
These variables var_xspectra_{0-1-2}tau are defined on the same grid than the cross spectrum xpsectra_{0-1-2}tau. They are also splitted in Real part and Imaginary part in the netCDF files, and they are also stored on the half part of the wavenumer along range axis since they are symmetric.
[9]:
ds['var_xspectra_2tau'].isel(tile_line=0,tile_sample=0,burst=3).squeeze().plot(cmap=cmap)
[9]:
<matplotlib.collections.QuadMesh at 0x7f405cc98c40>