dkist-processing-cryonirsp 1.8.3__tar.gz → 1.10.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.

Potentially problematic release.


This version of dkist-processing-cryonirsp might be problematic. Click here for more details.

Files changed (127) hide show
  1. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/CHANGELOG.rst +24 -0
  2. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/PKG-INFO +22 -21
  3. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/config.py +4 -1
  4. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/constants.py +14 -1
  5. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/parameters.py +11 -19
  6. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +3 -2
  7. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/__init__.py +1 -1
  8. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/parse.py +15 -3
  9. dkist_processing_cryonirsp-1.10.0/dkist_processing_cryonirsp/tasks/sp_wavelength_calibration.py +300 -0
  10. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/write_l1.py +6 -16
  11. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/conftest.py +13 -20
  12. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/header_models.py +12 -0
  13. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +17 -17
  14. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +17 -19
  15. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +3 -3
  16. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_cryo_constants.py +4 -1
  17. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_parameters.py +4 -0
  18. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_parse.py +28 -2
  19. dkist_processing_cryonirsp-1.8.3/dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py → dkist_processing_cryonirsp-1.10.0/dkist_processing_cryonirsp/tests/test_sp_wavelength_calibration.py +6 -23
  20. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_write_l1.py +15 -15
  21. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/workflows/ci_l0_processing.py +4 -1
  22. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/workflows/sp_l0_processing.py +7 -4
  23. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/workflows/trial_workflows.py +3 -3
  24. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp.egg-info/PKG-INFO +22 -21
  25. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp.egg-info/SOURCES.txt +3 -2
  26. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp.egg-info/requires.txt +21 -20
  27. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/index.rst +1 -0
  28. dkist_processing_cryonirsp-1.10.0/docs/wavelength_calibration.rst +62 -0
  29. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/pyproject.toml +23 -22
  30. dkist_processing_cryonirsp-1.8.3/dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +0 -465
  31. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/.gitignore +0 -0
  32. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/.pre-commit-config.yaml +0 -0
  33. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/.readthedocs.yml +0 -0
  34. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/.snyk +0 -0
  35. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/README.rst +0 -0
  36. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/SCIENCE_CHANGELOG.rst +0 -0
  37. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/bitbucket-pipelines.yml +0 -0
  38. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/changelog/.gitempty +0 -0
  39. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/__init__.py +0 -0
  40. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/codecs/__init__.py +0 -0
  41. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/codecs/fits.py +0 -0
  42. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/__init__.py +0 -0
  43. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/beam_boundaries.py +0 -0
  44. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/exposure_conditions.py +0 -0
  45. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/tags.py +0 -0
  46. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/models/task_name.py +0 -0
  47. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/__init__.py +0 -0
  48. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/check_for_gains.py +0 -0
  49. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +0 -0
  50. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/exposure_conditions.py +0 -0
  51. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/map_repeats.py +0 -0
  52. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/measurements.py +0 -0
  53. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/modstates.py +0 -0
  54. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/optical_density_filters.py +0 -0
  55. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/polarimetric_check.py +0 -0
  56. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/scan_step.py +0 -0
  57. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/time.py +0 -0
  58. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/parsers/wavelength.py +0 -0
  59. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/assemble_movie.py +0 -0
  60. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/bad_pixel_map.py +0 -0
  61. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +0 -0
  62. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +0 -0
  63. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/ci_science.py +0 -0
  64. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/cryonirsp_base.py +0 -0
  65. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/dark.py +0 -0
  66. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/gain.py +0 -0
  67. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/instrument_polarization.py +0 -0
  68. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/l1_output_data.py +0 -0
  69. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/linearity_correction.py +0 -0
  70. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/make_movie_frames.py +0 -0
  71. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/mixin/__init__.py +0 -0
  72. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/mixin/corrections.py +0 -0
  73. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +0 -0
  74. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/quality_metrics.py +0 -0
  75. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/science_base.py +0 -0
  76. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +0 -0
  77. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/sp_geometric.py +0 -0
  78. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/sp_science.py +0 -0
  79. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tasks/sp_solar_gain.py +0 -0
  80. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/__init__.py +0 -0
  81. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
  82. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +0 -0
  83. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_assemble_movie.py +0 -0
  84. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +0 -0
  85. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +0 -0
  86. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +0 -0
  87. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_ci_science.py +0 -0
  88. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_corrections.py +0 -0
  89. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_cryo_base.py +0 -0
  90. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_dark.py +0 -0
  91. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_gain.py +0 -0
  92. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_instrument_polarization.py +0 -0
  93. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_linearity_correction.py +0 -0
  94. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_make_movie_frames.py +0 -0
  95. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_quality.py +0 -0
  96. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +0 -0
  97. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_sp_geometric.py +0 -0
  98. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +0 -0
  99. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_sp_science.py +0 -0
  100. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_sp_solar.py +0 -0
  101. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +0 -0
  102. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/tests/test_workflows.py +0 -0
  103. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp/workflows/__init__.py +0 -0
  104. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp.egg-info/dependency_links.txt +0 -0
  105. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/dkist_processing_cryonirsp.egg-info/top_level.txt +0 -0
  106. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/Makefile +0 -0
  107. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/bad_pixel_calibration.rst +0 -0
  108. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/beam_angle_calculation.rst +0 -0
  109. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/beam_boundary_computation.rst +0 -0
  110. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/changelog.rst +0 -0
  111. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/ci_science_calibration.rst +0 -0
  112. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/conf.py +0 -0
  113. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/l0_to_l1_cryonirsp_ci-full-trial.rst +0 -0
  114. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/l0_to_l1_cryonirsp_ci.rst +0 -0
  115. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/l0_to_l1_cryonirsp_sp-full-trial.rst +0 -0
  116. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/l0_to_l1_cryonirsp_sp.rst +0 -0
  117. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/landing_page.rst +0 -0
  118. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/linearization.rst +0 -0
  119. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/make.bat +0 -0
  120. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/requirements.txt +0 -0
  121. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/requirements_table.rst +0 -0
  122. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/scientific_changelog.rst +0 -0
  123. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/docs/sp_science_calibration.rst +0 -0
  124. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/licenses/LICENSE.rst +0 -0
  125. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/science_towncrier.sh +0 -0
  126. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/setup.cfg +0 -0
  127. {dkist_processing_cryonirsp-1.8.3 → dkist_processing_cryonirsp-1.10.0}/towncrier_science.toml +0 -0
