dkist-processing-dlnirsp 0.32.9__py3-none-any.whl → 0.33.0rc1__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.
Files changed (35) hide show
  1. changelog/120.feature.rst +2 -0
  2. changelog/121.misc.rst +2 -0
  3. changelog/124.feature.rst +2 -0
  4. changelog/124.science.rst +5 -0
  5. dkist_processing_dlnirsp/models/constants.py +6 -0
  6. dkist_processing_dlnirsp/models/parameters.py +33 -3
  7. dkist_processing_dlnirsp/parsers/task.py +2 -25
  8. dkist_processing_dlnirsp/parsers/time.py +2 -2
  9. dkist_processing_dlnirsp/tasks/__init__.py +1 -2
  10. dkist_processing_dlnirsp/tasks/movie.py +1121 -0
  11. dkist_processing_dlnirsp/tasks/parse.py +13 -8
  12. dkist_processing_dlnirsp/tasks/solar.py +129 -30
  13. dkist_processing_dlnirsp/tests/conftest.py +43 -6
  14. dkist_processing_dlnirsp/tests/local_trial_workflows/l0_polcals_as_science.py +21 -18
  15. dkist_processing_dlnirsp/tests/local_trial_workflows/l0_solar_gain_as_science.py +21 -18
  16. dkist_processing_dlnirsp/tests/local_trial_workflows/l0_to_l1.py +21 -18
  17. dkist_processing_dlnirsp/tests/local_trial_workflows/local_trial_dev_mockers.py +1 -1
  18. dkist_processing_dlnirsp/tests/test_dlnirsp_constants.py +2 -0
  19. dkist_processing_dlnirsp/tests/test_movie.py +141 -0
  20. dkist_processing_dlnirsp/tests/test_parameters.py +8 -0
  21. dkist_processing_dlnirsp/tests/test_parse.py +10 -0
  22. dkist_processing_dlnirsp/tests/test_science.py +0 -9
  23. dkist_processing_dlnirsp/tests/test_solar.py +114 -17
  24. dkist_processing_dlnirsp/workflows/l0_processing.py +6 -8
  25. dkist_processing_dlnirsp/workflows/trial_workflow.py +7 -7
  26. {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0rc1.dist-info}/METADATA +40 -23
  27. {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0rc1.dist-info}/RECORD +31 -29
  28. docs/gain.rst +7 -3
  29. dkist_processing_dlnirsp/tasks/assemble_movie.py +0 -150
  30. dkist_processing_dlnirsp/tasks/make_movie_frames.py +0 -156
  31. dkist_processing_dlnirsp/tests/test_assemble_movie.py +0 -169
  32. dkist_processing_dlnirsp/tests/test_make_movie_frames.py +0 -98
  33. {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0rc1.dist-info}/WHEEL +0 -0
  34. {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0rc1.dist-info}/entry_points.txt +0 -0
  35. {dkist_processing_dlnirsp-0.32.9.dist-info → dkist_processing_dlnirsp-0.33.0rc1.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
13
13
  SingleValueSingleKeyFlower,
14
14
  )
15
15
  from dkist_processing_common.parsers.task import PolcalTaskFlower
16
+ from dkist_processing_common.parsers.task import parse_header_ip_task_with_gains
16
17
  from dkist_processing_common.parsers.time import ExposureTimeFlower
17
18
  from dkist_processing_common.parsers.time import ObsIpStartTimeBud
18
19
  from dkist_processing_common.parsers.time import TaskExposureTimesBud
@@ -36,7 +37,6 @@ from dkist_processing_dlnirsp.parsers.mosaic import NumMosaicRepeatsBud
36
37
  from dkist_processing_dlnirsp.parsers.mosaic import NumMosaicXTilesBud
37
38
  from dkist_processing_dlnirsp.parsers.mosaic import NumMosaicYTilesBud
38
39
  from dkist_processing_dlnirsp.parsers.task import DlnirspTaskTypeFlower
39
- from dkist_processing_dlnirsp.parsers.task import parse_header_ip_task
40
40
  from dkist_processing_dlnirsp.parsers.time import DLnirspSolarGainIpStartTimeBud
41
41
  from dkist_processing_dlnirsp.parsers.time import DlnirspTimeObsBud
42
42
  from dkist_processing_dlnirsp.parsers.wavelength import ObserveWavelengthBud
@@ -145,6 +145,11 @@ class ParseL0DlnirspLinearizedData(ParseDataBase):
145
145
  """Add DLNIRSP specific constants to common constants."""
