dkist-processing-cryonirsp 1.10.0rc1__py3-none-any.whl → 1.14.9rc1__py3-none-any.whl
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.
- changelog/232.misc.rst +1 -0
- dkist_processing_cryonirsp/__init__.py +1 -0
- dkist_processing_cryonirsp/codecs/fits.py +1 -0
- dkist_processing_cryonirsp/config.py +1 -0
- dkist_processing_cryonirsp/models/beam_boundaries.py +1 -0
- dkist_processing_cryonirsp/models/constants.py +8 -30
- dkist_processing_cryonirsp/models/exposure_conditions.py +6 -5
- dkist_processing_cryonirsp/models/fits_access.py +40 -0
- dkist_processing_cryonirsp/models/parameters.py +1 -0
- dkist_processing_cryonirsp/models/tags.py +1 -0
- dkist_processing_cryonirsp/models/task_name.py +1 -0
- dkist_processing_cryonirsp/parsers/check_for_gains.py +1 -0
- dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +40 -48
- dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +1 -0
- dkist_processing_cryonirsp/parsers/exposure_conditions.py +14 -13
- dkist_processing_cryonirsp/parsers/map_repeats.py +1 -0
- dkist_processing_cryonirsp/parsers/measurements.py +29 -16
- dkist_processing_cryonirsp/parsers/modstates.py +5 -1
- dkist_processing_cryonirsp/parsers/optical_density_filters.py +1 -0
- dkist_processing_cryonirsp/parsers/polarimetric_check.py +18 -7
- dkist_processing_cryonirsp/parsers/scan_step.py +12 -4
- dkist_processing_cryonirsp/parsers/time.py +7 -7
- dkist_processing_cryonirsp/parsers/wavelength.py +6 -1
- dkist_processing_cryonirsp/tasks/__init__.py +1 -0
- dkist_processing_cryonirsp/tasks/assemble_movie.py +1 -0
- dkist_processing_cryonirsp/tasks/bad_pixel_map.py +6 -5
- dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +9 -10
- dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +1 -0
- dkist_processing_cryonirsp/tasks/ci_science.py +1 -0
- dkist_processing_cryonirsp/tasks/cryonirsp_base.py +1 -0
- dkist_processing_cryonirsp/tasks/dark.py +5 -4
- dkist_processing_cryonirsp/tasks/gain.py +7 -6
- dkist_processing_cryonirsp/tasks/instrument_polarization.py +16 -15
- dkist_processing_cryonirsp/tasks/l1_output_data.py +1 -0
- dkist_processing_cryonirsp/tasks/linearity_correction.py +1 -0
- dkist_processing_cryonirsp/tasks/make_movie_frames.py +3 -2
- dkist_processing_cryonirsp/tasks/mixin/corrections.py +1 -0
- dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +1 -0
- dkist_processing_cryonirsp/tasks/parse.py +66 -61
- dkist_processing_cryonirsp/tasks/quality_metrics.py +15 -14
- dkist_processing_cryonirsp/tasks/science_base.py +8 -6
- dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +2 -1
- dkist_processing_cryonirsp/tasks/sp_geometric.py +11 -10
- dkist_processing_cryonirsp/tasks/sp_science.py +1 -0
- dkist_processing_cryonirsp/tasks/sp_solar_gain.py +15 -12
- dkist_processing_cryonirsp/tasks/sp_wavelength_calibration.py +9 -9
- dkist_processing_cryonirsp/tasks/write_l1.py +36 -7
- dkist_processing_cryonirsp/tests/conftest.py +6 -7
- dkist_processing_cryonirsp/tests/header_models.py +40 -3
- dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +11 -31
- dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +11 -30
- dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +3 -3
- dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +3 -2
- dkist_processing_cryonirsp/tests/test_assemble_movie.py +4 -5
- dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +5 -1
- dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +3 -4
- dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +3 -4
- dkist_processing_cryonirsp/tests/test_ci_science.py +3 -4
- dkist_processing_cryonirsp/tests/test_corrections.py +3 -3
- dkist_processing_cryonirsp/tests/test_cryo_base.py +3 -5
- dkist_processing_cryonirsp/tests/test_cryo_constants.py +1 -2
- dkist_processing_cryonirsp/tests/test_dark.py +5 -6
- dkist_processing_cryonirsp/tests/test_fits_access.py +44 -0
- dkist_processing_cryonirsp/tests/test_gain.py +5 -6
- dkist_processing_cryonirsp/tests/test_instrument_polarization.py +9 -6
- dkist_processing_cryonirsp/tests/test_linearity_correction.py +4 -3
- dkist_processing_cryonirsp/tests/test_make_movie_frames.py +2 -3
- dkist_processing_cryonirsp/tests/test_parameters.py +3 -4
- dkist_processing_cryonirsp/tests/test_parse.py +14 -8
- dkist_processing_cryonirsp/tests/test_quality.py +2 -3
- dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +4 -4
- dkist_processing_cryonirsp/tests/test_sp_geometric.py +3 -4
- dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +2 -3
- dkist_processing_cryonirsp/tests/test_sp_science.py +3 -4
- dkist_processing_cryonirsp/tests/test_sp_solar.py +5 -4
- dkist_processing_cryonirsp/tests/test_sp_wavelength_calibration.py +4 -5
- dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +1 -1
- dkist_processing_cryonirsp/tests/test_workflows.py +1 -0
- dkist_processing_cryonirsp/tests/test_write_l1.py +12 -16
- dkist_processing_cryonirsp/workflows/__init__.py +1 -0
- dkist_processing_cryonirsp/workflows/ci_l0_processing.py +6 -5
- dkist_processing_cryonirsp/workflows/sp_l0_processing.py +6 -5
- dkist_processing_cryonirsp/workflows/trial_workflows.py +9 -8
- dkist_processing_cryonirsp-1.14.9rc1.dist-info/METADATA +552 -0
- dkist_processing_cryonirsp-1.14.9rc1.dist-info/RECORD +115 -0
- docs/conf.py +1 -0
- docs/wavelength_calibration.rst +1 -1
- changelog/167.feature.rst +0 -1
- dkist_processing_cryonirsp-1.10.0rc1.dist-info/METADATA +0 -458
- dkist_processing_cryonirsp-1.10.0rc1.dist-info/RECORD +0 -113
- {dkist_processing_cryonirsp-1.10.0rc1.dist-info → dkist_processing_cryonirsp-1.14.9rc1.dist-info}/WHEEL +0 -0
- {dkist_processing_cryonirsp-1.10.0rc1.dist-info → dkist_processing_cryonirsp-1.14.9rc1.dist-info}/top_level.txt +0 -0
changelog/232.misc.rst
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Update dkist-processing-common to add a new constant for the dark number of frames per FPA.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"""CryoNIRSP additions to common constants."""
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
3
4
|
from enum import unique
|
|
4
5
|
|
|
5
6
|
import astropy.units as u
|
|
6
|
-
from dkist_processing_common.models.constants import BudName
|
|
7
7
|
from dkist_processing_common.models.constants import ConstantsBase
|
|
8
8
|
|
|
9
9
|
from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@unique
|
|
13
|
-
class CryonirspBudName(
|
|
13
|
+
class CryonirspBudName(StrEnum):
|
|
14
14
|
"""Names to be used for CryoNIRSP buds."""
|
|
15
15
|
|
|
16
16
|
arm_id = "ARM_ID"
|
|
@@ -19,8 +19,6 @@ class CryonirspBudName(Enum):
|
|
|
19
19
|
num_map_scans = "NUM_MAP_SCANS"
|
|
20
20
|
num_modstates = "NUM_MODSTATES"
|
|
21
21
|
wavelength = "WAVELENGTH"
|
|
22
|
-
wave_min = "WAVE_MIN"
|
|
23
|
-
wave_max = "WAVE_MAX"
|
|
24
22
|
grating_position_deg = "GRATING_POSITION_DEG"
|
|
25
23
|
grating_littrow_angle_deg = "GRATING_LITTROW_ANGLE_DEG"
|
|
26
24
|
grating_constant = "GRATING_CONSTANT"
|
|
@@ -51,7 +49,7 @@ class CryonirspBudName(Enum):
|
|
|
51
49
|
roi_1_size_x = "ROI_1_SIZE_X"
|
|
52
50
|
roi_1_size_y = "ROI_1_SIZE_Y"
|
|
53
51
|
optical_density_filter_picky_bud = "OPTICAL_DENSITY_FILTER_PICKY_BUD"
|
|
54
|
-
|
|
52
|
+
solar_gain_start_time = "SOLAR_GAIN_START_TIME"
|
|
55
53
|
gain_frame_type_list = "GAIN_FRAME_TYPE_LIST"
|
|
56
54
|
lamp_gain_frame_type_list = "LAMP_GAIN_FRAME_TYPE_LIST"
|
|
57
55
|
solar_gain_frame_type_list = "SOLAR_GAIN_FRAME_TYPE_LIST"
|
|
@@ -91,19 +89,9 @@ class CryonirspConstants(ConstantsBase):
|
|
|
91
89
|
return self._db_dict[CryonirspBudName.wavelength.value]
|
|
92
90
|
|
|
93
91
|
@property
|
|
94
|
-
def
|
|
95
|
-
"""
|
|
96
|
-
return self._db_dict[CryonirspBudName.
|
|
97
|
-
|
|
98
|
-
@property
|
|
99
|
-
def wave_max(self) -> float:
|
|
100
|
-
"""Wavelength maximum."""
|
|
101
|
-
return self._db_dict[CryonirspBudName.wave_max.value]
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def solar_gain_ip_start_time(self) -> str:
|
|
105
|
-
"""Solar gain IP start time."""
|
|
106
|
-
return self._db_dict[CryonirspBudName.solar_gain_ip_start_time.value]
|
|
92
|
+
def solar_gain_start_time(self) -> str:
|
|
93
|
+
"""Solar gain start time."""
|
|
94
|
+
return self._db_dict[CryonirspBudName.solar_gain_start_time.value]
|
|
107
95
|
|
|
108
96
|
@property
|
|
109
97
|
def grating_position_deg(self) -> float:
|
|
@@ -227,16 +215,6 @@ class CryonirspConstants(ConstantsBase):
|
|
|
227
215
|
conditions = [ExposureConditions(*item) for item in raw_conditions]
|
|
228
216
|
return conditions
|
|
229
217
|
|
|
230
|
-
@property
|
|
231
|
-
def num_modstates(self) -> int:
|
|
232
|
-
"""Find the number of modulation states."""
|
|
233
|
-
return self._db_dict[CryonirspBudName.num_modstates.value]
|
|
234
|
-
|
|
235
|
-
@property
|
|
236
|
-
def num_cs_steps(self) -> int:
|
|
237
|
-
"""Find the number of calibration sequence steps."""
|
|
238
|
-
return self._db_dict[BudName.num_cs_steps.value]
|
|
239
|
-
|
|
240
218
|
@property
|
|
241
219
|
def stokes_I_list(self) -> [str]:
|
|
242
220
|
"""List containing only the Stokes-I parameter."""
|
|
@@ -252,7 +230,7 @@ class CryonirspConstants(ConstantsBase):
|
|
|
252
230
|
@property
|
|
253
231
|
def pac_init_set(self):
|
|
254
232
|
"""Return the label for the initial set of parameter values used when fitting demodulation matrices."""
|
|
255
|
-
retarder_name = self.
|
|
233
|
+
retarder_name = self.retarder_name
|
|
256
234
|
match retarder_name:
|
|
257
235
|
case "SiO2 OC":
|
|
258
236
|
return "OCCal_VIS"
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"""Support classes for exposure conditions."""
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
from enum import StrEnum
|
|
4
|
+
from typing import NamedTuple
|
|
4
5
|
|
|
5
6
|
# Number of digits used to round the exposure when creating the ExposureConditions tuple in fits_access
|
|
6
7
|
CRYO_EXP_TIME_ROUND_DIGITS: int = 3
|
|
7
8
|
|
|
8
|
-
"""Base class to hold a tuple of exposure time and filter name."""
|
|
9
|
-
ExposureConditionsBase = namedtuple("ExposureConditions", ["exposure_time", "filter_name"])
|
|
10
9
|
|
|
10
|
+
class ExposureConditions(NamedTuple):
|
|
11
|
+
"""Named tuple to hold exposure time and filter name."""
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
exposure_time: float
|
|
14
|
+
filter_name: str
|
|
14
15
|
|
|
15
16
|
def __str__(self):
|
|
16
17
|
return f"{self.exposure_time}_{self.filter_name}"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""CryoNIRSP control of FITS key names and values."""
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from enum import unique
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@unique
|
|
8
|
+
class CryonirspMetadataKey(StrEnum):
|
|
9
|
+
"""Controlled list of names for FITS metadata header keys."""
|
|
10
|
+
|
|
11
|
+
camera_readout_mode = "CNCAMMD"
|
|
12
|
+
curr_frame_in_ramp = "CNCNDR"
|
|
13
|
+
num_frames_in_ramp = "CNNNDR"
|
|
14
|
+
arm_id = "CNARMID"
|
|
15
|
+
roi_1_origin_x = "HWROI1OX"
|
|
16
|
+
roi_1_origin_y = "HWROI1OY"
|
|
17
|
+
roi_1_size_x = "HWROI1SX"
|
|
18
|
+
roi_1_size_y = "HWROI1SY"
|
|
19
|
+
filter_name = "CNFILTNP"
|
|
20
|
+
number_of_modulator_states = "CNMODNST"
|
|
21
|
+
modulator_state = "CNMODCST"
|
|
22
|
+
scan_step = "CNCURSCN"
|
|
23
|
+
num_scan_steps = "CNNUMSCN"
|
|
24
|
+
num_cn1_scan_steps = "CNP1DNSP"
|
|
25
|
+
num_cn2_scan_steps = "CNP2DNSP"
|
|
26
|
+
cn2_step_size = "CNP2DSS"
|
|
27
|
+
cn1_scan_step = "CNP1DCUR"
|
|
28
|
+
meas_num = "CNCMEAS"
|
|
29
|
+
num_meas = "CNNMEAS"
|
|
30
|
+
sub_repeat_num = "CNCSREP"
|
|
31
|
+
num_sub_repeats = "CNSUBREP"
|
|
32
|
+
modulator_spin_mode = "CNSPINMD"
|
|
33
|
+
axis_1_type = "CTYPE1"
|
|
34
|
+
axis_2_type = "CTYPE2"
|
|
35
|
+
axis_3_type = "CTYPE3"
|
|
36
|
+
grating_position_deg = "CNGRTPOS"
|
|
37
|
+
grating_littrow_angle_deg = "CNGRTLAT"
|
|
38
|
+
grating_constant = "CNGRTCON"
|
|
39
|
+
center_wavelength = "CNCENWAV"
|
|
40
|
+
slit_width = "CNSLITW"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""CryoNIRSP FITS access for L0 data."""
|
|
2
|
+
|
|
2
3
|
import numpy as np
|
|
3
4
|
from astropy.io import fits
|
|
4
5
|
from dkist_processing_common.parsers.l0_fits_access import L0FitsAccess
|
|
5
6
|
|
|
6
7
|
from dkist_processing_cryonirsp.models.exposure_conditions import CRYO_EXP_TIME_ROUND_DIGITS
|
|
7
8
|
from dkist_processing_cryonirsp.models.exposure_conditions import ExposureConditions
|
|
9
|
+
from dkist_processing_cryonirsp.models.fits_access import CryonirspMetadataKey
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class CryonirspRampFitsAccess(L0FitsAccess):
|
|
@@ -33,16 +35,15 @@ class CryonirspRampFitsAccess(L0FitsAccess):
|
|
|
33
35
|
):
|
|
34
36
|
super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
|
|
35
37
|
|
|
36
|
-
self.camera_readout_mode = self.header[
|
|
37
|
-
self.curr_frame_in_ramp: int = self.header[
|
|
38
|
-
self.num_frames_in_ramp: int = self.header[
|
|
39
|
-
self.arm_id: str = self.header[
|
|
40
|
-
self.filter_name = self.header[
|
|
41
|
-
self.roi_1_origin_x = self.header[
|
|
42
|
-
self.roi_1_origin_y = self.header[
|
|
43
|
-
self.roi_1_size_x = self.header[
|
|
44
|
-
self.roi_1_size_y = self.header[
|
|
45
|
-
self.obs_ip_start_time = self.header["DKIST011"]
|
|
38
|
+
self.camera_readout_mode = self.header[CryonirspMetadataKey.camera_readout_mode]
|
|
39
|
+
self.curr_frame_in_ramp: int = self.header[CryonirspMetadataKey.curr_frame_in_ramp]
|
|
40
|
+
self.num_frames_in_ramp: int = self.header[CryonirspMetadataKey.num_frames_in_ramp]
|
|
41
|
+
self.arm_id: str = self.header[CryonirspMetadataKey.arm_id]
|
|
42
|
+
self.filter_name = self.header[CryonirspMetadataKey.filter_name].upper()
|
|
43
|
+
self.roi_1_origin_x = self.header[CryonirspMetadataKey.roi_1_origin_x]
|
|
44
|
+
self.roi_1_origin_y = self.header[CryonirspMetadataKey.roi_1_origin_y]
|
|
45
|
+
self.roi_1_size_x = self.header[CryonirspMetadataKey.roi_1_size_x]
|
|
46
|
+
self.roi_1_size_y = self.header[CryonirspMetadataKey.roi_1_size_y]
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
class CryonirspL0FitsAccess(L0FitsAccess):
|
|
@@ -71,47 +72,38 @@ class CryonirspL0FitsAccess(L0FitsAccess):
|
|
|
71
72
|
):
|
|
72
73
|
super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
|
|
73
74
|
|
|
74
|
-
self.arm_id: str = self.header[
|
|
75
|
-
self.number_of_modulator_states: int = self.header[
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
self.
|
|
79
|
-
self.
|
|
80
|
-
self.
|
|
81
|
-
self.
|
|
82
|
-
self.
|
|
83
|
-
self.
|
|
84
|
-
self.
|
|
85
|
-
self.
|
|
86
|
-
self.
|
|
87
|
-
self.
|
|
88
|
-
self.
|
|
89
|
-
self.
|
|
90
|
-
self.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
self.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
98
|
-
self.grating_position_deg: float = self.header["CNGRTPOS"]
|
|
99
|
-
self.grating_littrow_angle_deg: float = self.header["CNGRTLAT"]
|
|
100
|
-
self.grating_constant: float = self.header["CNGRTCON"]
|
|
101
|
-
self.obs_ip_start_time = self.header["DKIST011"]
|
|
75
|
+
self.arm_id: str = self.header[CryonirspMetadataKey.arm_id]
|
|
76
|
+
self.number_of_modulator_states: int = self.header[
|
|
77
|
+
CryonirspMetadataKey.number_of_modulator_states
|
|
78
|
+
]
|
|
79
|
+
self.modulator_state: int = self.header[CryonirspMetadataKey.modulator_state]
|
|
80
|
+
self.scan_step: int = self.header[CryonirspMetadataKey.scan_step]
|
|
81
|
+
self.num_scan_steps: int = self.header[CryonirspMetadataKey.num_scan_steps]
|
|
82
|
+
self.num_cn1_scan_steps: int = self.header[CryonirspMetadataKey.num_cn1_scan_steps]
|
|
83
|
+
self.num_cn2_scan_steps: int = self.header[CryonirspMetadataKey.num_cn2_scan_steps]
|
|
84
|
+
self.cn2_step_size: float = self.header[CryonirspMetadataKey.cn2_step_size]
|
|
85
|
+
self.meas_num: int = self.header[CryonirspMetadataKey.meas_num]
|
|
86
|
+
self.num_meas: int = self.header[CryonirspMetadataKey.num_meas]
|
|
87
|
+
self.sub_repeat_num = self.header[CryonirspMetadataKey.sub_repeat_num]
|
|
88
|
+
self.num_sub_repeats: int = self.header[CryonirspMetadataKey.num_sub_repeats]
|
|
89
|
+
self.modulator_spin_mode: str = self.header[CryonirspMetadataKey.modulator_spin_mode]
|
|
90
|
+
self.axis_1_type: str = self.header[CryonirspMetadataKey.axis_1_type]
|
|
91
|
+
self.axis_2_type: str = self.header[CryonirspMetadataKey.axis_2_type]
|
|
92
|
+
self.axis_3_type: str = self.header[CryonirspMetadataKey.axis_3_type]
|
|
93
|
+
self.grating_position_deg: float = self.header[CryonirspMetadataKey.grating_position_deg]
|
|
94
|
+
self.grating_littrow_angle_deg: float = self.header[
|
|
95
|
+
CryonirspMetadataKey.grating_littrow_angle_deg
|
|
96
|
+
]
|
|
97
|
+
self.grating_constant: float = self.header[CryonirspMetadataKey.grating_constant]
|
|
98
|
+
self.filter_name = self.header[CryonirspMetadataKey.filter_name.value].upper()
|
|
102
99
|
# The ExposureConditions are a combination of the exposure time and the OD filter name:
|
|
103
100
|
self.exposure_conditions = ExposureConditions(
|
|
104
|
-
round(self.fpa_exposure_time_ms, CRYO_EXP_TIME_ROUND_DIGITS),
|
|
105
|
-
self.header["CNFILTNP"].upper(),
|
|
101
|
+
round(self.fpa_exposure_time_ms, CRYO_EXP_TIME_ROUND_DIGITS), self.filter_name
|
|
106
102
|
)
|
|
107
|
-
self.
|
|
108
|
-
self.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@property
|
|
112
|
-
def cn1_scan_step(self):
|
|
113
|
-
"""Convert the inner loop step number from float to int."""
|
|
114
|
-
return int(self.header["CNP1DCUR"])
|
|
103
|
+
self.center_wavelength = self.header[CryonirspMetadataKey.center_wavelength]
|
|
104
|
+
self.slit_width = self.header[CryonirspMetadataKey.slit_width]
|
|
105
|
+
# Convert the inner loop step number from float to int:
|
|
106
|
+
self.cn1_scan_step = int(self.header[CryonirspMetadataKey.cn1_scan_step])
|
|
115
107
|
|
|
116
108
|
|
|
117
109
|
class CryonirspLinearizedFitsAccess(CryonirspL0FitsAccess):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Buds to parse the combination of exposure time and filter name."""
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
from typing import Hashable
|
|
4
|
+
from typing import NamedTuple
|
|
4
5
|
from typing import Type
|
|
5
6
|
|
|
6
7
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
@@ -28,7 +29,6 @@ class CryonirspTaskExposureConditionsBud(Stem):
|
|
|
28
29
|
|
|
29
30
|
def __init__(self, stem_name: str, ip_task_type: str):
|
|
30
31
|
super().__init__(stem_name=stem_name)
|
|
31
|
-
self.metadata_key = "exposure_conditions"
|
|
32
32
|
self.ip_task_type = ip_task_type
|
|
33
33
|
|
|
34
34
|
def setter(self, fits_obj: CryonirspL0FitsAccess):
|
|
@@ -42,7 +42,7 @@ class CryonirspTaskExposureConditionsBud(Stem):
|
|
|
42
42
|
"""
|
|
43
43
|
ip_task_type = parse_header_ip_task_with_gains(fits_obj)
|
|
44
44
|
if ip_task_type.lower() == self.ip_task_type.lower():
|
|
45
|
-
return
|
|
45
|
+
return fits_obj.exposure_conditions
|
|
46
46
|
return SpilledDirt
|
|
47
47
|
|
|
48
48
|
def getter(self, key: Hashable) -> tuple[ExposureConditions, ...]:
|
|
@@ -56,7 +56,6 @@ class CryonirspConditionalTaskExposureConditionsBudBase(Stem):
|
|
|
56
56
|
|
|
57
57
|
def __init__(self, stem_name: str, task_types_to_ignore: list[str]):
|
|
58
58
|
super().__init__(stem_name=stem_name)
|
|
59
|
-
self.metadata_key = "exposure_conditions"
|
|
60
59
|
self.task_types_to_ignore = task_types_to_ignore
|
|
61
60
|
|
|
62
61
|
def setter(self, fits_obj: CryonirspL0FitsAccess) -> ExposureConditions | Type[SpilledDirt]:
|
|
@@ -73,7 +72,7 @@ class CryonirspConditionalTaskExposureConditionsBudBase(Stem):
|
|
|
73
72
|
"""
|
|
74
73
|
task_type = parse_header_ip_task_with_gains(fits_obj=fits_obj)
|
|
75
74
|
if task_type.upper() not in self.task_types_to_ignore:
|
|
76
|
-
return
|
|
75
|
+
return fits_obj.exposure_conditions
|
|
77
76
|
return SpilledDirt
|
|
78
77
|
|
|
79
78
|
def getter(self, key: Hashable) -> tuple[ExposureConditions, ...]:
|
|
@@ -103,7 +102,6 @@ class CryonirspSPConditionalTaskExposureConditionsBud(
|
|
|
103
102
|
stem_name=CryonirspBudName.sp_non_dark_and_non_polcal_task_exposure_conditions_list.value,
|
|
104
103
|
task_types_to_ignore=[TaskName.dark.value, TaskName.polcal.value],
|
|
105
104
|
)
|
|
106
|
-
self.metadata_key = "exposure_conditions"
|
|
107
105
|
|
|
108
106
|
|
|
109
107
|
class CryonirspCIConditionalTaskExposureConditionsBud(
|
|
@@ -120,19 +118,22 @@ class CryonirspCIConditionalTaskExposureConditionsBud(
|
|
|
120
118
|
TaskName.lamp_gain.value,
|
|
121
119
|
],
|
|
122
120
|
)
|
|
123
|
-
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class DarkTaskTestAndExposureConditionsContainer(NamedTuple):
|
|
124
|
+
"""Named tuple to hold whether the task is dark along with the associated exposure conditions."""
|
|
125
|
+
|
|
126
|
+
is_dark: bool
|
|
127
|
+
exposure_conditions: ExposureConditions
|
|
124
128
|
|
|
125
129
|
|
|
126
130
|
class CryonirspPickyDarkExposureConditionsBudBase(Stem):
|
|
127
131
|
"""Parse exposure conditions tuples to ensure existence of dark frames with the required exposure conditions."""
|
|
128
132
|
|
|
129
|
-
DarkTaskTestAndExposureConditions =
|
|
130
|
-
"DarkTaskTestAndExposureConditions", ["is_dark", "exposure_conditions"]
|
|
131
|
-
)
|
|
133
|
+
DarkTaskTestAndExposureConditions = DarkTaskTestAndExposureConditionsContainer
|
|
132
134
|
|
|
133
135
|
def __init__(self, stem_name: str, task_types_to_ignore: list[str]):
|
|
134
136
|
super().__init__(stem_name=stem_name)
|
|
135
|
-
self.metadata_key = "exposure_conditions"
|
|
136
137
|
self.task_types_to_ignore = task_types_to_ignore
|
|
137
138
|
|
|
138
139
|
def setter(self, fits_obj: CryonirspL0FitsAccess) -> tuple | Type[SpilledDirt]:
|
|
@@ -150,11 +151,11 @@ class CryonirspPickyDarkExposureConditionsBudBase(Stem):
|
|
|
150
151
|
task_type = parse_header_ip_task_with_gains(fits_obj=fits_obj)
|
|
151
152
|
if task_type.upper() == TaskName.dark.value:
|
|
152
153
|
return self.DarkTaskTestAndExposureConditions(
|
|
153
|
-
is_dark=True, exposure_conditions=
|
|
154
|
+
is_dark=True, exposure_conditions=fits_obj.exposure_conditions
|
|
154
155
|
)
|
|
155
156
|
if task_type.upper() not in self.task_types_to_ignore:
|
|
156
157
|
return self.DarkTaskTestAndExposureConditions(
|
|
157
|
-
is_dark=False, exposure_conditions=
|
|
158
|
+
is_dark=False, exposure_conditions=fits_obj.exposure_conditions
|
|
158
159
|
)
|
|
159
160
|
# Ignored task types fall through
|
|
160
161
|
return SpilledDirt
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Stems for organizing files into separate dsps repeats."""
|
|
2
|
+
|
|
2
3
|
from dkist_processing_cryonirsp.models.constants import CryonirspBudName
|
|
3
4
|
from dkist_processing_cryonirsp.models.tags import CryonirspStemName
|
|
4
5
|
from dkist_processing_cryonirsp.parsers.scan_step import MapScanStepStemBase
|
|
@@ -1,45 +1,58 @@
|
|
|
1
1
|
"""Copies of UniqueBud and SingleValueSingleKeyFlower from common that only activate if the frames are "observe" task."""
|
|
2
|
+
|
|
3
|
+
from typing import Hashable
|
|
2
4
|
from typing import Type
|
|
3
5
|
|
|
4
6
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
5
7
|
from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
6
8
|
SingleValueSingleKeyFlower,
|
|
7
9
|
)
|
|
8
|
-
from dkist_processing_common.parsers.unique_bud import UniqueBud
|
|
9
10
|
|
|
10
11
|
from dkist_processing_cryonirsp.models.constants import CryonirspBudName
|
|
12
|
+
from dkist_processing_cryonirsp.models.fits_access import CryonirspMetadataKey
|
|
11
13
|
from dkist_processing_cryonirsp.models.tags import CryonirspStemName
|
|
12
14
|
from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import CryonirspL0FitsAccess
|
|
15
|
+
from dkist_processing_cryonirsp.parsers.scan_step import NumberOfScanStepsBase
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
class NumberOfMeasurementsBud(
|
|
18
|
+
class NumberOfMeasurementsBud(NumberOfScanStepsBase):
|
|
16
19
|
"""Bud for finding the total number of measurements per scan step."""
|
|
17
20
|
|
|
18
21
|
def __init__(self):
|
|
19
|
-
|
|
20
|
-
super().__init__(
|
|
21
|
-
constant_name=CryonirspBudName.num_meas.value, metadata_key=self.metadata_key
|
|
22
|
-
)
|
|
22
|
+
super().__init__(stem_name=CryonirspBudName.num_meas.value)
|
|
23
23
|
|
|
24
|
-
def
|
|
24
|
+
def getter(self, key: Hashable) -> Hashable:
|
|
25
25
|
"""
|
|
26
|
-
|
|
26
|
+
Search all scan steps to find the maximum number of measurements in a step.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
This maximum number should be the same for all measurements, with the possible exception of the last one in
|
|
29
|
+
an abort.
|
|
30
|
+
|
|
31
|
+
Abort possibilities:
|
|
32
|
+
* if a measurement is missing in the last scan step of a multi-scan, multi-map observation, it will be handled
|
|
33
|
+
as an incomplete scan step and truncated by the scan step abort handler.
|
|
34
|
+
* if a measurement is missing in a single-map, single-scan observation then the number of measurements will be
|
|
35
|
+
given by the last measurement value that had as many frames as the maximum number of frames of any
|
|
36
|
+
measurement.
|
|
37
|
+
This is a formality for intensity mode observations but is an important check for polarimetric data as the
|
|
38
|
+
abort may have happened in the middle of the modulator state sequence.
|
|
32
39
|
"""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
measurements_in_scan_steps = []
|
|
41
|
+
for meas_dict in self.scan_step_dict.values():
|
|
42
|
+
measurements_in_scan_steps.append(len(meas_dict))
|
|
43
|
+
return max(
|
|
44
|
+
measurements_in_scan_steps
|
|
45
|
+
) # if there are incomplete measurements, they should be at the end and will be truncated by an incomplete scan step
|
|
36
46
|
|
|
37
47
|
|
|
38
48
|
class MeasurementNumberFlower(SingleValueSingleKeyFlower):
|
|
39
49
|
"""Flower for a measurement number."""
|
|
40
50
|
|
|
41
51
|
def __init__(self):
|
|
42
|
-
super().__init__(
|
|
52
|
+
super().__init__(
|
|
53
|
+
tag_stem_name=CryonirspStemName.meas_num.value,
|
|
54
|
+
metadata_key=CryonirspMetadataKey.meas_num,
|
|
55
|
+
)
|
|
43
56
|
|
|
44
57
|
def setter(self, fits_obj: CryonirspL0FitsAccess) -> Type[SpilledDirt] | int:
|
|
45
58
|
"""
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Copy of SingleValueSingleKeyFlower from common that only activates if the frames are "observe" task."""
|
|
2
|
+
|
|
2
3
|
from typing import Type
|
|
3
4
|
|
|
4
5
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
@@ -7,6 +8,7 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
|
7
8
|
SingleValueSingleKeyFlower,
|
|
8
9
|
)
|
|
9
10
|
|
|
11
|
+
from dkist_processing_cryonirsp.models.fits_access import CryonirspMetadataKey
|
|
10
12
|
from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import CryonirspL0FitsAccess
|
|
11
13
|
|
|
12
14
|
|
|
@@ -14,7 +16,9 @@ class ModstateNumberFlower(SingleValueSingleKeyFlower):
|
|
|
14
16
|
"""Flower for a modstate number."""
|
|
15
17
|
|
|
16
18
|
def __init__(self):
|
|
17
|
-
super().__init__(
|
|
19
|
+
super().__init__(
|
|
20
|
+
tag_stem_name=StemName.modstate.value, metadata_key=CryonirspMetadataKey.modulator_state
|
|
21
|
+
)
|
|
18
22
|
|
|
19
23
|
def setter(self, fits_obj: CryonirspL0FitsAccess) -> Type[SpilledDirt] | int:
|
|
20
24
|
"""
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Copy of UniqueBud from common that only activates if the frames are polarimetric "observe" or "polcal" task, or non-polarimetric "observe" task."""
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from typing import NamedTuple
|
|
3
5
|
from typing import Type
|
|
4
6
|
|
|
5
7
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
@@ -7,21 +9,30 @@ from dkist_processing_common.models.flower_pot import Stem
|
|
|
7
9
|
from dkist_processing_common.models.flower_pot import Thorn
|
|
8
10
|
from dkist_processing_common.models.task_name import TaskName
|
|
9
11
|
|
|
10
|
-
from dkist_processing_cryonirsp.models.
|
|
12
|
+
from dkist_processing_cryonirsp.models.fits_access import CryonirspMetadataKey
|
|
11
13
|
from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import CryonirspL0FitsAccess
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
class PolarimetricValueDataContainer(NamedTuple):
|
|
17
|
+
"""Named tuple to hold polarimetric metadata about a task."""
|
|
18
|
+
|
|
19
|
+
task: str
|
|
20
|
+
num_modstates: int
|
|
21
|
+
spin_mode: str
|
|
22
|
+
bud_value: str | int | float | bool
|
|
23
|
+
|
|
24
|
+
|
|
14
25
|
class PolarimetricCheckingUniqueBud(Stem):
|
|
15
26
|
"""Bud for checking if frames are polarimetric."""
|
|
16
27
|
|
|
17
|
-
PolarimetricValueData =
|
|
18
|
-
"PolarimetricValueData", ["task", "num_modstates", "spin_mode", "bud_value"]
|
|
19
|
-
)
|
|
28
|
+
PolarimetricValueData = PolarimetricValueDataContainer
|
|
20
29
|
observe_task_name = TaskName.observe.value.casefold()
|
|
21
30
|
polcal_task_name = TaskName.polcal.value.casefold()
|
|
22
31
|
|
|
23
|
-
def __init__(self, constant_name: str, metadata_key: str):
|
|
32
|
+
def __init__(self, constant_name: str, metadata_key: str | StrEnum):
|
|
24
33
|
super().__init__(stem_name=constant_name)
|
|
34
|
+
if isinstance(metadata_key, StrEnum):
|
|
35
|
+
metadata_key = metadata_key.name
|
|
25
36
|
self.metadata_key = metadata_key
|
|
26
37
|
|
|
27
38
|
@property
|
|
@@ -71,7 +82,7 @@ class PolarimetricCheckingUniqueBud(Stem):
|
|
|
71
82
|
# Some intensity mode data has the number of modulator states set to 0
|
|
72
83
|
num_modstates = fits_obj.number_of_modulator_states or 1
|
|
73
84
|
|
|
74
|
-
if self.metadata_key ==
|
|
85
|
+
if self.metadata_key == CryonirspMetadataKey.number_of_modulator_states.name:
|
|
75
86
|
bud_value = num_modstates
|
|
76
87
|
else:
|
|
77
88
|
bud_value = getattr(fits_obj, self.metadata_key)
|
|
@@ -3,6 +3,7 @@ Machinery for sorting files based on scan step.
|
|
|
3
3
|
|
|
4
4
|
Also includes base classes that can be used to sort files based on map scan.
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
######################
|
|
7
8
|
# HOW THIS ALL WORKS #
|
|
8
9
|
######################
|
|
@@ -312,11 +313,11 @@ class MapScanStepStemBase(Stem, ABC):
|
|
|
312
313
|
return step_list.index(scan_step_obj) + 1 # Here we decide that map scan indices start at 1
|
|
313
314
|
|
|
314
315
|
|
|
315
|
-
class
|
|
316
|
-
"""
|
|
316
|
+
class NumberOfScanStepsBase(MapScanStepStemBase, ABC):
|
|
317
|
+
"""Base class for managing scan steps."""
|
|
317
318
|
|
|
318
|
-
def __init__(self):
|
|
319
|
-
super().__init__(stem_name=
|
|
319
|
+
def __init__(self, stem_name: CryonirspBudName):
|
|
320
|
+
super().__init__(stem_name=stem_name)
|
|
320
321
|
|
|
321
322
|
@cached_property
|
|
322
323
|
def map_scan_to_obj_dict(self) -> dict[int, list[SingleScanStep]]:
|
|
@@ -361,6 +362,13 @@ class NumberOfScanStepsBud(MapScanStepStemBase):
|
|
|
361
362
|
|
|
362
363
|
return len(completed_steps)
|
|
363
364
|
|
|
365
|
+
|
|
366
|
+
class NumberOfScanStepsBud(NumberOfScanStepsBase):
|
|
367
|
+
"""Bud for finding the total number of scan steps."""
|
|
368
|
+
|
|
369
|
+
def __init__(self):
|
|
370
|
+
super().__init__(stem_name=CryonirspBudName.num_scan_steps.value)
|
|
371
|
+
|
|
364
372
|
def getter(self, key):
|
|
365
373
|
"""
|
|
366
374
|
Compute the number of complete scan steps.
|