dkist-processing-visp 5.8.1__tar.gz → 5.8.2__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.
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/CHANGELOG.rst +9 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/PKG-INFO +25 -25
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/constants.py +17 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/fits_access.py +1 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/visp_l0_fits_access.py +4 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/assemble_movie.py +9 -2
- dkist_processing_visp-5.8.2/dkist_processing_visp/tasks/make_movie_frames.py +351 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/parse.py +5 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/conftest.py +8 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/header_models.py +11 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py +19 -7
- dkist_processing_visp-5.8.2/dkist_processing_visp/tests/test_assemble_movie.py +135 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_fits_access.py +4 -0
- dkist_processing_visp-5.8.2/dkist_processing_visp/tests/test_make_movie_frames.py +245 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_parse.py +4 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_visp_constants.py +43 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp.egg-info/PKG-INFO +25 -25
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp.egg-info/requires.txt +24 -24
- dkist_processing_visp-5.8.2/dkist_processing_visp.egg-info/scm_version.json +8 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/pyproject.toml +26 -26
- dkist_processing_visp-5.8.1/dkist_processing_visp/tasks/make_movie_frames.py +0 -160
- dkist_processing_visp-5.8.1/dkist_processing_visp/tests/test_assemble_movie.py +0 -62
- dkist_processing_visp-5.8.1/dkist_processing_visp/tests/test_make_movie_frames.py +0 -61
- dkist_processing_visp-5.8.1/dkist_processing_visp.egg-info/scm_version.json +0 -8
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/.gitignore +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/.pre-commit-config.yaml +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/.readthedocs.yml +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/.rovodev/.review-agent.md +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/.snyk +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/README.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/SCIENCE_CHANGELOG.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/bitbucket-pipelines.yml +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/changelog/.gitempty +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/config.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/fonts/Lato-Regular.ttf +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/dataset_extras.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/metric_code.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/parameters.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/tags.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/models/task_name.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/map_repeats.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/modulator_states.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/polarimeter_mode.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/raster_step.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/spectrograph_configuration.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/time.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/parsers/visp_l1_fits_access.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/background_light.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/dark.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/geometric.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/instrument_polarization.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/l1_output_data.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/lamp.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/mixin/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/mixin/beam_access.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/mixin/corrections.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/mixin/downsample.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/polcal_as_science.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/quality_metrics.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/science.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/solar.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/visp_base.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/wavelength_calibration.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/write_dataset_extras.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/write_l1.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/README.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/local_trial_workflows/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/local_trial_workflows/l0_cals_only.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/local_trial_workflows/l0_solar_gain_as_science.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/local_trial_workflows/l0_to_l1.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_assemble_quality.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_background_light.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_dark.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_downsample.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_geometric.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_instrument_polarization.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_lamp.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_map_repeats.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_parameters.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_polcal_as_science.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_quality.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_science.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_solar.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_trial_create_quality_report.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_visp_base.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_wavelength_calibration.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_workflows.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_write_dataset_extras.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/test_write_l1.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/workflows/__init__.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/workflows/l0_processing.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/workflows/single_task_workflows.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/workflows/trial_workflows.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp.egg-info/SOURCES.txt +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp.egg-info/dependency_links.txt +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp.egg-info/scm_file_list.json +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp.egg-info/top_level.txt +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/Makefile +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/background_light.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/changelog.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/conf.py +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/dataset_extras.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/gain_correction.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/geometric.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/index.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/l0_to_l1_visp.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/l0_to_l1_visp_full-trial.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/landing_page.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/make.bat +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/polarization_calibration.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/requirements.txt +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/requirements_table.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/science_calibration.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/scientific_changelog.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/docs/wavelength_calibration.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/licenses/LICENSE.rst +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/science_towncrier.sh +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/setup.cfg +0 -0
- {dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/towncrier_science.toml +0 -0
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
v5.8.2 (2026-09-04)
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
Bugfixes
|
|
5
|
+
--------
|
|
6
|
+
|
|
7
|
+
- Update movie generation for "sit'n'stare" datasets to avoid out-of-memory failures. Movies for sit'n'stare observations (when the slit doesn't move) are now displayed with time on the "spatial" axis; each column is the wavelength-integrated signal of a single slit observation. Genuine spatial raster scans, of any polarization mode, are unaffected and continue to use the existing algorithm. (`#329 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/329>`__)
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
v5.8.1 (2026-08-28)
|
|
2
11
|
===================
|
|
3
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dkist-processing-visp
|
|
3
|
-
Version: 5.8.
|
|
3
|
+
Version: 5.8.2
|
|
4
4
|
Summary: Science processing code for the ViSP instrument on DKIST
|
|
5
5
|
Author-email: NSO / AURA <dkistdc@nso.edu>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -84,11 +84,11 @@ Requires-Dist: aiohttp==3.14.3; extra == "frozen"
|
|
|
84
84
|
Requires-Dist: aiosignal==1.4.0; extra == "frozen"
|
|
85
85
|
Requires-Dist: aiosmtplib==5.1.2; extra == "frozen"
|
|
86
86
|
Requires-Dist: aiosqlite==0.22.1; extra == "frozen"
|
|
87
|
-
Requires-Dist: alembic==1.19.
|
|
87
|
+
Requires-Dist: alembic==1.19.2; extra == "frozen"
|
|
88
88
|
Requires-Dist: amqp==5.3.1; extra == "frozen"
|
|
89
89
|
Requires-Dist: annotated-doc==0.0.5; extra == "frozen"
|
|
90
90
|
Requires-Dist: annotated-types==0.8.0; extra == "frozen"
|
|
91
|
-
Requires-Dist: anyio==4.
|
|
91
|
+
Requires-Dist: anyio==4.15.0; extra == "frozen"
|
|
92
92
|
Requires-Dist: apache-airflow==3.3.1; extra == "frozen"
|
|
93
93
|
Requires-Dist: apache-airflow-core==3.3.1; extra == "frozen"
|
|
94
94
|
Requires-Dist: apache-airflow-providers-celery==3.23.1; extra == "frozen"
|
|
@@ -109,7 +109,7 @@ Requires-Dist: asdf_standard==1.5.0; extra == "frozen"
|
|
|
109
109
|
Requires-Dist: asgiref==3.12.1; extra == "frozen"
|
|
110
110
|
Requires-Dist: asteval==1.0.10; extra == "frozen"
|
|
111
111
|
Requires-Dist: astropy==7.0.2; extra == "frozen"
|
|
112
|
-
Requires-Dist: astropy-iers-data==0.2026.8.
|
|
112
|
+
Requires-Dist: astropy-iers-data==0.2026.8.31.0.57.9; extra == "frozen"
|
|
113
113
|
Requires-Dist: asttokens==3.0.2; extra == "frozen"
|
|
114
114
|
Requires-Dist: async-lru==2.3.0; extra == "frozen"
|
|
115
115
|
Requires-Dist: asyncpg==0.31.0; extra == "frozen"
|
|
@@ -118,9 +118,9 @@ Requires-Dist: babel==2.18.0; extra == "frozen"
|
|
|
118
118
|
Requires-Dist: beautifulsoup4==4.15.0; extra == "frozen"
|
|
119
119
|
Requires-Dist: billiard==4.2.4; extra == "frozen"
|
|
120
120
|
Requires-Dist: bleach==6.4.0; extra == "frozen"
|
|
121
|
-
Requires-Dist: boto3==1.43.
|
|
122
|
-
Requires-Dist: botocore==1.43.
|
|
123
|
-
Requires-Dist: cachetools==7.1.
|
|
121
|
+
Requires-Dist: boto3==1.43.88; extra == "frozen"
|
|
122
|
+
Requires-Dist: botocore==1.43.88; extra == "frozen"
|
|
123
|
+
Requires-Dist: cachetools==7.1.8; extra == "frozen"
|
|
124
124
|
Requires-Dist: cadwyn==7.0.0; extra == "frozen"
|
|
125
125
|
Requires-Dist: celery==5.6.3; extra == "frozen"
|
|
126
126
|
Requires-Dist: certifi==2026.7.22; extra == "frozen"
|
|
@@ -130,6 +130,7 @@ Requires-Dist: click==8.5.0; extra == "frozen"
|
|
|
130
130
|
Requires-Dist: click-didyoumean==0.3.1; extra == "frozen"
|
|
131
131
|
Requires-Dist: click-plugins==1.1.1.2; extra == "frozen"
|
|
132
132
|
Requires-Dist: click-repl==0.3.0; extra == "frozen"
|
|
133
|
+
Requires-Dist: cloudpickle==3.1.2; extra == "frozen"
|
|
133
134
|
Requires-Dist: colorlog==6.12.0; extra == "frozen"
|
|
134
135
|
Requires-Dist: comm==0.2.3; extra == "frozen"
|
|
135
136
|
Requires-Dist: contourpy==1.3.3; extra == "frozen"
|
|
@@ -146,7 +147,7 @@ Requires-Dist: dkist-processing-common==14.4.0; extra == "frozen"
|
|
|
146
147
|
Requires-Dist: dkist-processing-core==7.5.0; extra == "frozen"
|
|
147
148
|
Requires-Dist: dkist-processing-math==2.2.3; extra == "frozen"
|
|
148
149
|
Requires-Dist: dkist-processing-pac==3.1.2; extra == "frozen"
|
|
149
|
-
Requires-Dist: dkist-processing-visp==5.8.
|
|
150
|
+
Requires-Dist: dkist-processing-visp==5.8.2; extra == "frozen"
|
|
150
151
|
Requires-Dist: dkist-service-configuration==4.3.0; extra == "frozen"
|
|
151
152
|
Requires-Dist: dkist-spectral-lines==3.0.1; extra == "frozen"
|
|
152
153
|
Requires-Dist: dkist_fits_specifications==4.28.1; extra == "frozen"
|
|
@@ -157,12 +158,12 @@ Requires-Dist: fastapi==0.136.3; extra == "frozen"
|
|
|
157
158
|
Requires-Dist: fastapi-cli==0.0.32; extra == "frozen"
|
|
158
159
|
Requires-Dist: fastjsonschema==2.22.2; extra == "frozen"
|
|
159
160
|
Requires-Dist: flower==2.1.0; extra == "frozen"
|
|
160
|
-
Requires-Dist: fonttools==4.
|
|
161
|
+
Requires-Dist: fonttools==4.64.0; extra == "frozen"
|
|
161
162
|
Requires-Dist: fqdn==1.5.1; extra == "frozen"
|
|
162
163
|
Requires-Dist: frozenlist==1.8.0; extra == "frozen"
|
|
163
164
|
Requires-Dist: fsspec==2026.7.0; extra == "frozen"
|
|
164
165
|
Requires-Dist: globus-sdk==4.9.0; extra == "frozen"
|
|
165
|
-
Requires-Dist: googleapis-common-protos==1.75.
|
|
166
|
+
Requires-Dist: googleapis-common-protos==1.75.3; extra == "frozen"
|
|
166
167
|
Requires-Dist: gqlclient==2.0.0; extra == "frozen"
|
|
167
168
|
Requires-Dist: greenback==1.3.0; extra == "frozen"
|
|
168
169
|
Requires-Dist: greenlet==3.5.5; extra == "frozen"
|
|
@@ -174,22 +175,22 @@ Requires-Dist: httpx==0.28.1; extra == "frozen"
|
|
|
174
175
|
Requires-Dist: humanize==4.16.0; extra == "frozen"
|
|
175
176
|
Requires-Dist: idna==3.19; extra == "frozen"
|
|
176
177
|
Requires-Dist: imageio-ffmpeg==0.6.0; extra == "frozen"
|
|
177
|
-
Requires-Dist: importlib_metadata==9.0.
|
|
178
|
+
Requires-Dist: importlib_metadata==9.0.1; extra == "frozen"
|
|
178
179
|
Requires-Dist: ipykernel==7.3.0; extra == "frozen"
|
|
179
|
-
Requires-Dist: ipython==9.17.
|
|
180
|
+
Requires-Dist: ipython==9.17.1; extra == "frozen"
|
|
180
181
|
Requires-Dist: ipython_pygments_lexers==1.1.1; extra == "frozen"
|
|
181
182
|
Requires-Dist: isoduration==20.11.0; extra == "frozen"
|
|
182
183
|
Requires-Dist: itsdangerous==2.2.0; extra == "frozen"
|
|
183
184
|
Requires-Dist: jedi==0.20.0; extra == "frozen"
|
|
184
185
|
Requires-Dist: jmespath==1.1.0; extra == "frozen"
|
|
185
|
-
Requires-Dist: joblib==1.
|
|
186
|
+
Requires-Dist: joblib==1.6.0; extra == "frozen"
|
|
186
187
|
Requires-Dist: json5==0.15.0; extra == "frozen"
|
|
187
188
|
Requires-Dist: jsonpointer==3.1.1; extra == "frozen"
|
|
188
189
|
Requires-Dist: jsonschema==4.26.0; extra == "frozen"
|
|
189
190
|
Requires-Dist: jsonschema-specifications==2025.9.1; extra == "frozen"
|
|
190
191
|
Requires-Dist: jupyter-events==0.12.1; extra == "frozen"
|
|
191
192
|
Requires-Dist: jupyter-lsp==2.3.1; extra == "frozen"
|
|
192
|
-
Requires-Dist: jupyter_builder==1.2.
|
|
193
|
+
Requires-Dist: jupyter_builder==1.2.3; extra == "frozen"
|
|
193
194
|
Requires-Dist: jupyter_client==8.10.0; extra == "frozen"
|
|
194
195
|
Requires-Dist: jupyter_core==5.9.1; extra == "frozen"
|
|
195
196
|
Requires-Dist: jupyter_server==2.21.0; extra == "frozen"
|
|
@@ -203,7 +204,7 @@ Requires-Dist: lark==1.3.1; extra == "frozen"
|
|
|
203
204
|
Requires-Dist: lazy-loader==0.5; extra == "frozen"
|
|
204
205
|
Requires-Dist: lazy-object-proxy==1.12.0; extra == "frozen"
|
|
205
206
|
Requires-Dist: libcst==1.9.0; extra == "frozen"
|
|
206
|
-
Requires-Dist: linkify-it-py==2.
|
|
207
|
+
Requires-Dist: linkify-it-py==2.2.0; extra == "frozen"
|
|
207
208
|
Requires-Dist: lmfit==1.3.4; extra == "frozen"
|
|
208
209
|
Requires-Dist: lockfile==0.12.2; extra == "frozen"
|
|
209
210
|
Requires-Dist: loguru==0.7.3; extra == "frozen"
|
|
@@ -264,17 +265,17 @@ Requires-Dist: pexpect==4.9.0; extra == "frozen"
|
|
|
264
265
|
Requires-Dist: pika==1.4.4; extra == "frozen"
|
|
265
266
|
Requires-Dist: pillow==10.4.0; extra == "frozen"
|
|
266
267
|
Requires-Dist: pip==26.2.1; extra == "frozen"
|
|
267
|
-
Requires-Dist: platformdirs==4.11.
|
|
268
|
+
Requires-Dist: platformdirs==4.11.7; extra == "frozen"
|
|
268
269
|
Requires-Dist: pluggy==1.6.0; extra == "frozen"
|
|
269
270
|
Requires-Dist: pooch==1.9.0; extra == "frozen"
|
|
270
271
|
Requires-Dist: proglog==0.1.12; extra == "frozen"
|
|
271
272
|
Requires-Dist: prometheus_client==0.26.0; extra == "frozen"
|
|
272
273
|
Requires-Dist: prompt_toolkit==3.0.53; extra == "frozen"
|
|
273
274
|
Requires-Dist: propcache==0.5.2; extra == "frozen"
|
|
274
|
-
Requires-Dist: protobuf==7.36.
|
|
275
|
+
Requires-Dist: protobuf==7.36.1; extra == "frozen"
|
|
275
276
|
Requires-Dist: psutil==7.2.2; extra == "frozen"
|
|
276
|
-
Requires-Dist: psycopg==3.3.
|
|
277
|
-
Requires-Dist: psycopg-binary==3.3.
|
|
277
|
+
Requires-Dist: psycopg==3.3.5; extra == "frozen"
|
|
278
|
+
Requires-Dist: psycopg-binary==3.3.5; extra == "frozen"
|
|
278
279
|
Requires-Dist: psycopg2-binary==2.9.12; extra == "frozen"
|
|
279
280
|
Requires-Dist: ptyprocess==0.7.0; extra == "frozen"
|
|
280
281
|
Requires-Dist: pure_eval==0.2.3; extra == "frozen"
|
|
@@ -284,7 +285,7 @@ Requires-Dist: pydantic-extra-types==2.11.1; extra == "frozen"
|
|
|
284
285
|
Requires-Dist: pydantic-settings==2.15.0; extra == "frozen"
|
|
285
286
|
Requires-Dist: pydantic_core==2.46.5; extra == "frozen"
|
|
286
287
|
Requires-Dist: pyerfa==2.0.1.5; extra == "frozen"
|
|
287
|
-
Requires-Dist: pygtrie==2.
|
|
288
|
+
Requires-Dist: pygtrie==2.6.1; extra == "frozen"
|
|
288
289
|
Requires-Dist: pyparsing==3.3.2; extra == "frozen"
|
|
289
290
|
Requires-Dist: python-daemon==3.1.2; extra == "frozen"
|
|
290
291
|
Requires-Dist: python-dateutil==2.9.0.post0; extra == "frozen"
|
|
@@ -302,7 +303,7 @@ Requires-Dist: rfc3986-validator==0.1.1; extra == "frozen"
|
|
|
302
303
|
Requires-Dist: rfc3987-syntax==1.1.0; extra == "frozen"
|
|
303
304
|
Requires-Dist: rich==15.0.0; extra == "frozen"
|
|
304
305
|
Requires-Dist: rich-argparse==1.8.0; extra == "frozen"
|
|
305
|
-
Requires-Dist: rich-toolkit==0.20.
|
|
306
|
+
Requires-Dist: rich-toolkit==0.20.4; extra == "frozen"
|
|
306
307
|
Requires-Dist: rpds-py==2026.6.3; extra == "frozen"
|
|
307
308
|
Requires-Dist: s3transfer==0.19.2; extra == "frozen"
|
|
308
309
|
Requires-Dist: scikit-image==0.25.2; extra == "frozen"
|
|
@@ -339,7 +340,6 @@ Requires-Dist: typing-inspection==0.4.4; extra == "frozen"
|
|
|
339
340
|
Requires-Dist: typing_extensions==4.16.0; extra == "frozen"
|
|
340
341
|
Requires-Dist: tzdata==2026.3; extra == "frozen"
|
|
341
342
|
Requires-Dist: tzlocal==5.4.4; extra == "frozen"
|
|
342
|
-
Requires-Dist: uc-micro-py==2.0.0; extra == "frozen"
|
|
343
343
|
Requires-Dist: uncertainties==3.2.3; extra == "frozen"
|
|
344
344
|
Requires-Dist: universal_pathlib==0.3.10; extra == "frozen"
|
|
345
345
|
Requires-Dist: uri-template==1.3.0; extra == "frozen"
|
|
@@ -350,13 +350,13 @@ Requires-Dist: uvloop==0.22.1; extra == "frozen"
|
|
|
350
350
|
Requires-Dist: vine==5.1.0; extra == "frozen"
|
|
351
351
|
Requires-Dist: voluptuous==0.16.0; extra == "frozen"
|
|
352
352
|
Requires-Dist: watchfiles==1.2.0; extra == "frozen"
|
|
353
|
-
Requires-Dist: wcwidth==0.8.
|
|
353
|
+
Requires-Dist: wcwidth==0.8.3; extra == "frozen"
|
|
354
354
|
Requires-Dist: webcolors==25.10.0; extra == "frozen"
|
|
355
355
|
Requires-Dist: webencodings==0.6.1; extra == "frozen"
|
|
356
|
-
Requires-Dist: websocket-client==1.9.
|
|
356
|
+
Requires-Dist: websocket-client==1.9.2; extra == "frozen"
|
|
357
357
|
Requires-Dist: websockets==17.1; extra == "frozen"
|
|
358
358
|
Requires-Dist: wirerope==1.0.0; extra == "frozen"
|
|
359
|
-
Requires-Dist: wrapt==2.
|
|
359
|
+
Requires-Dist: wrapt==2.4.0; extra == "frozen"
|
|
360
360
|
Requires-Dist: yamale==6.1.0; extra == "frozen"
|
|
361
361
|
Requires-Dist: yarl==1.24.5; extra == "frozen"
|
|
362
362
|
Requires-Dist: zipp==4.1.0; extra == "frozen"
|
|
@@ -34,6 +34,7 @@ class VispBudName(Enum):
|
|
|
34
34
|
axis_3_type = "AXIS_3_TYPE"
|
|
35
35
|
dark_readout_exp_time_picky_bud = "DARK_READOUT_EXP_TIME_PICKY_BUD"
|
|
36
36
|
dark_readout_to_total_exp_times_lookup = "DARK_READOUT_TO_TOTAL_EXP_TIMES_LOOKUP"
|
|
37
|
+
slit_velocity_mm_per_s = "SLIT_VELOCITY_MM_PER_S"
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
class VispConstants(ConstantsBase):
|
|
@@ -196,3 +197,19 @@ class VispConstants(ConstantsBase):
|
|
|
196
197
|
raw_return = self._db_dict[VispBudName.dark_readout_to_total_exp_times_lookup]
|
|
197
198
|
# convert JSON string keys back to float
|
|
198
199
|
return {float(k): v for k, v in raw_return.items()}
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def slit_velocity_mm_per_s(self) -> float:
|
|
203
|
+
"""Return observation slit velocity [mm/s]."""
|
|
204
|
+
return self._db_dict[VispBudName.slit_velocity_mm_per_s.value]
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def is_sit_n_stare(self) -> bool:
|
|
208
|
+
"""
|
|
209
|
+
Return True if the data were acquired in the intensity-only, no-slit-motion "sit'n'stare" mode.
|
|
210
|
+
|
|
211
|
+
The data are sit'n'stare if they were acquired in intensity mode with a slit velocity of
|
|
212
|
+
exactly zero (the slit never moved).
|
|
213
|
+
"""
|
|
214
|
+
is_slit_stationary = self.slit_velocity_mm_per_s == 0.0
|
|
215
|
+
return self.polarimeter_mode == "observe_intensity" and is_slit_stationary
|
|
@@ -54,6 +54,10 @@ class VispL0FitsAccess(L0FitsAccess):
|
|
|
54
54
|
self.grating_constant_inverse_mm: float = self.header[
|
|
55
55
|
VispMetadataKey.grating_constant_inverse_mm
|
|
56
56
|
]
|
|
57
|
+
# Only present for intensity-mode data
|
|
58
|
+
self.slit_velocity_mm_per_s: float = self.header.get(
|
|
59
|
+
VispMetadataKey.slit_velocity_mm_per_s, HEADER_KEY_NOT_FOUND
|
|
60
|
+
)
|
|
57
61
|
self.axis_1_type: str = self.header[VispMetadataKey.axis_1_type]
|
|
58
62
|
self.axis_2_type: str = self.header[VispMetadataKey.axis_2_type]
|
|
59
63
|
self.axis_3_type: str = self.header[VispMetadataKey.axis_3_type]
|
|
@@ -44,9 +44,16 @@ class AssembleVispMovie(AssembleMovie):
|
|
|
44
44
|
def num_images(self) -> int:
|
|
45
45
|
"""Total number of images in final movie.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Overridden from `dkist-processing-common` because DSPS repeat does not correspond to map scan in ViSP.
|
|
48
|
+
|
|
49
|
+
This counts the movie frames actually written by `MakeVispMovieFrames`, rather than trusting
|
|
50
|
+
`self.constants.num_map_scans`. For the map-scan movie-frame path those two numbers are
|
|
51
|
+
always the same (one movie frame per map scan), but for the sit'n'stare movie-frame path a
|
|
52
|
+
single map scan can be chunked into many movie frames, so `num_map_scans` (which correctly
|
|
53
|
+
continues to describe the real map-scan structure of the science data, and is used as such
|
|
54
|
+
elsewhere) doesn't equal the number of movie frames in that case.
|
|
48
55
|
"""
|
|
49
|
-
return self.
|
|
56
|
+
return self.count(tags=[VispTag.movie_frame()])
|
|
50
57
|
|
|
51
58
|
def tags_for_image_n(self, n: int) -> list[str]:
|
|
52
59
|
"""Return tags that grab the n'th movie image.
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Visp make movie frames task."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from astropy.io import fits
|
|
5
|
+
from astropy.visualization import ZScaleInterval
|
|
6
|
+
from dkist_processing_common.codecs.fits import fits_access_decoder
|
|
7
|
+
from dkist_processing_common.codecs.fits import fits_array_encoder
|
|
8
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
9
|
+
from dkist_service_configuration.logging import logger
|
|
10
|
+
|
|
11
|
+
from dkist_processing_visp.models.tags import VispTag
|
|
12
|
+
from dkist_processing_visp.parsers.visp_l1_fits_access import VispL1FitsAccess
|
|
13
|
+
from dkist_processing_visp.tasks.assemble_movie import AssembleVispMovie
|
|
14
|
+
from dkist_processing_visp.tasks.visp_base import VispTaskBase
|
|
15
|
+
|
|
16
|
+
__all__ = ["MakeVispMovieFrames"]
|
|
17
|
+
|
|
18
|
+
MOVIE_FRAME_WIDTH = AssembleVispMovie.MOVIE_FRAME_SHAPE[0]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MakeVispMovieFrames(VispTaskBase):
|
|
22
|
+
"""
|
|
23
|
+
Make ViSP movie frames and tag with VispTag.movie_frame().
|
|
24
|
+
|
|
25
|
+
Frames are built via one of two code paths, chosen per-dataset in `run` based on
|
|
26
|
+
`self.constants.is_sit_n_stare`:
|
|
27
|
+
|
|
28
|
+
Map-scan path (`make_map_scan_movie_frames`): genuine spatially-scanning maps, of any
|
|
29
|
+
polarization mode, build one movie frame per map scan.
|
|
30
|
+
|
|
31
|
+
Sit'n'stare path (`make_sit_n_stare_movie_frames`): the single map scan is split into
|
|
32
|
+
fixed-size chunks of raster steps (see `generate_sit_n_stare_chunk_ranges`), and one movie
|
|
33
|
+
frame, capped at `MOVIE_FRAME_WIDTH`, is built per chunk. The final chunk is zero-padded up to
|
|
34
|
+
full width if it's undersized, guaranteeing that any dataset with at least one raster step
|
|
35
|
+
produces at least one movie frame. `self.constants.num_map_scans == 1` is required and checked
|
|
36
|
+
explicitly, since sit'n'stare is defined as a single, continuous, non-moving-slit observation
|
|
37
|
+
and this path only ever reads/writes `map_scan=1` frames.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
recipe_run_id : int
|
|
42
|
+
id of the recipe run used to identify the workflow run this task is part of
|
|
43
|
+
workflow_name : str
|
|
44
|
+
name of the workflow to which this instance of the task belongs
|
|
45
|
+
workflow_version : str
|
|
46
|
+
version of the workflow to which this instance of the task belongs
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def run(self) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Decide which movie-frame code path applies, based on `self.constants.is_sit_n_stare`, and build/write the frames.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
None
|
|
58
|
+
"""
|
|
59
|
+
if self.constants.is_sit_n_stare:
|
|
60
|
+
logger.info(
|
|
61
|
+
"Sit'n'stare mode detected. Building movie frames by chunking raster steps."
|
|
62
|
+
)
|
|
63
|
+
with self.telemetry_span("Making and writing sit'n'stare movie frames"):
|
|
64
|
+
self.make_sit_n_stare_movie_frames()
|
|
65
|
+
else:
|
|
66
|
+
logger.info("Building one movie frame per map scan.")
|
|
67
|
+
with self.telemetry_span("Making and writing movie frames"):
|
|
68
|
+
self.make_map_scan_movie_frames()
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def generate_sit_n_stare_chunk_ranges(
|
|
72
|
+
num_raster_steps: int, chunk_size: int = MOVIE_FRAME_WIDTH
|
|
73
|
+
) -> list[range]:
|
|
74
|
+
"""
|
|
75
|
+
Split a number of raster steps into consecutive, non-overlapping ranges of at most `chunk_size`.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
num_raster_steps
|
|
80
|
+
Total number of raster steps (time samples) in the single map scan. Can be arbitrarily
|
|
81
|
+
large.
|
|
82
|
+
|
|
83
|
+
chunk_size
|
|
84
|
+
The number of raster steps per full range/movie frame. Defaults to `MOVIE_FRAME_WIDTH`
|
|
85
|
+
so the produced frames need no resizing before being handed to `AssembleVispMovie`.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
A list of `range` objects, in ascending, non-overlapping, time order, together covering every raster step from `0` to `num_raster_steps`.
|
|
90
|
+
"""
|
|
91
|
+
num_chunks = -(-num_raster_steps // chunk_size) # ceiling division, no float round-trip
|
|
92
|
+
return [
|
|
93
|
+
range(i * chunk_size, min((i + 1) * chunk_size, num_raster_steps))
|
|
94
|
+
for i in range(num_chunks)
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
def make_sit_n_stare_movie_frames(self) -> None:
|
|
98
|
+
"""
|
|
99
|
+
For the single map scan, build one movie frame per fixed-size range of raster steps.
|
|
100
|
+
|
|
101
|
+
Stokes handling isn't needed here because this mode is intensity-only by definition.
|
|
102
|
+
|
|
103
|
+
For each range of up to `MOVIE_FRAME_WIDTH` sequential raster steps:
|
|
104
|
+
|
|
105
|
+
#. Integrate each raster step in the range over wavelength into a single column of
|
|
106
|
+
pixels
|
|
107
|
+
#. Build a movie frame by lining the columns up side by side, zero-padding out to
|
|
108
|
+
`MOVIE_FRAME_WIDTH` if this is a final, undersized chunk - guaranteeing at least one
|
|
109
|
+
frame is written even for a whole dataset smaller than `MOVIE_FRAME_WIDTH`
|
|
110
|
+
#. Write the frame as a "MOVIE_FRAME", tagged sequentially (chunk order, 1-indexed) -
|
|
111
|
+
*not* by any real map scan number, since there is only one
|
|
112
|
+
|
|
113
|
+
Raises
|
|
114
|
+
------
|
|
115
|
+
ValueError
|
|
116
|
+
If `self.constants.num_map_scans != 1`. Sit'n'stare is defined as a single,
|
|
117
|
+
continuous, non-moving-slit observation, so this code path only ever reads/writes
|
|
118
|
+
`map_scan=1` frames.
|
|
119
|
+
"""
|
|
120
|
+
num_map_scans = self.constants.num_map_scans
|
|
121
|
+
if num_map_scans != 1:
|
|
122
|
+
raise ValueError(
|
|
123
|
+
f"Sit'n'stare data is expected to have exactly one map scan, but {num_map_scans = }."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
num_raster_steps = self.constants.num_raster_steps
|
|
127
|
+
chunk_ranges = self.generate_sit_n_stare_chunk_ranges(
|
|
128
|
+
num_raster_steps, chunk_size=MOVIE_FRAME_WIDTH
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
for chunk_index, raster_step_chunk_range in enumerate(chunk_ranges, start=1):
|
|
132
|
+
logger.info(
|
|
133
|
+
f"Making and writing sit'n'stare movie frame for {chunk_index = }, "
|
|
134
|
+
f"{raster_step_chunk_range = }"
|
|
135
|
+
)
|
|
136
|
+
(
|
|
137
|
+
movie_frame_data,
|
|
138
|
+
time_obs,
|
|
139
|
+
calibrated_frame,
|
|
140
|
+
) = self.generate_frame_from_raster_steps(
|
|
141
|
+
stokes_state="I", map_scan=1, raster_steps=raster_step_chunk_range
|
|
142
|
+
)
|
|
143
|
+
# `num_raster_steps > 1` excludes the single-raster-step-in-the-whole-dataset
|
|
144
|
+
# special case in `generate_frame_from_raster_steps`, which returns the raw
|
|
145
|
+
# spectral frame (not a per-raster-step column stack) and so isn't paddable the same
|
|
146
|
+
# way; that one frame *is* the whole movie already, regardless of its native width.
|
|
147
|
+
if num_raster_steps > 1 and len(raster_step_chunk_range) < MOVIE_FRAME_WIDTH:
|
|
148
|
+
pad_width = MOVIE_FRAME_WIDTH - len(raster_step_chunk_range)
|
|
149
|
+
logger.info(
|
|
150
|
+
f"{chunk_index = } has only {len(raster_step_chunk_range)} raster "
|
|
151
|
+
f"steps; padding with {pad_width} zero-filled columns to reach "
|
|
152
|
+
f"{MOVIE_FRAME_WIDTH = }."
|
|
153
|
+
)
|
|
154
|
+
movie_frame_data = np.pad(
|
|
155
|
+
movie_frame_data, ((0, 0), (0, pad_width)), mode="constant"
|
|
156
|
+
)
|
|
157
|
+
header = self.build_movie_frame_header(
|
|
158
|
+
calibrated_frame=calibrated_frame,
|
|
159
|
+
time_obs=time_obs,
|
|
160
|
+
)
|
|
161
|
+
self.write_movie_frame(
|
|
162
|
+
movie_frame_data=movie_frame_data, header=header, index=chunk_index
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def make_map_scan_movie_frames(self) -> None:
|
|
166
|
+
"""
|
|
167
|
+
For each map scan, build one movie frame from all its raster steps, for each stokes state.
|
|
168
|
+
|
|
169
|
+
For each map scan:
|
|
170
|
+
|
|
171
|
+
#. For each stokes state present in the data:
|
|
172
|
+
|
|
173
|
+
#. Integrate each raster step in the scan over wavelength into a single column of
|
|
174
|
+
pixels
|
|
175
|
+
#. Build a movie frame by lining the columns up side by side
|
|
176
|
+
|
|
177
|
+
#. Combine stokes frames (normalizing across intensities if polarimetric)
|
|
178
|
+
#. Write the frame as a "MOVIE_FRAME", tagged by map scan number
|
|
179
|
+
"""
|
|
180
|
+
stokes_states = ["I", "Q", "U", "V"]
|
|
181
|
+
# Loop over the number of raster scans
|
|
182
|
+
for map_scan in range(1, self.constants.num_map_scans + 1):
|
|
183
|
+
logger.info(f"Making movie frame for {map_scan = }")
|
|
184
|
+
stokes_frames = dict()
|
|
185
|
+
time_obs = []
|
|
186
|
+
|
|
187
|
+
# Loop over the stokes states to add them to the frame array
|
|
188
|
+
for stokes_state in stokes_states:
|
|
189
|
+
num_stokes_frames = self.count(
|
|
190
|
+
tags=[
|
|
191
|
+
VispTag.frame(),
|
|
192
|
+
VispTag.calibrated(),
|
|
193
|
+
VispTag.stokes(stokes_state),
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
if num_stokes_frames > 0:
|
|
197
|
+
(
|
|
198
|
+
stokes_frames[stokes_state],
|
|
199
|
+
step_time_obs,
|
|
200
|
+
calibrated_frame,
|
|
201
|
+
) = self.generate_frame_from_raster_steps(
|
|
202
|
+
stokes_state=stokes_state,
|
|
203
|
+
map_scan=map_scan,
|
|
204
|
+
raster_steps=range(0, self.constants.num_raster_steps),
|
|
205
|
+
)
|
|
206
|
+
time_obs += step_time_obs
|
|
207
|
+
|
|
208
|
+
header = self.build_movie_frame_header(
|
|
209
|
+
calibrated_frame=calibrated_frame,
|
|
210
|
+
time_obs=time_obs,
|
|
211
|
+
)
|
|
212
|
+
movie_frame_data = self.combine_stokes_frames(
|
|
213
|
+
stokes_frames, self.constants.correct_for_polarization
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
logger.info(f"Writing movie frame for {map_scan = }")
|
|
217
|
+
self.write_movie_frame(movie_frame_data=movie_frame_data, header=header, index=map_scan)
|
|
218
|
+
|
|
219
|
+
def read_calibrated_frame(
|
|
220
|
+
self, *, stokes_state: str, map_scan: int, raster_step: int
|
|
221
|
+
) -> VispL1FitsAccess:
|
|
222
|
+
"""Read a single calibrated frame for the given stokes state, map scan, and raster step."""
|
|
223
|
+
return next(
|
|
224
|
+
self.read(
|
|
225
|
+
tags=[
|
|
226
|
+
VispTag.frame(),
|
|
227
|
+
VispTag.calibrated(),
|
|
228
|
+
VispTag.stokes(stokes_state),
|
|
229
|
+
VispTag.map_scan(map_scan),
|
|
230
|
+
VispTag.raster_step(raster_step),
|
|
231
|
+
],
|
|
232
|
+
decoder=fits_access_decoder,
|
|
233
|
+
fits_access_class=VispL1FitsAccess,
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def generate_frame_from_raster_steps(
|
|
238
|
+
self, *, stokes_state: str, map_scan: int, raster_steps: range
|
|
239
|
+
) -> tuple[np.ndarray, list[str], VispL1FitsAccess]:
|
|
240
|
+
"""
|
|
241
|
+
Read and wavelength-integrate each raster step into a column, lining columns up side by side.
|
|
242
|
+
|
|
243
|
+
This is the piece of logic shared by both movie-frame code paths; only the `raster_steps`
|
|
244
|
+
being looped over (and whether this is called once per stokes state or just once for "I")
|
|
245
|
+
differs between them.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
stokes_state
|
|
250
|
+
Which stokes state to read ("I", "Q", "U", or "V")
|
|
251
|
+
|
|
252
|
+
map_scan
|
|
253
|
+
The map scan number to read frames from. Always `1` for the sit'n'stare path, since
|
|
254
|
+
that mode is defined as a single map scan.
|
|
255
|
+
|
|
256
|
+
raster_steps
|
|
257
|
+
The raster steps (in time order) to line up side-by-side into the frame. This is the
|
|
258
|
+
full `range(0, num_raster_steps)` for the per-map-scan path, or a single
|
|
259
|
+
`generate_sit_n_stare_chunk_ranges` window for the sit'n'stare path.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
The assembled 2D frame data (shape `(spectral_size, len(raster_steps))`, unless there's only a single raster step in the *entire dataset*, in which case the raw spectral frame is returned instead)
|
|
264
|
+
|
|
265
|
+
The list of time_obs values seen, in raster-step order
|
|
266
|
+
|
|
267
|
+
The `FitsAccess` object for the last-read raster step, used for movie-frame metadata
|
|
268
|
+
"""
|
|
269
|
+
time_obs = []
|
|
270
|
+
frame_data = None
|
|
271
|
+
|
|
272
|
+
for i, raster_step in enumerate(raster_steps):
|
|
273
|
+
calibrated_frame = self.read_calibrated_frame(
|
|
274
|
+
stokes_state=stokes_state, map_scan=map_scan, raster_step=raster_step
|
|
275
|
+
)
|
|
276
|
+
data = np.nan_to_num(calibrated_frame.data, nan=0)
|
|
277
|
+
time_obs.append(calibrated_frame.time_obs)
|
|
278
|
+
|
|
279
|
+
if self.constants.num_raster_steps == 1:
|
|
280
|
+
logger.info("Only a single raster step found. Making a spectral movie.")
|
|
281
|
+
frame_data = data
|
|
282
|
+
else:
|
|
283
|
+
wavelength_integrated_data = np.sum(np.abs(data), axis=0)
|
|
284
|
+
if frame_data is None:
|
|
285
|
+
frame_data = np.empty((wavelength_integrated_data.shape[0], len(raster_steps)))
|
|
286
|
+
frame_data[:, i] = wavelength_integrated_data
|
|
287
|
+
|
|
288
|
+
return frame_data, time_obs, calibrated_frame
|
|
289
|
+
|
|
290
|
+
def build_movie_frame_header(
|
|
291
|
+
self,
|
|
292
|
+
*,
|
|
293
|
+
calibrated_frame: VispL1FitsAccess,
|
|
294
|
+
time_obs: list[str],
|
|
295
|
+
) -> fits.Header:
|
|
296
|
+
"""Build the header for a movie frame, updating `time_obs` to the start time of the raster scan."""
|
|
297
|
+
# Use the most recently read header as the base header because we need to be able to read it
|
|
298
|
+
# with VispL1FitsAccess.
|
|
299
|
+
header = fits.Header(calibrated_frame.header)
|
|
300
|
+
# The timestamp of a movie frame will be the time of raster scan start
|
|
301
|
+
header[MetadataKey.time_obs] = time_obs[0]
|
|
302
|
+
return header
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def combine_stokes_frames(
|
|
306
|
+
stokes_frames: dict[str, np.ndarray], is_polarized: bool
|
|
307
|
+
) -> np.ndarray:
|
|
308
|
+
"""Combine per-stokes frame data into the final movie frame, normalizing if polarimetric."""
|
|
309
|
+
if is_polarized:
|
|
310
|
+
i_norm = ZScaleInterval()(stokes_frames["I"])
|
|
311
|
+
q_norm = ZScaleInterval()(stokes_frames["Q"])
|
|
312
|
+
u_norm = ZScaleInterval()(stokes_frames["U"])
|
|
313
|
+
v_norm = ZScaleInterval()(stokes_frames["V"])
|
|
314
|
+
return np.concatenate(
|
|
315
|
+
(
|
|
316
|
+
np.concatenate((i_norm, q_norm), axis=1),
|
|
317
|
+
np.concatenate((u_norm, v_norm), axis=1),
|
|
318
|
+
),
|
|
319
|
+
axis=0,
|
|
320
|
+
)
|
|
321
|
+
return stokes_frames["I"]
|
|
322
|
+
|
|
323
|
+
def write_movie_frame(
|
|
324
|
+
self, *, movie_frame_data: np.ndarray, header: fits.Header, index: int
|
|
325
|
+
) -> None:
|
|
326
|
+
"""
|
|
327
|
+
Write a single movie frame to disk, tagged with VispTag.movie_frame().
|
|
328
|
+
|
|
329
|
+
Parameters
|
|
330
|
+
----------
|
|
331
|
+
movie_frame_data
|
|
332
|
+
The 2D frame array to write
|
|
333
|
+
|
|
334
|
+
header
|
|
335
|
+
The header to write alongside the frame
|
|
336
|
+
|
|
337
|
+
index
|
|
338
|
+
The sequential index used to tag the frame (reusing the `map_scan` tag stem). For the
|
|
339
|
+
map-scan path this is the real map scan number. For the sit'n'stare path this is the
|
|
340
|
+
1-indexed chunk number - *not* a real map scan number, since sit'n'stare data always
|
|
341
|
+
has exactly one.
|
|
342
|
+
"""
|
|
343
|
+
self.write(
|
|
344
|
+
data=movie_frame_data,
|
|
345
|
+
tags=[
|
|
346
|
+
VispTag.map_scan(index),
|
|
347
|
+
VispTag.movie_frame(),
|
|
348
|
+
],
|
|
349
|
+
encoder=fits_array_encoder,
|
|
350
|
+
header=header,
|
|
351
|
+
)
|
{dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tasks/parse.py
RENAMED
|
@@ -90,6 +90,11 @@ class ParseL0VispInputData(ParseL0InputDataBase):
|
|
|
90
90
|
TotalRasterStepsBud(),
|
|
91
91
|
NumCSStepBud(max_cs_step_time_sec=self.parameters.max_cs_step_time_sec),
|
|
92
92
|
NumFramesPerCSStepBud(max_cs_step_time_sec=self.parameters.max_cs_step_time_sec),
|
|
93
|
+
TaskUniqueBud(
|
|
94
|
+
constant_name=VispBudName.slit_velocity_mm_per_s.value,
|
|
95
|
+
metadata_key=VispMetadataKey.slit_velocity_mm_per_s,
|
|
96
|
+
ip_task_types=TaskName.observe.value,
|
|
97
|
+
),
|
|
93
98
|
ObsIpStartTimeBud(),
|
|
94
99
|
NumberModulatorStatesBud(),
|
|
95
100
|
ObserveWavelengthBud(),
|
{dkist_processing_visp-5.8.1 → dkist_processing_visp-5.8.2}/dkist_processing_visp/tests/conftest.py
RENAMED
|
@@ -20,6 +20,7 @@ from dkist_header_validator.translator import translate_spec122_to_spec214_l0
|
|
|
20
20
|
from dkist_processing_common.codecs.basemodel import basemodel_encoder
|
|
21
21
|
from dkist_processing_common.codecs.fits import fits_array_encoder
|
|
22
22
|
from dkist_processing_common.codecs.fits import fits_hdulist_encoder
|
|
23
|
+
from dkist_processing_common.models.fits_access import HEADER_KEY_NOT_FOUND
|
|
23
24
|
from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
|
|
24
25
|
from dkist_processing_common.tasks import WorkflowTaskBase
|
|
25
26
|
|
|
@@ -53,6 +54,11 @@ def init_visp_constants_db():
|
|
|
53
54
|
class VispConstantsDb:
|
|
54
55
|
ARM_ID: int = 1
|
|
55
56
|
POLARIMETER_MODE: str = "observe_polarimetric"
|
|
57
|
+
# Real polarimetric data never has this header key at all; every other default in this
|
|
58
|
+
# dataclass already models a non-sit'n'stare dataset, so this sentinel default is consistent
|
|
59
|
+
# with that: `VispConstants.is_sit_n_stare` is False regardless of this value whenever
|
|
60
|
+
# POLARIMETER_MODE isn't "observe_intensity".
|
|
61
|
+
SLIT_VELOCITY_MM_PER_S: float | str = HEADER_KEY_NOT_FOUND
|
|
56
62
|
OBS_IP_START_TIME: str = "2022-11-28T13:54:00"
|
|
57
63
|
NUM_MODSTATES: int = 10
|
|
58
64
|
NUM_MAP_SCANS: int = 2
|
|
@@ -712,6 +718,7 @@ def write_calibrated_frames_to_task(
|
|
|
712
718
|
wcs_axis_names: tuple[str, str] = ("HPLT-TAN", "AWAV"),
|
|
713
719
|
num_maps: int = 1,
|
|
714
720
|
num_steps: int = 1,
|
|
721
|
+
slit_velocity_mm_per_s: float = 1.5,
|
|
715
722
|
):
|
|
716
723
|
array_shape = (1, *data_shape)
|
|
717
724
|
dataset = VispHeadersValidCalibratedFrames(
|
|
@@ -721,6 +728,7 @@ def write_calibrated_frames_to_task(
|
|
|
721
728
|
num_raster_steps=num_steps,
|
|
722
729
|
polarimeter_mode=pol_mode,
|
|
723
730
|
wcs_axis_names=wcs_axis_names,
|
|
731
|
+
slit_velocity_mm_per_s=slit_velocity_mm_per_s,
|
|
724
732
|
)
|
|
725
733
|
|
|
726
734
|
num_written_frames = write_frames_to_task(
|