dkist-processing-cryonirsp 1.3.4__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.
Potentially problematic release.
This version of dkist-processing-cryonirsp might be problematic. Click here for more details.
- changelog/.gitempty +0 -0
- dkist_processing_cryonirsp/__init__.py +11 -0
- dkist_processing_cryonirsp/config.py +12 -0
- dkist_processing_cryonirsp/models/__init__.py +1 -0
- dkist_processing_cryonirsp/models/constants.py +248 -0
- dkist_processing_cryonirsp/models/exposure_conditions.py +26 -0
- dkist_processing_cryonirsp/models/parameters.py +296 -0
- dkist_processing_cryonirsp/models/tags.py +168 -0
- dkist_processing_cryonirsp/models/task_name.py +14 -0
- dkist_processing_cryonirsp/parsers/__init__.py +1 -0
- dkist_processing_cryonirsp/parsers/cryonirsp_l0_fits_access.py +111 -0
- dkist_processing_cryonirsp/parsers/cryonirsp_l1_fits_access.py +30 -0
- dkist_processing_cryonirsp/parsers/exposure_conditions.py +163 -0
- dkist_processing_cryonirsp/parsers/map_repeats.py +40 -0
- dkist_processing_cryonirsp/parsers/measurements.py +55 -0
- dkist_processing_cryonirsp/parsers/modstates.py +31 -0
- dkist_processing_cryonirsp/parsers/optical_density_filters.py +40 -0
- dkist_processing_cryonirsp/parsers/polarimetric_check.py +120 -0
- dkist_processing_cryonirsp/parsers/scan_step.py +412 -0
- dkist_processing_cryonirsp/parsers/time.py +80 -0
- dkist_processing_cryonirsp/parsers/wavelength.py +26 -0
- dkist_processing_cryonirsp/tasks/__init__.py +19 -0
- dkist_processing_cryonirsp/tasks/assemble_movie.py +202 -0
- dkist_processing_cryonirsp/tasks/bad_pixel_map.py +96 -0
- dkist_processing_cryonirsp/tasks/beam_boundaries_base.py +279 -0
- dkist_processing_cryonirsp/tasks/ci_beam_boundaries.py +55 -0
- dkist_processing_cryonirsp/tasks/ci_science.py +169 -0
- dkist_processing_cryonirsp/tasks/cryonirsp_base.py +67 -0
- dkist_processing_cryonirsp/tasks/dark.py +98 -0
- dkist_processing_cryonirsp/tasks/gain.py +251 -0
- dkist_processing_cryonirsp/tasks/instrument_polarization.py +447 -0
- dkist_processing_cryonirsp/tasks/l1_output_data.py +44 -0
- dkist_processing_cryonirsp/tasks/linearity_correction.py +582 -0
- dkist_processing_cryonirsp/tasks/make_movie_frames.py +302 -0
- dkist_processing_cryonirsp/tasks/mixin/__init__.py +1 -0
- dkist_processing_cryonirsp/tasks/mixin/beam_access.py +52 -0
- dkist_processing_cryonirsp/tasks/mixin/corrections.py +177 -0
- dkist_processing_cryonirsp/tasks/mixin/intermediate_frame.py +193 -0
- dkist_processing_cryonirsp/tasks/mixin/linearized_frame.py +309 -0
- dkist_processing_cryonirsp/tasks/mixin/shift_measurements.py +297 -0
- dkist_processing_cryonirsp/tasks/parse.py +281 -0
- dkist_processing_cryonirsp/tasks/quality_metrics.py +271 -0
- dkist_processing_cryonirsp/tasks/science_base.py +511 -0
- dkist_processing_cryonirsp/tasks/sp_beam_boundaries.py +270 -0
- dkist_processing_cryonirsp/tasks/sp_dispersion_axis_correction.py +484 -0
- dkist_processing_cryonirsp/tasks/sp_geometric.py +585 -0
- dkist_processing_cryonirsp/tasks/sp_science.py +299 -0
- dkist_processing_cryonirsp/tasks/sp_solar_gain.py +475 -0
- dkist_processing_cryonirsp/tasks/trial_output_data.py +61 -0
- dkist_processing_cryonirsp/tasks/write_l1.py +1033 -0
- dkist_processing_cryonirsp/tests/__init__.py +1 -0
- dkist_processing_cryonirsp/tests/conftest.py +456 -0
- dkist_processing_cryonirsp/tests/header_models.py +592 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/__init__.py +0 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/l0_cals_only.py +541 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/l0_to_l1.py +615 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/linearize_only.py +96 -0
- dkist_processing_cryonirsp/tests/local_trial_workflows/local_trial_helpers.py +592 -0
- dkist_processing_cryonirsp/tests/test_assemble_movie.py +144 -0
- dkist_processing_cryonirsp/tests/test_assemble_qualilty.py +517 -0
- dkist_processing_cryonirsp/tests/test_bad_pixel_maps.py +115 -0
- dkist_processing_cryonirsp/tests/test_ci_beam_boundaries.py +106 -0
- dkist_processing_cryonirsp/tests/test_ci_science.py +355 -0
- dkist_processing_cryonirsp/tests/test_corrections.py +126 -0
- dkist_processing_cryonirsp/tests/test_cryo_base.py +202 -0
- dkist_processing_cryonirsp/tests/test_cryo_constants.py +76 -0
- dkist_processing_cryonirsp/tests/test_dark.py +287 -0
- dkist_processing_cryonirsp/tests/test_gain.py +278 -0
- dkist_processing_cryonirsp/tests/test_instrument_polarization.py +531 -0
- dkist_processing_cryonirsp/tests/test_linearity_correction.py +245 -0
- dkist_processing_cryonirsp/tests/test_make_movie_frames.py +111 -0
- dkist_processing_cryonirsp/tests/test_parameters.py +266 -0
- dkist_processing_cryonirsp/tests/test_parse.py +1439 -0
- dkist_processing_cryonirsp/tests/test_quality.py +203 -0
- dkist_processing_cryonirsp/tests/test_sp_beam_boundaries.py +112 -0
- dkist_processing_cryonirsp/tests/test_sp_dispersion_axis_correction.py +155 -0
- dkist_processing_cryonirsp/tests/test_sp_geometric.py +319 -0
- dkist_processing_cryonirsp/tests/test_sp_make_movie_frames.py +121 -0
- dkist_processing_cryonirsp/tests/test_sp_science.py +483 -0
- dkist_processing_cryonirsp/tests/test_sp_solar.py +198 -0
- dkist_processing_cryonirsp/tests/test_trial_create_quality_report.py +79 -0
- dkist_processing_cryonirsp/tests/test_trial_output_data.py +251 -0
- dkist_processing_cryonirsp/tests/test_workflows.py +9 -0
- dkist_processing_cryonirsp/tests/test_write_l1.py +436 -0
- dkist_processing_cryonirsp/workflows/__init__.py +2 -0
- dkist_processing_cryonirsp/workflows/ci_l0_processing.py +77 -0
- dkist_processing_cryonirsp/workflows/sp_l0_processing.py +84 -0
- dkist_processing_cryonirsp/workflows/trial_workflows.py +190 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/METADATA +194 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/RECORD +111 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/WHEEL +5 -0
- dkist_processing_cryonirsp-1.3.4.dist-info/top_level.txt +4 -0
- docs/Makefile +134 -0
- docs/bad_pixel_calibration.rst +47 -0
- docs/beam_angle_calculation.rst +53 -0
- docs/beam_boundary_computation.rst +88 -0
- docs/changelog.rst +7 -0
- docs/ci_science_calibration.rst +33 -0
- docs/conf.py +52 -0
- docs/index.rst +21 -0
- docs/l0_to_l1_cryonirsp_ci-full-trial.rst +10 -0
- docs/l0_to_l1_cryonirsp_ci.rst +10 -0
- docs/l0_to_l1_cryonirsp_sp-full-trial.rst +10 -0
- docs/l0_to_l1_cryonirsp_sp.rst +10 -0
- docs/linearization.rst +43 -0
- docs/make.bat +170 -0
- docs/requirements.txt +1 -0
- docs/requirements_table.rst +8 -0
- docs/scientific_changelog.rst +10 -0
- docs/sp_science_calibration.rst +59 -0
- licenses/LICENSE.rst +11 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Mixin to support array shift calculations."""
|
|
2
|
+
import math
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import skimage.registration as skir
|
|
7
|
+
from dkist_service_configuration.logging import logger
|
|
8
|
+
|
|
9
|
+
SPATIAL = "SPATIAL"
|
|
10
|
+
SPECTRAL = "SPECTRAL"
|
|
11
|
+
ALLOWABLE_AXES = SPATIAL, SPECTRAL
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class AxisParams:
|
|
16
|
+
"""
|
|
17
|
+
Dataclass to support shift computations along an axis.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
axis
|
|
22
|
+
The axis along which a shift is to be computed
|
|
23
|
+
long_axis
|
|
24
|
+
The axis number that represents the long axis of the strip
|
|
25
|
+
axis_0_size
|
|
26
|
+
The size of the numpy 0 axis
|
|
27
|
+
axis_1_size:
|
|
28
|
+
The size of the numpy 1 axis
|
|
29
|
+
axis_0_offset
|
|
30
|
+
The offset from the center of the 0 axis about which to align the center of the strip
|
|
31
|
+
axis_1_offset
|
|
32
|
+
The offset from the center of the 1 axis about which to align the center of the strip
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
axis: str
|
|
36
|
+
long_axis: int
|
|
37
|
+
axis_0_size: int
|
|
38
|
+
axis_1_size: int
|
|
39
|
+
axis_0_offset: int = 0
|
|
40
|
+
axis_1_offset: int = 0
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ShiftMeasurementsMixin:
|
|
44
|
+
"""Methods to support array shift computations along a specified axis."""
|
|
45
|
+
|
|
46
|
+
def shift_measurements_compute_shift_along_axis(
|
|
47
|
+
self,
|
|
48
|
+
axis: str,
|
|
49
|
+
array_1: np.ndarray,
|
|
50
|
+
array_2: np.ndarray,
|
|
51
|
+
array_1_offset: tuple[int, int] = (0, 0),
|
|
52
|
+
array_2_offset: tuple[int, int] = (0, 0),
|
|
53
|
+
# Use no upsampling unless specified
|
|
54
|
+
upsample_factor: int = 1,
|
|
55
|
+
) -> float:
|
|
56
|
+
"""
|
|
57
|
+
Compute the relative shift between two images along a specified axis.
|
|
58
|
+
|
|
59
|
+
This method computes the relative shift of two images along an axis by extracting
|
|
60
|
+
two long, narrow strips, one from each image, computing the median along the narrow
|
|
61
|
+
axis, and then correlating the two signals to find the relative shift between them.
|
|
62
|
+
|
|
63
|
+
The long axis of the strip is the axis along which the shift measurement is to be made.
|
|
64
|
+
The narrow axis is the opposite axis (orthogonal axis) from the measurement axis.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
axis
|
|
69
|
+
The axis along which to measure the shift, SPATIAL or SPECTRAL
|
|
70
|
+
array_1
|
|
71
|
+
The reference, or non-moving array
|
|
72
|
+
array_2
|
|
73
|
+
The array whose shift is to be measured, or the moving array
|
|
74
|
+
array_1_offset
|
|
75
|
+
The offsets to be applied to each axis of array_1 when defining the strip slice
|
|
76
|
+
array_2_offset
|
|
77
|
+
The offsets to be applied to each axis of array_2 when defining the strip slice
|
|
78
|
+
upsample_factor
|
|
79
|
+
The upsample factor to be used when computing the correlation of the two signals.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
shift
|
|
84
|
+
The measured displacement of array_2 relative to array_1
|
|
85
|
+
"""
|
|
86
|
+
# "axis" is the axis along which we compute the gradient
|
|
87
|
+
# SPECTRAL means along the spectral axis, axis 1 in numpy, horizontal axis
|
|
88
|
+
# SPATIAL means along the spatial axis, axis 0 in numpy, vertical axis
|
|
89
|
+
|
|
90
|
+
array_1_axis_params = self.shift_measurements_get_axis_params(
|
|
91
|
+
axis, array_1.shape, array_1_offset
|
|
92
|
+
)
|
|
93
|
+
array_2_axis_params = self.shift_measurements_get_axis_params(
|
|
94
|
+
axis, array_2.shape, array_2_offset
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
array_1_axis_0_slice, array_1_axis_1_slice = self.shift_measurements_get_strip_slices(
|
|
98
|
+
array_1_axis_params, array_num=1
|
|
99
|
+
)
|
|
100
|
+
array_2_axis_0_slice, array_2_axis_1_slice = self.shift_measurements_get_strip_slices(
|
|
101
|
+
array_2_axis_params, array_num=2
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Make sure long axis slices start and end at same point.
|
|
105
|
+
long_axis = array_1_axis_params.long_axis
|
|
106
|
+
if long_axis == 0:
|
|
107
|
+
new_min = max(array_1_axis_0_slice.start, array_1_axis_0_slice.start)
|
|
108
|
+
new_max = min(array_1_axis_0_slice.stop, array_1_axis_0_slice.stop)
|
|
109
|
+
long_axis_slice = slice(new_min, new_max)
|
|
110
|
+
array_1_strip = array_1[long_axis_slice, array_1_axis_1_slice]
|
|
111
|
+
array_2_strip = array_2[long_axis_slice, array_2_axis_1_slice]
|
|
112
|
+
else:
|
|
113
|
+
new_min = max(array_1_axis_1_slice.start, array_1_axis_1_slice.start)
|
|
114
|
+
new_max = min(array_1_axis_1_slice.stop, array_1_axis_1_slice.stop)
|
|
115
|
+
long_axis_slice = slice(new_min, new_max)
|
|
116
|
+
array_1_strip = array_1[array_1_axis_0_slice, long_axis_slice]
|
|
117
|
+
array_2_strip = array_2[array_2_axis_0_slice, long_axis_slice]
|
|
118
|
+
|
|
119
|
+
array_1_gradient = self.shift_measurements_compute_gradient(
|
|
120
|
+
array_1_strip, axis=array_1_axis_params.long_axis
|
|
121
|
+
)
|
|
122
|
+
array_2_gradient = self.shift_measurements_compute_gradient(
|
|
123
|
+
array_2_strip, axis=array_2_axis_params.long_axis
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Correlate the gradient signals to get the shift of array 2 relative to array 1
|
|
127
|
+
shift_array, error, phasediff = skir.phase_cross_correlation(
|
|
128
|
+
array_1_gradient,
|
|
129
|
+
array_2_gradient,
|
|
130
|
+
upsample_factor=upsample_factor,
|
|
131
|
+
)
|
|
132
|
+
# Flip the sign to convert from correction to measurement.
|
|
133
|
+
shift = -shift_array[0]
|
|
134
|
+
logger.info(f"Measured shift of array 2 relative to array 1 in {axis} axis = {shift}")
|
|
135
|
+
|
|
136
|
+
return shift
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def shift_measurements_get_axis_params(
|
|
140
|
+
axis: str, array_shape: tuple[int, ...], offset: tuple[int, ...]
|
|
141
|
+
) -> AxisParams:
|
|
142
|
+
"""
|
|
143
|
+
Populate an AxisParams dataclass with information about this axis.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
axis
|
|
148
|
+
The axis along which the measurement is to be made, SPATIAL or SPECTRAL
|
|
149
|
+
array_shape
|
|
150
|
+
The numpy shape tuple for the array
|
|
151
|
+
offset
|
|
152
|
+
The offset along each axis where the extracted strip is to be centered
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
AxisParams
|
|
157
|
+
An AxisParams object, populated for the requested array and axis.
|
|
158
|
+
"""
|
|
159
|
+
long_axis = None
|
|
160
|
+
if axis in ALLOWABLE_AXES:
|
|
161
|
+
if axis == SPECTRAL:
|
|
162
|
+
long_axis = 1
|
|
163
|
+
elif axis == SPATIAL:
|
|
164
|
+
long_axis = 0
|
|
165
|
+
return AxisParams(
|
|
166
|
+
axis=axis,
|
|
167
|
+
long_axis=long_axis,
|
|
168
|
+
axis_0_size=array_shape[0],
|
|
169
|
+
axis_1_size=array_shape[1],
|
|
170
|
+
axis_0_offset=offset[0],
|
|
171
|
+
axis_1_offset=offset[1],
|
|
172
|
+
)
|
|
173
|
+
raise ValueError(f"Unknown value for {axis = }, allowable values are {ALLOWABLE_AXES}")
|
|
174
|
+
|
|
175
|
+
def shift_measurements_get_strip_slices(
|
|
176
|
+
self, axis_params: AxisParams, array_num: int
|
|
177
|
+
) -> tuple[slice, slice]:
|
|
178
|
+
"""
|
|
179
|
+
Return the axis slices for the desired strip based on the AxisParams object.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
axis_params
|
|
184
|
+
The AxisParams object defining the desired strip
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
slice, slice
|
|
189
|
+
A tuple of slice objects for extracting the desired srtip from the array
|
|
190
|
+
"""
|
|
191
|
+
long_axis_fraction = self.parameters.geo_strip_long_axis_size_fraction
|
|
192
|
+
short_axis_fraction = self.parameters.geo_strip_short_axis_size_fraction
|
|
193
|
+
|
|
194
|
+
if axis_params.axis == SPECTRAL:
|
|
195
|
+
axis_0_fraction = short_axis_fraction
|
|
196
|
+
axis_1_fraction = long_axis_fraction
|
|
197
|
+
else:
|
|
198
|
+
axis_0_fraction = long_axis_fraction
|
|
199
|
+
axis_1_fraction = short_axis_fraction
|
|
200
|
+
|
|
201
|
+
# Compute the strip sizes
|
|
202
|
+
axis_0_strip_size = math.ceil(axis_0_fraction * axis_params.axis_0_size)
|
|
203
|
+
if not axis_0_strip_size % 2 == 0:
|
|
204
|
+
axis_0_strip_size += 1
|
|
205
|
+
axis_1_strip_size = math.ceil(axis_1_fraction * axis_params.axis_1_size)
|
|
206
|
+
if not axis_1_strip_size % 2 == 0:
|
|
207
|
+
axis_1_strip_size += 1
|
|
208
|
+
|
|
209
|
+
# compute the slices from the strip sizes
|
|
210
|
+
axis_0_slice_idx = (
|
|
211
|
+
np.array([-axis_0_strip_size, axis_0_strip_size]) + axis_params.axis_0_size
|
|
212
|
+
) // 2 + axis_params.axis_0_offset
|
|
213
|
+
axis_1_slice_idx = (
|
|
214
|
+
np.array([-axis_1_strip_size, axis_1_strip_size]) + axis_params.axis_1_size
|
|
215
|
+
) // 2 + axis_params.axis_1_offset
|
|
216
|
+
|
|
217
|
+
axis_0_slice = slice(*axis_0_slice_idx)
|
|
218
|
+
axis_1_slice = slice(*axis_1_slice_idx)
|
|
219
|
+
|
|
220
|
+
logger.info(
|
|
221
|
+
f"Slice results: array {array_num}, axis: {axis_params.axis}, axis 0: {axis_0_slice}, axis 1: {axis_1_slice}"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return axis_0_slice, axis_1_slice
|
|
225
|
+
|
|
226
|
+
def shift_measurements_compute_gradient(self, strip: np.ndarray, axis: int) -> np.ndarray:
|
|
227
|
+
"""
|
|
228
|
+
Compute the gradient of the strip.
|
|
229
|
+
|
|
230
|
+
This method computes a normalized difference array (essentially a gradient) along the
|
|
231
|
+
long axis of the strip. The difference array is computed by subtracting a shifted version
|
|
232
|
+
of the strip along a desired axis. The shift is accomplished using the np.roll() method.
|
|
233
|
+
The difference is normalized by dividing by the sum of the strip and the shifted strip.
|
|
234
|
+
|
|
235
|
+
The gradient computation looks like this::
|
|
236
|
+
|
|
237
|
+
numerator = np.roll(strip, roll_amount, axis=axis) - np.roll(strip, -roll_amount, axis=axis)
|
|
238
|
+
denominator = np.roll(strip, roll_amount, axis=axis) + np.roll(strip, -roll_amount, axis=axis)
|
|
239
|
+
gradient = numerator / denominator
|
|
240
|
+
|
|
241
|
+
Finally, the ends are trimmed to remove edge effects from the partial overlap.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
strip
|
|
246
|
+
The array strip to be processed
|
|
247
|
+
axis
|
|
248
|
+
The numpy axis along which the difference is to be computed.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
The normalized difference array
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
roll_amount = self.parameters.geo_long_axis_gradient_displacement
|
|
256
|
+
numerator = np.roll(strip, roll_amount, axis=axis) - np.roll(strip, -roll_amount, axis=axis)
|
|
257
|
+
denominator = np.roll(strip, roll_amount, axis=axis) + np.roll(
|
|
258
|
+
strip, -roll_amount, axis=axis
|
|
259
|
+
)
|
|
260
|
+
gradient = numerator / denominator
|
|
261
|
+
# Take the median along the opposite axis from which we compute the gradient.
|
|
262
|
+
gradient_median = np.nanmedian(gradient, axis=self.shift_measurements_opposite_axis(axis))
|
|
263
|
+
# Trim the ends by twice the roll amount to remove edge effects resulting from the edge wrap.
|
|
264
|
+
trim_amount = 2 * roll_amount
|
|
265
|
+
trimmed_gradient = gradient_median[trim_amount:-trim_amount]
|
|
266
|
+
return trimmed_gradient
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def shift_measurements_opposite_axis(current_axis: int) -> int:
|
|
270
|
+
"""
|
|
271
|
+
Return the opposite axis relative to the one in use.
|
|
272
|
+
|
|
273
|
+
We assume a 2D coordinate system in numpy, with axes 0 and 1
|
|
274
|
+
This method returns the "other" or "opposite" axis from the one being
|
|
275
|
+
used, which is the current_axis.
|
|
276
|
+
|
|
277
|
+
Truth table::
|
|
278
|
+
|
|
279
|
+
current axis opposite axis
|
|
280
|
+
0 1
|
|
281
|
+
1 0
|
|
282
|
+
|
|
283
|
+
The bitwise exclusive or (XOR) operator '^' is used to flip the axis::
|
|
284
|
+
|
|
285
|
+
0 ^ 1 = 1
|
|
286
|
+
1 ^ 1 = 0
|
|
287
|
+
opposite_axis = current_axis ^ 1
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
current_axis
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
int representing the opposite axis
|
|
296
|
+
"""
|
|
297
|
+
return current_axis ^ 1
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Parse CryoNIRSP data."""
|
|
2
|
+
from typing import TypeVar
|
|
3
|
+
|
|
4
|
+
from dkist_processing_common.models.flower_pot import Stem
|
|
5
|
+
from dkist_processing_common.models.tags import Tag
|
|
6
|
+
from dkist_processing_common.models.task_name import TaskName
|
|
7
|
+
from dkist_processing_common.parsers.cs_step import CSStepFlower
|
|
8
|
+
from dkist_processing_common.parsers.cs_step import NumCSStepBud
|
|
9
|
+
from dkist_processing_common.parsers.near_bud import TaskNearFloatBud
|
|
10
|
+
from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
11
|
+
SingleValueSingleKeyFlower,
|
|
12
|
+
)
|
|
13
|
+
from dkist_processing_common.parsers.task import parse_header_ip_task_with_gains
|
|
14
|
+
from dkist_processing_common.parsers.task import PolcalTaskFlower
|
|
15
|
+
from dkist_processing_common.parsers.task import TaskTypeFlower
|
|
16
|
+
from dkist_processing_common.parsers.time import ObsIpStartTimeBud
|
|
17
|
+
from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
|
|
18
|
+
from dkist_processing_common.parsers.unique_bud import UniqueBud
|
|
19
|
+
from dkist_processing_common.tasks import default_constant_bud_factory
|
|
20
|
+
from dkist_processing_common.tasks import default_tag_flower_factory
|
|
21
|
+
from dkist_processing_common.tasks import ParseDataBase
|
|
22
|
+
from dkist_processing_common.tasks.mixin.input_dataset import InputDatasetMixin
|
|
23
|
+
|
|
24
|
+
from dkist_processing_cryonirsp.models.constants import CryonirspBudName
|
|
25
|
+
from dkist_processing_cryonirsp.models.parameters import CryonirspParsingParameters
|
|
26
|
+
from dkist_processing_cryonirsp.models.tags import CryonirspStemName
|
|
27
|
+
from dkist_processing_cryonirsp.models.tags import CryonirspTag
|
|
28
|
+
from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import CryonirspL0FitsAccess
|
|
29
|
+
from dkist_processing_cryonirsp.parsers.cryonirsp_l0_fits_access import CryonirspRampFitsAccess
|
|
30
|
+
from dkist_processing_cryonirsp.parsers.exposure_conditions import (
|
|
31
|
+
CryonirspNonDarkAndNonPolcalTaskExposureConditionsBud,
|
|
32
|
+
)
|
|
33
|
+
from dkist_processing_cryonirsp.parsers.exposure_conditions import (
|
|
34
|
+
CryonirspPickyDarkExposureConditionsBud,
|
|
35
|
+
)
|
|
36
|
+
from dkist_processing_cryonirsp.parsers.exposure_conditions import (
|
|
37
|
+
CryonirspTaskExposureConditionsBud,
|
|
38
|
+
)
|
|
39
|
+
from dkist_processing_cryonirsp.parsers.map_repeats import MapScanFlower
|
|
40
|
+
from dkist_processing_cryonirsp.parsers.map_repeats import NumMapScansBud
|
|
41
|
+
from dkist_processing_cryonirsp.parsers.measurements import MeasurementNumberFlower
|
|
42
|
+
from dkist_processing_cryonirsp.parsers.measurements import NumberOfMeasurementsBud
|
|
43
|
+
from dkist_processing_cryonirsp.parsers.modstates import ModstateNumberFlower
|
|
44
|
+
from dkist_processing_cryonirsp.parsers.optical_density_filters import OpticalDensityFiltersPickyBud
|
|
45
|
+
from dkist_processing_cryonirsp.parsers.polarimetric_check import PolarimetricCheckingUniqueBud
|
|
46
|
+
from dkist_processing_cryonirsp.parsers.scan_step import NumberOfScanStepsBud
|
|
47
|
+
from dkist_processing_cryonirsp.parsers.scan_step import ScanStepNumberFlower
|
|
48
|
+
from dkist_processing_cryonirsp.parsers.time import CryonirspSolarGainStartTimeBud
|
|
49
|
+
from dkist_processing_cryonirsp.parsers.time import CryonirspTimeObsBud
|
|
50
|
+
from dkist_processing_cryonirsp.parsers.wavelength import ObserveWavelengthBud
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"ParseL0CryonirspRampData",
|
|
55
|
+
"ParseL0CryonirspLinearizedData",
|
|
56
|
+
"ParseL0CryonirspCILinearizedData",
|
|
57
|
+
"ParseL0CryonirspSPLinearizedData",
|
|
58
|
+
]
|
|
59
|
+
S = TypeVar("S", bound=Stem)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ParseL0CryonirspRampData(ParseDataBase):
|
|
63
|
+
"""
|
|
64
|
+
Parse CryoNIRSP ramp data (raw Cryo data) to prepare for Linearity Correction, after which the rest of the common parsing will occur.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
recipe_run_id : int
|
|
69
|
+
id of the recipe run used to identify the workflow run this task is part of
|
|
70
|
+
workflow_name : str
|
|
71
|
+
name of the workflow to which this instance of the task belongs
|
|
72
|
+
workflow_version : str
|
|
73
|
+
version of the workflow to which this instance of the task belongs
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def fits_parsing_class(self):
|
|
79
|
+
"""FITS access class to be used with this task."""
|
|
80
|
+
return CryonirspRampFitsAccess
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def constant_buds(self) -> list[S]:
|
|
84
|
+
"""Add CryoNIRSP specific constants to common constants."""
|
|
85
|
+
return [
|
|
86
|
+
UniqueBud(
|
|
87
|
+
constant_name=CryonirspBudName.camera_readout_mode.value,
|
|
88
|
+
metadata_key="camera_readout_mode",
|
|
89
|
+
),
|
|
90
|
+
# Time Obs is the unique identifier for each ramp in the data set
|
|
91
|
+
CryonirspTimeObsBud(),
|
|
92
|
+
# This is used to determine which set of linearity correction tables to use.
|
|
93
|
+
UniqueBud(constant_name=CryonirspBudName.arm_id.value, metadata_key="arm_id"),
|
|
94
|
+
# Need wavelength to do filter compensation
|
|
95
|
+
ObserveWavelengthBud(),
|
|
96
|
+
# Need the optical density filter name for early failure detection
|
|
97
|
+
OpticalDensityFiltersPickyBud(),
|
|
98
|
+
# Need IP start time to support parameter access
|
|
99
|
+
ObsIpStartTimeBud(),
|
|
100
|
+
# Get the ROI 1 size and origin
|
|
101
|
+
UniqueBud(
|
|
102
|
+
constant_name=CryonirspBudName.roi_1_origin_x.value, metadata_key="roi_1_origin_x"
|
|
103
|
+
),
|
|
104
|
+
UniqueBud(
|
|
105
|
+
constant_name=CryonirspBudName.roi_1_origin_y.value, metadata_key="roi_1_origin_y"
|
|
106
|
+
),
|
|
107
|
+
UniqueBud(
|
|
108
|
+
constant_name=CryonirspBudName.roi_1_size_x.value, metadata_key="roi_1_size_x"
|
|
109
|
+
),
|
|
110
|
+
UniqueBud(
|
|
111
|
+
constant_name=CryonirspBudName.roi_1_size_y.value, metadata_key="roi_1_size_y"
|
|
112
|
+
),
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def tag_flowers(self) -> list[S]:
|
|
117
|
+
"""Add CryoNIRSP specific tags to common tags."""
|
|
118
|
+
return [
|
|
119
|
+
SingleValueSingleKeyFlower(
|
|
120
|
+
tag_stem_name=CryonirspStemName.curr_frame_in_ramp.value,
|
|
121
|
+
metadata_key="curr_frame_in_ramp",
|
|
122
|
+
),
|
|
123
|
+
# time_obs is a unique identifier for all raw frames in a single ramp
|
|
124
|
+
SingleValueSingleKeyFlower(
|
|
125
|
+
tag_stem_name=CryonirspStemName.time_obs.value,
|
|
126
|
+
metadata_key="time_obs",
|
|
127
|
+
),
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def tags_for_input_frames(self) -> list[Tag]:
|
|
132
|
+
"""Tags for the input data to parse."""
|
|
133
|
+
return [Tag.input(), Tag.frame()]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ParseL0CryonirspLinearizedData(ParseDataBase, InputDatasetMixin):
|
|
137
|
+
"""
|
|
138
|
+
Parse linearity corrected CryoNIRSP input data to add common and Cryonirsp specific constants.
|
|
139
|
+
|
|
140
|
+
Parameters
|
|
141
|
+
----------
|
|
142
|
+
recipe_run_id : int
|
|
143
|
+
id of the recipe run used to identify the workflow run this task is part of
|
|
144
|
+
workflow_name : str
|
|
145
|
+
name of the workflow to which this instance of the task belongs
|
|
146
|
+
workflow_version : str
|
|
147
|
+
version of the workflow to which this instance of the task belongs
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def __init__(
|
|
152
|
+
self,
|
|
153
|
+
recipe_run_id: int,
|
|
154
|
+
workflow_name: str,
|
|
155
|
+
workflow_version: str,
|
|
156
|
+
):
|
|
157
|
+
super().__init__(
|
|
158
|
+
recipe_run_id=recipe_run_id,
|
|
159
|
+
workflow_name=workflow_name,
|
|
160
|
+
workflow_version=workflow_version,
|
|
161
|
+
)
|
|
162
|
+
self.parameters = CryonirspParsingParameters(self.input_dataset_parameters)
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def fits_parsing_class(self):
|
|
166
|
+
"""FITS access class to be used in this task."""
|
|
167
|
+
return CryonirspL0FitsAccess
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def tags_for_input_frames(self) -> list[Tag]:
|
|
171
|
+
"""Tags for the linearity corrected input frames."""
|
|
172
|
+
return [CryonirspTag.linearized(), CryonirspTag.frame()]
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def constant_buds(self) -> list[S]:
|
|
176
|
+
"""Add CryoNIRSP specific constants to common constants."""
|
|
177
|
+
return default_constant_bud_factory() + [
|
|
178
|
+
NumMapScansBud(),
|
|
179
|
+
NumberOfScanStepsBud(),
|
|
180
|
+
NumberOfMeasurementsBud(),
|
|
181
|
+
CryonirspSolarGainStartTimeBud(),
|
|
182
|
+
NumCSStepBud(self.parameters.max_cs_step_time_sec),
|
|
183
|
+
CryonirspTaskExposureConditionsBud(
|
|
184
|
+
stem_name=CryonirspBudName.dark_frame_exposure_conditions_list.value,
|
|
185
|
+
ip_task_type=TaskName.dark.value,
|
|
186
|
+
),
|
|
187
|
+
CryonirspTaskExposureConditionsBud(
|
|
188
|
+
stem_name=CryonirspBudName.lamp_gain_exposure_conditions_list.value,
|
|
189
|
+
ip_task_type=TaskName.lamp_gain.value,
|
|
190
|
+
),
|
|
191
|
+
CryonirspTaskExposureConditionsBud(
|
|
192
|
+
stem_name=CryonirspBudName.solar_gain_exposure_conditions_list.value,
|
|
193
|
+
ip_task_type=TaskName.solar_gain.value,
|
|
194
|
+
),
|
|
195
|
+
CryonirspTaskExposureConditionsBud(
|
|
196
|
+
stem_name=CryonirspBudName.observe_exposure_conditions_list.value,
|
|
197
|
+
ip_task_type=TaskName.observe.value,
|
|
198
|
+
),
|
|
199
|
+
CryonirspTaskExposureConditionsBud(
|
|
200
|
+
stem_name=CryonirspBudName.polcal_exposure_conditions_list.value,
|
|
201
|
+
ip_task_type=TaskName.polcal.value,
|
|
202
|
+
),
|
|
203
|
+
CryonirspNonDarkAndNonPolcalTaskExposureConditionsBud(),
|
|
204
|
+
CryonirspPickyDarkExposureConditionsBud(),
|
|
205
|
+
UniqueBud(constant_name=CryonirspBudName.axis_1_type.value, metadata_key="axis_1_type"),
|
|
206
|
+
UniqueBud(constant_name=CryonirspBudName.axis_2_type.value, metadata_key="axis_2_type"),
|
|
207
|
+
UniqueBud(constant_name=CryonirspBudName.axis_3_type.value, metadata_key="axis_3_type"),
|
|
208
|
+
PolarimetricCheckingUniqueBud(
|
|
209
|
+
constant_name=CryonirspBudName.num_modstates.value,
|
|
210
|
+
metadata_key="number_of_modulator_states",
|
|
211
|
+
),
|
|
212
|
+
PolarimetricCheckingUniqueBud(
|
|
213
|
+
constant_name=CryonirspBudName.modulator_spin_mode.value,
|
|
214
|
+
metadata_key="modulator_spin_mode",
|
|
215
|
+
),
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def tag_flowers(self) -> list[S]:
|
|
220
|
+
"""Add CryoNIRSP specific tags to common tags."""
|
|
221
|
+
return default_tag_flower_factory() + [
|
|
222
|
+
TaskTypeFlower(header_task_parsing_func=parse_header_ip_task_with_gains),
|
|
223
|
+
PolcalTaskFlower(),
|
|
224
|
+
MapScanFlower(),
|
|
225
|
+
ModstateNumberFlower(),
|
|
226
|
+
CSStepFlower(max_cs_step_time_sec=self.parameters.max_cs_step_time_sec),
|
|
227
|
+
ScanStepNumberFlower(),
|
|
228
|
+
MeasurementNumberFlower(),
|
|
229
|
+
SingleValueSingleKeyFlower(
|
|
230
|
+
tag_stem_name=CryonirspStemName.exposure_conditions.value,
|
|
231
|
+
metadata_key="exposure_conditions",
|
|
232
|
+
),
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class ParseL0CryonirspSPLinearizedData(ParseL0CryonirspLinearizedData):
|
|
237
|
+
"""Parse linearity corrected CryoNIRSP-SP input data with SP arm specific constants."""
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def constant_buds(self) -> list[S]:
|
|
241
|
+
"""Add CryoNIRSP-SP specific constants to common constants."""
|
|
242
|
+
return super().constant_buds + [
|
|
243
|
+
TaskNearFloatBud(
|
|
244
|
+
constant_name=CryonirspBudName.grating_position_deg.value,
|
|
245
|
+
metadata_key="grating_position_deg",
|
|
246
|
+
ip_task_type=TaskName.solar_gain.value,
|
|
247
|
+
task_type_parsing_function=parse_header_ip_task_with_gains,
|
|
248
|
+
tolerance=0.01,
|
|
249
|
+
),
|
|
250
|
+
TaskNearFloatBud(
|
|
251
|
+
constant_name=CryonirspBudName.grating_littrow_angle_deg.value,
|
|
252
|
+
metadata_key="grating_littrow_angle_deg",
|
|
253
|
+
ip_task_type=TaskName.solar_gain.value,
|
|
254
|
+
task_type_parsing_function=parse_header_ip_task_with_gains,
|
|
255
|
+
tolerance=0.01,
|
|
256
|
+
),
|
|
257
|
+
TaskUniqueBud(
|
|
258
|
+
constant_name=CryonirspBudName.grating_constant.value,
|
|
259
|
+
metadata_key="grating_constant",
|
|
260
|
+
ip_task_type=TaskName.solar_gain.value,
|
|
261
|
+
task_type_parsing_function=parse_header_ip_task_with_gains,
|
|
262
|
+
),
|
|
263
|
+
TaskUniqueBud(
|
|
264
|
+
constant_name=CryonirspBudName.wave_min.value,
|
|
265
|
+
metadata_key="wave_min",
|
|
266
|
+
ip_task_type=TaskName.solar_gain.value,
|
|
267
|
+
task_type_parsing_function=parse_header_ip_task_with_gains,
|
|
268
|
+
),
|
|
269
|
+
TaskUniqueBud(
|
|
270
|
+
constant_name=CryonirspBudName.wave_max.value,
|
|
271
|
+
metadata_key="wave_max",
|
|
272
|
+
ip_task_type=TaskName.solar_gain.value,
|
|
273
|
+
task_type_parsing_function=parse_header_ip_task_with_gains,
|
|
274
|
+
),
|
|
275
|
+
]
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class ParseL0CryonirspCILinearizedData(ParseL0CryonirspLinearizedData):
|
|
279
|
+
"""Parse linearity corrected CryoNIRSP-CI input data with CI arm specific constants."""
|
|
280
|
+
|
|
281
|
+
pass
|