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_nmis 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: |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Wavelength axis of shape |
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: |
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
|
Returns:
| Type | Description |
|---|---|
dict
|
A dictionary compatible with |
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 |
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 |
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.