spectro-kernel 0.1.6__tar.gz → 0.2.0__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 (259) hide show
  1. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/CHANGELOG.md +90 -0
  2. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/PKG-INFO +1 -1
  3. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/gen_catalogue.py +8 -0
  4. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/index.md +7 -1
  5. spectro_kernel-0.2.0/docs/tutorials/long-slit-reduction.md +168 -0
  6. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/continuum/normalize_region.py +97 -0
  7. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/exports/export_fits_bess.py +278 -0
  8. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +146 -0
  9. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/reduction/denoise_2d.py +270 -0
  10. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/reduction/geometry.py +260 -0
  11. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +131 -0
  12. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +394 -0
  13. spectro_kernel-0.2.0/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +252 -0
  14. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/version.py +1 -1
  15. spectro_kernel-0.2.0/tests/unit/test_combine_arithmetic.py +99 -0
  16. spectro_kernel-0.2.0/tests/unit/test_denoise_2d.py +125 -0
  17. spectro_kernel-0.2.0/tests/unit/test_export_fits_bess.py +132 -0
  18. spectro_kernel-0.2.0/tests/unit/test_geometry_corrections.py +119 -0
  19. spectro_kernel-0.2.0/tests/unit/test_normalize_to_region.py +63 -0
  20. spectro_kernel-0.2.0/tests/unit/test_sky_lateral_bands.py +94 -0
  21. spectro_kernel-0.2.0/tests/unit/test_wavelength_calibration_in_situ.py +129 -0
  22. spectro_kernel-0.2.0/tests/unit/test_wavelength_calibration_solar.py +122 -0
  23. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/.gitignore +0 -0
  24. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/LICENSE +0 -0
  25. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/README.md +0 -0
  26. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/concepts/algorithms.md +0 -0
  27. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/concepts/architecture.md +0 -0
  28. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/concepts/data-types.md +0 -0
  29. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/concepts/pipelines.md +0 -0
  30. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/contributing.md +0 -0
  31. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/cookbook/bess-dashboard.md +0 -0
  32. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/cookbook/multi-star-viewer.md +0 -0
  33. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/cookbook/web-playground.md +0 -0
  34. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/getting-started.md +0 -0
  35. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
  36. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
  37. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
  38. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
  39. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
  40. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
  41. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
  42. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
  43. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
  44. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
  45. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
  46. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
  47. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
  48. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
  49. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
  50. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
  51. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
  52. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
  53. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
  54. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
  55. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
  56. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/alpha-cyg-time-series.md +0 -0
  57. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/alpha-dra-binary-period.md +0 -0
  58. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/aurora-line-monitor.md +0 -0
  59. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/be-star-variability.md +0 -0
  60. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
  61. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/exoplanet-transit-rv.md +0 -0
  62. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/full-reduction-walkthrough.md +0 -0
  63. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
  64. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/notebooks/your-first-sb2.md +0 -0
  65. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/reference.md +0 -0
  66. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/tutorials/add-an-algorithm.md +0 -0
  67. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/tutorials/analyse-a-spectrum.md +0 -0
  68. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/tutorials/discover-the-catalogue.md +0 -0
  69. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/tutorials/first-spectrum.md +0 -0
  70. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/usage/cli.md +0 -0
  71. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/usage/library.md +0 -0
  72. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/usage/mcp.md +0 -0
  73. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/docs/why.md +0 -0
  74. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/playground/README.md +0 -0
  75. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/pyproject.toml +0 -0
  76. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/__init__.py +0 -0
  77. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/adapters/__init__.py +0 -0
  78. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/adapters/easyspec.py +0 -0
  79. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/__init__.py +0 -0
  80. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/_common.py +0 -0
  81. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
  82. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
  83. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
  84. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
  85. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
  86. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
  87. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
  88. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
  89. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +0 -0
  90. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +0 -0
  91. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/normalize_max.py +0 -0
  92. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +0 -0
  93. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
  94. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +0 -0
  95. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
  96. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
  97. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
  98. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
  99. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
  100. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
  101. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
  102. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
  103. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
  104. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
  105. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +0 -0
  106. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +0 -0
  107. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +0 -0
  108. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +0 -0
  109. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +0 -0
  110. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_remote.py +0 -0
  111. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +0 -0
  112. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +0 -0
  113. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
  114. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
  115. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
  116. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
  117. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
  118. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
  119. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
  120. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
  121. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
  122. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
  123. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
  124. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
  125. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
  126. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
  127. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
  128. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
  129. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
  130. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/detect.py +0 -0
  131. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -0
  132. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
  133. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
  134. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
  135. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
  136. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
  137. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
  138. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -0
  139. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +0 -0
  140. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
  141. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +0 -0
  142. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
  143. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
  144. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
  145. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
  146. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
  147. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
  148. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
  149. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
  150. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
  151. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
  152. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
  153. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
  154. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
  155. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
  156. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
  157. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
  158. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/rv/cross_correlate.py +0 -0
  159. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
  160. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
  161. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
  162. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
  163. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
  164. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
  165. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
  166. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
  167. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
  168. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
  169. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
  170. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
  171. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
  172. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
  173. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
  174. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
  175. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
  176. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
  177. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
  178. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
  179. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
  180. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
  181. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
  182. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
  183. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
  184. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
  185. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/base.py +0 -0
  186. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/cli.py +0 -0
  187. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/embeddings.py +0 -0
  188. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/errors.py +0 -0
  189. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/io/__init__.py +0 -0
  190. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/io/ascii.py +0 -0
  191. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/io/fits.py +0 -0
  192. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/io/votable.py +0 -0
  193. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/pipeline.py +0 -0
  194. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/__init__.py +0 -0
  195. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
  196. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
  197. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
  198. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
  199. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
  200. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
  201. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
  202. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/presets/loader.py +0 -0
  203. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/py.typed +0 -0
  204. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/registry.py +0 -0
  205. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/__init__.py +0 -0
  206. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/catalog.py +0 -0
  207. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/context.py +0 -0
  208. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/enums.py +0 -0
  209. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/history.py +0 -0
  210. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/image.py +0 -0
  211. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/line.py +0 -0
  212. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/spectrum.py +0 -0
  213. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_kernel/types/timeseries.py +0 -0
  214. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/__init__.py +0 -0
  215. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/__main__.py +0 -0
  216. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/auth.py +0 -0
  217. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/auto_tools.py +0 -0
  218. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/observability.py +0 -0
  219. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/py.typed +0 -0
  220. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/server.py +0 -0
  221. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/session.py +0 -0
  222. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/src/spectro_mcp/url_safety.py +0 -0
  223. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/conftest.py +0 -0
  224. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/conftest.py +0 -0
  225. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/data/.gitkeep +0 -0
  226. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/data/README.md +0 -0
  227. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/data/sun/sun_reference_stis_002.fits +0 -0
  228. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/data/vega/alpha_lyr_stis_011.fits +0 -0
  229. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/test_known_answers.py +0 -0
  230. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/test_sun.py +0 -0
  231. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/reference/test_vega.py +0 -0
  232. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_base.py +0 -0
  233. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_cli.py +0 -0
  234. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_combine.py +0 -0
  235. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_continuum.py +0 -0
  236. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_corrections.py +0 -0
  237. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_easyspec_wrappers.py +0 -0
  238. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_embedding.py +0 -0
  239. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_embedding_pretrained.py +0 -0
  240. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_embedding_remote_lick.py +0 -0
  241. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_embedding_tier1.py +0 -0
  242. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_io.py +0 -0
  243. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_line_profiles.py +0 -0
  244. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_lines.py +0 -0
  245. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_mcp.py +0 -0
  246. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_mcp_production.py +0 -0
  247. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_mcp_security.py +0 -0
  248. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_misc_algorithms.py +0 -0
  249. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_no_circular_imports.py +0 -0
  250. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_pipeline.py +0 -0
  251. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_quality.py +0 -0
  252. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_read_sdss.py +0 -0
  253. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_registry.py +0 -0
  254. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_smoothing.py +0 -0
  255. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_timeseries.py +0 -0
  256. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_transforms.py +0 -0
  257. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_types.py +0 -0
  258. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/tests/unit/test_viz.py +0 -0
  259. {spectro_kernel-0.1.6 → spectro_kernel-0.2.0}/website/README.md +0 -0
