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.
Files changed (262) hide show
  1. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/CHANGELOG.md +170 -0
  2. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/PKG-INFO +1 -1
  3. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/gen_catalogue.py +8 -0
  4. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/index.md +7 -1
  5. spectro_kernel-0.2.1/docs/tutorials/long-slit-reduction.md +171 -0
  6. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/continuum/normalize_region.py +97 -0
  7. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/exports/export_fits_bess.py +278 -0
  8. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/extraction/boxcar.py +335 -0
  9. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +146 -0
  10. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +39 -3
  11. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/reduction/denoise_2d.py +270 -0
  12. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/reduction/geometry.py +260 -0
  13. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +131 -0
  14. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +394 -0
  15. spectro_kernel-0.2.1/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +252 -0
  16. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/version.py +1 -1
  17. spectro_kernel-0.2.1/tests/unit/test_combine_arithmetic.py +99 -0
  18. spectro_kernel-0.2.1/tests/unit/test_denoise_2d.py +125 -0
  19. spectro_kernel-0.2.1/tests/unit/test_easyspec_apply_staging.py +156 -0
  20. spectro_kernel-0.2.1/tests/unit/test_export_fits_bess.py +132 -0
  21. spectro_kernel-0.2.1/tests/unit/test_extract_boxcar.py +171 -0
  22. spectro_kernel-0.2.1/tests/unit/test_geometry_corrections.py +119 -0
  23. spectro_kernel-0.2.1/tests/unit/test_normalize_to_region.py +63 -0
  24. spectro_kernel-0.2.1/tests/unit/test_sky_lateral_bands.py +94 -0
  25. spectro_kernel-0.2.1/tests/unit/test_wavelength_calibration_in_situ.py +129 -0
  26. spectro_kernel-0.2.1/tests/unit/test_wavelength_calibration_solar.py +122 -0
  27. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/.gitignore +0 -0
  28. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/LICENSE +0 -0
  29. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/README.md +0 -0
  30. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/algorithms.md +0 -0
  31. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/architecture.md +0 -0
  32. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/data-types.md +0 -0
  33. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/concepts/pipelines.md +0 -0
  34. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/contributing.md +0 -0
  35. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/cookbook/bess-dashboard.md +0 -0
  36. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/cookbook/multi-star-viewer.md +0 -0
  37. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/cookbook/web-playground.md +0 -0
  38. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/getting-started.md +0 -0
  39. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
  40. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
  41. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
  42. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
  43. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
  44. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
  45. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
  46. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
  47. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
  48. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
  49. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
  50. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
  51. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
  52. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
  53. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
  54. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
  55. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
  56. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
  57. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
  58. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
  59. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
  60. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/alpha-cyg-time-series.md +0 -0
  61. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/alpha-dra-binary-period.md +0 -0
  62. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/aurora-line-monitor.md +0 -0
  63. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/be-star-variability.md +0 -0
  64. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
  65. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/exoplanet-transit-rv.md +0 -0
  66. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/full-reduction-walkthrough.md +0 -0
  67. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
  68. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/notebooks/your-first-sb2.md +0 -0
  69. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/reference.md +0 -0
  70. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/add-an-algorithm.md +0 -0
  71. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/analyse-a-spectrum.md +0 -0
  72. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/discover-the-catalogue.md +0 -0
  73. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/tutorials/first-spectrum.md +0 -0
  74. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/usage/cli.md +0 -0
  75. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/usage/library.md +0 -0
  76. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/usage/mcp.md +0 -0
  77. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/docs/why.md +0 -0
  78. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/playground/README.md +0 -0
  79. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/pyproject.toml +0 -0
  80. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/__init__.py +0 -0
  81. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/adapters/__init__.py +0 -0
  82. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/adapters/easyspec.py +0 -0
  83. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/__init__.py +0 -0
  84. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/_common.py +0 -0
  85. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
  86. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
  87. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
  88. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
  89. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
  90. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
  91. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
  92. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
  93. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +0 -0
  94. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +0 -0
  95. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_max.py +0 -0
  96. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +0 -0
  97. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
  98. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +0 -0
  99. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
  100. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
  101. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
  102. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
  103. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
  104. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
  105. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
  106. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
  107. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
  108. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
  109. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +0 -0
  110. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +0 -0
  111. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +0 -0
  112. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +0 -0
  113. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +0 -0
  114. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_remote.py +0 -0
  115. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +0 -0
  116. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +0 -0
  117. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
  118. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
  119. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
  120. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
  121. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
  122. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
  123. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
  124. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
  125. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
  126. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
  127. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
  128. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
  129. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
  130. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
  131. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
  132. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
  133. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
  134. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/detect.py +0 -0
  135. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -0
  136. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
  137. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
  138. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
  139. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
  140. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
  141. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
  142. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -0
  143. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +0 -0
  144. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
  145. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
  146. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
  147. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
  148. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
  149. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
  150. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
  151. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
  152. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
  153. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
  154. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
  155. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
  156. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
  157. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
  158. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
  159. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
  160. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
  161. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/cross_correlate.py +0 -0
  162. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
  163. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
  164. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
  165. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
  166. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
  167. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
  168. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
  169. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
  170. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
  171. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
  172. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
  173. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
  174. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
  175. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
  176. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
  177. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
  178. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
  179. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
  180. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
  181. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
  182. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
  183. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
  184. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
  185. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
  186. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
  187. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
  188. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/base.py +0 -0
  189. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/cli.py +0 -0
  190. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/embeddings.py +0 -0
  191. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/errors.py +0 -0
  192. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/__init__.py +0 -0
  193. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/ascii.py +0 -0
  194. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/fits.py +0 -0
  195. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/io/votable.py +0 -0
  196. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/pipeline.py +0 -0
  197. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/__init__.py +0 -0
  198. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
  199. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
  200. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
  201. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
  202. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
  203. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
  204. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
  205. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/presets/loader.py +0 -0
  206. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/py.typed +0 -0
  207. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/registry.py +0 -0
  208. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/__init__.py +0 -0
  209. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/catalog.py +0 -0
  210. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/context.py +0 -0
  211. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/enums.py +0 -0
  212. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/history.py +0 -0
  213. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/image.py +0 -0
  214. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/line.py +0 -0
  215. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/spectrum.py +0 -0
  216. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_kernel/types/timeseries.py +0 -0
  217. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/__init__.py +0 -0
  218. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/__main__.py +0 -0
  219. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/auth.py +0 -0
  220. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/auto_tools.py +0 -0
  221. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/observability.py +0 -0
  222. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/py.typed +0 -0
  223. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/server.py +0 -0
  224. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/session.py +0 -0
  225. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/src/spectro_mcp/url_safety.py +0 -0
  226. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/conftest.py +0 -0
  227. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/conftest.py +0 -0
  228. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/.gitkeep +0 -0
  229. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/README.md +0 -0
  230. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/sun/sun_reference_stis_002.fits +0 -0
  231. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/data/vega/alpha_lyr_stis_011.fits +0 -0
  232. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/test_known_answers.py +0 -0
  233. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/test_sun.py +0 -0
  234. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/reference/test_vega.py +0 -0
  235. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_base.py +0 -0
  236. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_cli.py +0 -0
  237. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_combine.py +0 -0
  238. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_continuum.py +0 -0
  239. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_corrections.py +0 -0
  240. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_easyspec_wrappers.py +0 -0
  241. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding.py +0 -0
  242. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding_pretrained.py +0 -0
  243. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding_remote_lick.py +0 -0
  244. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_embedding_tier1.py +0 -0
  245. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_io.py +0 -0
  246. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_line_profiles.py +0 -0
  247. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_lines.py +0 -0
  248. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_mcp.py +0 -0
  249. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_mcp_production.py +0 -0
  250. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_mcp_security.py +0 -0
  251. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_misc_algorithms.py +0 -0
  252. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_no_circular_imports.py +0 -0
  253. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_pipeline.py +0 -0
  254. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_quality.py +0 -0
  255. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_read_sdss.py +0 -0
  256. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_registry.py +0 -0
  257. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_smoothing.py +0 -0
  258. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_timeseries.py +0 -0
  259. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_transforms.py +0 -0
  260. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_types.py +0 -0
  261. {spectro_kernel-0.1.6 → spectro_kernel-0.2.1}/tests/unit/test_viz.py +0 -0
  262. {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.6
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.1.0` — alpha. The API may change until `v1.0.0`. See the
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
+ )