Creating a simulated LIDAR signal#

This page documents SignalCreation.Lidar.Lidar.create_simulated_lidar(), which generates a theoretical raw LIDAR signal per channel and a slope (log-derivative of PR²) used for diagnostics and comparison with measurements.

The simulated signal is calibrated on a reference altitude and computed using the currently loaded atmospheric density, ozone climatology, and cross-sections.

Overview#

###########################
# Lidar Signal Simulation #
###########################
lidar.create_simulated_lidar()

This method loops over all LIDAR channels with Signal_Type in [100, 119], collects their laser and received wavelengths (if available), and builds a simulated raw signal per channel. Each simulated signal is then calibrated to match the real signal at a given reference altitude. Finally, a slope is computed by filtering and differentiating \(\ln(P\,R^2)\) (PR²) for the simulated signals.

Prerequisites#

Method signature#

def create_simulated_lidar(self) -> None:
    """Create per-channel simulated raw LIDAR signals and their PR² slopes."""

What it does (step by step)#

  1. Select channels with Signal_Type in the interval 100–119 (uncertainty channels excluded).

  2. Read wavelengths from channel attributes (WAVELENGTH_LASER, WAVELENGTH_RECEIVED) when present.

  3. Pick reference altitude (Simulated_signal/altitude_ref) from XML per Signal_Type.

    • If not provided, it is set automatically and written back to XML:

      • Elastic (Rayleigh) channels (laser == received): 40 km

      • Inelastic (Raman / DIAL off-on) channels (laser != received): 25 km

  4. Build simulator (CreateLidarSignal) using: - altitude grid, ozone number density, ozone cross-sections, - atmospheric density, molecular cross-sections, - the measured channel as calibration reference and the reference altitude.

  5. Store outputs: - One ``simulated_raw_lidar_data`` variable per channel (same Signal_Type in attrs).

  6. Compute slopes: - Apply SignalCreation.Lidar.Lidar.filtering() on simulated signals with use_pr2=True and use_log=True to get ``Simulated_Slope`` variables.

XML configuration#

Reference altitude per channel (optional, recommended):

<Dial>
  <Simulated_signal>
    <!-- Example: set per-channel reference altitude (km) -->
    <altitude_ref units="km" condition_key="Read/Lidar_files/Signal_type" condition_value="100">40</altitude_ref>
    <altitude_ref units="km" condition_key="Read/Lidar_files/Signal_type" condition_value="101">25</altitude_ref>
  </Simulated_signal>
</Dial>

If not provided, defaults are applied (40 km elastic, 25 km inelastic) and saved back to the XML under the same conditional key/value.

Derivative filtering (used to compute the PR² slope of the simulated signals): the method calls filtering(..., name_param_tag="Derivative_Filtering", use_pr2=True, use_log=True). Provide suitable parameters in XML (schema analogous to your filtering config):

<Dial>
  <Derivative_Filtering>
    <parameters>
      <filter_name>Savitzky-Golay</filter_name>
      <mode>dial poly</mode>
      <ncan1>3,3,4,4,5,5</ncan1>
      <ican1>120,120,120,120,60,60</ican1>
      <ncan2>35,35,30,30,40,40</ncan2>
      <ican2>300,300,300,300,200,200</ican2>
      <alt_unit>km</alt_unit>
    </parameters>
    <resolution_methodology options="df,ir,origin" multiple_option="True">df, ir, origin</resolution_methodology>
  </Derivative_Filtering>
</Dial>

Outputs in _lidar (xarray.Dataset)#

For each processed channel:

  • simulated_raw_lidar_data_* Simulated raw photon-count signal matching the channel, with attribute Signal_Type copied from the source channel.

  • Simulated_Slope_* Slope of \(\ln(P\,R^2)\) after derivative filtering, used as a diagnostic curve.

Note

The internal dataset ``_lidar`` is an xarray.Dataset. You can list and plot the simulated variables exactly as for measured ones.

Example usage#

from SignalCreation.Lidar import Lidar

lidar = Lidar("Configuration_files/Parameters/example_station.xml", "2024-03-28")
lidar.import_lidar_data()
lidar.import_atmospheric_component()
lidar.import_other_atmospheric_component("ozone")

# Create simulated signals and slopes
lidar.create_simulated_lidar()

# Inspect a simulated variable
print([v for v in lidar._lidar.data_vars if "simulated_raw_lidar_data" in v])
print([v for v in lidar._lidar.data_vars if "Simulated_Slope" in v])

Notes & best practices#

  • Signal types: ensure each channel has a valid Signal_Type in [100–119]. See Signal types (Signal_Type) for definitions.

  • Wavelength metadata: set WAVELENGTH_LASER / WAVELENGTH_RECEIVED on channels whenever possible; defaults are handled but explicit metadata improve fidelity.

  • Reference altitude: choose a clean, aerosol-poor layer for calibration. Too low a reference (overlap region) may bias the simulation.

  • Consistency: the simulator depends on the previously loaded density, ozone, and cross-sections. Make sure these are present and on the same altitude grid.

See also#