@@ -1,3 +1,27 @@
1
+ v1.10.0 (2025-07-14)
2
+ ====================
3
+
4
+ Features
5
+ --------
6
+
7
+ - This change pulls the dispersion axis correction out of the Cryonirsp pipeline and uses the generalized solar-wavelength-calibration library. (`#167 <https://bitbucket.org/dkistdc/dkist-processing-cryonirsp/pull-requests/167>`__)
8
+
9
+
10
+ v1.9.0 (2025-07-10)
11
+ ===================
12
+
13
+ Bugfixes
14
+ --------
15
+
16
+ - Update the CRYO-NIRSP workflows such that the `SubmitDatasetMetadata` task is now dependent on the `AssembleCryonirspMovie` task. This ensures that the metadata is submitted only after the movie has been assembled and that it is counted in downstream checks. (`#210 <https://bitbucket.org/dkistdc/dkist-processing-cryonirsp/pull-requests/210>`__)
17
+
18
+
19
+ Misc
20
+ ----
21
+
22
+ - Update dkist-processing-common to handle non-finite values in quality wavecal metrics. (`#210 <https://bitbucket.org/dkistdc/dkist-processing-cryonirsp/pull-requests/210>`__)
23
+
24
+
1
25
  v1.8.3 (2025-07-08)
2
26
  ===================
3
27
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dkist-processing-cryonirsp
3
- Version: 1.8.3
3
+ Version: 1.10.0
4
4
  Summary: Science processing code for the Cryo-NIRSP instrument on DKIST
5
5
  Author-email: NSO / AURA <dkistdc@nso.edu>
6
6
  License: BSD-3-Clause
@@ -17,7 +17,7 @@ Requires-Dist: Pillow==10.4.0
17
17
  Requires-Dist: astropy==7.0.2
18
18
  Requires-Dist: dkist-fits-specifications==4.17.0
19
19
  Requires-Dist: dkist-header-validator==5.2.1
20
- Requires-Dist: dkist-processing-common==11.0.0
20
+ Requires-Dist: dkist-processing-common==11.0.1
21
21
  Requires-Dist: dkist-processing-math==2.2.0
22
22
  Requires-Dist: dkist-processing-pac==3.1.1
23
23
  Requires-Dist: dkist-spectral-lines==3.0.0
@@ -30,6 +30,7 @@ Requires-Dist: peakutils==1.3.5
30
30
  Requires-Dist: scikit-image==0.25.2
31
31
  Requires-Dist: scipy==1.15.3
32
32
  Requires-Dist: sunpy==6.1.1
33
+ Requires-Dist: solar-wavelength-calibration==1.0
33
34
  Provides-Extra: test
34
35
  Requires-Dist: pytest; extra == "test"
35
36
  Requires-Dist: pytest-cov; extra == "test"
@@ -88,9 +89,9 @@ Requires-Dist: WTForms==3.2.1; extra == "frozen"
88
89
  Requires-Dist: Werkzeug==2.2.3; extra == "frozen"
89
90
  Requires-Dist: aioftp==0.26.2; extra == "frozen"
90
91
  Requires-Dist: aiohappyeyeballs==2.6.1; extra == "frozen"
91
- Requires-Dist: aiohttp==3.12.13; extra == "frozen"
92
+ Requires-Dist: aiohttp==3.12.14; extra == "frozen"
92
93
  Requires-Dist: aiosignal==1.4.0; extra == "frozen"
93
- Requires-Dist: alembic==1.16.3; extra == "frozen"
94
+ Requires-Dist: alembic==1.16.4; extra == "frozen"
94
95
  Requires-Dist: amqp==5.3.1; extra == "frozen"
95
96
  Requires-Dist: annotated-types==0.7.0; extra == "frozen"
96
97
  Requires-Dist: anyio==4.9.0; extra == "frozen"
@@ -114,17 +115,17 @@ Requires-Dist: asdf_transform_schemas==0.6.0; extra == "frozen"
114
115
  Requires-Dist: asgiref==3.9.1; extra == "frozen"
115
116
  Requires-Dist: asteval==1.0.6; extra == "frozen"
116
117
  Requires-Dist: astropy==7.0.2; extra == "frozen"
117
- Requires-Dist: astropy-iers-data==0.2025.7.7.0.39.39; extra == "frozen"
118
+ Requires-Dist: astropy-iers-data==0.2025.7.14.0.40.29; extra == "frozen"
118
119
  Requires-Dist: asyncpg==0.30.0; extra == "frozen"
