diff --git a/doc/platforms_supported.rst b/doc/platforms_supported.rst
index e01b5e46..b583246f 100644
--- a/doc/platforms_supported.rst
+++ b/doc/platforms_supported.rst
@@ -95,8 +95,17 @@ have been included in Pyspectral.
- `rsr_viirs_Suomi-NPP.h5`
- GSICS_
* - Landsat-8 oli
- - `rsr_oli_Landsat-8.h5`
- - NASA-Landsat-OLI_
+ - `rsr_oli_tirs_Landsat-8.h5`
+ - NASA-Landsat-8-OLI_
+ * - Landsat-8 tirs
+ - `rsr_oli_tirs_Landsat-8.h5`
+ - NASA-Landsat-8-TIRS_
+ * - Landsat-9 oli-2
+ - `rsr_oli_tirs_Landsat-9.h5`
+ - NASA-Landsat-9-OLI_
+ * - Landsat-9 tirs-2
+ - `rsr_oli_tirs_Landsat-9.h5`
+ - NASA-Landsat-9-TIRS_
* - FY-3D mersi-2
- `rsr_mersi-2_FY-3D.h5`
- CMA_ (Acquired via personal contact)
@@ -125,7 +134,10 @@ have been included in Pyspectral.
.. _ESA-Sentinel-OLCI: https://sentinel.esa.int/documents/247904/322304/OLCI+SRF+%28NetCDF%29/15cfd7a6-b7bc-4051-87f8-c35d765ae43a
.. _ESA-Sentinel-SLSTR: https://sentinel.esa.int/web/sentinel/technical-guides/sentinel-3-slstr/instrument/measured-spectral-response-function-data
.. _ESA-Sentinel-MSI: https://earth.esa.int/documents/247904/685211/S2-SRF_COPE-GSEG-EOPG-TN-15-0007_3.0.xlsx
-.. _NASA-Landsat-OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2013/06/Ball_BA_RSR.v1.1-1.xlsx
+.. _NASA-Landsat-8-OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2014/09/Ball_BA_RSR.v1.2.xlsx
+.. _NASA-Landsat-9-OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2024/03/L9_OLI2_Ball_BA_RSR.v2-1.xlsx
+.. _NASA-Landsat-8-TIRS: https://landsat.gsfc.nasa.gov/wp-content/uploads/2013/06/TIRS_Relative_Spectral_Responses.BA_.v1.xlsx
+.. _NASA-Landsat-9-TIRS: https://landsat.gsfc.nasa.gov/wp-content/uploads/2021-10/L9_TIRS2_Relative_Spectral_Responses.BA.v1.0.xlsx
.. _NESDIS: https://ncc.nesdis.noaa.gov/J1VIIRS/J1VIIRSSpectralResponseFunctions.php
.. _CMA: http://www.cma.gov.cn/en2014/
.. _NWPSAF-MetImage: https://nwpsaf.eu/downloads/rtcoef_rttov12/ir_srf/rtcoef_metopsg_1_metimage_srf.html
diff --git a/pyspectral/etc/pyspectral.yaml b/pyspectral/etc/pyspectral.yaml
index 06d0aca6..df651f4e 100644
--- a/pyspectral/etc/pyspectral.yaml
+++ b/pyspectral/etc/pyspectral.yaml
@@ -136,6 +136,16 @@ download_from_internet: True
# Envisat-aatsr:
# path: /path/to/original/envisat/aatsr/data
+# Landsat-8-oli_tirs:
+# path: D:\sat_data\
+# oli: Ball_BA_RSR.v1.2.xlsx
+# tirs: TIRS_Relative_Spectral_Responses.BA_.v1.xlsx
+
+# Landsat-9-oli_tirs:
+# path: D:\sat_data\
+# oli: L9_OLI2_Ball_BA_RSR.v2-1.xlsx
+# tirs: L9_TIRS2_Relative_Spectral_Responses.BA.v1.0.xlsx
+
# Sentinel-3A-slstr:
# path: /path/to/original/sentinel-3a/slstr/data
# ch1: SLSTR_FM02_S1_20150122.nc
diff --git a/rsr_convert_scripts/oli_reader.py b/rsr_convert_scripts/oli_reader.py
deleted file mode 100644
index b833b795..00000000
--- a/rsr_convert_scripts/oli_reader.py
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2017-2022 Pytroll developers
-#
-#
-# This program 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.
-#
-# This program 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 this program. If not, see .
-
-"""Landsat-8 OLI reader."""
-
-import logging
-import os
-
-import numpy as np
-from xlrd import open_workbook
-
-from pyspectral.raw_reader import InstrumentRSR
-from pyspectral.utils import convert2hdf5 as tohdf5
-
-LOG = logging.getLogger(__name__)
-
-
-OLI_BAND_NAMES = {'CoastalAerosol': 'B1',
- 'Blue': 'B2',
- 'Green': 'B3',
- 'Red': 'B4',
- 'NIR': 'B5',
- 'Cirrus': 'B9',
- 'SWIR1': 'B6',
- 'SWIR2': 'B7',
- 'Pan': 'B8'}
-
-
-class OliRSR(InstrumentRSR):
- """Class for Landsat OLI RSR."""
-
- def __init__(self, bandname, platform_name):
- """Read the Landsat OLI relative spectral responses for all channels."""
- super(OliRSR, self).__init__(bandname, platform_name)
-
- self.instrument = 'oli'
- self._get_options_from_config()
-
- LOG.debug("Filename: %s", str(self.path))
- if os.path.exists(self.path):
- self._load()
- else:
- raise IOError("Couldn't find an existing file for this band: " +
- str(self.bandname))
-
- def _load(self, scale=0.001):
- """Load the Landsat OLI relative spectral responses."""
- with open_workbook(self.path) as wb_:
- for sheet in wb_.sheets():
- if sheet.name in ['Plot of AllBands', ]:
- continue
- ch_name = OLI_BAND_NAMES.get(sheet.name.strip())
-
- if ch_name != self.bandname:
- continue
-
- wvl = sheet.col_values(0, 2)
- resp = sheet.col_values(1, 2)
-
- self.rsr = {'wavelength': np.array(wvl) / 1000.,
- 'response': np.array(resp)}
- break
-
-
-if __name__ == "__main__":
- bands = OLI_BAND_NAMES.values()
- bands.sort()
- for platform_name in ['Landsat-8', ]:
- tohdf5(OliRSR, platform_name, bands)
diff --git a/rsr_convert_scripts/oli_tirs_reader.py b/rsr_convert_scripts/oli_tirs_reader.py
new file mode 100644
index 00000000..f8d01166
--- /dev/null
+++ b/rsr_convert_scripts/oli_tirs_reader.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017-2024 Pytroll developers
+#
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+
+"""Landsat-8/9 OLI/TIRS reader.
+This reader generates spectral responses for OLI and TIRS instruments aboard Landsat-8 and -9.
+We assume that the instruments are one combined instrument from the user perspective, called `oli_tirs` rather
+than generating RSRs for the two instruments separately.
+The original spectral response data can be found at the links below.
+Landsat-8/OLI: https://landsat.gsfc.nasa.gov/wp-content/uploads/2014/09/Ball_BA_RSR.v1.2.xlsx
+Landsat-9/OLI-2: https://landsat.gsfc.nasa.gov/wp-content/uploads/2024/03/L9_OLI2_Ball_BA_RSR.v2-1.xlsx
+Landsat-8/TIRS: https://landsat.gsfc.nasa.gov/wp-content/uploads/2013/06/TIRS_Relative_Spectral_Responses.BA_.v1.xlsx
+Landsat-9/TIRS-2: https://landsat.gsfc.nasa.gov/wp-content/uploads/2021-10/L9_TIRS2_Relative_Spectral_Responses.BA.v1.0.xlsx
+"""
+
+import logging
+import os
+
+import numpy as np
+import pandas as pd
+
+from pyspectral.raw_reader import InstrumentRSR
+from pyspectral.utils import convert2hdf5 as tohdf5
+
+LOG = logging.getLogger(__name__)
+
+OLI_BAND_NAMES = {"B1": "CoastalAerosol",
+ "B2": "Blue",
+ "B3": "Green",
+ "B4": "Red",
+ "B5": "NIR",
+ "B6": "Cirrus",
+ "B7": "SWIR1",
+ "B8": "SWIR2",
+ "B9": "Pan"}
+
+TIRS_SHEETNAMES_L8 = {"B10": "TIRS BA RSR",
+ "B11": "TIRS BA RSR"}
+TIRS_BAND_NAMES_L8 = {"B10": "TIRS1 10.8um band average",
+ "B11": "TIRS2 12.0um band average"}
+
+TIRS_SHEETNAMES_L9 = {"B10": "TIRS Band 10 BA RSR",
+ "B11": "TIRS Band 11 BA RSR"}
+TIRS_BAND_NAMES_L9 = {"B10": "Band 10 Band=Average RSR",
+ "B11": "Band 11 Band-Average RSR"}
+
+
+class OliRSR(InstrumentRSR):
+ """Class for Landsat OLI RSR."""
+
+ def __init__(self, bandname, platform_name):
+ """Read the Landsat OLI relative spectral responses for all channels."""
+ super(OliRSR, self).__init__(bandname, platform_name)
+ self.instrument = "oli_tirs"
+ self._get_options_from_config()
+ self.band = bandname
+ opts = self.options[f"{platform_name}-{self.instrument}"]
+ if bandname in OLI_BAND_NAMES:
+ self.path = opts["path"] + opts["oli"]
+ elif bandname in TIRS_BAND_NAMES_L8:
+ self.path = opts["path"] + opts["tirs"]
+ else:
+ raise ValueError(f"Unknown band name: {bandname}")
+
+ LOG.debug(f"Filename: {self.path}")
+ if os.path.exists(self.path):
+ self._load()
+ else:
+ raise IOError("Couldn't find an existing file for this band: " +
+ str(self.bandname))
+
+ def _load(self, scale=0.001):
+ """Load the Landsat OLI relative spectral responses."""
+ if self.band in OLI_BAND_NAMES:
+ df = pd.read_excel(self.path, engine="openpyxl", sheet_name=OLI_BAND_NAMES[self.band])
+ wvl = np.array(df["Wavelength"])
+ resp = np.array(df["BA RSR [watts]"])
+ else:
+ if self.platform_name == "Landsat-8":
+ sheet_name = TIRS_SHEETNAMES_L8[self.band]
+ band_name = TIRS_BAND_NAMES_L8[self.band]
+ elif self.platform_name == "Landsat-9":
+ sheet_name = TIRS_SHEETNAMES_L9[self.band]
+ band_name = TIRS_BAND_NAMES_L9[self.band]
+ df = pd.read_excel(self.path, engine="openpyxl", sheet_name=sheet_name)
+
+ wvl = np.array(df["wavelength [um]"])
+ resp = np.array(df[band_name])
+
+ # Cut unneeded points
+ pts = np.argwhere(resp > 0.002)
+ wvl = np.squeeze(wvl[pts])
+ resp = np.squeeze(resp[pts])
+
+ self.rsr = {"wavelength": wvl,
+ "response": resp}
+
+
+if __name__ == "__main__":
+ bands = sorted(OLI_BAND_NAMES.keys()) + sorted(TIRS_BAND_NAMES_L8.keys())
+ for platform_name in ["Landsat-8", "Landsat-9"]:
+ tohdf5(OliRSR, platform_name, bands)