Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
78894c3
Make sensor attribute a set
sfinkens May 4, 2026
dac0457
Use instrument instead of sensor attribute
sfinkens May 6, 2026
8ffb671
Add instrument attribute setter
sfinkens May 6, 2026
2d730ea
Reset ABI name
sfinkens May 6, 2026
180a040
Convert string type sensors to set for now
sfinkens May 6, 2026
46b08c2
Fix tests
sfinkens May 6, 2026
fe1d232
Update docstring
sfinkens May 6, 2026
32ee183
Restore accidental string replace
sfinkens May 6, 2026
bb04e29
Make sensor attribute a set
sfinkens May 4, 2026
fbb55cf
Use instrument instead of sensor attribute
sfinkens May 6, 2026
9633197
Add instrument attribute setter
sfinkens May 6, 2026
1d362c7
Reset ABI name
sfinkens May 6, 2026
ae1d8e7
Convert string type sensors to set for now
sfinkens May 6, 2026
d95a3f3
Fix tests
sfinkens May 6, 2026
2107f44
Update docstring
sfinkens May 6, 2026
78e0de9
Restore accidental string replace
sfinkens May 6, 2026
32a8e02
Replace direct attribute access with setter
sfinkens May 7, 2026
1e69183
Merge branch 'wmo-instruments-part1' of https://github.com/sfinkens/s…
sfinkens May 7, 2026
e4a53b6
Restore enhancer keyword arguments
sfinkens May 7, 2026
db7731d
Add deprecation warnings for sensor attribute
sfinkens May 7, 2026
8d50a8d
Add tests for instrument utilities
sfinkens May 7, 2026
f0970e3
Move instrument helpers to their own module
sfinkens May 8, 2026
bddff40
Deprecate sensor attribute in enhancements
sfinkens May 8, 2026
9ca9b79
Remove explicit sensor usage in dependency tree
sfinkens May 8, 2026
1ffe5b0
Ensure compatibility in enhancement decision tree
sfinkens May 11, 2026
184d538
Update doc/source/enhancements.rst
sfinkens May 11, 2026
f4bcd2f
Improve deprecation warning message
sfinkens May 11, 2026
8903545
Merge branch 'wmo-instruments-part2-enhancements' of https://github.c…
sfinkens May 11, 2026
91b753b
Keep single instruments as string instead of set
sfinkens May 11, 2026
2f69a1e
Use instrument attribute setter
sfinkens May 11, 2026
287a8ab
Fix typo
sfinkens May 11, 2026
8061698
Fix whitespace
sfinkens May 11, 2026
cc6bcbd
Replace sensor with instruments in file handlers
sfinkens May 11, 2026
ad763f2
Replace sensor with instruments in file handlers
sfinkens May 13, 2026
b448d83
Replace sensors with instruments in yaml reader
sfinkens May 13, 2026
cddd659
Add helper for GOES Imager
sfinkens May 13, 2026
69d6e18
Fix test
sfinkens May 13, 2026
0e32aab
Use Enum for WMO instrument names
sfinkens May 18, 2026
da20004
Revert MIMIC changes
sfinkens May 18, 2026
4f0ff26
Adapt instrument normalization to WMO names
sfinkens May 18, 2026
c859cd8
Use lists for instruments in enhancements
sfinkens May 19, 2026
fd259b3
Normalize instrument names from dataset attributes
sfinkens May 19, 2026
4cc22ee
Harmonize enum to string conversion
sfinkens May 19, 2026
e581271
Fix more string conversions
sfinkens May 19, 2026
85631d9
Restore normalized instrument names in YAMLs
sfinkens May 20, 2026
6fb27d5
Normalize instruments internally
sfinkens May 20, 2026
7a866b2
Revert "Normalize instruments internally"
sfinkens May 20, 2026
6fa9743
Use better name for instruments module
sfinkens May 20, 2026
677a388
Choose better method names
sfinkens May 20, 2026
0f9c99c
Add internal to WMO conversion method
sfinkens May 20, 2026
5ecfa7f
Make scene.sensor_names return WMO names
sfinkens May 20, 2026
020a1d9
Convert WMO to internal in enhancer
sfinkens May 20, 2026
18744ea
Fix sensor_names expectations in tests
sfinkens May 20, 2026
98eaa15
Merge branch 'main' into wmo-instruments-part1
sfinkens May 20, 2026
4553cc2
Merge branch 'wmo-instruments-part1' into wmo-instruments-part2-enhan…
sfinkens May 21, 2026
f33188e
Merge branch 'wmo-instruments-part1' into wmo-instruments-part2-file-…
sfinkens May 21, 2026
fc63d83
Fix conversion to WMO name
sfinkens May 21, 2026
e824e7e
Rename sensors -> instruments in reader yaml files
sfinkens May 21, 2026
1f0d9a0
Remove extra quotes
sfinkens May 21, 2026
f10216b
Remove trailing comma
sfinkens May 21, 2026
51c8abf
Improve docstring
sfinkens May 22, 2026
8a54b76
Simplify tuple creation
sfinkens May 22, 2026
a3f781c
Merge branch 'wmo-instruments-part2-enhancements' of https://github.c…
sfinkens May 22, 2026
830678e
Add v1.0 scissors
sfinkens May 22, 2026
30c9926
Wrap deprecation warning in v1.0 scissors
sfinkens May 22, 2026
8afd9f2
Wrap deprecation warning with v1.0 scissors
sfinkens May 22, 2026
bd2a355
Change scissor version to 1.1
sfinkens May 22, 2026
47bfb3d
Change scissor version to 1.1
sfinkens May 22, 2026
289116d
Change scissor version to 1.1
sfinkens May 22, 2026
e0e76de
Add config switch for legacy sensor attribute
sfinkens May 22, 2026
f6a5fee
Update documentation
sfinkens May 22, 2026
02c455a
Change scissors again
sfinkens May 22, 2026
5f836ea
Change scissors again
sfinkens May 22, 2026
8ceabf1
Change scissors again
sfinkens May 22, 2026
c147e09
Change scissors again
sfinkens May 22, 2026
e9e3e9f
Remove config switch
sfinkens May 22, 2026
bbe02e1
Merge branch 'wmo-instruments-part2-config-switch' into wmo-instrumen…
sfinkens May 22, 2026
831938d
Fix exceptions in sensor compatibility
sfinkens May 22, 2026
619f5ca
Merge branch 'wmo-instruments-part1' into wmo-instruments-part2-enhan…
sfinkens May 22, 2026
d20a659
Merge branch 'wmo-instruments-part1' into wmo-instruments-part2-file-…
sfinkens May 22, 2026
da4b460
Update documentation
sfinkens May 22, 2026
0111fa1
Merge branch 'wmo-instruments-part2-enhancements' into wmo-instrument…
sfinkens May 22, 2026
48bc4be
Merge branch 'wmo-instruments-part2-file-handlers' into wmo-instrumen…
sfinkens May 22, 2026
13f4ed2
Add filename/instrument consistency tests
sfinkens May 22, 2026
54a6630
Merge branch 'wmo-instruments-part2-enhancements' into wmo-instrument…
sfinkens Jun 18, 2026
47ca6bd
Merge branch 'wmo-instruments-part2-file-handlers' into wmo-instrumen…
sfinkens Jun 18, 2026
a9a97a5
Rename enhancement and composite configs
sfinkens Jun 23, 2026
15041e2
Fix import order
sfinkens Jun 23, 2026
db04d1c
Search for enhancements with old instrument name
sfinkens Jun 25, 2026
862dcb5
Search for composites with old instrument name
sfinkens Jun 26, 2026
7230672
Merge branch 'wmo-instruments-part1' into wmo-instruments-part3-rename
sfinkens Jun 26, 2026
3bd9dfa
Fix instrument in MWR readers
sfinkens Jun 26, 2026
20df4e8
Merge branch 'main' into wmo-instruments-part1
sfinkens Jun 26, 2026
11cd07b
Merge branch 'wmo-instruments-part1' into wmo-instruments-part3-rename
sfinkens Jun 26, 2026
ec68c2d
Fix HSAF enhancement test
sfinkens Jun 26, 2026
262d5fe
Complete list of renamed composites
sfinkens Jun 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repos:
- types-setuptools
- types-PyYAML
- types-requests
args: ["--python-version", "3.10", "--ignore-missing-imports"]
args: ["--python-version", "3.11", "--ignore-missing-imports"]
- repo: https://github.com/pycqa/isort
rev: 9.0.0a3
hooks:
Expand Down
2 changes: 1 addition & 1 deletion doc/source/dev_guide/custom_reader.rst
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ needs to implement a few methods:
successful, containing the data and :ref:`metadata <dataset_metadata>` of the
loaded dataset, or return None if the loading was unsuccessful. If the reader
is reading satellite data the returned xarray.DataArrays should also have the
attributes ``platform_name`` and ``sensor`` with names according to https://space.oscar.wmo.int/.
attributes ``platform_name`` and ``instruments`` with names according to https://space.oscar.wmo.int/.