119
120
  Requires-Dist: attrs==25.3.0; extra == "frozen"
120
121
  Requires-Dist: babel==2.17.0; extra == "frozen"
121
122
  Requires-Dist: billiard==4.2.1; extra == "frozen"
122
123
  Requires-Dist: blinker==1.9.0; extra == "frozen"
123
- Requires-Dist: boto3==1.39.3; extra == "frozen"
124
- Requires-Dist: botocore==1.39.3; extra == "frozen"
124
+ Requires-Dist: boto3==1.39.4; extra == "frozen"
125
+ Requires-Dist: botocore==1.39.4; extra == "frozen"
125
126
  Requires-Dist: cachelib==0.13.0; extra == "frozen"
126
127
  Requires-Dist: celery==5.5.3; extra == "frozen"
127
- Requires-Dist: certifi==2025.6.15; extra == "frozen"
128
+ Requires-Dist: certifi==2025.7.14; extra == "frozen"
128
129
  Requires-Dist: cffi==1.17.1; extra == "frozen"
129
130
  Requires-Dist: charset-normalizer==3.4.2; extra == "frozen"
130
131
  Requires-Dist: click==8.2.1; extra == "frozen"
@@ -144,9 +145,9 @@ Requires-Dist: dacite==1.9.2; extra == "frozen"
144
145
  Requires-Dist: decorator==5.2.1; extra == "frozen"
145
146
  Requires-Dist: dill==0.4.0; extra == "frozen"
146
147
  Requires-Dist: dkist-header-validator==5.2.1; extra == "frozen"
147
- Requires-Dist: dkist-processing-common==11.0.0; extra == "frozen"
148
+ Requires-Dist: dkist-processing-common==11.0.1; extra == "frozen"
148
149
  Requires-Dist: dkist-processing-core==5.1.1; extra == "frozen"
149
- Requires-Dist: dkist-processing-cryonirsp==1.8.3; extra == "frozen"
150
+ Requires-Dist: dkist-processing-cryonirsp==1.10.0; extra == "frozen"
150
151
  Requires-Dist: dkist-processing-math==2.2.0; extra == "frozen"
151
152
  Requires-Dist: dkist-processing-pac==3.1.1; extra == "frozen"
152
153
  Requires-Dist: dkist-service-configuration==2.2; extra == "frozen"
@@ -161,7 +162,7 @@ Requires-Dist: flower==2.0.1; extra == "frozen"
161
162
  Requires-Dist: fonttools==4.58.5; extra == "frozen"
162
163
  Requires-Dist: frozenlist==1.7.0; extra == "frozen"
163
164
  Requires-Dist: fsspec==2025.5.1; extra == "frozen"
164
- Requires-Dist: globus-sdk==3.59.0; extra == "frozen"
165
+ Requires-Dist: globus-sdk==3.60.0; extra == "frozen"
165
166
  Requires-Dist: google-re2==1.1.20240702; extra == "frozen"
166
167
  Requires-Dist: googleapis-common-protos==1.70.0; extra == "frozen"
167
168
  Requires-Dist: gqlclient==1.2.3; extra == "frozen"
@@ -208,14 +209,14 @@ Requires-Dist: networkx==3.5; extra == "frozen"
208
209
  Requires-Dist: numba==0.61.2; extra == "frozen"
209
210
  Requires-Dist: numpy==2.2.5; extra == "frozen"
210
211
  Requires-Dist: object-clerk==0.1.2; extra == "frozen"
211
- Requires-Dist: opentelemetry-api==1.34.1; extra == "frozen"
212
- Requires-Dist: opentelemetry-exporter-otlp==1.34.1; extra == "frozen"
213
- Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.34.1; extra == "frozen"
214
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.34.1; extra == "frozen"
215
- Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.34.1; extra == "frozen"
216
- Requires-Dist: opentelemetry-proto==1.34.1; extra == "frozen"
217
- Requires-Dist: opentelemetry-sdk==1.34.1; extra == "frozen"
218
- Requires-Dist: opentelemetry-semantic-conventions==0.55b1; extra == "frozen"
212
+ Requires-Dist: opentelemetry-api==1.35.0; extra == "frozen"
213
+ Requires-Dist: opentelemetry-exporter-otlp==1.35.0; extra == "frozen"
214
+ Requires-Dist: opentelemetry-exporter-otlp-proto-common==1.35.0; extra == "frozen"
215
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc==1.35.0; extra == "frozen"
216
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.35.0; extra == "frozen"
217
+ Requires-Dist: opentelemetry-proto==1.35.0; extra == "frozen"
218
+ Requires-Dist: opentelemetry-sdk==1.35.0; extra == "frozen"
219
+ Requires-Dist: opentelemetry-semantic-conventions==0.56b0; extra == "frozen"
219
220
  Requires-Dist: ordered-set==4.1.0; extra == "frozen"
220
221
  Requires-Dist: packaging==25.0; extra == "frozen"
221
222
  Requires-Dist: pandas==2.3.1; extra == "frozen"
@@ -233,7 +234,7 @@ Requires-Dist: proglog==0.1.12; extra == "frozen"
233
234
  Requires-Dist: prometheus_client==0.22.1; extra == "frozen"
234
235
  Requires-Dist: prompt_toolkit==3.0.51; extra == "frozen"
235
236
  Requires-Dist: propcache==0.3.2; extra == "frozen"