146
146
  return default_constant_bud_factory() + [
147
147
  ObserveWavelengthBud(),
148
+ TaskUniqueBud(
149
+ constant_name=DlnirspBudName.obs_ip_end_time.value,
150
+ metadata_key=MetadataKey.ip_end_time,
151
+ ip_task_types=TaskName.observe,
152
+ ),
148
153
  DLnirspSolarGainIpStartTimeBud(),
149
154
  NumCSStepBud(max_cs_step_time_sec=self.parameters.max_cs_step_time_sec),
150
155
  UniqueBud(
@@ -175,42 +180,42 @@ class ParseL0DlnirspLinearizedData(ParseDataBase):
175
180
  TaskExposureTimesBud(
176
181
  stem_name=DlnirspBudName.lamp_gain_exposure_times.value,
177
182
  ip_task_types=TaskName.lamp_gain.value,
178
- header_task_parsing_func=parse_header_ip_task,
183
+ header_task_parsing_func=parse_header_ip_task_with_gains,
179
184
  ),
180
185
  TaskExposureTimesBud(
181
186
  stem_name=DlnirspBudName.solar_gain_exposure_times.value,
182
187
  ip_task_types=TaskName.solar_gain.value,
183
- header_task_parsing_func=parse_header_ip_task,
188
+ header_task_parsing_func=parse_header_ip_task_with_gains,
184
189
  ),
185
190
  TaskExposureTimesBud(
186
191
  stem_name=DlnirspBudName.observe_exposure_times.value,
187
192
  ip_task_types=TaskName.observe.value,
188
- header_task_parsing_func=parse_header_ip_task,
193
+ header_task_parsing_func=parse_header_ip_task_with_gains,
189
194
  ),
190
195
  TaskExposureTimesBud(
191
196
  stem_name=DlnirspBudName.polcal_exposure_times.value,
192
197
  ip_task_types=TaskName.polcal.value,
193
- header_task_parsing_func=parse_header_ip_task,
198
+ header_task_parsing_func=parse_header_ip_task_with_gains,
194
199
  ),
195
200
  TaskNearFloatBud(
196
201
  constant_name=DlnirspBudName.arm_position_mm.value,
197
202
  metadata_key=DlnirspMetadataKey.arm_position_mm,
198
203
  ip_task_types=[TaskName.solar_gain.value, TaskName.observe.value],
199
204
  tolerance=0.01,
200
- task_type_parsing_function=parse_header_ip_task,
205
+ task_type_parsing_function=parse_header_ip_task_with_gains,
201
206
  ),
202
207
  TaskUniqueBud(
203
208
  constant_name=DlnirspBudName.grating_constant_inverse_mm.value,
204
209
  metadata_key=DlnirspMetadataKey.grating_constant_inverse_mm,
205
210
  ip_task_types=[TaskName.solar_gain.value, TaskName.observe.value],
206
- task_type_parsing_function=parse_header_ip_task,
211
+ task_type_parsing_function=parse_header_ip_task_with_gains,
207
212
  ),
208
213
  TaskNearFloatBud(
209
214
  constant_name=DlnirspBudName.grating_position_deg.value,
210
215
  metadata_key=DlnirspMetadataKey.grating_position_deg,
211
216
  ip_task_types=[TaskName.solar_gain.value, TaskName.observe.value],
212
217
  tolerance=0.01,
213
- task_type_parsing_function=parse_header_ip_task,
218
+ task_type_parsing_function=parse_header_ip_task_with_gains,
214
219
  ),
215
220
  ]
216
221
 
@@ -8,6 +8,7 @@ from dkist_processing_common.models.task_name import TaskName
8
8
  from dkist_processing_common.tasks.mixin.quality import QualityMixin
9
9
  from dkist_processing_math.arithmetic import divide_arrays_by_array
10
10
  from dkist_processing_math.arithmetic import subtract_array_from_arrays
11
+ from dkist_processing_math.linear_algebra import nd_left_matrix_multiply
11
12
  from dkist_processing_math.statistics import average_numpy_arrays
12
13
  from dkist_service_configuration.logging import logger
13
14
 
@@ -17,8 +18,10 @@ from dkist_processing_dlnirsp.tasks.mixin.corrections import CorrectionsMixin
17
18
 
18
19
  __all__ = ["SolarCalibration"]
19
20
 
21
+ from dkist_processing_dlnirsp.tasks.mixin.group_id import GroupIdMixin
20
22
 
21
- class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
23
+
24
+ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin, GroupIdMixin):
22
25
  """
23
26
  Task for computing an intermediate solar gain image.
24
27
 
@@ -33,21 +36,23 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
33
36
  """
34
37
  Compute a solar gain image with the solar spectrum removed.
35
38
 
36
- 1. Apply dark, lamp, and geometric corrections to all solar gain frames and average them together.
37
-
38
- 2. Compute a single characteristic spectrum across all slitbeams and place into the full array
39
-
40
- 3. Re-apply the geometric calibration (spectral shifts and scales) to the characteristic spectra
41
-
42
- 4. Remove the characteristic solar spectra from the dark-corrected solar gain image
39
+ #. Compute dark-only and fully (additional lamp and geometric) corrected solar gain data.
43
40
 
