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> Size: 53MB 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 8B ... * burst (burst) int16 18B 0 1 2 3 4 5 6 7 8 k_rg (burst, tile_sample, freq_sample) float32 58kB ... k_az (freq_line) float32 204B ... line (burst, tile_line) int16 18B ... sample (burst, tile_sample) int16 72B ... longitude (burst, tile_line, tile_sample) float32 144B ... latitude (burst, tile_line, tile_sample) float32 144B ... 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 144B ... normalized_variance (burst, tile_line, tile_sample) float32 144B ... sigma0 (burst, tile_line, tile_sample) float32 144B ... nesz (burst, tile_line, tile_sample) float32 144B ... ground_heading (burst, tile_line, tile_sample) float32 144B ... doppler_centroid (burst, tile_line, tile_sample) float32 144B ... ... ... xspectra_0tau_Re (burst, tile_line, tile_sample, freq_line, freq_sample, 0tau) float32 9MB ... xspectra_0tau_Im (burst, tile_line, tile_sample, freq_line, freq_sample, 0tau) float32 9MB ... xspectra_1tau_Re (burst, tile_line, tile_sample, freq_line, freq_sample, 1tau) float32 6MB ... xspectra_1tau_Im (burst, tile_line, tile_sample, freq_line, freq_sample, 1tau) float32 6MB ... xspectra_2tau_Re (burst, tile_line, tile_sample, freq_line, freq_sample, 2tau) float32 3MB ... xspectra_2tau_Im (burst, tile_line, tile_sample, freq_line, freq_sample, 2tau) float32 3MB ... 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)> Size: 2kB [401 values with dtype=float32] Coordinates: pol <U2 8B ... burst int16 2B 2 k_rg (freq_sample) float32 2kB ... sample int16 2B ... 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 0x74865c57ac60>
[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)> Size: 164kB 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 8B 'VV' burst int16 2B 6 k_rg (freq_sample) float32 2kB ... k_az (freq_line) float32 204B ... line int16 2B 9642 sample int16 2B 12215 longitude float32 4B 3.652 latitude float32 4B 40.42 * freq_line (freq_line) float32 204B -0.04414 -0.04238 ... 0.04238 0.04414 * freq_sample (freq_sample) float32 2kB 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 0x7486525c1910>
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 0x7486524c1f10>