The DataArray should at least have a ``y`` dimension. For data covering
a 2D region on the Earth, their should be at least a ``y`` and ``x``
Expand Down
4 changes: 2 additions & 2 deletions doc/source/dev_guide/xarray_migration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ To create such an array, you can do for example

my_dataarray = xr.DataArray(my_data, dims=['y', 'x'],
coords={'x': np.arange(...)},
attrs={'sensor': 'olci'})
attrs={'instruments': {'OLCI'})

where ``my_data`` can be a regular numpy array, a numpy memmap, or, if you
want to keep things lazy, a dask array (more on dask later). Satpy uses dask
Expand Down Expand Up @@ -105,7 +105,7 @@ Some metadata that should always be present in our dataarrays:

- ``area`` the area of the dataset. This should be handled in the reader.
- ``start_time``, ``end_time``
- ``sensor``
- ``instruments``

Operations on DataArrays
************************
Expand Down
8 changes: 5 additions & 3 deletions doc/source/enhancements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ as well. See :ref:`component_configuration` for more information.

Enhancements can be defined in a ``generic.yaml`` file that is always loaded
for all data or in an instrument-specific file (e.g. ``seviri.yaml``)
corresponding to the ``.attrs["sensor"]`` metadata of the ``DataArray`` being
processed. Generic enhancements are loaded first followed by sensor-specific
corresponding to the ``.attrs["instruments"]`` metadata of the ``DataArray``
being processed. For the filename, instruments are normalized using
:meth:`satpy._instruments.normalize_instrument_name`.
Generic enhancements are loaded first followed by instrument-specific
enhancement files.