@@ -6,6 +6,96 @@ Until `1.0.0` the public API may change between minor versions.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.0] — 2026-06-09
10
+
11
+ A library expansion focused on long-slit reduction (aurora, stellar,
12
+ solar — wherever a 2-D spectrograph frame becomes a 1-D spectrum).
13
+ **13 new algorithms** ported from a downstream aurora-reduction codebase
14
+ into the kernel's standard pattern: same ``BaseAlgorithm`` / ``@register_algorithm``
15
+ shape, every reference cited, every parameter documented. Existing
16
+ behaviour is untouched; this is a pure additive bump.
17
+
18
+ The minor-version bump (``0.1.x`` → ``0.2.0``) signals the broader scope
19
+ even though no API breaks landed.
20
+
21
+ ### Added — geometric long-slit corrections (3 algorithms)
22
+
23
+ - ``correct_tilt_affine`` — vertical shear re-aligning the slit with
24
+ the detector columns (Howell 2006 §5.2 ; Tody 1986 IRAF heritage).
25
+ - ``correct_slant_affine`` — horizontal shear pivoted on the trace row,
26
+ making monochromatic lines parallel to the rows.
27
+ - ``correct_smile_polynomial`` — closed-form Schroeder 2000 even-order
28
+ expansion ``dx(dy) = dy²/(2R) + dy⁴/(8R³) + 5·dy⁶/(16R⁵)`` undoing
29
+ the cylindrical "smile" of concave-grating spectrographs (Bottema 1980).
30
+
31
+ All three are no-ops when their amplitude parameter is zero, so they
32
+ can be composed unconditionally in a preset.
33
+
34
+ ### Added — 2D image denoising (3 algorithms)
35
+
36
+ - ``outlier_rejection_mad_adaptive`` — vectorised Hwang & Haddad 1995
37
+ rank-conditional median filter with MAD as the local dispersion
38
+ estimator. Suppresses impulsive noise (CMOS RTS, hot pixels) without
39
+ smearing narrow spectral lines.
40
+ - ``denoise_gaussian_2d`` — 2D companion of the existing
41
+ ``smooth_gaussian`` (which is 1D only).
42
+ - ``denoise_median_2d`` — square median filter, optionally banded by
43
+ rows so the science aperture is cleaned without disturbing the sky.
44
+
45
+ ### Added — sky extraction + in-situ wavelength calibration
46
+
47
+ - ``extract_sky_lateral_bands`` — median-combine two off-trace bands
48
+ into a 1-D pixel-axis sky reference, stored at
49
+ ``ctx.extras["sky_spectrum"]``. Builds the input for in-situ
50
+ calibration when no arc lamp is available.
51
+ - ``fit_emission_lines_gaussian`` — multi-line centroid fitter
52
+ (Gaussian + constant continuum). Distinct from ``fit_gaussian_line``
53
+ (single line, linear continuum) — both have their use.
54
+ - ``wavelength_calibration_in_situ`` — Stoughton et al. 2002 recipe:
55
+ centroid sky lines, derive Δλ vs. the catalogue, resample science on
56
+ a uniform Å grid and apply the shift.
57
+ - ``wavelength_calibration_solar`` — built-in catalogue of 15 strong
58
+ Fraunhofer lines (Ca II K/H, Hβ, Mg b triplet, Na D, Hα, O₂ telluric
59
+ bands), peak detection on the inverted spectrum, polynomial fit.
60
+ Lets daylight / twilight / solar spectra be calibrated without an arc
61
+ lamp.
62
+
63
+ ### Added — 1D postprocessing (2 algorithms)
64
+
65
+ - ``combine_spectra_arithmetic`` — element-wise ``add``/``sub``/``mul``/``div``
66
+ between ``ctx.spectrum`` and a reference. The reference comes from
67
+ ``reference_path`` (FITS) or ``ctx.extras["reference_spectrum"]`` —
68
+ the same idiom ``remove_telluric_division`` uses. IRAF ``sarith``
69
+ pass-through outside the reference's domain.
70
+ - ``normalize_to_region`` — divide the flux by its NaN-safe mean over
71
+ ``[wave_lo, wave_hi]``. Sits beside the existing ``normalize_edges`` /
72
+ ``normalize_polynomial`` / ``normalize_max`` / ``normalize_percentile``
73
+ family with the explicit "normalise to *this* band" semantic.
74
+
75
+ ### Added — BeSS / ARAS-compliant FITS exporter
76
+
77
+ - ``export_fits_bess`` — writes ``ctx.spectrum`` (linear Å grid) with
78
+ the full BeSS observation-header convention (Teyssier 2015 §3.2 ;
79
+ Buil 2012 ARAS Observation Guide): OBJNAME, BSS_INST, BSS_SITE,
80
+ OBSERVER, DATE-OBS, MJD-OBS, JD-OBS (computed offline via Meeus 1991
81
+ §7), EXPTIME, BSS_VHEL, BSS_ESRC, plus the ``-32000`` masked-pixel
82
+ sentinel. Generates the canonical filename
83
+ ``_<object>_<date>_<time>_<observer>.fits`` automatically.
84
+
85
+ ### Improved — catalogue page
86
+
87
+ ``docs/gen_catalogue.py`` now has friendly titles for every category
88
+ the kernel registers — the categories that joined the catalogue with
89
+ v0.1 (preprocessing, extraction, wavelength_calibration, embedding, …)
90
+ no longer fall back to the raw ``snake_case`` rendering.
91
+
92
+ ### Numbers
93
+
94
+ **97 algorithms** are registered after this release, up from 84 in
95
+ v0.1.6. 243 tests pass (47 new). ``ruff check`` is clean, ``mkdocs
96
+ build --strict`` is clean. Boot-time discovery stays at ~1.9 s thanks
97
+ to v0.1.5/v0.1.6's lazy-import work.
98
+
9
99
  ## [0.1.6] — 2026-06-09