44
- 5. Rescale each slitbeam to have the same average value as the raw, dark corrected solar gain image
41
+ a. For polarimetric data, compute a separate average for each modstate and then demodulate and use the Stokes I data
42
+ #. For intensity data, use the average over all modstates (there should be only one).
45
43
 
46
- 6. Write the final, solar-spectrum-removed, solar gain image.
44
+ #. Compute a single characteristic spectrum across all slitbeams and place into the full array
45
+ #. Re-apply the geometric calibration (spectral shifts and scales) to the characteristic spectra
46
+ #. Remove the characteristic solar spectra from the dark-corrected solar gain image
47
+ #. Rescale each slitbeam to have the same average value as the raw, dark corrected solar gain image
48
+ #. Write the final, solar-spectrum-removed solar gain image.
47
49
  """
48
50
  with self.telemetry_span("Apply dark and lamp corrections"):
49
51
  logger.info("Computing average dark/lamp corrected gains")
50
- self.compute_average_corrected_gains()
52
+ if self.constants.correct_for_polarization:
53
+ self.compute_demodulated_I_gains()
54
+ else:
55
+ self.compute_intensity_only_avg_gains()
51
56
 
52
57
  with self.telemetry_span("Compute characteristic spectra"):
53
58
  logger.info("Computing characteristic spectra")
@@ -81,9 +86,12 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
81
86
 
82
87
  with self.telemetry_span("Write solar flat calibration"):
83
88
  logger.info("Writing solar flat calibration")
89
+ tags = [DlnirspTag.intermediate_frame(), DlnirspTag.task_solar_gain()]
90
+ if self.constants.correct_for_polarization:
91
+ tags.append(DlnirspTag.stokes("I"))
84
92
  self.write(
85
93
  data=cleaned_gain,
86
- tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task_solar_gain()],
94
+ tags=tags,
87
95
  encoder=fits_array_encoder,
88
96
  )
89
97
 
@@ -96,13 +104,110 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
96
104
  task_type=TaskName.solar_gain.value, total_frames=no_of_raw_solar_frames
97
105
  )
98
106
 
99
- def compute_average_corrected_gains(self):
107
+ def compute_intensity_only_avg_gains(self):
108
+ """
109
+ Compute dark-only and fully-corrected average solar gains for intensity mode data.
110
+
111
+ The raw solar gain frames are averaged over all modstates (of which there should only be one anyway).
112
+ The lamp-corrected data also have geometric corrections applied prior to saving.
113
+ """
114
+ dark_corr, lamp_corr = self.compute_average_corrected_gain_for_modstate(modstate=None)
115
+
116
+ # This is the array we remove the characteristic solar spectrum from to produce the final gain array
117
+ self.write(
118
+ data=dark_corr,
119
+ tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_DARK_ONLY")],
120
+ encoder=fits_array_encoder,
121
+ )
122
+
123
+ geo_corr_gain = self.apply_geometric_correction(lamp_corr)
124
+
125
+ self.write(
126
+ data=geo_corr_gain,
127
+ tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_FULL_CORR")],
128
+ encoder=fits_array_encoder,
129
+ )
130
+
131
+ def compute_demodulated_I_gains(self):
132
+ """
133
+ Compute demodulated dark-only and fully-corrected solar gain data.
134
+
135
+ The raw solar gains are averaged once for each modstate before having dark and lamp corrections applied.
136
+ These two sets of arrays (dark-only and dark + lamp) are then demodulated. The resulting Stokes I are then saved
137
+ to disk for later use. The lamp-corrected data have a geometric correction applied prior to saving.
138
+ """
139
+ dark_corr_modstate_stack = np.empty(
140
+ self.unrectified_array_shape + (self.constants.num_modstates,)
141
+ )
142
+ lamp_corr_modstate_stack = np.empty_like(dark_corr_modstate_stack)
143
+ for modstate in range(1, self.constants.num_modstates + 1):
144
+ dark_corr, lamp_corr = self.compute_average_corrected_gain_for_modstate(modstate)
145
+ dark_corr_modstate_stack[:, :, modstate - 1] = dark_corr
146
+ lamp_corr_modstate_stack[:, :, modstate - 1] = lamp_corr
147
+
148
+ logger.info("Loading demodulation matrices")
149
+ demod_matrices = next(
150
+ self.read(
151
+ tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task_demodulation_matrices()],
152
+ decoder=fits_array_decoder,
153
+ )
154
+ )
155
+
156
+ demodulated_dark_corr_gain = nd_left_matrix_multiply(
157
+ vector_stack=dark_corr_modstate_stack, matrix_stack=demod_matrices
158
+ )
159
+ demodulated_lamp_corr_gain = nd_left_matrix_multiply(
160
+ vector_stack=lamp_corr_modstate_stack, matrix_stack=demod_matrices
161
+ )
162
+
163
+ dark_corr_I = demodulated_dark_corr_gain[:, :, 0]
164
+ lamp_corr_I = demodulated_lamp_corr_gain[:, :, 0]
165
+
166
+ # This is the array we remove the characteristic solar spectrum from to produce the final gain array
167
+ self.write(
168
+ data=dark_corr_I,
169
+ tags=[
170
+ DlnirspTag.intermediate_frame(),
171
+ DlnirspTag.task("SC_DARK_ONLY"),
172
+ DlnirspTag.stokes("I"),
173
+ ],
174
+ encoder=fits_array_encoder,
175
+ )
176
+
177
+ geo_corr_gain = self.apply_geometric_correction(lamp_corr_I)
178
+
179
+ self.write(
180
+ data=geo_corr_gain,
181
+ tags=[
182
+ DlnirspTag.intermediate_frame(),
183
+ DlnirspTag.task("SC_FULL_CORR"),
184
+ DlnirspTag.stokes("I"),
185
+ ],
186
+ encoder=fits_array_encoder,
187
+ )
188
+
189
+ def compute_average_corrected_gain_for_modstate(
190
+ self, modstate: int | None
191
+ ) -> tuple[np.ndarray, np.ndarray]:
100
192
  """