Enhancement YAML Format
Expand Down Expand Up @@ -95,7 +97,7 @@ implementation depends on the following keys:
1. ``name``
2. ``reader``
3. ``platform_name``
4. ``sensor``
4. ``instruments`` (previously ``sensor`` in Satpy <1.0)
5. ``standard_name``
6. ``units``

Expand Down
10 changes: 10 additions & 0 deletions doc/source/reading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ time etc. The following attributes are standardized across all readers:
* ``sensor``: The name of the sensor that recorded the data. For full support through Satpy this
should be all lowercase. If the dataset is the result of observations from multiple sensors a
``set`` object can be used to specify more than one sensor name.

.. deprecated:: 0.61.0

The ``sensor`` attribute will be replaced by ``instruments`` in Satpy v1.0.

* ``instruments``: Names of instruments that recorded the data, stored in a ``set`` object.
Instrument names follow the WMO OSCAR naming conventions.

.. versionadded:: 0.61.0

* ``reader``: The name of the Satpy reader that produced the dataset.
* ``orbital_parameters``: Dictionary of orbital parameters describing the satellite's position.
See the :ref:`orbital_parameters` section below for more information.
Expand Down
1 change: 1 addition & 0 deletions satpy/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"readers": {
"clip_negative_radiances": False,
},
"instruments_key": "instruments"
}

# Satpy main configuration object
Expand Down
272 changes: 272 additions & 0 deletions satpy/_instruments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
# Copyright (c) 2026 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""Helpers for accessing and modifying instrument attributes."""

import logging
import warnings
from enum import StrEnum
from typing import Any

import satpy

logger = logging.getLogger(__name__)

def get_instruments_from_attrs(attrs: dict[str,Any], to_internal: bool=False) -> set[str]:
"""Get instrument names from dataset attributes.

String type attributes are converted to set. This can be
removed once all file handlers provide instruments as a
set.
"""
instruments = attrs.get("instruments", set())
# 8< v1.0
sensor = attrs.get("sensor", set())
if sensor:
warnings.warn(
"Satpy will ignore the 'sensor' attribute as of v1.0. "
"Use the 'instruments' attribute instead.",
DeprecationWarning,
stacklevel=2
)
if not instruments:
instruments = sensor
if isinstance(instruments, str):
warnings.warn(
"Converting 'instruments' attribute from string to set. "
"This will result in an error in v1.0, when Satpy will require "
"set type instruments attributes.",
DeprecationWarning,
stacklevel=2
)
instruments = set([instruments])
# >8 v1.0
if to_internal:
return {
wmo_to_internal(inst) for inst in instruments
}
return instruments