236
- Requires-Dist: protobuf==5.29.5; extra == "frozen"
237
+ Requires-Dist: protobuf==6.31.1; extra == "frozen"
237
238
  Requires-Dist: psutil==7.0.0; extra == "frozen"
238
239
  Requires-Dist: psycopg2-binary==2.9.10; extra == "frozen"
239
240
  Requires-Dist: py==1.11.0; extra == "frozen"
@@ -263,7 +264,7 @@ Requires-Dist: scikit-image==0.25.2; extra == "frozen"
263
264
  Requires-Dist: scipy==1.15.3; extra == "frozen"
264
265
  Requires-Dist: semantic-version==2.10.0; extra == "frozen"
265
266
  Requires-Dist: setproctitle==1.3.6; extra == "frozen"
266
- Requires-Dist: setuptools==75.6.0; extra == "frozen"
267
+ Requires-Dist: setuptools==65.5.0; extra == "frozen"
267
268
  Requires-Dist: six==1.17.0; extra == "frozen"
268
269
  Requires-Dist: sniffio==1.3.1; extra == "frozen"
269
270
  Requires-Dist: solar-wavelength-calibration==1.0; extra == "frozen"
@@ -1,11 +1,14 @@
1
1
  """Configuration for the dkist-processing-cryonirsp package and the logging thereof."""
2
2
  from dkist_processing_common.config import DKISTProcessingCommonConfiguration
3
+ from pydantic import Field
3
4
 
4
5
 
5
6
  class DKISTProcessingCryoNIRSPConfigurations(DKISTProcessingCommonConfiguration):
6
7
  """Configurations custom to the dkist-processing-cryonirsp package."""
7
8
 
8
- pass # nothing custom yet
9
+ fts_atlas_data_dir: str | None = Field(
10
+ default=None, description="Common cached directory for a downloaded FTS Atlas."
11
+ )
9
12
 
10
13
 
11
14
  dkist_processing_cryonirsp_configurations = DKISTProcessingCryoNIRSPConfigurations()
@@ -2,6 +2,7 @@
2
2
  from enum import Enum
3
3
  from enum import unique
4
4
 
5
+ import astropy.units as u
5
6
  from dkist_processing_common.models.constants import BudName
6
7
  from dkist_processing_common.models.constants import ConstantsBase
7
8
 
@@ -54,6 +55,8 @@ class CryonirspBudName(Enum):
54
55
  gain_frame_type_list = "GAIN_FRAME_TYPE_LIST"
55
56
  lamp_gain_frame_type_list = "LAMP_GAIN_FRAME_TYPE_LIST"
56
57
  solar_gain_frame_type_list = "SOLAR_GAIN_FRAME_TYPE_LIST"
58
+ center_wavelength = "CENTER_WAVELENGTH"
59
+ slit_width = "SLIT_WIDTH"
57
60
 
58
61
 
59
62
  class CryonirspConstants(ConstantsBase):
@@ -112,10 +115,20 @@ class CryonirspConstants(ConstantsBase):
112
115
  """Grating littrow angle (deg)."""
113
116
  return self._db_dict[CryonirspBudName.grating_littrow_angle_deg.value]
114
117
 
118
+ @property
119
+ def center_wavelength(self) -> float:
120
+ """Center wavelength of the selected filter (nm)."""
121
+ return self._db_dict[CryonirspBudName.center_wavelength.value]
122
+
123
+ @property
124
+ def slit_width(self) -> float:
125
+ """Physical width of the selected slit (um)."""
126
+ return self._db_dict[CryonirspBudName.slit_width.value]
127
+
115
128
  @property
116
129
  def grating_constant(self) -> float:
117
130
  """Grating constant."""
118
- return self._db_dict[CryonirspBudName.grating_constant.value]
131
+ return self._db_dict[CryonirspBudName.grating_constant.value] / u.mm
119
132
 
120
133
  @property
121
134
  def camera_readout_mode(self) -> str:
@@ -7,6 +7,7 @@ import numpy as np
7
7
  from dkist_processing_common.models.parameters import ParameterArmIdMixin
8
8
  from dkist_processing_common.models.parameters import ParameterBase
9
9
  from dkist_processing_common.models.parameters import ParameterWavelengthMixin
10
+ from solar_wavelength_calibration import DownloadConfig
10
11
 
11
12
  from dkist_processing_cryonirsp.models.exposure_conditions import AllowableOpticalDensityFilterNames
12
13
 
@@ -267,24 +268,15 @@ class CryonirspParameters(ParameterBase, ParameterWavelengthMixin, ParameterArmI
267
268
  """Return the CryoNIRSP pixel pitch."""
268
269
  return self._find_most_recent_past_value("cryonirsp_pixel_pitch_micron") * u.micron
269
270
 
270
- @cached_property
271
- def solar_atlas(self) -> np.ndarray:
272
- """Solar reference atlas.
273
-
274
- Contains two arrays:
275
- - wavelength in nanometers
276
- - transmission at given wavelength
277
- """
278
- param_obj = self._find_most_recent_past_value("cryonirsp_solar_atlas")
279
- return self._load_param_value_from_numpy_save(param_obj=param_obj)
271
+ @property
272
+ def wavecal_atlas_download_config(self) -> DownloadConfig:
273
+ """Define the `~solar_wavelength_calibration.DownloadConfig` used to grab the Solar atlas used for wavelength calibration."""
274
+ config_dict = self._find_most_recent_past_value("cryonirsp_wavecal_atlas_download_config")
275
+ return DownloadConfig.model_validate(config_dict)
280
276
 
281
277
  @cached_property
282
- def telluric_atlas(self) -> np.ndarray:
283
- """Telluric reference atlas.
284
-
285
- Contains two arrays:
286
- - wavelength in nanometers
287
- - transmission at given wavelength
288
- """
289
- param_obj = self._find_most_recent_past_value("cryonirsp_telluric_atlas")
290
- return self._load_param_value_from_numpy_save(param_obj=param_obj)
278
+ def wavecal_fraction_of_unweighted_edge_pixels(self) -> int:
279
+ """Return the fraction of edge pixels to weight to zero during the wavelength calibration."""
280
+ return self._find_most_recent_past_value(
281
+ "cryonirsp_wavecal_fraction_of_unweighted_edge_pixels"
282
+ )
@@ -97,8 +97,7 @@ class CryonirspL0FitsAccess(L0FitsAccess):
97
97
  )
