Skip to content

Sentinel-1 RTC - offsets between S1A and S1C platforms #492

Description

@natborn2

I've come across what appear to be systematic and persistent shifts in the geolocation or terrain correction of some Sentinel-1 RTC scenes.

The example below is in relative orbit 69 in Peru. The shift seems to affect large areas of the scene but is only at the level of a few pixels so is not immediately obvious unless you focus on sharp-edged landscape features, particularly in areas of steep slopes.

The shift appears to occur quite suddenly around May 2025. I have analysed several locations around this region of Peru and found similar issues occurring around the same time, at least in orbit 69 and orbit 120, but possibly others too.

When we look at the platform information from the stac items it becomes clear that the shift is associated with scenes from Sentinel-1C, while scenes from Sentinel-1A after May 2025 appear to be consistent with earlier ones. The more recent scenes from Sentinel-1D appear to be split between both S1C-like and S1A-like examples.

Is there anything that can be done to fix these differences? Although subtle, they are significant for change detection algorithms. In the example shown, there is data from S1A throughout the time series, but in other examples there is a gap in S1A data after May 2025 so if we were to exclude S1C then it would lead to significant data gaps.

Minimal reproducible example

import planetary_computer
import numpy as np
import odc.stac
import pystac_client
import matplotlib.pyplot as plt
import xarray as xr

bbox = [-76.95838, -6.89814, -76.95626, -6.89612]
daterange = "2023-01-01/2026-07-16"

catalog = pystac_client.Client.open(
    "https://planetarycomputer.microsoft.com/api/stac/v1",
    modifier=planetary_computer.sign_inplace)

rtc_items = catalog.search(
    collections=["sentinel-1-rtc"],
    bbox = bbox,
    datetime=daterange,
    query = {"sar:instrument_mode":{"eq":"IW"},
             "sar:polarizations":{"eq":["VV","VH"]},
             "sat:relative_orbit":{"eq":69}}
).item_collection()

ds = odc.stac.load(rtc_items,
              bands = ["vv"],
              groupby="solar_day",
              bbox = bbox,
             )

baseline = ds.isel(time=slice(None,10)).mean(dim="time")
deltas = np.abs( ds.isel(time=slice(10, None)) - baseline ).mean(dim=["x","y"])

# Create an array of platform values for each time point
ds['platform'] = xr.DataArray(
    [item.properties['platform'] for item in rtc_items.items],
    coords={"time": [np.datetime64(item.properties['datetime']) 
                     for item in rtc_items.items]},
    )

# Plot VV by platform
fig, ax = plt.subplots(figsize=(12, 5))
platforms = ds['platform'].sel(time=deltas.time)
unique_platforms = sorted(set(platforms.values))
colors = plt.cm.tab10.colors
platform_colors = {p: colors[i] for i, p in enumerate(unique_platforms)}

for platform in unique_platforms:
    mask = platforms.values == platform
    ax.scatter(deltas.time.values[mask], deltas.vv.values[mask],
               label=platform, s=10, color=platform_colors[platform])

ax.set_title("Average absolute shift in VV")
ax.legend()
fig.autofmt_xdate()

# Plot mean VV image for each platform
fig, axes = plt.subplots(2,2, figsize=(12, 10), sharex=True, sharey=True)

for ax, platform in zip(axes.flatten(), unique_platforms):
    mask = ds['platform'].values == platform
    ds.sel(time=ds.time.values[mask]).vv.mean(dim='time').plot(ax=ax, vmin=0, vmax=1)
    ax.set_title(f"{platform} mean VV")
    ax.grid(visible=True, which='major', axis='both')

Outputs

Image Image

Additional context

At first glance this looks similar to #404 and I initially thought it was the same issue. That issue actually discussed two very similar issues, one affecting scenes over the Philipines in late 2024 (which was fixed) and a second (apparently unrelated issue) affecting scenes over Kenya in 2022. However, the shift seen in Kenya is involving scenes that are exclusively from S1A. I plotted the time series in the Kenya AOI up to 2026 and found that there is no offset between S1A and S1C (but I still reproduce the jump seen in 2022). So there are some differences from what I'm seeing here in Peru, although the sudden and persistent nature of the shift are similar.