def wmo_to_internal(instrument: str) -> str:
"""Convert WMO to internal instrument name."""
sep_map = {
"-": "-",
"(": "",
")": "",
" ": "_",
"/": "-"
}
sep_trans = str.maketrans(sep_map)
return instrument.translate(sep_trans).lower()


def get_one_instrument_from_attrs(attrs: dict[str,Any]) -> str:
"""Get a single instrument name from dataset attributes."""
instruments = get_instruments_from_attrs(attrs)
if not instruments:
raise KeyError("No 'instruments' in dataset attribute")
if len(instruments) > 1:
logger.warning(f"More than one instrument in dataset attributes, will use the first value: {instruments}")
return list(instruments)[0]


def get_pyspectral_instrument_name(instrument: str) -> str:
"""Get instrument name expected by pyspectral."""
return wmo_to_internal(instrument)


def join_instrument_names(instruments: set[str]) -> str:
"""Join a set of instrument names."""
return "-".join(sorted(instruments))


def set_instruments_attr(attrs: dict[str,Any], instruments: set[str]|str) -> None:
"""Set 'instruments' dataset atrribute."""
key = get_instruments_key()
attrs[key] = instruments


def get_instruments_key():
"""Get key for instruments in dataset attributes."""
return satpy.config.get("instruments_key")


class OSCAR(StrEnum):
"""WMO OSCAR instrument names."""
ABI = "ABI"
AHI = "AHI"
AGRI = "AGRI"
AMI = "AMI"
AMSR_2 = "AMSR2"
AMSU_A = "AMSU-A"
AMSU_B = "AMSU-B"
ATMS = "ATMS"
AVHRR = "AVHRR"
AVHRR_2 = "AVHRR/2"
AVHRR_3 = "AVHRR/3"
CRIS = "CrIS"
EPIC = "EPIC"
ETM_PLUS = "ETM+"
FCI = "FCI"
GHI = "GHI"
GLM = "GLM"
GMI = "GMI"
IASI = "IASI"
IASI_NG = "IASI-NG"
IMAGER_GOES_12_15 = "IMAGER (GOES 12-15)"
IMAGER_GOES_8_11 = "IMAGER (GOES 8-11)"
IMAGER_INSAT = "IMAGER (INSAT)"
IMAGER_MTSAT_2 = "IMAGER (MTSAT-2)"
JAMI = "JAMI"
LI = "LI"
MERIS = "MERIS"
MERSI_1 = "MERSI-1"
MERSI_2 = "MERSI-2"
MERSI_3 = "MERSI-3"
MERSI_LL = "MERSI-LL"
MERSI_RM = "MERSI-RM"
METIMAGE = "METimage"
MHS = "MHS"
MODIS = "MODIS"
MSI = "MSI"
MSI_SENTINEL_2A = "MSI (Sentinel-2A)"
MSS = "MSS"
MSU_GS = "MSU-GS"
MSU_GS_A = "MSU-GS/A"
MVIRI = "MVIRI"
MWR_AWS = "MWR (AWS)"
MWR_STERNA = "MWR (Sterna)"
OCI = "OCI"
OLCI = "OLCI"
OLI = "OLI"
SAR_C_SENTINEL_1 = "SAR-C (Sentinel-1)"
SEAWIFS = "SeaWiFS"
SEVIRI = "SEVIRI"
SGLI = "SGLI"
SLSTR = "SLSTR"
SSMIS = "SSMIS"
TIRS = "TIRS"
TM = "TM"
TROPOMI = "TROPOMI"
VIIRS = "VIIRS"
VIRR_FY_3 = "VIRR (FY-3)"
VISSR = "VISSR"
VISSR_HIMAWARI_5 = "VISSR (Himawari-5)"


def enum_to_str(instruments: set[StrEnum]) -> set[str]:
"""Convert OSCAR enums to string."""
return {str(i) for i in instruments}


_INTERNAL_TO_WMO = {
wmo_to_internal(inst): str(inst)
for inst in OSCAR
}

def internal_to_wmo(instrument: str) -> str:
"""Convert internal to WMO instrument name."""
return _INTERNAL_TO_WMO.get(instrument, instrument)