101
- Compute a single, averaged frame from all linearized solar gain frames.
193
+ Compute a single, averaged frame from all linearized solar gain frames for a given modstate.
102
194
 
103
- Also apply dark, lamp, and geometric calibrations. If there are multiple exposure times present in the solar
195
+ Also apply dark and lamp corrections. If there are multiple exposure times present in the solar
104
196
  gain images, all frames for a single exposure time are averged prior to dark correction. Then all averaged
105
197
  exposure time frames are averaged again into a single frame.
198
+
199
+ Parameters
200
+ ----------
201
+ modstate
202
+ The modstate to average over. If `None` then the average is performed over all modstates.
203
+
204
+ Returns
205
+ -------
206
+ dark_corrected
207
+ The average solar gains with only a dark correction applied
208
+
209
+ lamp_corrected
210
+ The average solar gains with both dark and lamp corrections applied
106
211
  """
107
212
  all_exp_times = []
108
213
  for exp_time in self.constants.solar_gain_exposure_times:
@@ -114,12 +219,14 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
114
219
  )
115
220
  )
116
221
 
117
- logger.info(f"Loading solar gain frames for {exp_time = }")
222
+ logger.info(f"Loading solar gain frames for {modstate = } and {exp_time = }")
118
223
  tags = [
119
224
  DlnirspTag.linearized_frame(),
120
225
  DlnirspTag.task_solar_gain(),
121
226
  DlnirspTag.exposure_time(exp_time),
122
227
  ]
228
+ if modstate is not None:
229
+ tags.append(DlnirspTag.modstate(modstate))
123
230
  gain_arrays = self.read(tags=tags, decoder=fits_array_decoder)
124
231
 
125
232
  logger.info("Averaging solar gain frames")
@@ -135,13 +242,6 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
135
242
  logger.info(f"Computing final average gain array for {len(all_exp_times)} exposure times")
136
243
  avg_gain_array = average_numpy_arrays(all_exp_times)
137
244
 
138
- # This is the array we remove the characteristic solar spectrum from to produce the final gain array
139
- self.write(
140
- data=avg_gain_array,
141
- tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_DARK_ONLY")],
142
- encoder=fits_array_encoder,
143
- )
144
-
145
245
  logger.info("Loading lamp calibration")
146
246
  lamp_array = next(
147
247
  self.read(
@@ -155,8 +255,11 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
155
255
  divide_arrays_by_array(arrays=avg_gain_array, array_to_divide_by=lamp_array)
156
256
  )
157
257
 
158
- logger.info("Loading geometric calibration")
258
+ return avg_gain_array, lamp_corrected_array
159
259
 
260
+ def apply_geometric_correction(self, array: np.ndarray) -> np.ndarray:
261
+ """Apply the geometric correction to an array."""
262
+ logger.info("Loading geometric calibration")
160
263
  geometric_correction = next(
161
264
  self.read(
162
265
  tags=[DlnirspTag.intermediate(), DlnirspTag.task_geometric()], decoder=asdf_decoder
@@ -169,7 +272,7 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
169
272
  logger.info("Applying geometric calibration")
170
273
  final_gain_array = next(
171
274
  self.corrections_remove_spec_geometry(
172
- arrays=lamp_corrected_array,
275
+ arrays=array,
173
276
  shift_dict=shifts,
174
277
  scale_dict=scales,
175
278
  reference_wavelength_axis=reference_wavelength_axis,
@@ -177,11 +280,7 @@ class SolarCalibration(DlnirspTaskBase, CorrectionsMixin, QualityMixin):
177
280
  )
178
281
  )
179
282
 
180
- self.write(
181
- data=final_gain_array,
182
- tags=[DlnirspTag.intermediate_frame(), DlnirspTag.task("SC_FULL_CORR")],
183
- encoder=fits_array_encoder,
184
- )
283
+ return final_gain_array
185
284
 
186
285
  def compute_characteristic_spectra(self) -> np.ndarray:
187
286
  """
@@ -170,12 +170,23 @@ class DlnirspTestingParameters:
170
170
  dlnirsp_bad_pixel_correction_interpolation_kernel_shape: tuple[int, int] = (10, 3)
171
171
  dlnirsp_wcs_pc_correction_matrix: tuple[tuple[int]] = ((1, 0), (0, -1))
172
172
  dlnirsp_wcs_crpix_correction_method: str = "swap_then_flip_crpix2"
173
+ dlnirsp_movie_core_wave_value_nm_vis: float = 853.98
174
+ dlnirsp_movie_core_wave_value_nm_jband: float = 1083.0
175
+ dlnirsp_movie_core_wave_value_nm_hband: float = 1564.8
176
+ dlnirsp_movie_cont_wave_value_nm_vis: float = 854.23
177
+ dlnirsp_movie_cont_wave_value_nm_jband: float = 1083.268
178
+ dlnirsp_movie_cont_wave_value_nm_hband: float = 1565.06
179
+ dlnirsp_movie_vertical_nan_slices: list[list[int | None]] = field(
180
+ default_factory=lambda: [[None, 1], [31, 34], [-2, None]]
181
+ )
182
+ dlnirsp_movie_nan_replacement_kernel_shape: tuple[int, int] = (5, 5)
173
183
 
174
184
 
175
185
  @dataclass
176
186
  class DlnirspTestingConstants:
177
187
  INSTRUMENT: str = "DLNIRSP"
178
- OBS_IP_START_TIME: str = "2024-06-06"
188
+ OBS_IP_START_TIME: str = "2024-06-06T00:00:00"
189
+ OBS_IP_END_TIME: str = "2024-06-06T00:10:00"
179
190
  ARM_ID: str = "HBand"
180
191
  NUM_MODSTATES: int = 8
181
192
  NUM_MOSAIC_TILES_X: int = 2
@@ -204,6 +215,7 @@ class DlnirspTestingConstants:
204
215
  "EXPERID2",
205
216
  "EXPERID3",
206
217
  )
218
+ EXPERIMENT_ID: str = "eid_6_28"
207
219
  ARM_POSITION_MM: float = -4.2
208
220
  GRATING_CONSTANT_INVERSE_MM: float = 20.0
209
221
  GRATING_POSITION_DEG: float = 87.4
@@ -1229,6 +1241,8 @@ class CalibratedHeaders(ModulatedObserveHeaders):
1229
1241
  is_polarimetric: bool,
1230
1242
  dither_mode_on: bool = False,
1231
1243
  array_shape: tuple[int, int, int] = (3, 4, 5),
1244
+ crpix1_step_delta: float = 40.0,
1245
+ crpix2_step_delta: float = 30.0,
1232
1246
  ):
1233
1247
 
1234
1248
  # Use the data_cycles loop to represent Stokes parameters
@@ -1246,6 +1260,8 @@ class CalibratedHeaders(ModulatedObserveHeaders):
1246
1260
  allow_3D_arrays=True,
1247
1261
  )
1248
1262
 
1263
+ self.crpix1_step_delta = crpix1_step_delta
1264
+ self.crpix2_step_delta = crpix2_step_delta
1249
1265
  self.stokes_name_list = ["I", "Q", "U", "V"]
1250
1266
  self.add_constant_key("DLN__014", 8 if is_polarimetric else 1)
1251
1267
  self.add_constant_key("DLN__008", "Full Stokes" if is_polarimetric else "Stokes I")
@@ -1263,12 +1279,21 @@ class CalibratedHeaders(ModulatedObserveHeaders):
1263
1279
  ) # -1 b/c data cycles are indexed from 1
1264
1280
  return self.stokes_name_list[stokes_axis_id]
1265
1281
 
1282
+ @property
1283
+ def current_crpix1(self) -> float:
1284
+ return self.array_shape[2] / 2 - self.crpix1_step_delta * self.current_MINDEX1_value
1285
+
1286
+ @property
1287
+ def current_crpix2(self) -> float:
1288
+ return self.array_shape[1] / 2 - self.crpix2_step_delta * self.current_MINDEX2_value
1289
+
1266
1290
  @property
1267
1291
  def fits_wcs(self):
1292
+ # Taken from real data from eid_2_24
1268
1293
  w = WCS(naxis=3)
1269
- w.wcs.crpix = self.array_shape[2] / 2, self.array_shape[1] / 2, self.array_shape[0] / 2
1270
- w.wcs.crval = 666.0, 999.0, 1565.0
1271
- w.wcs.cdelt = 1, 1, 0.2
1294
+ w.wcs.crpix = self.current_crpix1, self.current_crpix2, self.array_shape[0] / 2
1295
+ w.wcs.crval = 176.118, -289.575, 1565.0
1296
+ w.wcs.cdelt = 0.031, 0.031, 0.2
1272
1297
  w.wcs.cunit = "arcsec", "arcsec", "nm"