Activity

  1. benritchie commented on Aug 6, 2026

    @benritchie

    Folks - these issues are a significant blocker for change detection use cases for this data - as pixel shifts this caues cause spurious, phantom change to be detected, and so this significantly impacts the utility of what is otherwise a fantastic, and unique set of S1 processing. Any chance of an update on this issue? thanks.

  2. natborn2 commented on Aug 11, 2026

    @natborn2
    Author

    We have been investigating these issues a bit further and found similar problems for a location in Indonesia where we are seeing widespread changes between data obtained from Sentinel-1A up to mid 2025, and data obtained by S1A, S1C, S1D in the same relative orbit from mid 2025 onwards. Digging a bit further, we traced the issue in Indonesia back to the GRD data hosted by Planetary Computer. We find a significant spatial offset between the geolocation of adjacent slices in acquisitions before mid 2025. This persists into the RTC data, resulting in over/under-correction of backscatter values in regions of strong terrain slopes, which only compounds the issue with the geolocation error.

    Example:

    import planetary_computer
    import odc.stac
    import pystac_client
    
    bbox = [97.6834951464175, 3.1134330480831607, 97.70453247154423, 3.129370415603404]
    daterange = "2023-01-01/2026-08-01"
    relative_orbit = 62
    catalog = pystac_client.Client.open(
        "https://planetarycomputer.microsoft.com/api/stac/v1",
        modifier=planetary_computer.sign_inplace)
    
    grd_items = catalog.search(
        collections=["sentinel-1-grd"],
        bbox = bbox,
        datetime=daterange,
        query = {
            "sar:instrument_mode":{"eq":"IW"},
            "sar:polarizations":{"eq":["VV","VH"]},
            "sat:relative_orbit":{"eq":relative_orbit}
        }
    ).item_collection()
    
    ds_grd = odc.stac.load(grd_items,
                  bands = ["vv"],
                  groupby="solar_day",
                  bbox = bbox,
                 )
    
    # Plot mean VV image before/after the switch
    fig, axes = plt.subplots(1,2, figsize=(12, 5), sharex=True, sharey=True)
    
    ax = axes[0]
    mask = ds_grd['time'].values <= pd.Timestamp("2025-03-01") 
    ds_grd.sel(time=ds_grd.time.values[mask]).vv.mean(dim='time').plot(ax=ax, vmin=0, vmax=600)
    ax.set_title(f"Mean VV (orbit 62) up to 2025-03-01")
    ax.grid(visible=True, which='major', axis='both')
    
    ax = axes[1]
    mask = ds_grd['time'].values > pd.Timestamp("2025-07-01"))
    ds_grd.sel(time=ds_grd.time.values[mask]).vv.mean(dim='time').plot(ax=ax, vmin=0, vmax=600)
    ax.set_title(f"Mean VV (orbit 62) after 2025-07-01")
    ax.grid(visible=True, which='major', axis='both')
    Image

    Notice the terrain shadows in the upper part of the image are shifted left in the earlier data.

    If we inspect acquisitions from a single day we find that the problem stems from adjacent slices from the orbit, which are held in separate stac items. In this case, slices 22 and 23 of relative orbit 62. In the example below we plot separately the two slices from 2023-01-07 that intersect this AOI (S1A_IW_GRDH_1SDV_20230107T231248_20230107T231313_046684_059883, S1A_IW_GRDH_1SDV_20230107T231223_20230107T231248_046684_059883).

    Image

    And when these have been combined using odc.stac.load(groupby="solar_day") we obtain:

    Image

    where we can see the same features as in the left panel of the first figure.

    Most convincing, this offset between slices is readily apparent in the Planetary Computer browser: we see an offset across the images from this search for orbit 62 on 2023-01-07:

    Image

    Comparing with the same data from Copernicus we see no artefact:

    Image

    Our investigation seems to indicate this isn't an isolated occurrence, but something that affects all S1A scenes up to some point in 2025 in this Indonesia location. The effect appears to be the same as that we found in Peru, which further suggests that this is not restricted to a single geographical location either.

  3. natborn2 commented on Aug 12, 2026

    @natborn2
    Author

    I'm not sure if this is relevant, but when I look at the metadata of affected scenes from 2023 and 2024 in Copernicus, they show modification times in Dec 2025 or later (eg search for S1A_IW_GRDH_1SDV_20230107T231248_20230107T231313_046684_059883 in the copernicus dataspace browser). So is it possible that ESA applied some retrospective correction to their GRD collection around that time but this was not ingested by Planetary Computer? However, the plantetary computer asset does not seem to contain any processing/modification time so I cannot confirm.

  4. benritchie commented on Aug 12, 2026

    @benritchie

    Update - the GRD issues we reported above are at least partly visualisation artifacts, that affect GDAL in its default mode, and also titiler / PC-Pro explorer, rather than underlying data bugs I think. Its possible they are still related (I guess a similar issue could affect the RTC pipeline) but less clear now.

    Details below:

    • GRD is in satellite coordinates, not map co-ordinates (as expected).
    • It provides an attached series of GPC's (roughtly 20*20 per slice) to allow conversion.
    • The "right" way to apply those would be a complex TPS transform - as recommended by ESA - utilising all 20 points. you can do that in e.g. gdalwarp with the tps flag.
    • however GDAL default is not to do that. its instead to apply a 2nd order polynomial fitted to the points. That's likely ok in many places, but clearly completely fails to fit in v mountainous regions - in this slice we have altitudes ranging from 0 to 2km, and that results in geolocation erros of up t ~ 1.5km. This is the source of the discontinuities.
    • And more specifically - the discontinuities are caused because if the GCP grid is used "properly", the transforms will be continuous between tiles, avoiding edge effects. However, if a separate 2D polynomial is fitted per slice, that will inevitable create edge effects between slices. - I'm assuming however those are only becoming onvious in the extremely mountainous regions, hence why we only see them in a few tiles.
    • Why does this effect PC as well as GDAL created images? - I'm guessing here, as I don't have access to PC source, but the underlying titiler PC uses doesn't support doing GCP's "correctly", it only supports an approximate polynomial - so its no surprise PC is not correctly displaying this data.
    • Why does this issue go away in 2025? - looks like a fix went into the metadata in 2025. The fix is a bit of a bodge - it doesn't fix the underlying issue, so there will still be distortion across the scenes, it applies a transform in metadata that warps the image to match the expected footprint, mostly eliminating the visible edge effects / steps.

    What does this mean for RTC?

    • we think it means that the above suggestion that GRD is that cause may not be the cause of the RTC issues. - although the offsets etc we see in the GRD frames caused by this issue bear some similarity to the RTC artifacts, so its possible there's still some relationship between the RTC artifacts and this error path - hard to know though without seeing the RTC code.

    Too much here to include fill repos etc for all of the above inline, but I have code demonstrating most of this, shout if you'd like any bits demo'd.

  5. benritchie commented on Aug 13, 2026

    @benritchie

    1 other observation is that the Slices for Sentinel 1A / 1C / 1D do not line up (although the orbits do) - the point where the slice boundary is for 1A and 1C is 10's of km different for orbit 62 at least. Is it possible that the RTC correction code is not invariant with respect to slice locations?

  6. natborn2 commented on Aug 13, 2026

    @natborn2
    Author

    Other orbits where I've come across similar problems in the RTC data are 112, 127, 134. (I have also confirmed that the same discontinuities at slice boundaries are visible in the GRD data in the PC browser for all of these).

    Interestingly, similar to Ben's comment, I've noticed that all of these orbits (like 62) have coverage from both S1A and S1C, and I think they all have offsets between the slice footprints.

    Conversely, I have checked other orbits in the same geographical area (eg 3, 41, 76) in which I did not find issues in RTC and which do not have any data from S1C (only S1A and S1D). However, these do still show some discontinuities at slice boundaries in GRD, which perhaps indicates (as Ben suggests) that this could be unrelated.

  7. benritchie commented on Aug 17, 2026

    @benritchie

    Just to expand on the above, The pattern in these makes the cause clearer.
    We have 3 states:
    Sentinel 1A, "Old" slice boundaries
    (link above)
    Sentinel 1D, "Old" slice boundaries - the satellite switches to 1D, but the location of the slice boundaries, and the slice numbering is unchanged.
    https://planetarycomputer.microsoft.com/explore?c=116.2174%2C2.2100&z=12.11&v=2&d=sentinel-1-rtc&m=cql%3Abf36aa750908a688d38765a6999864f7&r=VV%2C+VH+False-color+composite&s=false%3A%3A100%3A%3Atrue&sr=desc&ae=0
    Sentinel 1D, "New" slice boundaries - 2 passes later - the slice boundaries are changed.
    (link above)

    This issue exhibits on the change of slice boundaries, not the change of sensor platform. - that seems to pretty definitively show that's there's something in the processing that is not invariant with respect to slice location/numbering/boundaries. - and that this is the cause of this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions