Skip to content

wavecal.py API

bh_molecule.instruments.wavecal

Wavelength calibration utilities for Vis133M (no pandas, numpy + scipy only).

Attributes

H_GAMMA_NM = 434.0462 module-attribute

Functions

apply_polynomial_wavecal(n_pixels: int, *, cw_nm: float | None = None, wavecal: Mapping[str, Any] | None = None) -> np.ndarray

Backward-compatible wrapper for :func:apply_wavecal.

This function is kept for compatibility with existing code. It does not attempt to infer CW from headers or data; if cw_nm is omitted, the calibration's own reference_cw_nm is used (i.e. zero CW shift).

apply_wavecal(n_pixels: int, cw_nm: float, wavecal: Mapping[str, Any]) -> np.ndarray

Return a wavelength axis using a stored dispersion + explicit CW.

This helper evaluates the calibration polynomial in the calibration pixel coordinate system and then applies a global CW shift:

λ(x_data) = P(x_cal - pixel_reference) + (CW_data - reference_cw_nm)

where

  • P is the dispersion polynomial defined during calibration
  • x_cal = x_data * (calibration_pixels / n_pixels)
  • CW_data is the user-supplied cw_nm
  • reference_cw_nm is the CW of the calibration image

Parameters:

Name Type Description Default
n_pixels int

Number of detector pixels along the dispersion direction in the data.

required
cw_nm float

Central wavelength (CW) in nm for the current measurement (must be supplied explicitly; it is not inferred automatically).

required
wavecal Mapping[str, Any]

Calibration dictionary as returned by :func:load_bh_wavecal_json.

required

Returns:

Type Description
ndarray

Wavelength axis of shape (n_pixels,) in nanometres.

compute_calibration_from_reference(wavcal_csv: str, fits_path: str, *, channel: int = 0, degree: int = 2, pixel_reference: int | None = None) -> dict

Compute a pixel→wavelength polynomial from a reference CSV + FITS cube.

This is intended for offline use to derive a compact wavelength calibration formula that can be reused at runtime without needing the original CSV file.

Parameters:

Name Type Description Default
wavcal_csv str

Path to a Vis133M instrument CSV file containing per-channel wavelength calibration (dispersion) in the format expected by :func:load_wavecal_csv.

required
fits_path str

Path to a reference FITS cube acquired with the spectrometer.

required
channel int

Channel index to use when fitting the polynomial (default 0).

0
degree int

Polynomial degree for the pixel→wavelength mapping. The default (2) usually provides enough flexibility for mild non-linearity.

2
pixel_reference int | None

Optional reference pixel index. When None (default), the centre of the calibrated pixel range for the chosen channel is used.

None

Returns:

Type Description
dict

A dictionary compatible with bh_wavecal.json containing the keys reference_cw_nm, coefficients, formula_type and pixel_reference.

compute_wavelength_shift(peak_pixel_old: float, peak_pixel_new: float, slope: float) -> float

Compute wavelength shift Δλ from pixel shift and dispersion.

Δλ = a_c * (peak_pixel_new - peak_pixel_old)

Parameters:

Name Type Description Default
peak_pixel_old float

Peak pixel in reference (old) dataset.

required
peak_pixel_new float

Peak pixel in new dataset.

required
slope float

Dispersion a_c (nm/pixel) for the channel.

required

Returns:

Name Type Description
delta_lambda float

Wavelength shift in nm.

csv_to_linear_formulas(wl_nm: np.ndarray) -> tuple[np.ndarray, np.ndarray]

Convert wavelength matrix to per-channel linear formulas λ(x) = a*x + b.

Parameters:

Name Type Description Default
wl_nm ndarray

Shape (n_channels, n_pixels), wavelength per pixel per channel.

required

Returns:

Name Type Description
slopes ndarray

Shape (n_channels,), slope a_c for each channel.

intercepts ndarray

Shape (n_channels,), intercept b_c for each channel.

estimate_cw_from_features(spectrum: np.ndarray, *, wavecal: Mapping[str, Any] | None = None, diagnostic: bool = False, assumed_line_nm: float | None = None) -> float

Estimate central wavelength (CW) from spectral features.

The strongest peak in the spectrum is used. By default we assume that peak is H-gamma (434.05 nm). The calibration polynomial (in calibration- pixel space) is evaluated at the peak pixel (with correct data→cal pixel scaling); then:

CW = reference_cw_nm + (assumed_line_nm - wavelength_at_peak_in_ref)

so that the centre of the detector corresponds to the correct CW for that line.

For a 3D cube (F, C, P): the frame and channel with strongest total signal are selected; a few neighbouring frames are averaged to improve SNR. The brightest pixel in the resulting 1D spectrum is the "chosen peak".

Parameters:

Name Type Description Default
spectrum ndarray

1D or ND array of intensities along the dispersion axis in the last dimension. If 3D (F, C, P), frame and channel are auto-selected by signal strength and neighbouring frames may be averaged.

required
wavecal Mapping[str, Any] | None

Optional pre-loaded calibration dictionary. When omitted, it is loaded from bh_wavecal.json.

None
diagnostic bool

If True, create a matplotlib plot showing the spectrum used, all detected peaks (green), the selected peak (red), and a title with estimated CW, frame/channel used, and peak pixel.

False
assumed_line_nm float | None

Wavelength in nm of the line assumed at the strongest peak. Default None means use H-gamma (434.05 nm). Set to another value if your dominant line is different.

None

Returns:

Type Description
float

Estimated CW in nanometres.

fit_peak_gaussian(spectrum: np.ndarray, pixel_window: tuple[int, int] | None = None, baseline_zero: bool = False) -> tuple[float, float, float] | None

Fit strongest peak in 1D spectrum with Gaussian + baseline.

Parameters:

Name Type Description Default
spectrum ndarray

1D array of intensities (pixel index = x).

required
pixel_window tuple of int

(start, stop) pixel indices to search. If None, use full spectrum.

None
baseline_zero bool

If True, subtract minimum before fitting (default False).

False

Returns:

Name Type Description
result tuple or None

(peak_pixel, peak_amplitude, sigma) if fit succeeds, else None.

get_cw_from_header(header: Mapping[str, Any]) -> float | None

Return central wavelength (CW) in nm from a FITS header, if available.

The search is heuristic and checks a small set of commonly used keyword names (e.g. CWL, CENWAVE, CRVAL1). If no suitable value is found, None is returned.

load_bh_wavecal_json(path: str | None = None) -> dict

Load bh_wavecal.json from disk or package resources.

Parameters:

Name Type Description Default
path str | None

Optional explicit path to a JSON file. When omitted, the file is loaded from the installed package resources directory.

None

Returns:

Type Description
dict

Parsed JSON dictionary with at least the keys reference_cw_nm, coefficients, formula_type and pixel_reference.

load_wavecal_csv(path: str, n_channels: int, n_pixels: int | None = None) -> np.ndarray

Load wavelength calibration from CSV using stdlib csv (no pandas).

CSV format: first column = channel index (0-based or 1-based), remaining columns = wavelength [nm] per pixel. Returns shape (n_channels, n_pixels).

When n_pixels is None, loads all available wavelength columns from the CSV. This allows using a CSV with fewer pixels than the cube (e.g. 1024 vs 2048); the formula fit from the CSV can then be extrapolated to the cube's pixel count.

Parameters:

Name Type Description Default
path str

Path to CSV file.

required
n_channels int

Expected number of channels.

required
n_pixels int | None

Expected number of pixels per channel. If None, use all wavelength columns present in the CSV (convenient when cube has more pixels).

None

Returns:

Name Type Description
wl_nm ndarray

Shape (n_channels, n_pixels), wavelength in nm.

measure_peak_from_cube(cube: np.ndarray, channel: int, pixel_window: tuple[int, int] | None = None, average_frames: bool = True, baseline_zero: bool = False) -> tuple[float, float, float] | None

Extract 1D spectrum from FITS cube and fit strongest peak.

Parameters:

Name Type Description Default
cube ndarray

Shape (F, C, P).

required
channel int

Channel index.

required
pixel_window tuple of int

(start, stop) pixel indices. If None, use full extent.

None
average_frames bool

If True, average over frames; else use first frame (default True).

True
baseline_zero bool

Subtract minimum before fitting (default False).

False

Returns:

Name Type Description
result tuple or None

(peak_pixel, peak_amplitude, sigma) if fit succeeds.

save_bh_wavecal_json(params: Mapping[str, Any], path: str | None = None) -> None

Save calibration parameters to bh_wavecal.json.

When path is None, the JSON is written into the installed package resources directory under bh_molecule._resources/bh_wavecal.json. This helper is primarily intended for the offline builder in bh_molecule.calibration_builder.

Wavelength calibration utilities for Vis133M: CSV loading, linear formula fitting, peak measurement, and wavelength shift computation.