98
98
  self.grating_position_deg: float = self.header["CNGRTPOS"]
99
99
  self.grating_littrow_angle_deg: float = self.header["CNGRTLAT"]
100
- # grating_constant is in the L0 header in (mm)^(-1) when needed in (m)^(-1) hence multiply by 1000.
101
- self.grating_constant: float = self.header["CNGRTCON"] * 1000
100
+ self.grating_constant: float = self.header["CNGRTCON"]
102
101
  self.obs_ip_start_time = self.header["DKIST011"]
103
102
  # The ExposureConditions are a combination of the exposure time and the OD filter name:
104
103
  self.exposure_conditions = ExposureConditions(
@@ -106,6 +105,8 @@ class CryonirspL0FitsAccess(L0FitsAccess):
106
105
  self.header["CNFILTNP"].upper(),
107
106
  )
108
107
  self.solar_gain_ip_start_time = self.header["DATE-OBS"]
108
+ self.center_wavelength = self.header["CNCENWAV"]
109
+ self.slit_width = self.header["CNSLITW"]
109
110
 
110
111
  @property
111
112
  def cn1_scan_step(self):
@@ -11,8 +11,8 @@ from dkist_processing_cryonirsp.tasks.make_movie_frames import *
11
11
  from dkist_processing_cryonirsp.tasks.parse import *
12
12
  from dkist_processing_cryonirsp.tasks.quality_metrics import *
13
13
  from dkist_processing_cryonirsp.tasks.sp_beam_boundaries import *
14
- from dkist_processing_cryonirsp.tasks.sp_dispersion_axis_correction import *
15
14
  from dkist_processing_cryonirsp.tasks.sp_geometric import *
16
15
  from dkist_processing_cryonirsp.tasks.sp_science import *
17
16
  from dkist_processing_cryonirsp.tasks.sp_solar_gain import *
17
+ from dkist_processing_cryonirsp.tasks.sp_wavelength_calibration import *
18
18
  from dkist_processing_cryonirsp.tasks.write_l1 import *
@@ -247,21 +247,21 @@ class ParseL0CryonirspSPLinearizedData(ParseL0CryonirspLinearizedData):
247
247
  TaskNearFloatBud(
248
248
  constant_name=CryonirspBudName.grating_position_deg.value,
249
249
  metadata_key="grating_position_deg",
250
- ip_task_types=TaskName.solar_gain.value,
250
+ ip_task_types=[TaskName.observe.value, TaskName.solar_gain.value],
251
251
  task_type_parsing_function=parse_header_ip_task_with_gains,
252
252
  tolerance=0.01,
253
253
  ),
254
254
  TaskNearFloatBud(
255
255
  constant_name=CryonirspBudName.grating_littrow_angle_deg.value,
256
256
  metadata_key="grating_littrow_angle_deg",
257
- ip_task_types=TaskName.solar_gain.value,
257
+ ip_task_types=[TaskName.observe.value, TaskName.solar_gain.value],
258
258
  task_type_parsing_function=parse_header_ip_task_with_gains,
259
259
  tolerance=0.01,
260
260
  ),
261
261
  TaskUniqueBud(
262
262
  constant_name=CryonirspBudName.grating_constant.value,
263
263
  metadata_key="grating_constant",
264
- ip_task_types=TaskName.solar_gain.value,
264
+ ip_task_types=[TaskName.observe.value, TaskName.solar_gain.value],
265
265
  task_type_parsing_function=parse_header_ip_task_with_gains,
266
266
  ),
267
267
  TaskUniqueBud(
@@ -276,6 +276,18 @@ class ParseL0CryonirspSPLinearizedData(ParseL0CryonirspLinearizedData):
276
276
  ip_task_types=TaskName.solar_gain.value,
277
277
  task_type_parsing_function=parse_header_ip_task_with_gains,
278
278
  ),
279
+ TaskUniqueBud(
280
+ constant_name=CryonirspBudName.center_wavelength.value,
281
+ metadata_key="center_wavelength",
282
+ ip_task_types=[TaskName.observe.value, TaskName.solar_gain.value],
283
+ task_type_parsing_function=parse_header_ip_task_with_gains,
284
+ ),
285
+ TaskUniqueBud(
286
+ constant_name=CryonirspBudName.slit_width.value,
287
+ metadata_key="slit_width",
288
+ ip_task_types=[TaskName.observe.value, TaskName.solar_gain.value],
289
+ task_type_parsing_function=parse_header_ip_task_with_gains,
290
+ ),
279
291
  CheckLampGainFramesPickyBud(),
