Skip to content

Tutorial: long-slit reduction (aurora, stellar, solar)

Take a raw 2-D detector frame and walk it all the way to a flux-calibrated 1-D spectrum on a uniform Å grid, ready for line fitting or BeSS submission. This tutorial shows how the 13 long-slit bricks added in v0.2.0 compose into a complete reduction. The example uses an aurora-style setup (sky emission lines as wavelength reference), but the same recipe works for stellar long-slit, solar twilight, or any small-spectrograph field setup.

If you only need 1-D analysis on an already-reduced spectrum, see Analyse a spectrum instead.

Pipeline overview

The new bricks form three groups, with one entry point each:

Group Bricks When to use
Geometry correct_tilt_affine, correct_slant_affine, correct_smile_polynomial Once per instrument: align slit with columns, rectify monochromatic lines, undo cylindrical smile of concave gratings.
2-D denoising outlier_rejection_mad_adaptive, denoise_gaussian_2d, denoise_median_2d After cosmic-ray clipping, before extraction. CMOS RTS / hot pixels.
In-situ λ extract_sky_lateral_bands + fit_emission_lines_gaussian + wavelength_calibration_in_situ (or wavelength_calibration_solar) When no arc lamp is available - field aurora monitors, twilight, daytime solar.

Two postprocessing helpers and the BeSS exporter round out the set:

Group Bricks
Postprocessing 1-D combine_spectra_arithmetic (response/std-star division), normalize_to_region (normalise to a chosen continuum band)
Export export_fits_bess (BeSS / ARAS-compliant FITS with full header, JD/MJD, canonical filename)

A complete preset

The kernel's preset format strings these bricks together. The example below is the field-aurora workflow; substitute parameters for stellar or solar setups.

# presets/aurora_long_slit.yaml
name: aurora_long_slit
description: |
  Long-slit aurora reduction: geometry → denoising → extraction →
  in-situ wavelength calibration → continuum normalisation.
steps:
  # ── 1. Geometric rectification (instrument-specific, one-shot values).
  - algorithm: correct_tilt_affine
    params: {tilt_deg: 0.4}
  - algorithm: correct_slant_affine
    params: {slant_deg: 0.6, pivot_row: 120}
  - algorithm: correct_smile_polynomial
    params: {reference_row: 120, smile_radius: 1500.0, polynomial_order: 4}

  # ── 2. Per-frame denoising (skip the science band when running median).
  - algorithm: outlier_rejection_mad_adaptive
    params: {kernel_size: 3, threshold: 0.6}
  - algorithm: denoise_median_2d
    params: {kernel_size: 3, row_lo: 0, row_hi: 100}      # sky strip only

  # ── 3. Sky-aware extraction.
  - algorithm: extract_sky_lateral_bands
    params:
      band_above_lo: 80
      band_above_hi: 110
      band_below_lo: 130
      band_below_hi: 160
  - algorithm: subtract_sky_2d
    params: {trace_row: 120, trace_half_width: 6, sky_offset: 4, sky_half_width: 10}
  # extract_spectrum_boxcar is the edge-safe boxcar for extended sources
  # (aurora, nebulae). For a stellar point source, extract_spectrum_sum
  # or extract_spectrum_easyspec are also valid choices.
  - algorithm: extract_spectrum_boxcar
    params: {trace_method: "argmax", trace_half_width: 6, extraction_weights: "tophat", shift_y_pixels: 0}

  # ── 4. In-situ wavelength calibration from the simultaneously-acquired
  #     sky reference (no arc lamp needed).
  - algorithm: wavelength_calibration_in_situ
    params:
      polynomial_coef: [1.0, 4000.0]          # initial λ(x) ≈ 4000 + x
      reference_wavelengths: [5577.34, 6300.30, 6363.78]
      guess_positions: [1577, 2300, 2363]
      search_width: 40.0
      oversampling: 2.0

  # ── 5. Continuum normalisation to a known line-free band.
  - algorithm: normalize_to_region
    params: {wave_lo: 6400.0, wave_hi: 6500.0}

  # ── 6. BeSS-compliant FITS for sharing.
  - algorithm: export_fits_bess
    params:
      object_name: "aurora_2026-06-09"
      instrument: "Alpy-600"
      site: "Skibotn"
      observer: "field-monitor"
      date_obs_utc: "2026-06-09T22:13:45"
      exposure_seconds: 30.0
      vhelio_kms: 0.0

Save and run with the CLI:

spectro run aurora_long_slit.yaml --image my_raw_frame.fits

… or in Python:

from spectro_kernel import WorkContext, run_preset
from spectro_kernel.io import read_fits_image  # 2D variant

ctx = WorkContext(image=read_fits_image("my_raw_frame.fits"))
result = run_preset("aurora_long_slit", ctx)
print(result.history)         # full audit trail
bess_blob = ctx.exports["fits_bess"]

Variants

Stellar long-slit (arc-lamp calibration)

Replace step 4 with wavelength_calibrate_polynomial (existing brick) on identifications from your arc-lamp frame. Steps 1–3, 5 and 6 stay as-is.

Solar / twilight (no sky lines, no arc lamp)

Use wavelength_calibration_solar in place of step 3+4. It needs only two instrument-specific values (an approximate λ_min and a dispersion guess in Å/pixel) and the built-in Fraunhofer catalogue:

- algorithm: wavelength_calibration_solar
  params:
    approx_wavelength_min_angstrom: 3800.0
    approx_dispersion_angstrom_per_pixel: 1.0
    poly_order: 3
    min_lines_for_fit: 6

Response correction with a standard star

After step 5 (or in place of it), divide by the instrumental response:

- algorithm: combine_spectra_arithmetic
  params:
    operation: div
    reference_path: "/path/to/response.fits"
    min_denominator: 1.0e-5

The reference can also live in ctx.extras["reference_spectrum"] if your pipeline derives it on the fly - same idiom as the existing remove_telluric_division.

What the bricks share

Every algorithm above follows the same kernel contract:

  • No new context fields: 2-D frames go through ctx.image, the science spectrum through ctx.spectrum, second spectra (sky, response, standard) through ctx.extras[...].
  • Literature references on every class (references = […]).
  • No-op when zero: geometric corrections and the Gaussian denoiser pass through unchanged when their amplitude parameter is zero, so a single preset can run on instruments with and without those distortions.
  • Provenance: every step writes a fingerprint of what it did into ctx.image.meta (for 2-D) or ctx.spectrum.meta (for 1-D), and the per-step metrics are JSON-serialisable for later auditing.

See Algorithm catalogue for the full parameter and reference list of each brick.