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>
../_images/examples_L1B_Sentinel1_variables_explanation_7_1.png
[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')
../_images/examples_L1B_Sentinel1_variables_explanation_8_1.png

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)
../_images/examples_L1B_Sentinel1_variables_explanation_13_0.png

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>
../_images/examples_L1B_Sentinel1_variables_explanation_16_1.png

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>
../_images/examples_L1B_Sentinel1_variables_explanation_19_1.png