Synthetic elastic and Raman signals

import numpy as np

from lidarpy.retrieval.synthetic.generator import synthetic_signals

ranges = np.arange(30.0, 6000.0, 30.0)
elastic, raman, params = synthetic_signals(
    ranges,
    wavelengths=(532, 607),
    ae=1.0,
    lr=50.0,
    apply_overlap=False,
    number_of_initial_nan_values=0,
)

The synthetic generators create compact elastic, Raman and depolarization cases that can be inspected visually before using them in retrieval tests.

Expected result: returned arrays share the requested range grid, and params contains the molecular and particle fields used as synthetic truth.

Preprocess a Converted ALHAMBRA NetCDF File

The preprocessing entry point expects a converted NetCDF product, not a RAW zip. Conversion is tested separately so preprocessing can focus on corrections and product contracts.

from pathlib import Path
import xarray as xr

from lidarpy.preprocessing import preprocess

dataset = preprocess(
    Path("path/to/RS_20230830_0315.nc"),
    channels=["1064fta"],
    crop_ranges=(0.0, 15000.0),
    apply_dc=False,
    apply_dt=False,
    apply_bg=True,
    apply_bz=True,
    apply_ov=False,
    gluing_products=False,
    apply_sm=False,
)

assert dataset.attrs["bg_corrected"] == "True"
assert dataset.attrs["bz_corrected"] == "True"
assert "signal_1064fta" in dataset
assert dataset["signal_1064fta"].dims == ("time", "range")
dataset.close()

Expected result: a dataset with time and range dimensions, channel metadata and correction flags in attributes. If the signal variable is missing, first check that the requested channel exists in the converted file.

Overlap Correction from a Profile File

Use an overlap file when a trusted per-channel profile already exists. The profile is selected by channel and interpolated to the dataset range grid when needed.

corrected = preprocess(
    "path/to/RS_20230830_0315.nc",
    channels=["1064fta"],
    crop_ranges=(0.0, 15000.0),
    apply_bg=True,
    apply_bz=True,
    apply_ov=True,
    overlap_path="path/to/overlap_1064fta.nc",
    apply_dc=False,
    apply_dt=False,
    apply_sm=False,
)

assert corrected.attrs["ov_corrected"] == "True"
assert corrected["overlap_corrected"].sel(channel="1064fta").item() == 1

Derived Overlap from Full-Field and Near-Field Channels

If no overlap file is provided, the ALHAMBRA configuration can derive the overlap profile from configured full-field and near-field channel pairs. This path is covered by tests for 1064fta and 1064nta.

corrected = preprocess(
    "path/to/RS_20230830_0315.nc",
    channels=["1064fta", "1064nta"],
    crop_ranges=(0.0, 15000.0),
    apply_bg=True,
    apply_bz=True,
    apply_ov=True,
    apply_dc=False,
    apply_dt=False,
    apply_sm=False,
)

assert "overlap_1064fta" in corrected
assert corrected["signal_1064fta"].attrs["overlap_applied"] == "1064fta"
Synthetic elastic quicklook example
Elastic synthetic quicklook for the 532 nm channel.
Synthetic Raman quicklook example
Raman synthetic quicklook for the 607 nm channel.
Synthetic LPDR quicklook example
Synthetic LPDR quicklook built from the depolarization signal generator.

Klett retrieval against synthetic signal

from lidarpy.retrieval.klett import klett_rcs
from lidarpy.utils.utils import signal_to_rcs

rcs = signal_to_rcs(elastic.values, ranges)
particle_beta = klett_rcs(
    rcs,
    ranges,
    params["molecular_beta"],
    reference=(3000.0, 3500.0),
    lr_part=50.0,
)

Forward iterative retrieval with boundary condition

from scipy.integrate import cumulative_trapezoid
from lidarpy.retrieval.klett import iterative_beta_forward

start_height = 600.0
start_idx = abs(ranges - start_height).argmin()
initial_aod = cumulative_trapezoid(
    params["particle_alpha"],
    ranges,
    initial=0.0,
)[start_idx]

particle_beta = iterative_beta_forward(
    rcs,
    calibration_factor=1e11,
    range_profile=ranges,
    params=params,
    lr_part=50.0,
    start_height=start_height,
    height_top=2400.0,
    initial_particle_optical_depth=float(initial_aod),
)

Retrieval validation with synthetic truth

Retrieval validation with synthetic truth
Expected comparison pattern for retrieval tests: the synthetic particle property is plotted against the property retrieved or derived from the generated signal.

Use the generated truth fields to verify whether the retrieval is recovering the expected aerosol structure and where boundary or reference assumptions limit the comparison.

SCC Access Client Without a Real Server

SCC end-to-end operation requires credentials and a real server, but the client contract can be validated offline. The test suite uses fake HTTP sessions to check URL construction, API parsing, streamed downloads and upload response parsing.

$env:PYTHONPATH = "src"
.\.venv\Scripts\python -m pytest tests\scc -q

Expected result: SCC tests pass without network. If they fail, fix the local client or package-data issue before investigating a real SCC integration failure.