10
100
 
11
101
  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.0
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,168 @@
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
+ - algorithm: extract_spectrum_sum
67
+ params: {trace_row: 120, half_width: 6}
68
+
69
+ # ── 4. In-situ wavelength calibration from the simultaneously-acquired
70
+ # sky reference (no arc lamp needed).
71
+ - algorithm: wavelength_calibration_in_situ
72
+ params:
73
+ polynomial_coef: [1.0, 4000.0] # initial λ(x) ≈ 4000 + x
74
+ reference_wavelengths: [5577.34, 6300.30, 6363.78]
75
+ guess_positions: [1577, 2300, 2363]
76
+ search_width: 40.0
77
+ oversampling: 2.0
78
+
79
+ # ── 5. Continuum normalisation to a known line-free band.
80
+ - algorithm: normalize_to_region
81
+ params: {wave_lo: 6400.0, wave_hi: 6500.0}
82
+
83
+ # ── 6. BeSS-compliant FITS for sharing.
84
+ - algorithm: export_fits_bess
85
+ params:
86
+ object_name: "aurora_2026-06-09"
87
+ instrument: "Alpy-600"
88
+ site: "Skibotn"
89
+ observer: "field-monitor"
90
+ date_obs_utc: "2026-06-09T22:13:45"
91
+ exposure_seconds: 30.0
92
+ vhelio_kms: 0.0
93
+ ```
94
+
95
+ Save and run with the CLI:
96
+
97
+ ```sh
98
+ spectro run aurora_long_slit.yaml --image my_raw_frame.fits
99
+ ```
100
+
101
+ … or in Python:
102
+
103
+ ```python
104
+ from spectro_kernel import WorkContext, run_preset
105
+ from spectro_kernel.io import read_fits_image # 2D variant
106
+
107
+ ctx = WorkContext(image=read_fits_image("my_raw_frame.fits"))
108
+ result = run_preset("aurora_long_slit", ctx)
109
+ print(result.history) # full audit trail
110
+ bess_blob = ctx.exports["fits_bess"]
111
+ ```
112
+
113
+ ## Variants
114
+
115
+ ### Stellar long-slit (arc-lamp calibration)
116
+
117
+ Replace step 4 with `wavelength_calibrate_polynomial` (existing brick)
118
+ on identifications from your arc-lamp frame. Steps 1–3, 5 and 6 stay
119
+ as-is.
120
+
121
+ ### Solar / twilight (no sky lines, no arc lamp)
122
+
123
+ Use `wavelength_calibration_solar` in place of step 3+4. It needs only
124
+ two instrument-specific values (an approximate ``λ_min`` and a
125
+ dispersion guess in Å/pixel) and the built-in Fraunhofer catalogue:
126
+
127
+ ```yaml
128
+ - algorithm: wavelength_calibration_solar
129
+ params:
130
+ approx_wavelength_min_angstrom: 3800.0
131
+ approx_dispersion_angstrom_per_pixel: 1.0
132
+ poly_order: 3
133
+ min_lines_for_fit: 6
134
+ ```
135
+
136
+ ### Response correction with a standard star
137
+
138
+ After step 5 (or in place of it), divide by the instrumental response:
139
+
140
+ ```yaml
141
+ - algorithm: combine_spectra_arithmetic
142
+ params:
143
+ operation: div
144
+ reference_path: "/path/to/response.fits"
145
+ min_denominator: 1.0e-5
146
+ ```
147
+
148
+ The reference can also live in `ctx.extras["reference_spectrum"]` if
149
+ your pipeline derives it on the fly — same idiom as the existing
150
+ `remove_telluric_division`.
151
+
152
+ ## What the bricks share
153
+
154
+ Every algorithm above follows the same kernel contract:
155
+
156
+ - **No new context fields**: 2-D frames go through `ctx.image`, the
157
+ science spectrum through `ctx.spectrum`, second spectra (sky, response,
158
+ standard) through `ctx.extras[...]`.
159
+ - **Literature references** on every class (`references = […]`).
160
+ - **No-op when zero**: geometric corrections and the Gaussian denoiser
161
+ pass through unchanged when their amplitude parameter is zero, so a
162
+ single preset can run on instruments with and without those distortions.
163
+ - **Provenance**: every step writes a fingerprint of what it did into
164
+ `ctx.image.meta` (for 2-D) or `ctx.spectrum.meta` (for 1-D), and the
165
+ per-step `metrics` are JSON-serialisable for later auditing.
166
+
167
+ See [Algorithm catalogue](../catalogue.md) for the full parameter and
168
+ 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
+ )