1273
1298
  w.wcs.ctype = "HPLN-TAN", "HPLT-TAN", "AWAV"
1274
1299
  w.wcs.pc = np.identity(self.array_ndim)
@@ -1342,6 +1367,15 @@ def make_cs_data(
1342
1367
  return data
1343
1368
 
1344
1369
 
1370
+ @pytest.fixture
1371
+ def make_full_demodulation_matrix(demodulation_matrix):
1372
+ def make_array(frame: Spec122Dataset):
1373
+ array_shape = frame.array_shape[1:]
1374
+ return np.ones(array_shape + demodulation_matrix.shape) * demodulation_matrix
1375
+
1376
+ return make_array
1377
+
1378
+
1345
1379
  def tag_on_modstate(frame: Spec122Dataset) -> list[str]:
1346
1380
  modstate = frame.header()["DLN__015"]
1347
1381
  return [DlnirspTag.modstate(modstate)]
@@ -1631,11 +1665,12 @@ def write_observe_frames_to_task(
1631
1665
  array_shape: tuple[int, int] = (10, 10),
1632
1666
  crpix_delta: tuple[float, float] = (80.3, 20.7),
1633
1667
  swap_crpix_values: bool = False,
1668
+ start_date: str = "2023-01-01T01:23:45",
1669
+ modstate_length_sec: float = 0.5,
1634
1670
  data_func: callable = make_random_data,
1635
1671
  tags: list[str] | None = None,
1636
1672
  tag_func: callable = lambda x: [],
1637
1673
  ) -> int:
1638
-
1639
1674
  frame_generator = ModulatedObserveHeaders(
1640
1675
  num_modstates=num_modstates,
1641
1676
  num_X_tiles=num_X_tiles,
@@ -1650,6 +1685,8 @@ def write_observe_frames_to_task(
1650
1685
  grating_angle=grating_angle,
1651
1686
  crpix_delta=crpix_delta,
1652
1687
  swap_crpix_values=swap_crpix_values,
1688
+ start_date=start_date,
1689
+ modstate_length_sec=modstate_length_sec,
1653
1690
  )
1654
1691
 
1655
1692
  num_frames = write_frames_to_task(
@@ -1700,7 +1737,7 @@ def write_calibrated_frames_to_task(
1700
1737
  is_polarimetric: bool,
1701
1738
  array_shape: tuple[int, int, int],
1702
1739
  dither_mode_on: bool = False,
1703
- data_func: callable = make_3D_random_data,
1740
+ data_func: Callable = make_3D_random_data,
1704
1741
  ):
1705
1742
  dataset = CalibratedHeaders(
1706
1743
  num_mosaics=num_mosaics,
@@ -13,11 +13,10 @@ from dkist_processing_common.tasks import WorkflowTaskBase
13
13
  from dkist_service_configuration.logging import logger
14
14
 
15
15
  from dkist_processing_dlnirsp.models.tags import DlnirspTag
16
- from dkist_processing_dlnirsp.tasks import AssembleDlnirspMovie
17
16
  from dkist_processing_dlnirsp.tasks import DlnirspAssembleQualityData
18
17
  from dkist_processing_dlnirsp.tasks import DlnirspL0QualityMetrics
19
18
  from dkist_processing_dlnirsp.tasks import DlnirspL1QualityMetrics
20
- from dkist_processing_dlnirsp.tasks import MakeDlnirspMovieFrames
19
+ from dkist_processing_dlnirsp.tasks import MakeDlnirspMovie
21
20
  from dkist_processing_dlnirsp.tasks.bad_pixel_map import BadPixelCalibration
22
21
  from dkist_processing_dlnirsp.tasks.dark import DarkCalibration
23
22
  from dkist_processing_dlnirsp.tasks.geometric import GeometricCalibration
@@ -197,10 +196,11 @@ def main(
197
196
  load_bad_pixel: bool = False,
198
197
  load_geometric: bool = False,
199
198
  load_wavelength_calibration: bool = False,
200
- load_solar: bool = False,
201
199
  load_inst_polcal: bool = False,
200
+ load_solar: bool = False,
202
201
  load_polcals_as_science: bool = False,
203
202
  load_calibrated_data: bool = False,
203
+ skip_movie: bool = False,
204
204
  transfer_trial_data: str | None = None,
205
205
  ):
206
206
  with ManualProcessing(
@@ -298,18 +298,18 @@ def main(
298
298
  manual_processing_run.run_task(task=WavelengthCalibration)
299
299
  manual_processing_run.run_task(task=SaveWavelengthCal)
300
300
 
301
- if load_solar:
302
- manual_processing_run.run_task(task=LoadSolarCal)
303
- else:
304
- manual_processing_run.run_task(task=SolarCalibration)
305
- manual_processing_run.run_task(task=SaveSolarCal)
306
-
307
301
  if load_inst_polcal:
308
302
  manual_processing_run.run_task(task=LoadInstPolCal)
309
303
  else:
310
304
  manual_processing_run.run_task(task=InstrumentPolarizationCalibration)
311
305
  manual_processing_run.run_task(task=SaveInstPolCal)
312
306
 
307
+ if load_solar:
308
+ manual_processing_run.run_task(task=LoadSolarCal)
309
+ else:
310
+ manual_processing_run.run_task(task=SolarCalibration)
311
+ manual_processing_run.run_task(task=SaveSolarCal)
312
+
313
313
  if load_polcals_as_science:
314
314
  manual_processing_run.run_task(task=LoadPolCalAsScience)
315
315
  else:
@@ -324,8 +324,6 @@ def main(
324
324
 
325
325
  manual_processing_run.run_task(task=permissive_write_l1_task(force_intensity_only=False))
326
326
  manual_processing_run.run_task(task=ValidateL1Output)
327
- manual_processing_run.run_task(task=MakeDlnirspMovieFrames)
328
- manual_processing_run.run_task(task=AssembleDlnirspMovie)
329
327
 
330
328
  manual_processing_run.run_task(task=DlnirspL0QualityMetrics)
331
329
  manual_processing_run.run_task(task=DlnirspL1QualityMetrics)
@@ -334,6 +332,9 @@ def main(
334
332
 
335
333
  manual_processing_run.run_task(task=CreateTrialQualityReport)
336
334
 
335
+ if not skip_movie:
336
+ manual_processing_run.run_task(task=MakeDlnirspMovie)
337
+
337
338
  if transfer_trial_data:
338
339
  transfer_trial_data_locally(
339
340
  trial_output_location=transfer_trial_data, processing_run=manual_processing_run
@@ -425,18 +426,18 @@ if __name__ == "__main__":
425
426
  help="Load wavelength calibration solution from previously saved run",
426
427
  action="store_true",
427
428
  )
428
- parser.add_argument(
429
- "-S",
430
- "--load-solar",
431
- help="Load solar calibration from previously saved run",
432
- action="store_true",
433
- )
434
429
  parser.add_argument(
435
430
  "-P",
436
431
  "--load-inst-polcal",
437
432
  help="Load instrument polarization calibration from previously saved run",
438
433
  action="store_true",
439
434
  )
435
+ parser.add_argument(
436
+ "-S",
437
+ "--load-solar",
438
+ help="Load solar calibration from previously saved run",
439
+ action="store_true",
440
+ )
440
441
  parser.add_argument(
441
442
  "-O",
442
443
  "--load-polcals-as-science",
@@ -446,6 +447,7 @@ if __name__ == "__main__":
446
447
  parser.add_argument(
447
448
  "-C", "--load-calibrated-data", help="Load CALIBRATED 'science' frames", action="store_true"
448
449
  )
450
+ parser.add_argument("-V", "--skip-movie", help="Don't make a browse movie", action="store_true")
449
451
  parser.add_argument(
450
452
  "-p",
451
453
  "--param-dir",
@@ -472,10 +474,11 @@ if __name__ == "__main__":
472
474
  load_bad_pixel=args.load_bad_pixel,
473
475
  load_geometric=args.load_geometric,
474
476
  load_wavelength_calibration=args.load_wavelength_calibration,
475
- load_solar=args.load_solar,
476
477
  load_inst_polcal=args.load_inst_polcal,
478
+ load_solar=args.load_solar,
477
479
  load_polcals_as_science=args.load_polcals_as_science,
478
480
  load_calibrated_data=args.load_calibrated_data,
481
+ skip_movie=args.skip_movie,
479
482
  transfer_trial_data=args.transfer_trial_data,
480
483
  )
481
484
  )
@@ -13,11 +13,10 @@ from dkist_processing_common.tasks import WorkflowTaskBase
13
13
  from dkist_service_configuration.logging import logger
14
14
 
15
15
  from dkist_processing_dlnirsp.models.tags import DlnirspTag
16
- from dkist_processing_dlnirsp.tasks import AssembleDlnirspMovie
17
16
  from dkist_processing_dlnirsp.tasks import DlnirspAssembleQualityData
18
17
  from dkist_processing_dlnirsp.tasks import DlnirspL0QualityMetrics
19
18
  from dkist_processing_dlnirsp.tasks import DlnirspL1QualityMetrics
20
- from dkist_processing_dlnirsp.tasks import MakeDlnirspMovieFrames
19
+ from dkist_processing_dlnirsp.tasks import MakeDlnirspMovie
21
20
  from dkist_processing_dlnirsp.tasks.bad_pixel_map import BadPixelCalibration
22
21
  from dkist_processing_dlnirsp.tasks.dark import DarkCalibration
23
22
  from dkist_processing_dlnirsp.tasks.geometric import GeometricCalibration
@@ -203,10 +202,11 @@ def main(
203
202
  load_bad_pixel: bool = False,
204
203
  load_geometric: bool = False,
205
204
  load_wavelength_calibration: bool = False,
206
- load_solar: bool = False,
207
205
  load_inst_polcal: bool = False,
206
+ load_solar: bool = False,
208
207
  load_solar_gain_as_science: bool = False,
209
208
  load_calibrated_data: bool = False,
209
+ skip_movie: bool = False,
210
210
  force_intensity_only: bool = False,
211
211
  transfer_trial_data: str | None = None,
212
212
  ):
@@ -305,18 +305,18 @@ def main(
305
305
  manual_processing_run.run_task(task=WavelengthCalibration)
306
306
  manual_processing_run.run_task(task=SaveWavelengthCal)
307
307
 
308
- if load_solar:
309
- manual_processing_run.run_task(task=LoadSolarCal)
310
- else:
311
- manual_processing_run.run_task(task=SolarCalibration)
312
- manual_processing_run.run_task(task=SaveSolarCal)
313
-
314
308
  if load_inst_polcal:
315
309
  manual_processing_run.run_task(task=LoadInstPolCal)
316
310
  else:
317
311
  manual_processing_run.run_task(task=InstrumentPolarizationCalibration)
318
312
  manual_processing_run.run_task(task=SaveInstPolCal)
319
313
 
314
+ if load_solar:
315
+ manual_processing_run.run_task(task=LoadSolarCal)
316
+ else:
317
+ manual_processing_run.run_task(task=SolarCalibration)
318
+ manual_processing_run.run_task(task=SaveSolarCal)
319
+
320
320
  if load_solar_gain_as_science:
321
321
  manual_processing_run.run_task(
322
322
  task=load_solar_gain_as_science_task(force_intensity_only=force_intensity_only)
@@ -338,8 +338,6 @@ def main(
338
338
  task=permissive_write_l1_task(force_intensity_only=force_intensity_only)
339
339
  )
340
340
  manual_processing_run.run_task(task=ValidateL1Output)
341
- manual_processing_run.run_task(task=MakeDlnirspMovieFrames)
342
- manual_processing_run.run_task(task=AssembleDlnirspMovie)
343
341
 
344
342
  manual_processing_run.run_task(task=DlnirspL0QualityMetrics)
345
343
  manual_processing_run.run_task(task=DlnirspL1QualityMetrics)
@@ -348,6 +346,9 @@ def main(
348
346
 
349
347
  manual_processing_run.run_task(task=CreateTrialQualityReport)
350
348
 
349
+ if not skip_movie:
350
+ manual_processing_run.run_task(task=MakeDlnirspMovie)
351
+
351
352
  if transfer_trial_data:
352
353
  transfer_trial_data_locally(
353
354
  trial_output_location=transfer_trial_data, processing_run=manual_processing_run
@@ -444,18 +445,18 @@ if __name__ == "__main__":
444
445
  help="Load wavelength calibration solution from previously saved run",
445
446
  action="store_true",
446
447
  )
447
- parser.add_argument(
448
- "-S",
449
- "--load-solar",
450
- help="Load solar calibration from previously saved run",
451
- action="store_true",
452
- )
453
448
  parser.add_argument(
454
449
  "-P",
455
450
  "--load-inst-polcal",
456
451
  help="Load instrument polarization calibration from previously saved run",
457
452
  action="store_true",
458
453
  )
454
+ parser.add_argument(
455
+ "-S",
456
+ "--load-solar",
457
+ help="Load solar calibration from previously saved run",
458
+ action="store_true",
459
+ )
459
460
  parser.add_argument(
460
461
  "-O",
461
462
  "--load-solar-gain-as-science",
@@ -465,6 +466,7 @@ if __name__ == "__main__":
465
466
  parser.add_argument(
466
467
  "-C", "--load-calibrated-data", help="Load CALIBRATED 'science' frames", action="store_true"
467
468
  )
469
+ parser.add_argument("-V", "--skip-movie", help="Don't make a browse movie", action="store_true")
468
470
  parser.add_argument(
469
471
  "-p",
470
472
  "--param-dir",
@@ -491,10 +493,11 @@ if __name__ == "__main__":
491
493
  load_bad_pixel=args.load_bad_pixel,
492
494
  load_geometric=args.load_geometric,
493
495
  load_wavelength_calibration=args.load_wavelength_calibration,
494
- load_solar=args.load_solar,
495
496
  load_inst_polcal=args.load_inst_polcal,
497
+ load_solar=args.load_solar,
496
498
  load_solar_gain_as_science=args.load_solar_gain_as_science,
497
499
  load_calibrated_data=args.load_calibrated_data,
500
+ skip_movie=args.skip_movie,
498
501
  force_intensity_only=args.force_I_only,
499
502
  transfer_trial_data=args.transfer_trial_data,
500
503
  )