280
292
  CryonirspTaskExposureConditionsBud(
281
293
  stem_name=CryonirspBudName.lamp_gain_exposure_conditions_list.value,
@@ -0,0 +1,300 @@
1
+ """Cryo SP wavelength calibration task. See :doc:`this page </wavelength_calibration>` for more information."""
2
+ import math
3
+
4
+ import astropy.units as u
5
+ import numpy as np
6
+ from astropy.time import Time
7
+ from astropy.wcs import WCS
8
+ from dkist_processing_common.codecs.json import json_encoder
9
+ from dkist_processing_common.models.dkist_location import location_of_dkist
10
+ from dkist_service_configuration.logging import logger
11
+ from solar_wavelength_calibration import Atlas
12
+ from solar_wavelength_calibration import WavelengthCalibrationFitter
13
+ from solar_wavelength_calibration.fitter.parameters import AngleBoundRange
14
+ from solar_wavelength_calibration.fitter.parameters import BoundsModel
15
+ from solar_wavelength_calibration.fitter.parameters import DispersionBoundRange
16
+ from solar_wavelength_calibration.fitter.parameters import LengthBoundRange
17
+ from solar_wavelength_calibration.fitter.parameters import UnitlessBoundRange
18
+ from solar_wavelength_calibration.fitter.parameters import WavelengthCalibrationParameters
19
+ from solar_wavelength_calibration.fitter.wavelength_fitter import calculate_initial_crval_guess
20
+ from solar_wavelength_calibration.fitter.wavelength_fitter import WavelengthParameters
21
+ from sunpy.coordinates import HeliocentricInertial
22
+
23
+ from dkist_processing_cryonirsp.codecs.fits import cryo_fits_array_decoder
24
+ from dkist_processing_cryonirsp.models.tags import CryonirspTag
25
+ from dkist_processing_cryonirsp.tasks.cryonirsp_base import CryonirspTaskBase
26
+
27
+ __all__ = ["SPWavelengthCalibration"]
28
+
29
+
30
+ class SPWavelengthCalibration(CryonirspTaskBase):
31
+ """Task class for correcting the dispersion axis wavelength values.
32
+
33
+ Parameters
34
+ ----------
35
+ recipe_run_id : int
36
+ id of the recipe run used to identify the workflow run this task is part of
37
+ workflow_name : str
38
+ name of the workflow to which this instance of the task belongs
39
+ workflow_version : str
40
+ version of the workflow to which this instance of the task belongs
41
+
42
+ """
43
+
44
+ record_provenance = True
45
+
46
+ def run(self) -> None:
47
+ """
48
+ Run method for the task.
49
+
50
+ - Gather 1D characteristic spectrum. This will be the initial spectrum.
51
+ - Get a header from a solar gain frame for initial wavelength estimation.
52
+ - Compute the theoretical dispersion, order, and incident light angle.
53
+ - Compute the input wavelength vector from the spectrum and header.
54
+ - Get the Doppler velocity and resolving power.
55
+ - Define fitting bounds and initialize model parameters.
56
+ - Set and normalize spectral weights, zeroing edges.
57
+ - Fit the profile using WavelengthCalibrationFitter.
58
+ - Write fit results to disk.
59
+
60
+ Returns
61
+ -------
62
+ None
63
+ """
64
+ with self.apm_processing_step("Load input spectrum and wavelength"):
65
+ logger.info("Loading input spectrum")
66
+ input_spectrum = next(
67
+ self.read(
68
+ tags=[
69
+ CryonirspTag.intermediate_frame(beam=1),
70
+ CryonirspTag.task_characteristic_spectra(),
71
+ ],
72
+ decoder=cryo_fits_array_decoder,
73
+ )
74
+ )
75
+
76
+ logger.info(
77
+ "Computing instrument specific dispersion, order, and incident light angle."
78
+ )
79
+ (
80
+ dispersion,
81
+ order,
82
+ incident_light_angle,
83
+ ) = self.get_theoretical_dispersion_order_light_angle()
84
+
85
+ logger.info("Computing initial wavelength vector.")
86
+ input_wavelength_vector = self.compute_input_wavelength_vector(
87
+ spectrum=input_spectrum,
88
+ dispersion=dispersion,
89
+ order=order,
90
+ incident_light_angle=incident_light_angle,
91
+ )
92
+
93
+ # Get the doppler velocity
94
+ doppler_velocity = self.get_doppler_velocity()
95
+ logger.info(f"{doppler_velocity = !s}")
96
+
97
+ # Get the resolving power
98
+ resolving_power = self.get_resolving_power()
99
+ logger.info(f"{resolving_power = }")
100
+
101
+ with self.apm_processing_step("Compute brute-force CRVAL initial guess"):
102
+ atlas = Atlas(config=self.parameters.wavecal_atlas_download_config)
103
+ crval = calculate_initial_crval_guess(
104
+ input_wavelength_vector=input_wavelength_vector,
105
+ input_spectrum=input_spectrum,
106
+ atlas=atlas,
107
+ negative_limit=-2 * u.nm,
108
+ positive_limit=2 * u.nm,
109
+ num_steps=550,
110
+ )
111
+ logger.info(f"{crval = !s}")
112
+
113
+ with self.apm_task_step("Set up wavelength fit"):
114
+ logger.info("Setting bounds")
115
+ bounds = BoundsModel(
116
+ crval=LengthBoundRange(min=crval - (5 * u.nm), max=crval + (5 * u.nm)),
117
+ dispersion=DispersionBoundRange(
118
+ min=dispersion - (0.05 * u.nm / u.pix), max=dispersion + (0.05 * u.nm / u.pix)
119
+ ),
120
+ incident_light_angle=AngleBoundRange(
121
+ min=incident_light_angle - (180 * u.deg),
122
+ max=incident_light_angle + (180 * u.deg),
123
+ ),
124
+ resolving_power=UnitlessBoundRange(
125
+ min=resolving_power - (resolving_power * 0.1),
126
+ max=resolving_power + (resolving_power * 0.1),
127
+ ),
128
+ opacity_factor=UnitlessBoundRange(min=0.0, max=10.0),
129
+ straylight_fraction=UnitlessBoundRange(min=0.0, max=0.4),
130
+ )
131
+
132
+ logger.info("Initializing parameters")
133
+ input_parameters = WavelengthCalibrationParameters(
134
+ crval=crval,
135
+ dispersion=dispersion,
136
+ incident_light_angle=incident_light_angle,
137
+ resolving_power=resolving_power,
138
+ opacity_factor=5.0,
139
+ straylight_fraction=0.2,
140
+ grating_constant=self.constants.grating_constant,
141
+ doppler_velocity=doppler_velocity,
142
+ order=order,
143
+ bounds=bounds,
144
+ )
145
+
146
+ # Define spectral weights to apply
147
+ weights = np.ones_like(input_spectrum)
148
+ # Set edge weights to zero to mitigate flat field artifacts (inner and outer 10% of array)
149
+ num_pixels = len(weights)
150
+ weights[: num_pixels // self.parameters.wavecal_fraction_of_unweighted_edge_pixels] = 0
151
+ weights[-num_pixels // self.parameters.wavecal_fraction_of_unweighted_edge_pixels :] = 0
152
+
153
+ fitter = WavelengthCalibrationFitter(
154
+ input_parameters=input_parameters,
155
+ atlas=atlas,
156
+ )
157
+
158
+ logger.info(f"Input parameters: {input_parameters.lmfit_parameters.pretty_repr()}")
159
+
160
+ with self.apm_processing_step("Run wavelength solution fit"):
161
+ fit_result = fitter(
162
+ input_wavelength_vector=input_wavelength_vector,
163
+ input_spectrum=input_spectrum,
164
+ spectral_weights=weights,
165
+ )
166
+
167
+ with self.apm_writing_step("Save wavelength solution and quality metrics"):
168
+ self.write(
169
+ data=fit_result.wavelength_parameters.to_header(
170
+ axis_num=1, add_alternate_keys=True
171
+ ),
172
+ tags=[CryonirspTag.task_spectral_fit(), CryonirspTag.intermediate()],
173
+ encoder=json_encoder,
174
+ )
175
+ self.quality_store_wavecal_results(
176
+ input_wavelength=input_wavelength_vector,
177
+ input_spectrum=input_spectrum,
178
+ fit_result=fit_result,
179
+ weights=weights,
180
+ )
181
+
182
+ def get_theoretical_dispersion_order_light_angle(self) -> tuple[u.Quantity, int, u.Quantity]:
183
+ # TODO: Make this docstring correct (we use grating constant, not grating spacing) and show calculation of all values.
184
+ r"""
185
+ Compute theoretical dispersion, spectral order, and incident light angle.
186
+
187
+ The incident light angle, :math:`\alpha`, is computed as
188
+
189
+ .. math::
190
+ \alpha = \phi + \theta_L
191
+
192
+ where :math:`\phi`, the grating position, and :math:`\theta_L`, the Littrow angle, come from L0 headers.
193
+
194
+ From the grating equation, the spectral order, :math:`m`, is
195
+
196
+ .. math::
197
+ m = \frac{\sin\alpha + \sin\beta}{\sigma\lambda}
198
+
199
+ where :math:`\sigma` is the grating constant (lines per mm), :math:`\beta` is the diffracted light angle,
200
+ and :math:`\lambda` is the wavelength. The wavelength comes from L0 headers and :math:`\beta = \phi - \theta_L`.
201
+
202
+ Finally, the linear dispersion (nm / px) is
203
+
204
+ .. math::
205
+ \frac{d\lambda}{dl} = \frac{\mu \cos\beta}{m \sigma f}
206
+
207
+ where :math:`\mu` is the detector pixel pitch and :math:`f` is the camera focal length, both of which come from
208
+ L0 headers.
209
+ """
210
+ wavelength = self.constants.wavelength * u.nanometer
211
+ grating_position_angle_phi = self.constants.grating_position_deg * u.deg
212
+ grating_littrow_angle_theta = self.constants.grating_littrow_angle_deg * u.deg
213
+ incident_light_angle = grating_position_angle_phi + grating_littrow_angle_theta
214
+ beta = grating_position_angle_phi - grating_littrow_angle_theta
215
+ order = int(
216
+ (np.sin(incident_light_angle) + np.sin(beta))
217
+ / (wavelength * self.constants.grating_constant)
218
+ )
219
+ camera_mirror_focal_length = self.parameters.camera_mirror_focal_length_mm
220
+ pixpitch = self.parameters.pixel_pitch_micron
221
+ linear_disp = (
222
+ order * (self.constants.grating_constant / np.cos(beta)) * camera_mirror_focal_length
223
+ )
224
+ theoretical_dispersion = (pixpitch / linear_disp).to(u.nanometer) / u.pix
225
+
226
+ logger.info(f"{theoretical_dispersion = !s}")
227
+ logger.info(f"{order = }")
228
+ logger.info(f"{incident_light_angle = !s}")
229
+
230
+ return theoretical_dispersion, order, incident_light_angle
231
+
232
+ def compute_input_wavelength_vector(
233
+ self,
234
+ *,
235
+ spectrum: np.ndarray,
236
+ dispersion: u.Quantity,
237
+ order: int,
238
+ incident_light_angle: u.Quantity,
239
+ ) -> u.Quantity:
240
+ """Compute the expected wavelength vector based on the header information."""
241
+ num_wave_pix = spectrum.size
242
+ wavelength_parameters = WavelengthParameters(
243
+ crpix=num_wave_pix // 2 + 1,
244
+ crval=self.constants.wavelength,
245
+ dispersion=dispersion.to_value(u.nm / u.pix),
246
+ grating_constant=self.constants.grating_constant.to_value(1 / u.mm),
247
+ order=order,
248
+ incident_light_angle=incident_light_angle.to_value(u.deg),
249
+ cunit="nm",
250
+ )
251
+ header = wavelength_parameters.to_header(axis_num=1)
252
+ wcs = WCS(header)
253
+ input_wavelength_vector = wcs.spectral.pixel_to_world(np.arange(num_wave_pix)).to(u.nm)
254
+
255
+ return input_wavelength_vector
256
+
257
+ def get_doppler_velocity(self) -> u.Quantity:
258
+ """Find the speed at which DKIST is moving relative to the Sun's center.
259
+
260
+ Positive values refer to when DKIST is moving away from the sun.
261
+ """
262
+ coord = location_of_dkist.get_gcrs(obstime=Time(self.constants.solar_gain_ip_start_time))
263
+ heliocentric_coord = coord.transform_to(
264
+ HeliocentricInertial(obstime=Time(self.constants.solar_gain_ip_start_time))
265
+ )
266
+ obs_vr_kms = heliocentric_coord.d_distance
267
+ return obs_vr_kms
268
+
269
+ def get_resolving_power(self) -> int:
270
+ """Find the resolving power for the slit and filter center wavelength used during observation."""
271
+ # Map of (center wavelength) → (slit width) → resolving power
272
+ resolving_power_map = {1080.0: {175: 39580, 52: 120671}, 1430.0: {175: 42943, 52: 133762}}
273
+
274
+ center_wavelength = self.constants.center_wavelength
275
+ slit_width = self.constants.slit_width
276
+
277
+ # Find the closest matching key within tolerance
278
+ matched_wavelength = next(
279
+ (
280
+ key
281
+ for key in resolving_power_map
282
+ if math.isclose(center_wavelength, key, abs_tol=10)
283
+ ),
284
+ None,
285
+ )
286
+
287
+ if matched_wavelength is None:
288
+ raise ValueError(
289
+ f"{center_wavelength} not a valid filter center wavelength. "
290
+ f"Should be within 10 nm of one of {', '.join(str(k) for k in resolving_power_map)} nm."
291
+ )
292
+
293
+ slit_dict = resolving_power_map[matched_wavelength]
294
+ if slit_width not in slit_dict:
295
+ raise ValueError(
296
+ f"{slit_width} not a valid slit width. "
297
+ f"Should be one of {', '.join(str(k) for k in slit_dict)} µm."
298
+ )
299
+
300
+ return slit_dict[slit_width]
@@ -11,7 +11,7 @@ from astropy.coordinates.builtin_frames.altaz import AltAz
11
11
  from astropy.coordinates.sky_coordinate import SkyCoord
12
12
  from astropy.io import fits
13
13
  from astropy.time.core import Time
14
- from dkist_processing_common.codecs.asdf import asdf_decoder
14
+ from dkist_processing_common.codecs.json import json_decoder
15
15
  from dkist_processing_common.models.dkist_location import location_of_dkist
16
16
  from dkist_processing_common.models.wavelength import WavelengthRange
17
17
  from dkist_processing_common.tasks import WriteL1Frame
@@ -1020,24 +1020,14 @@ class SPWriteL1Frame(CryonirspWriteL1Frame):
1020
1020
 
1021
1021
  @cached_property
1022
1022
  def spectral_fit_results(self) -> dict:
1023
- """Get the spectral fit results."""
1024
- fit_dict = next(
1023
+ """Get the spectral fit results from disk."""
1024
+ return next(
1025
1025
  self.read(
1026
- tags=[CryonirspTag.task_spectral_fit(), CryonirspTag.intermediate()],
1027
- decoder=asdf_decoder,
1026
+ tags=[CryonirspTag.intermediate(), CryonirspTag.task_spectral_fit()],
1027
+ decoder=json_decoder,
1028
1028
  )
1029
1029
  )
1030
- del fit_dict["asdf_library"]
1031
- del fit_dict["history"]
1032
- return fit_dict
1033
1030
 
1034
1031
  def update_spectral_headers(self, header: fits.Header):
1035
1032
  """Update spectral headers after spectral correction."""
1036
- for key, value in self.spectral_fit_results.items():
1037
- # update the headers
1038
- header[key] = value
1039
-
1040
- header["CTYPE1"] = "AWAV-GRA"
1041
- header["CUNIT1"] = "nm"
1042
- header["CTYPE1A"] = "AWAV-GRA"
1043
- header["CUNIT1A"] = "nm"
1033
+ header.update(self.spectral_fit_results)