spectro-kernel 0.1.6__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/CHANGELOG.md +170 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/PKG-INFO +1 -1
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/gen_catalogue.py +8 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/index.md +7 -1
- spectro_kernel-0.2.1/docs/tutorials/long-slit-reduction.md +171 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/continuum/normalize_region.py +97 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/exports/export_fits_bess.py +278 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/extraction/boxcar.py +335 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +146 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +39 -3
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/reduction/denoise_2d.py +270 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/reduction/geometry.py +260 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +131 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +394 -0
- spectro_kernel-0.2.1/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +252 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/version.py +1 -1
- spectro_kernel-0.2.1/tests/unit/test_combine_arithmetic.py +99 -0
- spectro_kernel-0.2.1/tests/unit/test_denoise_2d.py +125 -0
- spectro_kernel-0.2.1/tests/unit/test_easyspec_apply_staging.py +156 -0
- spectro_kernel-0.2.1/tests/unit/test_export_fits_bess.py +132 -0
- spectro_kernel-0.2.1/tests/unit/test_extract_boxcar.py +171 -0
- spectro_kernel-0.2.1/tests/unit/test_geometry_corrections.py +119 -0
- spectro_kernel-0.2.1/tests/unit/test_normalize_to_region.py +63 -0
- spectro_kernel-0.2.1/tests/unit/test_sky_lateral_bands.py +94 -0
- spectro_kernel-0.2.1/tests/unit/test_wavelength_calibration_in_situ.py +129 -0
- spectro_kernel-0.2.1/tests/unit/test_wavelength_calibration_solar.py +122 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/.gitignore +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/LICENSE +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/README.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/algorithms.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/architecture.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/data-types.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/pipelines.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/contributing.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/cookbook/bess-dashboard.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/cookbook/multi-star-viewer.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/cookbook/web-playground.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/getting-started.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/alpha-cyg-time-series.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/alpha-dra-binary-period.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/aurora-line-monitor.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/be-star-variability.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/exoplanet-transit-rv.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/full-reduction-walkthrough.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/your-first-sb2.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/reference.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/add-an-algorithm.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/analyse-a-spectrum.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/discover-the-catalogue.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/first-spectrum.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/usage/cli.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/usage/library.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/usage/mcp.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/why.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/playground/README.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/pyproject.toml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/adapters/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/adapters/easyspec.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/_common.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_max.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_remote.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/detect.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/cross_correlate.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/base.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/cli.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/embeddings.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/errors.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/ascii.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/fits.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/votable.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/pipeline.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/loader.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/py.typed +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/registry.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/catalog.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/context.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/enums.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/history.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/image.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/line.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/spectrum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/timeseries.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/__init__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/__main__.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/auth.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/auto_tools.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/observability.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/py.typed +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/server.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/session.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/url_safety.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/conftest.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/conftest.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/.gitkeep +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/README.md +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/sun/sun_reference_stis_002.fits +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/vega/alpha_lyr_stis_011.fits +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/test_known_answers.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/test_sun.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/test_vega.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_base.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_cli.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_combine.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_continuum.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_corrections.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_easyspec_wrappers.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding_pretrained.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding_remote_lick.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding_tier1.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_io.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_line_profiles.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_lines.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_mcp.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_mcp_production.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_mcp_security.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_misc_algorithms.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_no_circular_imports.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_pipeline.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_quality.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_read_sdss.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_registry.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_smoothing.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_timeseries.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_transforms.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_types.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_viz.py +0 -0
- {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/website/README.md +0 -0
|
@@ -6,6 +6,176 @@ Until `1.0.0` the public API may change between minor versions.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.1] — 2026-06-09
|
|
10
|
+
|
|
11
|
+
End-to-end aurora-parity patch. Diagnosed and validated bit-exactly on
|
|
12
|
+
real M42 long-slit data: with these two changes a downstream aurora
|
|
13
|
+
reduction pipeline running entirely on spectro-kernel matches its
|
|
14
|
+
science-core baseline at **Pearson 0.99996** (median |Δ| 0.82 %),
|
|
15
|
+
where v0.2.0 alone scored 0.107 (output silently shifted by +32768 ADU
|
|
16
|
+
on every step).
|
|
17
|
+
|
|
18
|
+
### Fixed — uint16 ``BZERO``/``BSCALE`` leak in the easyspec staging helpers (BUG)
|
|
19
|
+
|
|
20
|
+
The shared helper ``stage_target`` in
|
|
21
|
+
[`reduction/_easyspec_apply.py`](src/spectro_kernel/algorithms/reduction/_easyspec_apply.py)
|
|
22
|
+
wrote ``ctx.image.data`` as float64 on a new ``PrimaryHDU`` and copied
|
|
23
|
+
the source header verbatim. When the source frame originated from a
|
|
24
|
+
**uint16 raw file** (``BITPIX=16``, ``BZERO=32768``, ``BSCALE=1`` —
|
|
25
|
+
astropy's convention), the scaling keywords landed on the float64 HDU
|
|
26
|
+
and astropy then re-applied them on read-back, **silently adding 32768
|
|
27
|
+
ADU to every pixel**. The companion ``build_corrected_imageframe``
|
|
28
|
+
re-attached the same scaling block to the corrected ``ImageFrame``, so
|
|
29
|
+
the next pipeline step that re-staged it inherited the leak.
|
|
30
|
+
|
|
31
|
+
The fix strips a small set of reserved / scaling keywords —
|
|
32
|
+
``SIMPLE, BITPIX, EXTEND, BZERO, BSCALE, BLANK, END, NAXIS*, PCOUNT,
|
|
33
|
+
GCOUNT, XTENSION`` — before they reach either the staged HDU or the
|
|
34
|
+
header re-attached downstream. astropy re-derives the structural keys
|
|
35
|
+
from the data itself; the non-structural metadata (``EXPTIME``,
|
|
36
|
+
``OBSERVER``, etc.) survives.
|
|
37
|
+
|
|
38
|
+
Affects all four easyspec apply wrappers in one shot:
|
|
39
|
+
``subtract_bias_easyspec``, ``subtract_dark_easyspec``,
|
|
40
|
+
``flat_normalize_easyspec``, ``extract_spectrum_easyspec``. Native-numpy
|
|
41
|
+
algorithms (``dark_subtract``, ``flat_normalize``, ``subtract_sky_2d``,
|
|
42
|
+
the v0.2.0 geometry / denoising suite) were never affected — they
|
|
43
|
+
operate directly on ``ctx.image.data`` without round-tripping through a
|
|
44
|
+
FITS file.
|
|
45
|
+
|
|
46
|
+
5 regression tests added in
|
|
47
|
+
[`tests/unit/test_easyspec_apply_staging.py`](tests/unit/test_easyspec_apply_staging.py).
|
|
48
|
+
|
|
49
|
+
### Added — ``extract_spectrum_boxcar`` (literature aperture extraction)
|
|
50
|
+
|
|
51
|
+
New algorithm in
|
|
52
|
+
[`algorithms/extraction/boxcar.py`](src/spectro_kernel/algorithms/extraction/boxcar.py).
|
|
53
|
+
Same easyspec tracing step as ``extract_spectrum_easyspec``, but
|
|
54
|
+
extraction is a **pure-numpy aperture sum** (clamped to the detector
|
|
55
|
+
bounds on every column).
|
|
56
|
+
|
|
57
|
+
Why a new brick: ``extract_spectrum_easyspec`` delegates the extraction
|
|
58
|
+
to ``easyspec.extracting``, whose per-column internal sky window
|
|
59
|
+
crashes with ``ValueError: broadcast (W,) vs (S,)`` when the aperture
|
|
60
|
+
half-width is wide (≥ a few hundred px) and the trace sits near a
|
|
61
|
+
detector edge — the typical aurora / extended-source geometry. The new
|
|
62
|
+
brick keeps easyspec's argmax tracing (the trace itself is fine) but
|
|
63
|
+
performs the aperture sum in numpy, with ``max(0, …)`` / ``min(rows,
|
|
64
|
+
…)`` clipping on every column so it is **edge-safe by construction**.
|
|
65
|
+
|
|
66
|
+
Supports two weightings:
|
|
67
|
+
|
|
68
|
+
- ``"tophat"`` — unweighted box sum (standard IRAF ``apall``).
|
|
69
|
+
- ``"gaussian"`` — profile-weighted Horne 1986 estimator (the average
|
|
70
|
+
column profile is fit with ``astropy.modeling`` to derive the
|
|
71
|
+
weights).
|
|
72
|
+
|
|
73
|
+
``shift_y_pixels = 0`` disables the background subtraction (pure
|
|
74
|
+
column sum); a positive value samples sky from two symmetric flanking
|
|
75
|
+
windows. References: **Horne 1986, PASP 98, 609** ; **Tody 1986, Proc.
|
|
76
|
+
SPIE 627, 733** (IRAF ``apall`` / ``aptrace``). easyspec is imported
|
|
77
|
+
lazily inside ``run()`` (v0.1.5+ pattern).
|
|
78
|
+
|
|
79
|
+
6 tests in
|
|
80
|
+
[`tests/unit/test_extract_boxcar.py`](tests/unit/test_extract_boxcar.py),
|
|
81
|
+
including the wide-aperture edge case that motivated the brick.
|
|
82
|
+
|
|
83
|
+
### Numbers
|
|
84
|
+
|
|
85
|
+
**98 algorithms** registered (was 97). 254 tests pass (11 new), 3
|
|
86
|
+
skipped. ``ruff check`` clean, ``mkdocs build --strict`` clean,
|
|
87
|
+
``twine check --strict`` clean.
|
|
88
|
+
|
|
89
|
+
## [0.2.0] — 2026-06-09
|
|
90
|
+
|
|
91
|
+
A library expansion focused on long-slit reduction (aurora, stellar,
|
|
92
|
+
solar — wherever a 2-D spectrograph frame becomes a 1-D spectrum).
|
|
93
|
+
**13 new algorithms** ported from a downstream aurora-reduction codebase
|
|
94
|
+
into the kernel's standard pattern: same ``BaseAlgorithm`` / ``@register_algorithm``
|
|
95
|
+
shape, every reference cited, every parameter documented. Existing
|
|
96
|
+
behaviour is untouched; this is a pure additive bump.
|
|
97
|
+
|
|
98
|
+
The minor-version bump (``0.1.x`` → ``0.2.0``) signals the broader scope
|
|
99
|
+
even though no API breaks landed.
|
|
100
|
+
|
|
101
|
+
### Added — geometric long-slit corrections (3 algorithms)
|
|
102
|
+
|
|
103
|
+
- ``correct_tilt_affine`` — vertical shear re-aligning the slit with
|
|
104
|
+
the detector columns (Howell 2006 §5.2 ; Tody 1986 IRAF heritage).
|
|
105
|
+
- ``correct_slant_affine`` — horizontal shear pivoted on the trace row,
|
|
106
|
+
making monochromatic lines parallel to the rows.
|
|
107
|
+
- ``correct_smile_polynomial`` — closed-form Schroeder 2000 even-order
|
|
108
|
+
expansion ``dx(dy) = dy²/(2R) + dy⁴/(8R³) + 5·dy⁶/(16R⁵)`` undoing
|
|
109
|
+
the cylindrical "smile" of concave-grating spectrographs (Bottema 1980).
|
|
110
|
+
|
|
111
|
+
All three are no-ops when their amplitude parameter is zero, so they
|
|
112
|
+
can be composed unconditionally in a preset.
|
|
113
|
+
|
|
114
|
+
### Added — 2D image denoising (3 algorithms)
|
|
115
|
+
|
|
116
|
+
- ``outlier_rejection_mad_adaptive`` — vectorised Hwang & Haddad 1995
|
|
117
|
+
rank-conditional median filter with MAD as the local dispersion
|
|
118
|
+
estimator. Suppresses impulsive noise (CMOS RTS, hot pixels) without
|
|
119
|
+
smearing narrow spectral lines.
|
|
120
|
+
- ``denoise_gaussian_2d`` — 2D companion of the existing
|
|
121
|
+
``smooth_gaussian`` (which is 1D only).
|
|
122
|
+
- ``denoise_median_2d`` — square median filter, optionally banded by
|
|
123
|
+
rows so the science aperture is cleaned without disturbing the sky.
|
|
124
|
+
|
|
125
|
+
### Added — sky extraction + in-situ wavelength calibration
|
|
126
|
+
|
|
127
|
+
- ``extract_sky_lateral_bands`` — median-combine two off-trace bands
|
|
128
|
+
into a 1-D pixel-axis sky reference, stored at
|
|
129
|
+
``ctx.extras["sky_spectrum"]``. Builds the input for in-situ
|
|
130
|
+
calibration when no arc lamp is available.
|
|
131
|
+
- ``fit_emission_lines_gaussian`` — multi-line centroid fitter
|
|
132
|
+
(Gaussian + constant continuum). Distinct from ``fit_gaussian_line``
|
|
133
|
+
(single line, linear continuum) — both have their use.
|
|
134
|
+
- ``wavelength_calibration_in_situ`` — Stoughton et al. 2002 recipe:
|
|
135
|
+
centroid sky lines, derive Δλ vs. the catalogue, resample science on
|
|
136
|
+
a uniform Å grid and apply the shift.
|
|
137
|
+
- ``wavelength_calibration_solar`` — built-in catalogue of 15 strong
|
|
138
|
+
Fraunhofer lines (Ca II K/H, Hβ, Mg b triplet, Na D, Hα, O₂ telluric
|
|
139
|
+
bands), peak detection on the inverted spectrum, polynomial fit.
|
|
140
|
+
Lets daylight / twilight / solar spectra be calibrated without an arc
|
|
141
|
+
lamp.
|
|
142
|
+
|
|
143
|
+
### Added — 1D postprocessing (2 algorithms)
|
|
144
|
+
|
|
145
|
+
- ``combine_spectra_arithmetic`` — element-wise ``add``/``sub``/``mul``/``div``
|
|
146
|
+
between ``ctx.spectrum`` and a reference. The reference comes from
|
|
147
|
+
``reference_path`` (FITS) or ``ctx.extras["reference_spectrum"]`` —
|
|
148
|
+
the same idiom ``remove_telluric_division`` uses. IRAF ``sarith``
|
|
149
|
+
pass-through outside the reference's domain.
|
|
150
|
+
- ``normalize_to_region`` — divide the flux by its NaN-safe mean over
|
|
151
|
+
``[wave_lo, wave_hi]``. Sits beside the existing ``normalize_edges`` /
|
|
152
|
+
``normalize_polynomial`` / ``normalize_max`` / ``normalize_percentile``
|
|
153
|
+
family with the explicit "normalise to *this* band" semantic.
|
|
154
|
+
|
|
155
|
+
### Added — BeSS / ARAS-compliant FITS exporter
|
|
156
|
+
|
|
157
|
+
- ``export_fits_bess`` — writes ``ctx.spectrum`` (linear Å grid) with
|
|
158
|
+
the full BeSS observation-header convention (Teyssier 2015 §3.2 ;
|
|
159
|
+
Buil 2012 ARAS Observation Guide): OBJNAME, BSS_INST, BSS_SITE,
|
|
160
|
+
OBSERVER, DATE-OBS, MJD-OBS, JD-OBS (computed offline via Meeus 1991
|
|
161
|
+
§7), EXPTIME, BSS_VHEL, BSS_ESRC, plus the ``-32000`` masked-pixel
|
|
162
|
+
sentinel. Generates the canonical filename
|
|
163
|
+
``_<object>_<date>_<time>_<observer>.fits`` automatically.
|
|
164
|
+
|
|
165
|
+
### Improved — catalogue page
|
|
166
|
+
|
|
167
|
+
``docs/gen_catalogue.py`` now has friendly titles for every category
|
|
168
|
+
the kernel registers — the categories that joined the catalogue with
|
|
169
|
+
v0.1 (preprocessing, extraction, wavelength_calibration, embedding, …)
|
|
170
|
+
no longer fall back to the raw ``snake_case`` rendering.
|
|
171
|
+
|
|
172
|
+
### Numbers
|
|
173
|
+
|
|
174
|
+
**97 algorithms** are registered after this release, up from 84 in
|
|
175
|
+
v0.1.6. 243 tests pass (47 new). ``ruff check`` is clean, ``mkdocs
|
|
176
|
+
build --strict`` is clean. Boot-time discovery stays at ~1.9 s thanks
|
|
177
|
+
to v0.1.5/v0.1.6's lazy-import work.
|
|
178
|
+
|
|
9
179
|
## [0.1.6] — 2026-06-09
|
|
10
180
|
|
|
11
181
|
A hygiene pass on top of v0.1.5: same algorithms, same numerics, less
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spectro-kernel
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A shared catalogue of astronomical spectroscopy algorithms, composable into reproducible pipelines, usable as a Python library, a CLI, or an MCP server.
|
|
5
5
|
Project-URL: Homepage, https://github.com/matthieulel/spectro-kernel
|
|
6
6
|
Project-URL: Documentation, https://matthieulel.github.io/spectro-kernel/
|
|
@@ -12,6 +12,12 @@ from spectro_kernel.registry import list_algorithms
|
|
|
12
12
|
|
|
13
13
|
_CATEGORY_TITLES = {
|
|
14
14
|
"io": "Input / output",
|
|
15
|
+
"master_creation": "Master frames",
|
|
16
|
+
"preprocessing": "Preprocessing (2D image)",
|
|
17
|
+
"cosmic_ray": "Cosmic-ray rejection",
|
|
18
|
+
"extraction": "Extraction (2D → 1D)",
|
|
19
|
+
"wavelength_calibration": "Wavelength calibration",
|
|
20
|
+
"flux_calibration": "Flux calibration",
|
|
15
21
|
"continuum": "Continuum",
|
|
16
22
|
"smoothing": "Smoothing",
|
|
17
23
|
"resampling": "Resampling",
|
|
@@ -23,9 +29,11 @@ _CATEGORY_TITLES = {
|
|
|
23
29
|
"radial_velocity": "Radial velocity",
|
|
24
30
|
"timeseries": "Time series",
|
|
25
31
|
"stacking": "Stacking",
|
|
32
|
+
"embedding": "Embeddings",
|
|
26
33
|
"catalog": "External catalogues",
|
|
27
34
|
"visualization": "Visualisation",
|
|
28
35
|
"export": "Export",
|
|
36
|
+
"advanced": "Advanced",
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
_BACKEND_NOTE = {
|
|
@@ -58,6 +58,12 @@ through MCP, you can do from a terminal, and vice-versa.
|
|
|
58
58
|
|
|
59
59
|
## Status
|
|
60
60
|
|
|
61
|
-
`v0.
|
|
61
|
+
`v0.2.0` — alpha. The catalogue covers ~97 algorithms across image-frame
|
|
62
|
+
reduction (bias / dark / flat, geometric rectification, denoising,
|
|
63
|
+
cosmic-ray clipping, sky subtraction, trace extraction), wavelength &
|
|
64
|
+
flux calibration (arc-lamp polynomial, in-situ from sky lines, solar
|
|
65
|
+
Fraunhofer), 1-D analysis (continuum, smoothing, line fitting, radial
|
|
66
|
+
velocity, time series), embeddings, exports (FITS / CSV / VOTable / HDF5
|
|
67
|
+
/ BeSS), and visualisation. The API may change until `v1.0.0`. See the
|
|
62
68
|
[changelog](https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md)
|
|
63
69
|
for release notes.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Tutorial: long-slit reduction (aurora, stellar, solar)
|
|
2
|
+
|
|
3
|
+
Take a raw 2-D detector frame and walk it all the way to a flux-calibrated
|
|
4
|
+
1-D spectrum on a uniform Å grid, ready for line fitting or BeSS submission.
|
|
5
|
+
This tutorial shows how the **13 long-slit bricks added in v0.2.0** compose
|
|
6
|
+
into a complete reduction. The example uses an aurora-style setup (sky
|
|
7
|
+
emission lines as wavelength reference), but the same recipe works for
|
|
8
|
+
stellar long-slit, solar twilight, or any small-spectrograph field setup.
|
|
9
|
+
|
|
10
|
+
> If you only need 1-D analysis on an already-reduced spectrum, see
|
|
11
|
+
> [Analyse a spectrum](analyse-a-spectrum.md) instead.
|
|
12
|
+
|
|
13
|
+
## Pipeline overview
|
|
14
|
+
|
|
15
|
+
The new bricks form three groups, with one entry point each:
|
|
16
|
+
|
|
17
|
+
| Group | Bricks | When to use |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **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. |
|
|
20
|
+
| **2-D denoising** | `outlier_rejection_mad_adaptive`, `denoise_gaussian_2d`, `denoise_median_2d` | After cosmic-ray clipping, before extraction. CMOS RTS / hot pixels. |
|
|
21
|
+
| **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. |
|
|
22
|
+
|
|
23
|
+
Two postprocessing helpers and the BeSS exporter round out the set:
|
|
24
|
+
|
|
25
|
+
| Group | Bricks |
|
|
26
|
+
|---|---|
|
|
27
|
+
| **Postprocessing 1-D** | `combine_spectra_arithmetic` (response/std-star division), `normalize_to_region` (normalise to a chosen continuum band) |
|
|
28
|
+
| **Export** | `export_fits_bess` (BeSS / ARAS-compliant FITS with full header, JD/MJD, canonical filename) |
|
|
29
|
+
|
|
30
|
+
## A complete preset
|
|
31
|
+
|
|
32
|
+
The kernel's preset format strings these bricks together. The example
|
|
33
|
+
below is the field-aurora workflow; substitute parameters for stellar or
|
|
34
|
+
solar setups.
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
# presets/aurora_long_slit.yaml
|
|
38
|
+
name: aurora_long_slit
|
|
39
|
+
description: |
|
|
40
|
+
Long-slit aurora reduction: geometry → denoising → extraction →
|
|
41
|
+
in-situ wavelength calibration → continuum normalisation.
|
|
42
|
+
steps:
|
|
43
|
+
# ── 1. Geometric rectification (instrument-specific, one-shot values).
|
|
44
|
+
- algorithm: correct_tilt_affine
|
|
45
|
+
params: {tilt_deg: 0.4}
|
|
46
|
+
- algorithm: correct_slant_affine
|
|
47
|
+
params: {slant_deg: 0.6, pivot_row: 120}
|
|
48
|
+
- algorithm: correct_smile_polynomial
|
|
49
|
+
params: {reference_row: 120, smile_radius: 1500.0, polynomial_order: 4}
|
|
50
|
+
|
|
51
|
+
# ── 2. Per-frame denoising (skip the science band when running median).
|
|
52
|
+
- algorithm: outlier_rejection_mad_adaptive
|
|
53
|
+
params: {kernel_size: 3, threshold: 0.6}
|
|
54
|
+
- algorithm: denoise_median_2d
|
|
55
|
+
params: {kernel_size: 3, row_lo: 0, row_hi: 100} # sky strip only
|
|
56
|
+
|
|
57
|
+
# ── 3. Sky-aware extraction.
|
|
58
|
+
- algorithm: extract_sky_lateral_bands
|
|
59
|
+
params:
|
|
60
|
+
band_above_lo: 80
|
|
61
|
+
band_above_hi: 110
|
|
62
|
+
band_below_lo: 130
|
|
63
|
+
band_below_hi: 160
|
|
64
|
+
- algorithm: subtract_sky_2d
|
|
65
|
+
params: {trace_row: 120, trace_half_width: 6, sky_offset: 4, sky_half_width: 10}
|
|
66
|
+
# extract_spectrum_boxcar is the edge-safe boxcar for extended sources
|
|
67
|
+
# (aurora, nebulae). For a stellar point source, extract_spectrum_sum
|
|
68
|
+
# or extract_spectrum_easyspec are also valid choices.
|
|
69
|
+
- algorithm: extract_spectrum_boxcar
|
|
70
|
+
params: {trace_method: "argmax", trace_half_width: 6, extraction_weights: "tophat", shift_y_pixels: 0}
|
|
71
|
+
|
|
72
|
+
# ── 4. In-situ wavelength calibration from the simultaneously-acquired
|
|
73
|
+
# sky reference (no arc lamp needed).
|
|
74
|
+
- algorithm: wavelength_calibration_in_situ
|
|
75
|
+
params:
|
|
76
|
+
polynomial_coef: [1.0, 4000.0] # initial λ(x) ≈ 4000 + x
|
|
77
|
+
reference_wavelengths: [5577.34, 6300.30, 6363.78]
|
|
78
|
+
guess_positions: [1577, 2300, 2363]
|
|
79
|
+
search_width: 40.0
|
|
80
|
+
oversampling: 2.0
|
|
81
|
+
|
|
82
|
+
# ── 5. Continuum normalisation to a known line-free band.
|
|
83
|
+
- algorithm: normalize_to_region
|
|
84
|
+
params: {wave_lo: 6400.0, wave_hi: 6500.0}
|
|
85
|
+
|
|
86
|
+
# ── 6. BeSS-compliant FITS for sharing.
|
|
87
|
+
- algorithm: export_fits_bess
|
|
88
|
+
params:
|
|
89
|
+
object_name: "aurora_2026-06-09"
|
|
90
|
+
instrument: "Alpy-600"
|
|
91
|
+
site: "Skibotn"
|
|
92
|
+
observer: "field-monitor"
|
|
93
|
+
date_obs_utc: "2026-06-09T22:13:45"
|
|
94
|
+
exposure_seconds: 30.0
|
|
95
|
+
vhelio_kms: 0.0
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Save and run with the CLI:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
spectro run aurora_long_slit.yaml --image my_raw_frame.fits
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
… or in Python:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from spectro_kernel import WorkContext, run_preset
|
|
108
|
+
from spectro_kernel.io import read_fits_image # 2D variant
|
|
109
|
+
|
|
110
|
+
ctx = WorkContext(image=read_fits_image("my_raw_frame.fits"))
|
|
111
|
+
result = run_preset("aurora_long_slit", ctx)
|
|
112
|
+
print(result.history) # full audit trail
|
|
113
|
+
bess_blob = ctx.exports["fits_bess"]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Variants
|
|
117
|
+
|
|
118
|
+
### Stellar long-slit (arc-lamp calibration)
|
|
119
|
+
|
|
120
|
+
Replace step 4 with `wavelength_calibrate_polynomial` (existing brick)
|
|
121
|
+
on identifications from your arc-lamp frame. Steps 1–3, 5 and 6 stay
|
|
122
|
+
as-is.
|
|
123
|
+
|
|
124
|
+
### Solar / twilight (no sky lines, no arc lamp)
|
|
125
|
+
|
|
126
|
+
Use `wavelength_calibration_solar` in place of step 3+4. It needs only
|
|
127
|
+
two instrument-specific values (an approximate ``λ_min`` and a
|
|
128
|
+
dispersion guess in Å/pixel) and the built-in Fraunhofer catalogue:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
- algorithm: wavelength_calibration_solar
|
|
132
|
+
params:
|
|
133
|
+
approx_wavelength_min_angstrom: 3800.0
|
|
134
|
+
approx_dispersion_angstrom_per_pixel: 1.0
|
|
135
|
+
poly_order: 3
|
|
136
|
+
min_lines_for_fit: 6
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Response correction with a standard star
|
|
140
|
+
|
|
141
|
+
After step 5 (or in place of it), divide by the instrumental response:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
- algorithm: combine_spectra_arithmetic
|
|
145
|
+
params:
|
|
146
|
+
operation: div
|
|
147
|
+
reference_path: "/path/to/response.fits"
|
|
148
|
+
min_denominator: 1.0e-5
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The reference can also live in `ctx.extras["reference_spectrum"]` if
|
|
152
|
+
your pipeline derives it on the fly — same idiom as the existing
|
|
153
|
+
`remove_telluric_division`.
|
|
154
|
+
|
|
155
|
+
## What the bricks share
|
|
156
|
+
|
|
157
|
+
Every algorithm above follows the same kernel contract:
|
|
158
|
+
|
|
159
|
+
- **No new context fields**: 2-D frames go through `ctx.image`, the
|
|
160
|
+
science spectrum through `ctx.spectrum`, second spectra (sky, response,
|
|
161
|
+
standard) through `ctx.extras[...]`.
|
|
162
|
+
- **Literature references** on every class (`references = […]`).
|
|
163
|
+
- **No-op when zero**: geometric corrections and the Gaussian denoiser
|
|
164
|
+
pass through unchanged when their amplitude parameter is zero, so a
|
|
165
|
+
single preset can run on instruments with and without those distortions.
|
|
166
|
+
- **Provenance**: every step writes a fingerprint of what it did into
|
|
167
|
+
`ctx.image.meta` (for 2-D) or `ctx.spectrum.meta` (for 1-D), and the
|
|
168
|
+
per-step `metrics` are JSON-serialisable for later auditing.
|
|
169
|
+
|
|
170
|
+
See [Algorithm catalogue](../catalogue.md) for the full parameter and
|
|
171
|
+
reference list of each brick.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Algorithm: normalise a spectrum by its mean flux over a chosen wavelength window."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from ...base import AlgorithmOutput, BaseAlgorithm
|
|
10
|
+
from ...errors import InvalidParameterError
|
|
11
|
+
from ...registry import register_algorithm
|
|
12
|
+
from ...types import AlgorithmCategory, WorkContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@register_algorithm(
|
|
16
|
+
"normalize_to_region",
|
|
17
|
+
category=AlgorithmCategory.CONTINUUM,
|
|
18
|
+
version="1.0.0",
|
|
19
|
+
)
|
|
20
|
+
class NormalizeToRegion(BaseAlgorithm):
|
|
21
|
+
"""Divide the flux by its NaN-safe mean over ``[wave_lo, wave_hi]``.
|
|
22
|
+
|
|
23
|
+
The window should be a line-free continuum region so the output sits
|
|
24
|
+
near unity inside it. The companion of ``normalize_edges`` /
|
|
25
|
+
``normalize_polynomial`` / ``normalize_max`` /
|
|
26
|
+
``normalize_percentile`` with the explicit "normalise to *this*
|
|
27
|
+
band" convention — exactly what the IRAF ``continuum`` task does
|
|
28
|
+
when scaled by a measured band mean.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
backend = "numpy"
|
|
32
|
+
references = [
|
|
33
|
+
"Tody 1986, Proc. SPIE 627, 733 — IRAF continuum heritage.",
|
|
34
|
+
]
|
|
35
|
+
long_description = (
|
|
36
|
+
"out = flux / nanmean(flux[wave_lo:wave_hi]). The window is "
|
|
37
|
+
"closed at both ends and clamped to the spectrum's wavelength "
|
|
38
|
+
"range. Uncertainty (if present) is scaled by |region_mean|. "
|
|
39
|
+
"If the region mean is zero or non-finite the algorithm fails "
|
|
40
|
+
"with a clear message rather than emitting NaN."
|
|
41
|
+
)
|
|
42
|
+
default_params = {"wave_lo": 0.0, "wave_hi": 0.0}
|
|
43
|
+
required_params = ["wave_lo", "wave_hi"]
|
|
44
|
+
param_descriptions = {
|
|
45
|
+
"wave_lo": "Lower bound of the reference window (same unit as the wavelength axis).",
|
|
46
|
+
"wave_hi": "Upper bound of the reference window.",
|
|
47
|
+
}
|
|
48
|
+
input_requirements = ["spectrum"]
|
|
49
|
+
output_produces = ["spectrum", "metrics.region_mean"]
|
|
50
|
+
|
|
51
|
+
def run(self, ctx: WorkContext, params: dict[str, Any]) -> AlgorithmOutput:
|
|
52
|
+
wave_lo = float(params["wave_lo"])
|
|
53
|
+
wave_hi = float(params["wave_hi"])
|
|
54
|
+
if wave_hi <= wave_lo:
|
|
55
|
+
raise InvalidParameterError("wave_hi must be strictly greater than wave_lo.")
|
|
56
|
+
spec = ctx.spectrum
|
|
57
|
+
if spec is None:
|
|
58
|
+
return AlgorithmOutput.fail("No ctx.spectrum to normalise.")
|
|
59
|
+
|
|
60
|
+
wave = np.asarray(spec.wavelength, dtype=np.float64)
|
|
61
|
+
flux = np.asarray(spec.flux, dtype=np.float64)
|
|
62
|
+
# Nearest-sample window so users don't need to know the grid step.
|
|
63
|
+
lo_idx = int(np.argmin(np.abs(wave - wave_lo)))
|
|
64
|
+
hi_idx = int(np.argmin(np.abs(wave - wave_hi)))
|
|
65
|
+
lo_idx, hi_idx = sorted((lo_idx, hi_idx))
|
|
66
|
+
if hi_idx == lo_idx:
|
|
67
|
+
return AlgorithmOutput.fail(
|
|
68
|
+
f"Reference window [{wave_lo}, {wave_hi}] is narrower than "
|
|
69
|
+
"one sample of the spectrum's wavelength grid."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
region_mean = float(np.nanmean(flux[lo_idx : hi_idx + 1]))
|
|
73
|
+
if not np.isfinite(region_mean) or region_mean == 0.0:
|
|
74
|
+
return AlgorithmOutput.fail(
|
|
75
|
+
f"Reference region mean is {region_mean} — pick a window "
|
|
76
|
+
"with finite, non-zero flux."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
out = spec.with_flux(flux / region_mean, flux_unit="normalized")
|
|
80
|
+
if spec.uncertainty is not None:
|
|
81
|
+
out.uncertainty = spec.uncertainty / abs(region_mean)
|
|
82
|
+
out.meta["normalized_to_region"] = (wave_lo, wave_hi)
|
|
83
|
+
out.meta["normalization_factor"] = region_mean
|
|
84
|
+
ctx.spectrum = out
|
|
85
|
+
|
|
86
|
+
return AlgorithmOutput.ok(
|
|
87
|
+
metrics={
|
|
88
|
+
"region_mean": region_mean,
|
|
89
|
+
"wave_lo": wave_lo,
|
|
90
|
+
"wave_hi": wave_hi,
|
|
91
|
+
"n_samples": float(hi_idx - lo_idx + 1),
|
|
92
|
+
},
|
|
93
|
+
message=(
|
|
94
|
+
f"Normalised to mean {region_mean:.4g} over "
|
|
95
|
+
f"[{wave_lo:g}, {wave_hi:g}] ({hi_idx - lo_idx + 1} samples)."
|
|
96
|
+
),
|
|
97
|
+
)
|