# 8< v1.0
from pathlib import Path # noqa

import satpy._instruments as inst_utils # noqa
from satpy._config import PACKAGE_CONFIG_PATH # noqa
from satpy._instruments import OSCAR # noqa

RENAMED_ENH_INSTRUMENTS = {
OSCAR.MSI_SENTINEL_2A: "sen2_msi",
OSCAR.MWR_AWS: "mwr",
OSCAR.MWR_STERNA: "mwr",
}
RENAMED_COMP_INSTRUMENTS = {
OSCAR.IMAGER_GOES_8_11: "goes_imager",
OSCAR.IMAGER_GOES_12_15: "goes_imager",
OSCAR.IMAGER_INSAT: "insat3d_img",
OSCAR.METIMAGE: "vii",
OSCAR.MSI: "ec_msi",
OSCAR.MSU_GS_A: "msu-gsa",
OSCAR.MWR_AWS: "mwr",
OSCAR.MWR_STERNA: "mwr",
OSCAR.OLI: "oli_tirs",
OSCAR.TIRS: "oli_tirs",
OSCAR.VIRR_FY_3: "virr",
OSCAR.SAR_C_SENTINEL_1: "sar-c",
OSCAR.MSI_SENTINEL_2A: "sen2_msi",
}

def get_deprecated_instrument_aliases_for_enhancements(instruments: set[str]) -> set[str]:
"""Get deprecated instrument aliases for enhancements that were renamed."""
return _get_deprecated_instrument_aliases(instruments, RENAMED_ENH_INSTRUMENTS) # type: ignore


def get_deprecated_instrument_aliases_for_composites(instruments: set[str]) -> set[str]:
"""Get deprecated instrument aliases for composites that were renamed."""
return _get_deprecated_instrument_aliases(instruments, RENAMED_COMP_INSTRUMENTS) # type: ignore


def _get_deprecated_instrument_aliases(instruments: set[str], renamed_instruments: dict[str,str]) -> set[str]:
old_names = [
old_name
for new_name in instruments
if (old_name := renamed_instruments.get(new_name))
]
return set(old_names)


def warn_if_deprecated_instrument_in_enhancement_filename(instrument: str, config_file: str) -> None:
"""Warn if enhancement filename contains a deprecated instrument name."""
_warn_if_deprecated_instrument_in_filename(
instrument,
config_file,
RENAMED_ENH_INSTRUMENTS, # type: ignore
)


def warn_if_deprecated_instrument_in_composite_filename(instrument: str, config_file: str) -> None:
"""Warn if composite filename contains a deprecated instrument name."""
_warn_if_deprecated_instrument_in_filename(
instrument,
config_file,
RENAMED_COMP_INSTRUMENTS, # type: ignore
)


def _warn_if_deprecated_instrument_in_filename(
instrument: str, config_file: str, renamed_instruments: dict[str,str],
) -> None:
is_old = instrument in renamed_instruments.values()
is_user_file = Path(config_file).parent.parent != Path(PACKAGE_CONFIG_PATH)
if is_old and is_user_file:
new_names = [
new_name
for new_name, old_name in renamed_instruments.items()
if old_name == instrument
]
new_files = [
inst_utils.wmo_to_internal(new_name) + ".yaml"
for new_name in new_names
]
msg = (f"Instrument '{instrument}' has been renamed to the official "
f"WMO name '{'/'.join(new_names)}'. Your config file "
f"{config_file} still uses the old instrument name. Rename "
f"the file to '{'/'.join(new_files)}', otherwise it will be "
f"ignored in Satpy v1.0.")
warnings.warn(msg, DeprecationWarning, stacklevel=3)
3 changes: 2 additions & 1 deletion satpy/composites/aux_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import os

import satpy
import satpy._instruments as inst_utils
from satpy.aux_download import DataDownloadMixin

from .core import GenericCompositor
Expand Down Expand Up @@ -156,7 +157,7 @@ def __call__(self, *args, **kwargs):
if self.area is None:
raise AttributeError("Area definition needs to be configured")
img.attrs["area"] = self.area
img.attrs["sensor"] = None
inst_utils.set_instruments_attr(img.attrs, set())
img.attrs["mode"] = "".join(img.bands.data)
img.attrs.pop("modifiers", None)
img.attrs.pop("calibration", None)
Expand Down
Loading
Loading