dkist-processing-visp 3.6.2__py3-none-any.whl → 4.0.0__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.
- dkist_processing_visp/models/constants.py +0 -1
- dkist_processing_visp/models/fits_access.py +16 -0
- dkist_processing_visp/parsers/polarimeter_mode.py +2 -1
- dkist_processing_visp/parsers/raster_step.py +3 -1
- dkist_processing_visp/parsers/time.py +3 -2
- dkist_processing_visp/parsers/visp_l0_fits_access.py +12 -8
- dkist_processing_visp/tasks/make_movie_frames.py +5 -4
- dkist_processing_visp/tasks/mixin/corrections.py +53 -4
- dkist_processing_visp/tasks/parse.py +13 -3
- dkist_processing_visp/tasks/quality_metrics.py +1 -1
- dkist_processing_visp/tasks/science.py +102 -23
- dkist_processing_visp/tasks/write_l1.py +7 -3
- dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py +5 -3
- dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py +7 -6
- dkist_processing_visp/tests/test_fits_access.py +43 -0
- dkist_processing_visp/tests/test_make_movie_frames.py +2 -1
- dkist_processing_visp/tests/test_map_repeats.py +3 -1
- dkist_processing_visp/tests/test_science.py +105 -9
- dkist_processing_visp/tests/test_write_l1.py +3 -2
- {dkist_processing_visp-3.6.2.dist-info → dkist_processing_visp-4.0.0.dist-info}/METADATA +14 -15
- {dkist_processing_visp-3.6.2.dist-info → dkist_processing_visp-4.0.0.dist-info}/RECORD +23 -21
- {dkist_processing_visp-3.6.2.dist-info → dkist_processing_visp-4.0.0.dist-info}/WHEEL +0 -0
- {dkist_processing_visp-3.6.2.dist-info → dkist_processing_visp-4.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""ViSP control of FITS key names and values."""
|
|
2
|
+
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class VispMetadataKey(StrEnum):
|
|
7
|
+
"""Controlled list of names for FITS metadata header keys."""
|
|
8
|
+
|
|
9
|
+
raster_scan_step = "VSPSTP"
|
|
10
|
+
total_raster_steps = "VSPNSTP"
|
|
11
|
+
modulator_state = "VSPSTNUM"
|
|
12
|
+
number_of_modulator_states = "VSPNUMST"
|
|
13
|
+
polarimeter_mode = "VISP_006"
|
|
14
|
+
axis_1_type = "CTYPE1"
|
|
15
|
+
axis_2_type = "CTYPE2"
|
|
16
|
+
axis_3_type = "CTYPE3"
|
|
@@ -4,6 +4,7 @@ from dkist_processing_common.models.task_name import TaskName
|
|
|
4
4
|
from dkist_processing_common.parsers.unique_bud import TaskUniqueBud
|
|
5
5
|
|
|
6
6
|
from dkist_processing_visp.models.constants import VispBudName
|
|
7
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class PolarimeterModeBud(TaskUniqueBud):
|
|
@@ -12,6 +13,6 @@ class PolarimeterModeBud(TaskUniqueBud):
|
|
|
12
13
|
def __init__(self):
|
|
13
14
|
super().__init__(
|
|
14
15
|
constant_name=VispBudName.polarimeter_mode.value,
|
|
15
|
-
metadata_key=
|
|
16
|
+
metadata_key=VispMetadataKey.polarimeter_mode,
|
|
16
17
|
ip_task_types=TaskName.observe.value,
|
|
17
18
|
)
|
|
@@ -10,6 +10,7 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
from dkist_processing_visp.models.constants import VispBudName
|
|
13
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
13
14
|
from dkist_processing_visp.models.tags import VispStemName
|
|
14
15
|
from dkist_processing_visp.parsers.visp_l0_fits_access import VispL0FitsAccess
|
|
15
16
|
|
|
@@ -69,7 +70,8 @@ class RasterScanStepFlower(SingleValueSingleKeyFlower):
|
|
|
69
70
|
|
|
70
71
|
def __init__(self):
|
|
71
72
|
super().__init__(
|
|
72
|
-
tag_stem_name=VispStemName.raster_step.value,
|
|
73
|
+
tag_stem_name=VispStemName.raster_step.value,
|
|
74
|
+
metadata_key=VispMetadataKey.raster_scan_step,
|
|
73
75
|
)
|
|
74
76
|
|
|
75
77
|
def setter(self, fits_obj: VispL0FitsAccess):
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import NamedTuple
|
|
5
5
|
|
|
6
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
6
7
|
from dkist_processing_common.models.flower_pot import SpilledDirt
|
|
7
8
|
from dkist_processing_common.models.flower_pot import Stem
|
|
8
9
|
from dkist_processing_common.models.flower_pot import Thorn
|
|
@@ -18,7 +19,7 @@ class NonDarkNonPolcalTaskReadoutExpTimesBud(Stem):
|
|
|
18
19
|
|
|
19
20
|
def __init__(self):
|
|
20
21
|
super().__init__(stem_name=VispBudName.non_dark_task_readout_exp_times.value)
|
|
21
|
-
self.metadata_key =
|
|
22
|
+
self.metadata_key = MetadataKey.sensor_readout_exposure_time_ms.name
|
|
22
23
|
|
|
23
24
|
def setter(self, fits_obj: VispL0FitsAccess) -> float | SpilledDirt:
|
|
24
25
|
"""
|
|
@@ -73,7 +74,7 @@ class DarkReadoutExpTimePickyBud(Stem):
|
|
|
73
74
|
|
|
74
75
|
def __init__(self):
|
|
75
76
|
super().__init__(stem_name=VispBudName.dark_readout_exp_time_picky_bud.value)
|
|
76
|
-
self.metadata_key =
|
|
77
|
+
self.metadata_key = MetadataKey.sensor_readout_exposure_time_ms.name
|
|
77
78
|
|
|
78
79
|
def setter(self, fits_obj: VispL0FitsAccess) -> tuple:
|
|
79
80
|
"""
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from astropy.io import fits
|
|
4
4
|
from dkist_processing_common.parsers.l0_fits_access import L0FitsAccess
|
|
5
5
|
|
|
6
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class VispL0FitsAccess(L0FitsAccess):
|
|
8
10
|
"""
|
|
@@ -30,11 +32,13 @@ class VispL0FitsAccess(L0FitsAccess):
|
|
|
30
32
|
):
|
|
31
33
|
super().__init__(hdu=hdu, name=name, auto_squeeze=auto_squeeze)
|
|
32
34
|
|
|
33
|
-
self.number_of_modulator_states: int = self.header[
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
self.
|
|
37
|
-
self.
|
|
38
|
-
self.
|
|
39
|
-
self.
|
|
40
|
-
self.
|
|
35
|
+
self.number_of_modulator_states: int = self.header[
|
|
36
|
+
VispMetadataKey.number_of_modulator_states
|
|
37
|
+
]
|
|
38
|
+
self.raster_scan_step: int = self.header[VispMetadataKey.raster_scan_step]
|
|
39
|
+
self.total_raster_steps: int = self.header[VispMetadataKey.total_raster_steps]
|
|
40
|
+
self.modulator_state: int = self.header[VispMetadataKey.modulator_state]
|
|
41
|
+
self.polarimeter_mode: str = self.header[VispMetadataKey.polarimeter_mode]
|
|
42
|
+
self.axis_1_type: str = self.header[VispMetadataKey.axis_1_type]
|
|
43
|
+
self.axis_2_type: str = self.header[VispMetadataKey.axis_2_type]
|
|
44
|
+
self.axis_3_type: str = self.header[VispMetadataKey.axis_3_type]
|
|
@@ -5,6 +5,7 @@ from astropy.io import fits
|
|
|
5
5
|
from astropy.visualization import ZScaleInterval
|
|
6
6
|
from dkist_processing_common.codecs.fits import fits_access_decoder
|
|
7
7
|
from dkist_processing_common.codecs.fits import fits_array_encoder
|
|
8
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
8
9
|
from dkist_service_configuration.logging import logger
|
|
9
10
|
|
|
10
11
|
from dkist_processing_visp.models.tags import VispTag
|
|
@@ -78,7 +79,7 @@ class MakeVispMovieFrames(VispTaskBase):
|
|
|
78
79
|
fits_access_class=VispL1FitsAccess,
|
|
79
80
|
)
|
|
80
81
|
)
|
|
81
|
-
data = calibrated_frame.data
|
|
82
|
+
data = np.nan_to_num(calibrated_frame.data, nan=0)
|
|
82
83
|
if self.constants.num_raster_steps == 1:
|
|
83
84
|
logger.info(
|
|
84
85
|
"Only a single raster step found. Making a spectral movie."
|
|
@@ -121,16 +122,16 @@ class MakeVispMovieFrames(VispTaskBase):
|
|
|
121
122
|
f"There should only be one instrument value in the headers. "
|
|
122
123
|
f"Found {len(instrument_set)}: {instrument_set=}"
|
|
123
124
|
)
|
|
124
|
-
header[
|
|
125
|
+
header[MetadataKey.instrument] = instrument_set.pop()
|
|
125
126
|
# The timestamp of a movie frame will be the time of raster scan start
|
|
126
|
-
header[
|
|
127
|
+
header[MetadataKey.time_obs] = time_obs[0]
|
|
127
128
|
# Make sure only one wavelength value was found
|
|
128
129
|
if len(wavelength_set) != 1:
|
|
129
130
|
raise ValueError(
|
|
130
131
|
f"There should only be one wavelength value in the headers. "
|
|
131
132
|
f"Found {len(wavelength_set)}: {wavelength_set=}"
|
|
132
133
|
)
|
|
133
|
-
header[
|
|
134
|
+
header[MetadataKey.wavelength] = wavelength_set.pop()
|
|
134
135
|
# Write the movie frame file to disk and tag it, normalizing across stokes intensities
|
|
135
136
|
if is_polarized:
|
|
136
137
|
i_norm = ZScaleInterval()(stokes_i_data)
|
|
@@ -17,6 +17,9 @@ class CorrectionsMixin:
|
|
|
17
17
|
arrays: Iterable[np.ndarray] | np.ndarray,
|
|
18
18
|
shift: np.ndarray = np.zeros(2),
|
|
19
19
|
angle: float = 0.0,
|
|
20
|
+
mode: str = "edge",
|
|
21
|
+
order: int = 5,
|
|
22
|
+
cval: float = np.nan,
|
|
20
23
|
) -> Generator[np.ndarray, None, None]:
|
|
21
24
|
"""
|
|
22
25
|
Shift and then rotate data.
|
|
@@ -35,6 +38,24 @@ class CorrectionsMixin:
|
|
|
35
38
|
angle : float
|
|
36
39
|
The angle (in radians) between slit hairlines and pixel axes.
|
|
37
40
|
|
|
41
|
+
mode : {'constant', 'edge', 'symmetric', 'reflect', 'wrap'}
|
|
42
|
+
Points outside the boundaries of the input are filled according
|
|
43
|
+
to the given mode. Modes match the behaviour of `numpy.pad`.
|
|
44
|
+
|
|
45
|
+
order : int
|
|
46
|
+
The order of interpolation. The order has to be in the range 0-5:
|
|
47
|
+
- 0: Nearest-neighbor
|
|
48
|
+
- 1: Bi-linear (default)
|
|
49
|
+
- 2: Bi-quadratic
|
|
50
|
+
- 3: Bi-cubic
|
|
51
|
+
- 4: Bi-quartic
|
|
52
|
+
- 5: Bi-quintic
|
|
53
|
+
|
|
54
|
+
cval : float
|
|
55
|
+
Used in conjunction with mode 'constant', the value outside
|
|
56
|
+
the image boundaries.
|
|
57
|
+
|
|
58
|
+
|
|
38
59
|
Returns
|
|
39
60
|
-------
|
|
40
61
|
Generator
|
|
@@ -46,12 +67,21 @@ class CorrectionsMixin:
|
|
|
46
67
|
array[np.where(array == np.inf)] = np.max(array[np.isfinite(array)])
|
|
47
68
|
array[np.where(array == -np.inf)] = np.min(array[np.isfinite(array)])
|
|
48
69
|
array[np.isnan(array)] = np.nanmedian(array)
|
|
49
|
-
translated = affine_transform_arrays(
|
|
50
|
-
|
|
70
|
+
translated = affine_transform_arrays(
|
|
71
|
+
array, translation=-shift, mode=mode, order=order, cval=cval
|
|
72
|
+
)
|
|
73
|
+
yield next(
|
|
74
|
+
rotate_arrays_about_point(
|
|
75
|
+
translated, angle=-angle, mode=mode, order=order, cval=cval
|
|
76
|
+
)
|
|
77
|
+
)
|
|
51
78
|
|
|
52
79
|
@staticmethod
|
|
53
80
|
def corrections_remove_spec_geometry(
|
|
54
|
-
arrays: Iterable[np.ndarray] | np.ndarray,
|
|
81
|
+
arrays: Iterable[np.ndarray] | np.ndarray,
|
|
82
|
+
spec_shift: np.ndarray,
|
|
83
|
+
cval: float | None = None,
|
|
84
|
+
order: int = 3,
|
|
55
85
|
) -> Generator[np.ndarray, None, None]:
|
|
56
86
|
"""
|
|
57
87
|
Remove spectral curvature.
|
|
@@ -67,6 +97,19 @@ class CorrectionsMixin:
|
|
|
67
97
|
Array with shape (X), where X is the number of pixels in the spatial dimension.
|
|
68
98
|
This dimension gives the spectral shift.
|
|
69
99
|
|
|
100
|
+
order : int
|
|
101
|
+
The order of interpolation. The order has to be in the range 0-5:
|
|
102
|
+
- 0: Nearest-neighbor
|
|
103
|
+
- 1: Bi-linear (default)
|
|
104
|
+
- 2: Bi-quadratic
|
|
105
|
+
- 3: Bi-cubic
|
|
106
|
+
- 4: Bi-quartic
|
|
107
|
+
- 5: Bi-quintic
|
|
108
|
+
|
|
109
|
+
cval : float
|
|
110
|
+
Used in conjunction with mode 'constant', the value outside
|
|
111
|
+
the image boundaries.
|
|
112
|
+
|
|
70
113
|
Returns
|
|
71
114
|
-------
|
|
72
115
|
Generator
|
|
@@ -78,8 +121,14 @@ class CorrectionsMixin:
|
|
|
78
121
|
numy = array.shape[1]
|
|
79
122
|
array_output = np.zeros(array.shape)
|
|
80
123
|
for j in range(numy):
|
|
124
|
+
if cval is None:
|
|
125
|
+
cval = np.nanmedian(array[:, j])
|
|
81
126
|
array_output[:, j] = spnd.shift(
|
|
82
|
-
array[:, j],
|
|
127
|
+
array[:, j],
|
|
128
|
+
-spec_shift[j],
|
|
129
|
+
mode="constant",
|
|
130
|
+
cval=cval,
|
|
131
|
+
order=order,
|
|
83
132
|
)
|
|
84
133
|
yield array_output
|
|
85
134
|
|
|
@@ -20,6 +20,7 @@ from dkist_processing_common.parsers.wavelength import ObserveWavelengthBud
|
|
|
20
20
|
from dkist_processing_common.tasks import ParseL0InputDataBase
|
|
21
21
|
|
|
22
22
|
from dkist_processing_visp.models.constants import VispBudName
|
|
23
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
23
24
|
from dkist_processing_visp.models.parameters import VispParsingParameters
|
|
24
25
|
from dkist_processing_visp.parsers.map_repeats import MapScanFlower
|
|
25
26
|
from dkist_processing_visp.parsers.map_repeats import NumMapScansBud
|
|
@@ -123,9 +124,18 @@ class ParseL0VispInputData(ParseL0InputDataBase):
|
|
|
123
124
|
ip_task_types=TaskName.polcal.value,
|
|
124
125
|
header_task_parsing_func=parse_header_ip_task_with_gains,
|
|
125
126
|
),
|
|
126
|
-
UniqueBud(
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
UniqueBud(
|
|
128
|
+
constant_name=VispBudName.axis_1_type.value,
|
|
129
|
+
metadata_key=VispMetadataKey.axis_1_type,
|
|
130
|
+
),
|
|
131
|
+
UniqueBud(
|
|
132
|
+
constant_name=VispBudName.axis_2_type.value,
|
|
133
|
+
metadata_key=VispMetadataKey.axis_2_type,
|
|
134
|
+
),
|
|
135
|
+
UniqueBud(
|
|
136
|
+
constant_name=VispBudName.axis_3_type.value,
|
|
137
|
+
metadata_key=VispMetadataKey.axis_3_type,
|
|
138
|
+
),
|
|
129
139
|
]
|
|
130
140
|
|
|
131
141
|
@property
|
|
@@ -162,7 +162,7 @@ class VispL1QualityMetrics(VispTaskBase, QualityMixin):
|
|
|
162
162
|
continue
|
|
163
163
|
|
|
164
164
|
# compute sensitivity for this Stokes parameter
|
|
165
|
-
data_list.append(np.
|
|
165
|
+
data_list.append(np.nanstd(stokes_frame.data) / stokesI_med)
|
|
166
166
|
|
|
167
167
|
all_datetimes.append(Time(np.mean(polarization_data.datetimes), format="mjd").isot)
|
|
168
168
|
for target, source in zip(
|
|
@@ -12,6 +12,7 @@ from astropy.time import TimeDelta
|
|
|
12
12
|
from dkist_processing_common.codecs.fits import fits_access_decoder
|
|
13
13
|
from dkist_processing_common.codecs.fits import fits_array_decoder
|
|
14
14
|
from dkist_processing_common.codecs.fits import fits_hdulist_encoder
|
|
15
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
15
16
|
from dkist_processing_common.models.task_name import TaskName
|
|
16
17
|
from dkist_processing_common.tasks.mixin.quality import QualityMixin
|
|
17
18
|
from dkist_processing_math.arithmetic import divide_arrays_by_array
|
|
@@ -21,6 +22,7 @@ from dkist_processing_math.statistics import average_numpy_arrays
|
|
|
21
22
|
from dkist_processing_pac.optics.telescope import Telescope
|
|
22
23
|
from dkist_service_configuration.logging import logger
|
|
23
24
|
|
|
25
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
24
26
|
from dkist_processing_visp.models.tags import VispTag
|
|
25
27
|
from dkist_processing_visp.parsers.visp_l0_fits_access import VispL0FitsAccess
|
|
26
28
|
from dkist_processing_visp.tasks.mixin.beam_access import BeamAccessMixin
|
|
@@ -49,6 +51,9 @@ class CalibrationCollection:
|
|
|
49
51
|
|
|
50
52
|
This is done by considering that state offset values computed by the GeometricCalibration task. Any sub-pixel
|
|
51
53
|
overlaps are rounded to the next integer that still guarantees overlap.
|
|
54
|
+
|
|
55
|
+
When "start pixels" are mentioned, those are pixels being counted from zero on a given axis in the positive direction.
|
|
56
|
+
When "end pixels" are mentioned, those are pixels being counted from the end of a given axis in the negative direction.
|
|
52
57
|
"""
|
|
53
58
|
logger.info("Computing beam overlap slices")
|
|
54
59
|
# This will be a flat list of (x, y) pairs for all modstates and beams
|
|
@@ -61,25 +66,33 @@ class CalibrationCollection:
|
|
|
61
66
|
logger.info(f"All x shifts: {all_x_shifts}")
|
|
62
67
|
logger.info(f"All y shifts: {all_y_shifts}")
|
|
63
68
|
|
|
64
|
-
# The amount we need to "slice in" from the
|
|
69
|
+
# The amount we need to "slice in" from the start of the array is equivalent to the absolute value of the most negative shift.
|
|
65
70
|
# The call to `np.ceil` ensures that the integer rounding doesn't allow non-overlap regions to leak in.
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
start_pixels_to_slice_x = int(np.ceil(abs(np.min(all_x_shifts))))
|
|
72
|
+
start_pixels_to_slice_y = int(np.ceil(abs(np.min(all_y_shifts))))
|
|
68
73
|
|
|
69
|
-
# The amount we need to "chop off" the end of the array is the most
|
|
74
|
+
# The amount we need to "chop off" the end of the array is the most positive shift.
|
|
70
75
|
#
|
|
71
76
|
# Here we rely on the fact that the fiducial array's shift is *always* (0, 0)
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
77
|
+
# (see `geometric.compute_modstate_offset`). Thus, if there are no negative shifts then the following lines
|
|
78
|
+
# will result in None. This is required for slicing because array[x:0] is no good. So if the max is 0 then we
|
|
79
|
+
# end up with array[x:None] which goes all the way to the end of the array.
|
|
75
80
|
#
|
|
76
|
-
# The call to `np.
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
# The call to `np.ceil` ensures that the integer rounding doesn't allow non-overlap regions to leak in.
|
|
82
|
+
# (because more negative slices will cut out more data).
|
|
83
|
+
end_pixels_to_slice_x = int(np.ceil(np.max(all_x_shifts))) or None
|
|
84
|
+
end_pixels_to_slice_y = int(np.ceil(np.max(all_y_shifts))) or None
|
|
85
|
+
|
|
86
|
+
# As the pixels to remove from the end of axes is given as a positive number, we need to make it negative for slicing.
|
|
87
|
+
if end_pixels_to_slice_x is not None:
|
|
88
|
+
end_pixels_to_slice_x *= -1
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
if end_pixels_to_slice_y is not None:
|
|
91
|
+
end_pixels_to_slice_y *= -1
|
|
92
|
+
|
|
93
|
+
# Construct the slices
|
|
94
|
+
x_slice = slice(start_pixels_to_slice_x, end_pixels_to_slice_x)
|
|
95
|
+
y_slice = slice(start_pixels_to_slice_y, end_pixels_to_slice_y)
|
|
83
96
|
|
|
84
97
|
return x_slice, y_slice
|
|
85
98
|
|
|
@@ -275,6 +288,7 @@ class ScienceCalibration(
|
|
|
275
288
|
for raster_step in range(0, self.constants.num_raster_steps):
|
|
276
289
|
beam_storage = dict()
|
|
277
290
|
header_storage = dict()
|
|
291
|
+
nan_storage = dict()
|
|
278
292
|
for beam in range(1, self.constants.num_beams + 1):
|
|
279
293
|
apm_str = f"{map_scan = }, {raster_step = }, and {beam = }"
|
|
280
294
|
with self.telemetry_span(f"Basic corrections for {apm_str}"):
|
|
@@ -286,6 +300,7 @@ class ScienceCalibration(
|
|
|
286
300
|
(
|
|
287
301
|
intermediate_array,
|
|
288
302
|
intermediate_header,
|
|
303
|
+
nan_mask,
|
|
289
304
|
) = self.process_polarimetric_modstates(
|
|
290
305
|
beam=beam,
|
|
291
306
|
raster_step=raster_step,
|
|
@@ -297,7 +312,11 @@ class ScienceCalibration(
|
|
|
297
312
|
logger.info(
|
|
298
313
|
f"Processing spectrographic observe frames from {apm_str}"
|
|
299
314
|
)
|
|
300
|
-
|
|
315
|
+
(
|
|
316
|
+
intermediate_array,
|
|
317
|
+
intermediate_header,
|
|
318
|
+
nan_mask,
|
|
319
|
+
) = self.correct_single_frame(
|
|
301
320
|
beam=beam,
|
|
302
321
|
modstate=1,
|
|
303
322
|
raster_step=raster_step,
|
|
@@ -308,6 +327,7 @@ class ScienceCalibration(
|
|
|
308
327
|
intermediate_header = self.compute_date_keys(intermediate_header)
|
|
309
328
|
beam_storage[VispTag.beam(beam)] = intermediate_array
|
|
310
329
|
header_storage[VispTag.beam(beam)] = intermediate_header
|
|
330
|
+
nan_storage[VispTag.beam(beam)] = nan_mask
|
|
311
331
|
|
|
312
332
|
with self.telemetry_span("Combining beams"):
|
|
313
333
|
calibrated = self.combine_beams(beam_storage, header_storage, calibrations)
|
|
@@ -316,12 +336,29 @@ class ScienceCalibration(
|
|
|
316
336
|
with self.telemetry_span("Correcting telescope polarization"):
|
|
317
337
|
calibrated = self.telescope_polarization_correction(calibrated)
|
|
318
338
|
|
|
339
|
+
with self.telemetry_span("Combining NaN masks from beams"):
|
|
340
|
+
cut_combined_nan_mask = self.combine_and_cut_nan_masks(
|
|
341
|
+
list(nan_storage.values()), calibrations
|
|
342
|
+
)
|
|
343
|
+
|
|
319
344
|
# Save the final output files
|
|
320
345
|
with self.telemetry_span("Writing calibrated arrays"):
|
|
321
346
|
self.write_calibrated_array(
|
|
322
|
-
calibrated,
|
|
347
|
+
calibrated,
|
|
348
|
+
map_scan=map_scan,
|
|
349
|
+
calibrations=calibrations,
|
|
350
|
+
nan_mask=cut_combined_nan_mask,
|
|
323
351
|
)
|
|
324
352
|
|
|
353
|
+
@staticmethod
|
|
354
|
+
def combine_and_cut_nan_masks(
|
|
355
|
+
nan_masks: list[np.ndarray], calibrations: CalibrationCollection
|
|
356
|
+
) -> np.ndarray:
|
|
357
|
+
"""Combine two NaN masks into one, cropping the result based on pre-calculated shifts."""
|
|
358
|
+
combined_nan_mask = np.logical_or.reduce(nan_masks)
|
|
359
|
+
x_slice, y_slice = calibrations.beams_overlap_slice
|
|
360
|
+
return combined_nan_mask[x_slice, y_slice]
|
|
361
|
+
|
|
325
362
|
def process_polarimetric_modstates(
|
|
326
363
|
self,
|
|
327
364
|
beam: int,
|
|
@@ -329,7 +366,7 @@ class ScienceCalibration(
|
|
|
329
366
|
map_scan: int,
|
|
330
367
|
readout_exp_time: float,
|
|
331
368
|
calibrations: CalibrationCollection,
|
|
332
|
-
) -> tuple[np.ndarray, fits.Header]:
|
|
369
|
+
) -> tuple[np.ndarray, fits.Header, np.ndarray]:
|
|
333
370
|
"""
|
|
334
371
|
Process a single polarimetric beam as much as is possible.
|
|
335
372
|
|
|
@@ -341,11 +378,12 @@ class ScienceCalibration(
|
|
|
341
378
|
].shape
|
|
342
379
|
array_stack = np.zeros(array_shape + (self.constants.num_modstates,))
|
|
343
380
|
header_stack = []
|
|
381
|
+
nan_mask_stack = np.zeros(array_shape + (self.constants.num_modstates,))
|
|
344
382
|
|
|
345
383
|
with self.telemetry_span(f"Correcting {self.constants.num_modstates} modstates"):
|
|
346
384
|
for modstate in range(1, self.constants.num_modstates + 1):
|
|
347
385
|
# Correct the arrays
|
|
348
|
-
corrected_array, corrected_header = self.correct_single_frame(
|
|
386
|
+
corrected_array, corrected_header, nan_mask = self.correct_single_frame(
|
|
349
387
|
beam=beam,
|
|
350
388
|
modstate=modstate,
|
|
351
389
|
raster_step=raster_step,
|
|
@@ -356,6 +394,7 @@ class ScienceCalibration(
|
|
|
356
394
|
# Add this result to the 3D stack
|
|
357
395
|
array_stack[:, :, modstate - 1] = corrected_array
|
|
358
396
|
header_stack.append(corrected_header)
|
|
397
|
+
nan_mask_stack[:, :, modstate - 1] = nan_mask
|
|
359
398
|
|
|
360
399
|
with self.telemetry_span("Applying instrument polarization correction"):
|
|
361
400
|
intermediate_array = nd_left_matrix_multiply(
|
|
@@ -364,7 +403,8 @@ class ScienceCalibration(
|
|
|
364
403
|
)
|
|
365
404
|
intermediate_header = self.compute_date_keys(header_stack)
|
|
366
405
|
|
|
367
|
-
|
|
406
|
+
# The modulator state NaN masks are stacked along axis=2 with axis=0 & 1 being the array axes of one modstate
|
|
407
|
+
return intermediate_array, intermediate_header, np.logical_or.reduce(nan_mask_stack, axis=2)
|
|
368
408
|
|
|
369
409
|
def combine_beams(
|
|
370
410
|
self,
|
|
@@ -438,6 +478,7 @@ class ScienceCalibration(
|
|
|
438
478
|
calibrated_object: VispL0FitsAccess,
|
|
439
479
|
map_scan: int,
|
|
440
480
|
calibrations: CalibrationCollection,
|
|
481
|
+
nan_mask: np.ndarray,
|
|
441
482
|
) -> None:
|
|
442
483
|
"""
|
|
443
484
|
Write out calibrated science frames.
|
|
@@ -456,6 +497,9 @@ class ScienceCalibration(
|
|
|
456
497
|
calibrations
|
|
457
498
|
Calibration collection
|
|
458
499
|
|
|
500
|
+
nan_mask
|
|
501
|
+
A mask containing the known areas where data does not exist for both beams
|
|
502
|
+
|
|
459
503
|
Returns
|
|
460
504
|
-------
|
|
461
505
|
None
|
|
@@ -471,7 +515,8 @@ class ScienceCalibration(
|
|
|
471
515
|
stokes_I_data = calibrated_object.data[:, :, 0]
|
|
472
516
|
for i, stokes_param in enumerate(self.constants.stokes_params):
|
|
473
517
|
stokes_data = calibrated_object.data[:, :, i]
|
|
474
|
-
|
|
518
|
+
nan_masked_data = np.where(nan_mask, np.nan, stokes_data)
|
|
519
|
+
final_data = self.re_dummy_data(nan_masked_data)
|
|
475
520
|
pol_header = self.add_L1_pol_headers(final_header, stokes_data, stokes_I_data)
|
|
476
521
|
self.write_cal_array(
|
|
477
522
|
data=final_data,
|
|
@@ -481,7 +526,8 @@ class ScienceCalibration(
|
|
|
481
526
|
map_scan=map_scan,
|
|
482
527
|
)
|
|
483
528
|
else: # Only write stokes I
|
|
484
|
-
|
|
529
|
+
nan_masked_data = np.where(nan_mask, np.nan, calibrated_object.data)
|
|
530
|
+
final_data = self.re_dummy_data(nan_masked_data)
|
|
485
531
|
self.write_cal_array(
|
|
486
532
|
data=final_data,
|
|
487
533
|
header=final_header,
|
|
@@ -498,7 +544,7 @@ class ScienceCalibration(
|
|
|
498
544
|
map_scan: int,
|
|
499
545
|
readout_exp_time: float,
|
|
500
546
|
calibrations: CalibrationCollection,
|
|
501
|
-
) -> tuple[np.ndarray, fits.Header]:
|
|
547
|
+
) -> tuple[np.ndarray, fits.Header, np.ndarray]:
|
|
502
548
|
"""
|
|
503
549
|
Apply basic corrections to a single frame.
|
|
504
550
|
|
|
@@ -593,7 +639,40 @@ class ScienceCalibration(
|
|
|
593
639
|
self.corrections_remove_spec_geometry(geo_corrected_array, spec_shift)
|
|
594
640
|
)
|
|
595
641
|
|
|
596
|
-
|
|
642
|
+
nan_mask = self.generate_nan_mask(
|
|
643
|
+
solar_corrected_array=solar_corrected_array,
|
|
644
|
+
state_offset=state_offset,
|
|
645
|
+
angle=angle,
|
|
646
|
+
spec_shift=spec_shift,
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
return (
|
|
650
|
+
spectral_corrected_array,
|
|
651
|
+
observe_object.header,
|
|
652
|
+
nan_mask,
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
def generate_nan_mask(
|
|
656
|
+
self,
|
|
657
|
+
solar_corrected_array: np.ndarray,
|
|
658
|
+
state_offset: np.ndarray,
|
|
659
|
+
angle: float,
|
|
660
|
+
spec_shift: np.ndarray,
|
|
661
|
+
) -> np.ndarray:
|
|
662
|
+
"""Calculate the NaN mask through geometric correction to be applied to the final L1 arrays."""
|
|
663
|
+
# Using a bi-cubic polynomial (order = 3) best converges to the desired result in the underlying fits
|
|
664
|
+
geo_corrected_with_nan = next(
|
|
665
|
+
self.corrections_correct_geometry(
|
|
666
|
+
solar_corrected_array, state_offset, angle, mode="constant", order=3, cval=np.nan
|
|
667
|
+
)
|
|
668
|
+
)
|
|
669
|
+
# Interpolating with nearest neighbor (order = 0) prevents NaN values from "taking over" the whole array
|
|
670
|
+
spectral_corrected_with_nan = next(
|
|
671
|
+
self.corrections_remove_spec_geometry(
|
|
672
|
+
geo_corrected_with_nan, spec_shift, cval=np.nan, order=0
|
|
673
|
+
)
|
|
674
|
+
)
|
|
675
|
+
return np.isnan(spectral_corrected_with_nan)
|
|
597
676
|
|
|
598
677
|
def telescope_polarization_correction(
|
|
599
678
|
self,
|
|
@@ -653,7 +732,7 @@ class ScienceCalibration(
|
|
|
653
732
|
date_end = (Time(sorted_obj_list[-1].time_obs) + exp_time).isot
|
|
654
733
|
|
|
655
734
|
header = sorted_obj_list[0].header
|
|
656
|
-
header[
|
|
735
|
+
header[MetadataKey.time_obs] = date_beg
|
|
657
736
|
header["DATE-END"] = date_end
|
|
658
737
|
|
|
659
738
|
return header
|
|
@@ -5,11 +5,13 @@ from typing import Literal
|
|
|
5
5
|
|
|
6
6
|
import astropy.units as u
|
|
7
7
|
from astropy.io import fits
|
|
8
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
8
9
|
from dkist_processing_common.tasks import WriteL1Frame
|
|
9
10
|
from dkist_processing_common.tasks.write_l1 import WavelengthRange
|
|
10
11
|
from dkist_service_configuration.logging import logger
|
|
11
12
|
|
|
12
13
|
from dkist_processing_visp.models.constants import VispConstants
|
|
14
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
13
15
|
|
|
14
16
|
cached_info_logger = cache(logger.info)
|
|
15
17
|
__all__ = ["VispWriteL1Frame"]
|
|
@@ -55,7 +57,7 @@ class VispWriteL1Frame(WriteL1Frame):
|
|
|
55
57
|
"""
|
|
56
58
|
# Correct the headers for the number of map and scan steps per map due to potential observation aborts
|
|
57
59
|
header["VSPNMAPS"] = self.constants.num_map_scans
|
|
58
|
-
header[
|
|
60
|
+
header[VispMetadataKey.total_raster_steps] = self.constants.num_raster_steps
|
|
59
61
|
|
|
60
62
|
if stokes.upper() not in self.constants.stokes_params:
|
|
61
63
|
raise ValueError("The stokes parameter must be one of I, Q, U, V")
|
|
@@ -92,7 +94,7 @@ class VispWriteL1Frame(WriteL1Frame):
|
|
|
92
94
|
header[f"CNAME{i}"] = "helioprojective longitude"
|
|
93
95
|
header[f"DUNIT{i}"] = header[f"CUNIT{i}"]
|
|
94
96
|
# Current position in raster scan which counts from zero
|
|
95
|
-
header[f"DINDEX{i}"] = header[
|
|
97
|
+
header[f"DINDEX{i}"] = header[VispMetadataKey.raster_scan_step] + 1
|
|
96
98
|
else:
|
|
97
99
|
raise ValueError(
|
|
98
100
|
f"Unexpected axis type. Expected ['HPLT-TAN', 'AWAV', 'HPLN-TAN']. Got {axis_type}"
|
|
@@ -207,5 +209,7 @@ class VispWriteL1Frame(WriteL1Frame):
|
|
|
207
209
|
header["CADMIN"] = self.constants.minimum_cadence * self.constants.num_modstates
|
|
208
210
|
header["CADMAX"] = self.constants.maximum_cadence * self.constants.num_modstates
|
|
209
211
|
header["CADVAR"] = self.constants.variance_cadence * self.constants.num_modstates
|
|
210
|
-
header[
|
|
212
|
+
header[MetadataKey.fpa_exposure_time_ms] = (
|
|
213
|
+
header[MetadataKey.fpa_exposure_time_ms] * self.constants.num_modstates
|
|
214
|
+
)
|
|
211
215
|
return header
|
|
@@ -11,6 +11,7 @@ from typing import Literal
|
|
|
11
11
|
from astropy.io import fits
|
|
12
12
|
from dkist_processing_common.codecs.basemodel import basemodel_encoder
|
|
13
13
|
from dkist_processing_common.manual import ManualProcessing
|
|
14
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
14
15
|
from dkist_processing_common.models.input_dataset import InputDatasetPartDocumentList
|
|
15
16
|
from dkist_processing_common.tasks import CreateTrialQualityReport
|
|
16
17
|
from dkist_processing_common.tasks import QualityL1Metrics
|
|
@@ -18,6 +19,7 @@ from dkist_processing_common.tasks import WorkflowTaskBase
|
|
|
18
19
|
from dkist_service_configuration.logging import logger
|
|
19
20
|
|
|
20
21
|
from dkist_processing_visp.models.constants import VispBudName
|
|
22
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
21
23
|
from dkist_processing_visp.models.tags import VispTag
|
|
22
24
|
from dkist_processing_visp.tasks import AssembleVispMovie
|
|
23
25
|
from dkist_processing_visp.tasks import MakeVispMovieFrames
|
|
@@ -156,7 +158,7 @@ class TagPolcalAsScience(VispTaskBase):
|
|
|
156
158
|
idx = 0
|
|
157
159
|
if hdul[idx].data is None:
|
|
158
160
|
idx = 1
|
|
159
|
-
hdul[idx].header[
|
|
161
|
+
hdul[idx].header[VispMetadataKey.raster_scan_step] = raster_step
|
|
160
162
|
hdul.flush()
|
|
161
163
|
del hdul
|
|
162
164
|
|
|
@@ -201,9 +203,9 @@ def write_L1_files_task(prefix: str = ""):
|
|
|
201
203
|
|
|
202
204
|
def l1_filename(self, header: fits.Header, stokes: Literal["I", "Q", "U", "V"]):
|
|
203
205
|
"""Do."""
|
|
204
|
-
wavelength = str(round(header[
|
|
206
|
+
wavelength = str(round(header[MetadataKey.wavelength] * 1000)).zfill(8)
|
|
205
207
|
cs_step = header["VSPMAP"]
|
|
206
|
-
raster_step = header[
|
|
208
|
+
raster_step = header[VispMetadataKey.raster_scan_step]
|
|
207
209
|
return f"{prefix}CS_STEP_{cs_step:02n}_{raster_step:02n}_{wavelength}_{stokes}_L1.fits"
|
|
208
210
|
|
|
209
211
|
return WritePolcalL1Files
|
|
@@ -30,6 +30,7 @@ from loguru import logger
|
|
|
30
30
|
|
|
31
31
|
from dkist_processing_visp.models.constants import VispBudName
|
|
32
32
|
from dkist_processing_visp.models.constants import VispConstants
|
|
33
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
33
34
|
from dkist_processing_visp.models.parameters import VispParsingParameters
|
|
34
35
|
from dkist_processing_visp.models.tags import VispTag
|
|
35
36
|
from dkist_processing_visp.models.task_name import VispTaskName
|
|
@@ -575,9 +576,9 @@ class TagSingleSolarGainAsScience(VispTaskBase):
|
|
|
575
576
|
avg_array = average_numpy_arrays(arrays=arrays)
|
|
576
577
|
|
|
577
578
|
hdul = fits.HDUList([fits.PrimaryHDU(data=avg_array, header=first_header)])
|
|
578
|
-
hdul[0].header[
|
|
579
|
-
hdul[0].header[
|
|
580
|
-
hdul[0].header[
|
|
579
|
+
hdul[0].header[VispMetadataKey.raster_scan_step] = 0
|
|
580
|
+
hdul[0].header[VispMetadataKey.total_raster_steps] = 1
|
|
581
|
+
hdul[0].header[VispMetadataKey.modulator_state] = 1
|
|
581
582
|
hdul[0].header["VSPPOLMD"] = "observe_intensity"
|
|
582
583
|
# hdul[0].header["POL_NOIS"] = 0.666
|
|
583
584
|
# hdul[0].header["POL_SENS"] = 0.666
|
|
@@ -626,9 +627,9 @@ class TagModulatedSolarGainsAsScience(VispTaskBase):
|
|
|
626
627
|
avg_array = average_numpy_arrays(arrays=arrays)
|
|
627
628
|
|
|
628
629
|
hdul = fits.HDUList([fits.PrimaryHDU(data=avg_array, header=first_header)])
|
|
629
|
-
hdul[0].header[
|
|
630
|
-
hdul[0].header[
|
|
631
|
-
hdul[0].header[
|
|
630
|
+
hdul[0].header[VispMetadataKey.raster_scan_step] = 0
|
|
631
|
+
hdul[0].header[VispMetadataKey.total_raster_steps] = 1
|
|
632
|
+
hdul[0].header[VispMetadataKey.modulator_state] = modstate
|
|
632
633
|
hdul[0].header["VSPPOLMD"] = "observe_polarimetric"
|
|
633
634
|
|
|
634
635
|
new_tags = [
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from dkist_header_validator.translator import translate_spec122_to_spec214_l0
|
|
3
|
+
|
|
4
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
5
|
+
from dkist_processing_visp.parsers.visp_l0_fits_access import VispL0FitsAccess
|
|
6
|
+
from dkist_processing_visp.parsers.visp_l1_fits_access import VispL1FitsAccess
|
|
7
|
+
from dkist_processing_visp.tests.header_models import VispHeadersValidObserveFrames
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(scope="session")
|
|
11
|
+
def complete_header():
|
|
12
|
+
dataset = VispHeadersValidObserveFrames(
|
|
13
|
+
array_shape=(1, 1, 1),
|
|
14
|
+
time_delta=10,
|
|
15
|
+
num_maps=1,
|
|
16
|
+
num_raster_steps=1,
|
|
17
|
+
num_modstates=1,
|
|
18
|
+
)
|
|
19
|
+
header = translate_spec122_to_spec214_l0(dataset.header())
|
|
20
|
+
return header
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_metadata_keys_in_access_bases(complete_header):
|
|
24
|
+
"""
|
|
25
|
+
Given: the set of metadata key names in VispMetadataKey
|
|
26
|
+
When: the ViSP FITS access classes define a set of new attributes
|
|
27
|
+
Then: the sets are the same and the attributes have the correct values
|
|
28
|
+
"""
|
|
29
|
+
# Given
|
|
30
|
+
visp_metadata_key_names = {vmk.name for vmk in VispMetadataKey}
|
|
31
|
+
# When
|
|
32
|
+
all_visp_fits_access_attrs = set()
|
|
33
|
+
for access_class in [VispL0FitsAccess, VispL1FitsAccess]:
|
|
34
|
+
fits_obj = access_class.from_header(complete_header)
|
|
35
|
+
visp_instance_attrs = set(vars(fits_obj).keys())
|
|
36
|
+
parent_class = access_class.mro()[1]
|
|
37
|
+
parent_fits_obj = parent_class.from_header(complete_header)
|
|
38
|
+
parent_instance_attrs = set(vars(parent_fits_obj).keys())
|
|
39
|
+
visp_fits_access_attrs = visp_instance_attrs - parent_instance_attrs
|
|
40
|
+
for attr in visp_fits_access_attrs:
|
|
41
|
+
assert getattr(fits_obj, attr) == fits_obj.header[VispMetadataKey[attr]]
|
|
42
|
+
all_visp_fits_access_attrs |= visp_fits_access_attrs
|
|
43
|
+
assert visp_metadata_key_names == all_visp_fits_access_attrs
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from astropy.io import fits
|
|
3
3
|
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
4
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
4
5
|
|
|
5
6
|
from dkist_processing_visp.models.tags import VispTag
|
|
6
7
|
from dkist_processing_visp.tasks.make_movie_frames import MakeVispMovieFrames
|
|
@@ -56,5 +57,5 @@ def test_make_movie_frames(movie_frames_task, pol_mode, mocker, fake_gql_client)
|
|
|
56
57
|
for filepath in task.read(tags=[VispTag.movie_frame()]):
|
|
57
58
|
assert filepath.exists()
|
|
58
59
|
hdul = fits.open(filepath)
|
|
59
|
-
assert hdul[0].header[
|
|
60
|
+
assert hdul[0].header[MetadataKey.instrument] == "VISP"
|
|
60
61
|
assert hdul[0].data.shape == expected_movie_fram_shape
|
|
@@ -14,6 +14,7 @@ from dkist_processing_common.parsers.single_value_single_key_flower import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
from dkist_processing_visp.models.constants import VispBudName
|
|
17
|
+
from dkist_processing_visp.models.fits_access import VispMetadataKey
|
|
17
18
|
from dkist_processing_visp.models.tags import VispStemName
|
|
18
19
|
from dkist_processing_visp.models.tags import VispTag
|
|
19
20
|
from dkist_processing_visp.parsers.map_repeats import MapScanFlower
|
|
@@ -90,7 +91,8 @@ class ParseTaskJustMapStuff(ParseL0VispInputData):
|
|
|
90
91
|
MapScanFlower(),
|
|
91
92
|
RasterScanStepFlower(),
|
|
92
93
|
SingleValueSingleKeyFlower(
|
|
93
|
-
tag_stem_name=VispStemName.modstate.value,
|
|
94
|
+
tag_stem_name=VispStemName.modstate.value,
|
|
95
|
+
metadata_key=VispMetadataKey.modulator_state,
|
|
94
96
|
),
|
|
95
97
|
]
|
|
96
98
|
|
|
@@ -12,6 +12,7 @@ from dkist_header_validator import spec122_validator
|
|
|
12
12
|
from dkist_processing_common._util.scratch import WorkflowFileSystem
|
|
13
13
|
from dkist_processing_common.codecs.fits import fits_array_encoder
|
|
14
14
|
from dkist_processing_common.codecs.fits import fits_hdu_decoder
|
|
15
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
15
16
|
from dkist_processing_common.models.tags import Tag
|
|
16
17
|
|
|
17
18
|
from dkist_processing_visp.models.tags import VispStemName
|
|
@@ -183,7 +184,7 @@ def headers_with_dates() -> tuple[list[fits.Header], str, int, int]:
|
|
|
183
184
|
]
|
|
184
185
|
random.shuffle(headers) # Shuffle to make sure they're not already in time order
|
|
185
186
|
for h in headers:
|
|
186
|
-
h[
|
|
187
|
+
h[MetadataKey.fpa_exposure_time_ms] = exp_time # Exposure time, in ms
|
|
187
188
|
|
|
188
189
|
return headers, start_time, exp_time, time_delta
|
|
189
190
|
|
|
@@ -363,10 +364,10 @@ def test_science_calibration_task(
|
|
|
363
364
|
assert header["VSPMAP"] == map_scan
|
|
364
365
|
|
|
365
366
|
# Check that WCS keys were updated
|
|
366
|
-
if offsets[1, 0, 0]
|
|
367
|
-
assert header["CRPIX2"] == input_header["CRPIX2"] - np.ceil(offsets[1, 0, 0])
|
|
368
|
-
if offsets[1, 0, 1]
|
|
369
|
-
assert header["CRPIX1"] == input_header["CRPIX1"] - np.ceil(offsets[1, 0, 1])
|
|
367
|
+
if offsets[1, 0, 0] < 0:
|
|
368
|
+
assert header["CRPIX2"] == input_header["CRPIX2"] - np.ceil(-offsets[1, 0, 0])
|
|
369
|
+
if offsets[1, 0, 1] < 0:
|
|
370
|
+
assert header["CRPIX1"] == input_header["CRPIX1"] - np.ceil(-offsets[1, 0, 1])
|
|
370
371
|
|
|
371
372
|
quality_files = task.read(tags=[Tag.quality("TASK_TYPES")])
|
|
372
373
|
for file in quality_files:
|
|
@@ -419,7 +420,7 @@ def test_readout_normalization_correct(
|
|
|
419
420
|
)
|
|
420
421
|
|
|
421
422
|
# When:
|
|
422
|
-
corrected_array, _ = task.correct_single_frame(
|
|
423
|
+
corrected_array, _, _ = task.correct_single_frame(
|
|
423
424
|
beam=1,
|
|
424
425
|
modstate=1,
|
|
425
426
|
raster_step=1,
|
|
@@ -512,7 +513,7 @@ def test_compute_date_keys_compressed_headers(
|
|
|
512
513
|
[[1.0, 2.0], [11.0, 10.0], [3.0, 2.0]], # Beam 2
|
|
513
514
|
]
|
|
514
515
|
),
|
|
515
|
-
[slice(
|
|
516
|
+
[slice(0, -11, None), slice(0, -10, None)],
|
|
516
517
|
),
|
|
517
518
|
(
|
|
518
519
|
np.array(
|
|
@@ -521,7 +522,7 @@ def test_compute_date_keys_compressed_headers(
|
|
|
521
522
|
[[-1.0, -2.0], [-11.0, -10.0], [-3.0, -2.0]], # Beam 2
|
|
522
523
|
]
|
|
523
524
|
),
|
|
524
|
-
[slice(
|
|
525
|
+
[slice(11, None, None), slice(10, None, None)],
|
|
525
526
|
),
|
|
526
527
|
(
|
|
527
528
|
np.array(
|
|
@@ -530,7 +531,7 @@ def test_compute_date_keys_compressed_headers(
|
|
|
530
531
|
[[1.0, 2.0], [-11.0, 10.0], [-3.0, -2.0]], # Beam 2
|
|
531
532
|
]
|
|
532
533
|
),
|
|
533
|
-
[slice(
|
|
534
|
+
[slice(11, -10, None), slice(2, -10, None)],
|
|
534
535
|
),
|
|
535
536
|
],
|
|
536
537
|
ids=["All positive", "All negative", "Positive and negative"],
|
|
@@ -580,3 +581,98 @@ def test_combine_beams(
|
|
|
580
581
|
expected = np.ones((10, 10, 4)) * 2.5
|
|
581
582
|
|
|
582
583
|
np.testing.assert_array_equal(data, expected)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
@pytest.mark.parametrize(
|
|
587
|
+
"shifts",
|
|
588
|
+
# Shifts have shape (num_beams, num_modstates, 2)
|
|
589
|
+
# So the inner-most lists below (e.g., [5.0, 6.0]) correspond to [x_shift, y_shit]
|
|
590
|
+
[
|
|
591
|
+
np.array(
|
|
592
|
+
[
|
|
593
|
+
[[0.0, 0.0], [10.0, 2.0], [5.0, 6.0]], # Beam 1
|
|
594
|
+
[[1.0, 2.0], [-11.0, 10.0], [-3.0, -2.0]], # Beam 2
|
|
595
|
+
]
|
|
596
|
+
),
|
|
597
|
+
],
|
|
598
|
+
ids=["Positive and negative"],
|
|
599
|
+
)
|
|
600
|
+
def test_combine_and_cut_nan_masks(
|
|
601
|
+
science_calibration_task, calibration_collection_with_geo_shifts, shifts
|
|
602
|
+
):
|
|
603
|
+
"""
|
|
604
|
+
Given: A ScienceCalibration task and NaN masks, along with geometric shifts
|
|
605
|
+
When: Combining the two NaN masks
|
|
606
|
+
Then: The final mask has NaN values in the correct place and is correctly cropped
|
|
607
|
+
"""
|
|
608
|
+
nan_1_location = [0, 1]
|
|
609
|
+
nan_2_location = [50, 50]
|
|
610
|
+
nan_3_location = [4, 1]
|
|
611
|
+
nan_4_location = [55, 63]
|
|
612
|
+
nan_mask_shape = (100, 100)
|
|
613
|
+
nan_mask_1 = np.zeros(shape=nan_mask_shape)
|
|
614
|
+
nan_mask_1[nan_1_location[0], nan_1_location[1]] = np.nan
|
|
615
|
+
nan_mask_1[nan_2_location[0], nan_2_location[1]] = np.nan
|
|
616
|
+
nan_mask_2 = np.zeros(shape=nan_mask_shape)
|
|
617
|
+
nan_mask_2[nan_3_location[0], nan_3_location[1]] = np.nan
|
|
618
|
+
nan_mask_2[nan_4_location[0], nan_4_location[1]] = np.nan
|
|
619
|
+
task, _, _, _, _, _ = science_calibration_task
|
|
620
|
+
combined_nan_mask = task.combine_and_cut_nan_masks(
|
|
621
|
+
nan_masks=[nan_mask_1, nan_mask_2], calibrations=calibration_collection_with_geo_shifts
|
|
622
|
+
)
|
|
623
|
+
beam_1_shifts = shifts[0]
|
|
624
|
+
beam_2_shifts = shifts[1]
|
|
625
|
+
beam_1_x_shifts = [i[0] for i in beam_1_shifts]
|
|
626
|
+
beam_2_x_shifts = [i[0] for i in beam_2_shifts]
|
|
627
|
+
beam_1_y_shifts = [i[1] for i in beam_1_shifts]
|
|
628
|
+
beam_2_y_shifts = [i[1] for i in beam_2_shifts]
|
|
629
|
+
x_shifts = beam_1_x_shifts + beam_2_x_shifts
|
|
630
|
+
y_shifts = beam_1_y_shifts + beam_2_y_shifts
|
|
631
|
+
assert combined_nan_mask.shape == (
|
|
632
|
+
nan_mask_shape[0] - (max(x_shifts) - min(x_shifts)),
|
|
633
|
+
nan_mask_shape[1] - (max(y_shifts) - min(y_shifts)),
|
|
634
|
+
)
|
|
635
|
+
# Check that one NaN value from each original mask is present in the combined mask and in the correct place
|
|
636
|
+
assert (
|
|
637
|
+
combined_nan_mask[
|
|
638
|
+
nan_2_location[0] - int(abs(min(x_shifts))), nan_2_location[1] - int(abs(min(y_shifts)))
|
|
639
|
+
]
|
|
640
|
+
== True
|
|
641
|
+
)
|
|
642
|
+
assert (
|
|
643
|
+
combined_nan_mask[
|
|
644
|
+
nan_4_location[0] - int(abs(min(x_shifts))), nan_4_location[1] - int(abs(min(y_shifts)))
|
|
645
|
+
]
|
|
646
|
+
== True
|
|
647
|
+
)
|
|
648
|
+
assert np.sum(combined_nan_mask) == 2 # only two NaN values are in the final mask
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def test_generate_nan_mask(science_calibration_task, dummy_calibration_collection):
|
|
652
|
+
"""
|
|
653
|
+
Given: a calibration collection
|
|
654
|
+
When: calculating the NaN mask to use
|
|
655
|
+
Then: the mask takes up some, but not all, of the frame size
|
|
656
|
+
"""
|
|
657
|
+
task, _, _, _, _, _ = science_calibration_task
|
|
658
|
+
calibration_collection, _, _ = dummy_calibration_collection
|
|
659
|
+
beam = 1
|
|
660
|
+
modstate = 1
|
|
661
|
+
solar_gain_array = calibration_collection.solar_gain[VispTag.beam(beam)][
|
|
662
|
+
VispTag.modstate(modstate)
|
|
663
|
+
]
|
|
664
|
+
angle = calibration_collection.angle[VispTag.beam(beam)]
|
|
665
|
+
spec_shift = calibration_collection.spec_shift[VispTag.beam(beam)]
|
|
666
|
+
state_offset = calibration_collection.state_offset[VispTag.beam(beam)][
|
|
667
|
+
VispTag.modstate(modstate)
|
|
668
|
+
]
|
|
669
|
+
nan_mask = task.generate_nan_mask(
|
|
670
|
+
solar_corrected_array=np.random.random(size=solar_gain_array.shape),
|
|
671
|
+
state_offset=state_offset,
|
|
672
|
+
angle=angle,
|
|
673
|
+
spec_shift=spec_shift,
|
|
674
|
+
)
|
|
675
|
+
# Some of the mask is marked as NaN but not all
|
|
676
|
+
assert np.sum(nan_mask) < np.size(nan_mask)
|
|
677
|
+
# Ensure that only zeroes and ones are in the mask
|
|
678
|
+
assert set(np.unique(nan_mask)) == {0, 1}
|
|
@@ -4,6 +4,7 @@ from astropy.io import fits
|
|
|
4
4
|
from astropy.time import Time
|
|
5
5
|
from dkist_fits_specifications import __version__ as spec_version
|
|
6
6
|
from dkist_header_validator import spec214_validator
|
|
7
|
+
from dkist_processing_common.models.fits_access import MetadataKey
|
|
7
8
|
from dkist_processing_common.models.tags import Tag
|
|
8
9
|
from dkist_processing_common.models.wavelength import WavelengthRange
|
|
9
10
|
from dkist_spectral_lines import get_closest_spectral_line
|
|
@@ -176,7 +177,7 @@ def test_write_l1_frame(
|
|
|
176
177
|
|
|
177
178
|
if pol_mode == "observe_polarimetric":
|
|
178
179
|
assert header["CADENCE"] == 100
|
|
179
|
-
assert header[
|
|
180
|
+
assert header[MetadataKey.fpa_exposure_time_ms] == 150
|
|
180
181
|
else:
|
|
181
182
|
assert header["CADENCE"] == 10
|
|
182
|
-
assert header[
|
|
183
|
+
assert header[MetadataKey.fpa_exposure_time_ms] == 15
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dkist-processing-visp
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: Science processing code for the ViSP instrument on DKIST
|
|
5
5
|
Author-email: NSO / AURA <dkistdc@nso.edu>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -76,6 +76,7 @@ Requires-Dist: Flask-Login==0.6.3; extra == "frozen"
|
|
|
76
76
|
Requires-Dist: Flask-SQLAlchemy==2.5.1; extra == "frozen"
|
|
77
77
|
Requires-Dist: Flask-Session==0.5.0; extra == "frozen"
|
|
78
78
|
Requires-Dist: Flask-WTF==1.2.2; extra == "frozen"
|
|
79
|
+
Requires-Dist: ImageIO==2.37.2; extra == "frozen"
|
|
79
80
|
Requires-Dist: Jinja2==3.1.6; extra == "frozen"
|
|
80
81
|
Requires-Dist: Mako==1.3.10; extra == "frozen"
|
|
81
82
|
Requires-Dist: MarkupSafe==3.0.3; extra == "frozen"
|
|
@@ -115,19 +116,19 @@ Requires-Dist: asdf==3.5.0; extra == "frozen"
|
|
|
115
116
|
Requires-Dist: asdf_standard==1.4.0; extra == "frozen"
|
|
116
117
|
Requires-Dist: asdf_transform_schemas==0.6.0; extra == "frozen"
|
|
117
118
|
Requires-Dist: asgiref==3.10.0; extra == "frozen"
|
|
118
|
-
Requires-Dist: asteval==1.0.
|
|
119
|
+
Requires-Dist: asteval==1.0.7; extra == "frozen"
|
|
119
120
|
Requires-Dist: astropy==7.0.2; extra == "frozen"
|
|
120
|
-
Requires-Dist: astropy-iers-data==0.2025.11.
|
|
121
|
+
Requires-Dist: astropy-iers-data==0.2025.11.10.0.38.31; extra == "frozen"
|
|
121
122
|
Requires-Dist: asyncpg==0.30.0; extra == "frozen"
|
|
122
123
|
Requires-Dist: attrs==25.4.0; extra == "frozen"
|
|
123
124
|
Requires-Dist: babel==2.17.0; extra == "frozen"
|
|
124
125
|
Requires-Dist: billiard==4.2.2; extra == "frozen"
|
|
125
126
|
Requires-Dist: blinker==1.9.0; extra == "frozen"
|
|
126
|
-
Requires-Dist: boto3==1.40.
|
|
127
|
-
Requires-Dist: botocore==1.40.
|
|
127
|
+
Requires-Dist: boto3==1.40.71; extra == "frozen"
|
|
128
|
+
Requires-Dist: botocore==1.40.71; extra == "frozen"
|
|
128
129
|
Requires-Dist: cachelib==0.13.0; extra == "frozen"
|
|
129
130
|
Requires-Dist: celery==5.3.1; extra == "frozen"
|
|
130
|
-
Requires-Dist: certifi==2025.
|
|
131
|
+
Requires-Dist: certifi==2025.11.12; extra == "frozen"
|
|
131
132
|
Requires-Dist: cffi==2.0.0; extra == "frozen"
|
|
132
133
|
Requires-Dist: charset-normalizer==3.4.4; extra == "frozen"
|
|
133
134
|
Requires-Dist: click==8.3.0; extra == "frozen"
|
|
@@ -151,7 +152,7 @@ Requires-Dist: dkist-processing-common==11.8.0; extra == "frozen"
|
|
|
151
152
|
Requires-Dist: dkist-processing-core==6.0.0; extra == "frozen"
|
|
152
153
|
Requires-Dist: dkist-processing-math==2.2.1; extra == "frozen"
|
|
153
154
|
Requires-Dist: dkist-processing-pac==3.1.1; extra == "frozen"
|
|
154
|
-
Requires-Dist: dkist-processing-visp==
|
|
155
|
+
Requires-Dist: dkist-processing-visp==4.0.0; extra == "frozen"
|
|
155
156
|
Requires-Dist: dkist-service-configuration==4.1.7; extra == "frozen"
|
|
156
157
|
Requires-Dist: dkist-spectral-lines==3.0.0; extra == "frozen"
|
|
157
158
|
Requires-Dist: dkist_fits_specifications==4.17.0; extra == "frozen"
|
|
@@ -163,10 +164,9 @@ Requires-Dist: fonttools==4.60.1; extra == "frozen"
|
|
|
163
164
|
Requires-Dist: frozenlist==1.8.0; extra == "frozen"
|
|
164
165
|
Requires-Dist: fsspec==2025.10.0; extra == "frozen"
|
|
165
166
|
Requires-Dist: globus-sdk==3.65.0; extra == "frozen"
|
|
166
|
-
Requires-Dist: google-re2==1.1.
|
|
167
|
-
Requires-Dist: googleapis-common-protos==1.
|
|
167
|
+
Requires-Dist: google-re2==1.1.20251105; extra == "frozen"
|
|
168
|
+
Requires-Dist: googleapis-common-protos==1.72.0; extra == "frozen"
|
|
168
169
|
Requires-Dist: gqlclient==1.2.3; extra == "frozen"
|
|
169
|
-
Requires-Dist: greenlet==3.2.4; extra == "frozen"
|
|
170
170
|
Requires-Dist: grpcio==1.76.0; extra == "frozen"
|
|
171
171
|
Requires-Dist: gunicorn==23.0.0; extra == "frozen"
|
|
172
172
|
Requires-Dist: h11==0.16.0; extra == "frozen"
|
|
@@ -174,7 +174,6 @@ Requires-Dist: httpcore==1.0.9; extra == "frozen"
|
|
|
174
174
|
Requires-Dist: httpx==0.28.1; extra == "frozen"
|
|
175
175
|
Requires-Dist: humanize==4.14.0; extra == "frozen"
|
|
176
176
|
Requires-Dist: idna==3.11; extra == "frozen"
|
|
177
|
-
Requires-Dist: imageio==2.37.0; extra == "frozen"
|
|
178
177
|
Requires-Dist: imageio-ffmpeg==0.6.0; extra == "frozen"
|
|
179
178
|
Requires-Dist: importlib_metadata==8.7.0; extra == "frozen"
|
|
180
179
|
Requires-Dist: inflection==0.5.1; extra == "frozen"
|
|
@@ -254,9 +253,9 @@ Requires-Dist: protobuf==6.33.0; extra == "frozen"
|
|
|
254
253
|
Requires-Dist: psutil==7.1.3; extra == "frozen"
|
|
255
254
|
Requires-Dist: psycopg2-binary==2.9.11; extra == "frozen"
|
|
256
255
|
Requires-Dist: pycparser==2.23; extra == "frozen"
|
|
257
|
-
Requires-Dist: pydantic==2.12.
|
|
258
|
-
Requires-Dist: pydantic-settings==2.
|
|
259
|
-
Requires-Dist: pydantic_core==2.41.
|
|
256
|
+
Requires-Dist: pydantic==2.12.4; extra == "frozen"
|
|
257
|
+
Requires-Dist: pydantic-settings==2.12.0; extra == "frozen"
|
|
258
|
+
Requires-Dist: pydantic_core==2.41.5; extra == "frozen"
|
|
260
259
|
Requires-Dist: pyerfa==2.0.1.5; extra == "frozen"
|
|
261
260
|
Requires-Dist: pyparsing==3.2.5; extra == "frozen"
|
|
262
261
|
Requires-Dist: python-daemon==3.1.2; extra == "frozen"
|
|
@@ -300,7 +299,7 @@ Requires-Dist: typing_extensions==4.15.0; extra == "frozen"
|
|
|
300
299
|
Requires-Dist: tzdata==2025.2; extra == "frozen"
|
|
301
300
|
Requires-Dist: uc-micro-py==1.0.3; extra == "frozen"
|
|
302
301
|
Requires-Dist: uncertainties==3.2.3; extra == "frozen"
|
|
303
|
-
Requires-Dist: universal_pathlib==0.3.
|
|
302
|
+
Requires-Dist: universal_pathlib==0.3.5; extra == "frozen"
|
|
304
303
|
Requires-Dist: urllib3==2.5.0; extra == "frozen"
|
|
305
304
|
Requires-Dist: vine==5.1.0; extra == "frozen"
|
|
306
305
|
Requires-Dist: voluptuous==0.15.2; extra == "frozen"
|
|
@@ -3,17 +3,18 @@ dkist_processing_visp/__init__.py,sha256=LC8o31oTIro4F7IgwoWalX1W3KcPU27yJhlDUeG
|
|
|
3
3
|
dkist_processing_visp/config.py,sha256=GMr0CreW4qavbueTtsH_Gx5P52v4yZd2PNKyPmxBKQE,478
|
|
4
4
|
dkist_processing_visp/fonts/Lato-Regular.ttf,sha256=1jbkaDIx-THtoiLViOlE0IK_0726AvkovuRhwPGFslE,656568
|
|
5
5
|
dkist_processing_visp/models/__init__.py,sha256=z2nFVvvIzirxklQ9i5-F1nR-WOgcDttYtog_jx4yN5I,12
|
|
6
|
-
dkist_processing_visp/models/constants.py,sha256=
|
|
6
|
+
dkist_processing_visp/models/constants.py,sha256=sizf2PB3H8BVughHsJhh_fJUeNkN-0uHpo-DVy1_wdo,5140
|
|
7
|
+
dkist_processing_visp/models/fits_access.py,sha256=-W5hP5U9n4uMCg2A_CSJuM3L6dipktZIjoUxyQfT0O8,435
|
|
7
8
|
dkist_processing_visp/models/parameters.py,sha256=m5o41KsPO99WUgWlY4-ezu4wqvQVMGpQ3fNuMcFkSu0,9831
|
|
8
9
|
dkist_processing_visp/models/tags.py,sha256=RKtDlNA9O3LMinUP7BJ3tXWn6CWAARaKhXIVB1EHtg0,3055
|
|
9
10
|
dkist_processing_visp/models/task_name.py,sha256=ykLXcmCBWYguopdBlg_G0X_u-0o_Ugh2117XrCqdbFk,187
|
|
10
11
|
dkist_processing_visp/parsers/__init__.py,sha256=z2nFVvvIzirxklQ9i5-F1nR-WOgcDttYtog_jx4yN5I,12
|
|
11
12
|
dkist_processing_visp/parsers/map_repeats.py,sha256=YuO1VROQLuE-Hn9hSzityjNhIDe-EgQ4kjZV6l9xF2Q,5468
|
|
12
13
|
dkist_processing_visp/parsers/modulator_states.py,sha256=dHAZZaG3i_UUT5FjTg1oJdCBiOKCqkrx1jiQnzp2t2o,3006
|
|
13
|
-
dkist_processing_visp/parsers/polarimeter_mode.py,sha256=
|
|
14
|
-
dkist_processing_visp/parsers/raster_step.py,sha256=
|
|
15
|
-
dkist_processing_visp/parsers/time.py,sha256=
|
|
16
|
-
dkist_processing_visp/parsers/visp_l0_fits_access.py,sha256=
|
|
14
|
+
dkist_processing_visp/parsers/polarimeter_mode.py,sha256=vOE8IYlBrgAFGSrDbpUDfHnI3OTPBDpd3U_j5totZb8,625
|
|
15
|
+
dkist_processing_visp/parsers/raster_step.py,sha256=tCmXPhG99z54YVX3X-kLOGDKJRTDJHnDbjNev4An7Pg,3142
|
|
16
|
+
dkist_processing_visp/parsers/time.py,sha256=uudQ5manYdL7SgxqABfFPDzW2iTNrTYF0klqCRsP0CI,4812
|
|
17
|
+
dkist_processing_visp/parsers/visp_l0_fits_access.py,sha256=vZGLGQNofRFziTSBOTf4_digLlOdRRGeFEvrAQouXTI,1609
|
|
17
18
|
dkist_processing_visp/parsers/visp_l1_fits_access.py,sha256=1MrFfsJjT_7fd1cj8tFr5rHX2JdRSrlwiMCzu-Q8ejY,860
|
|
18
19
|
dkist_processing_visp/tasks/__init__.py,sha256=qlPlahiM9_sCsaIj_wzQpzWkMITJ1dPdT93iV9q-fgg,713
|
|
19
20
|
dkist_processing_visp/tasks/assemble_movie.py,sha256=8UujniXlV_sSGeuISud8wMHihLy6Gc5fKZpwkXLUQB8,3330
|
|
@@ -23,16 +24,16 @@ dkist_processing_visp/tasks/geometric.py,sha256=rvWa13T2_cPQtiMjlWrqMYS5UvL8Xgmi
|
|
|
23
24
|
dkist_processing_visp/tasks/instrument_polarization.py,sha256=uj7iyzM3CiJcbQeF4eKpk_KCoheXaM4FpDI83GYDld4,25854
|
|
24
25
|
dkist_processing_visp/tasks/l1_output_data.py,sha256=lon6bIUBvURV_7gn1HFGAGVuEiLoOiMaJwfbdhSRW3w,459
|
|
25
26
|
dkist_processing_visp/tasks/lamp.py,sha256=RciNB8zW5fDx5yrsaNZgFP7LsajmSvkTNvbe3Sm6tjc,6060
|
|
26
|
-
dkist_processing_visp/tasks/make_movie_frames.py,sha256=
|
|
27
|
-
dkist_processing_visp/tasks/parse.py,sha256=
|
|
28
|
-
dkist_processing_visp/tasks/quality_metrics.py,sha256=
|
|
29
|
-
dkist_processing_visp/tasks/science.py,sha256
|
|
27
|
+
dkist_processing_visp/tasks/make_movie_frames.py,sha256=fw25ksKiJJNS57XV5a7rHpYGcSkYxS2Qf13Fb1UGNpE,7544
|
|
28
|
+
dkist_processing_visp/tasks/parse.py,sha256=3f8LWSKQtuY7n-PdHiw1j55i5l4juBn3pIHqTtp2-rk,6752
|
|
29
|
+
dkist_processing_visp/tasks/quality_metrics.py,sha256=Pw55-PXW0cl39FuNkEQCGGhvI_zMDimwmh-swVPVBD4,8133
|
|
30
|
+
dkist_processing_visp/tasks/science.py,sha256=BiEtgDowExHAXXzfO4BSr34BT0PZ28CdtOJMP4TDA20,34680
|
|
30
31
|
dkist_processing_visp/tasks/solar.py,sha256=QPkrJqUz6gYxFFvNlATMKa4NIEdn4iassYn86qPb60k,27567
|
|
31
32
|
dkist_processing_visp/tasks/visp_base.py,sha256=flUM-dCWkjcGvfaiFWH89DRZXz7NADgShxzpFjFqVEw,1370
|
|
32
|
-
dkist_processing_visp/tasks/write_l1.py,sha256=
|
|
33
|
+
dkist_processing_visp/tasks/write_l1.py,sha256=bsDZ0BwoqpTtS_f_rAzUn7Ra8UvYb-kINQhX6BwwFQw,8796
|
|
33
34
|
dkist_processing_visp/tasks/mixin/__init__.py,sha256=z2nFVvvIzirxklQ9i5-F1nR-WOgcDttYtog_jx4yN5I,12
|
|
34
35
|
dkist_processing_visp/tasks/mixin/beam_access.py,sha256=1VSJkH6yMxCiZWdWOp_RJ37fX5ULMYmB_0_ulT7YJpI,870
|
|
35
|
-
dkist_processing_visp/tasks/mixin/corrections.py,sha256=
|
|
36
|
+
dkist_processing_visp/tasks/mixin/corrections.py,sha256=FhLFgD9ZYLZd3SaC3PFF-szrcs-zmdrUYNDUEK-h7JA,7145
|
|
36
37
|
dkist_processing_visp/tasks/mixin/downsample.py,sha256=SvKzY6HJRn-FeyG7O6HPvyOS5dmMu6uPoWkfnpPXpVw,1344
|
|
37
38
|
dkist_processing_visp/tasks/mixin/line_zones.py,sha256=5jfea9V5RJAi-834z_Y9v4fhlRFJdK1McAqO9X92bZo,4065
|
|
38
39
|
dkist_processing_visp/tests/README.rst,sha256=rnedwwg25c0lB9Me7cT7QNZA17FYlqCu9ZnjQxR5hi0,12502
|
|
@@ -44,26 +45,27 @@ dkist_processing_visp/tests/test_assemble_quality.py,sha256=Hm0nAW90Kbb-6OLkUsW6
|
|
|
44
45
|
dkist_processing_visp/tests/test_background_light.py,sha256=Zvm8s38qx_ybviEhnKqPI4s36VFBJKtsNrp31-o8lEQ,17553
|
|
45
46
|
dkist_processing_visp/tests/test_dark.py,sha256=iKp12bHOOKPf7GAB8iKYpZu1AXFaESAW6C0ua1nVFXA,5552
|
|
46
47
|
dkist_processing_visp/tests/test_downsample.py,sha256=iSmb4PwpZtnVU06tmlko1wwepWueQ3KJ459XYgNIpws,2211
|
|
48
|
+
dkist_processing_visp/tests/test_fits_access.py,sha256=jOaSHDydzPl48SSBxt3z3CtCzBgCmSCc3rZ92CpoAaY,1822
|
|
47
49
|
dkist_processing_visp/tests/test_geometric.py,sha256=60W1Vv16JtQBnsP5CS10sOksXF8NzfoQDvl46C7fT3U,12536
|
|
48
50
|
dkist_processing_visp/tests/test_instrument_polarization.py,sha256=AntdpdmKtUJmr0VtooucqaMzXwgPqFgChU88_vFUtBU,11853
|
|
49
51
|
dkist_processing_visp/tests/test_lamp.py,sha256=_ibg4ORd_19eORS24zgJZfkOxIdijr1eWKVGHWk-PKY,5212
|
|
50
|
-
dkist_processing_visp/tests/test_make_movie_frames.py,sha256=
|
|
51
|
-
dkist_processing_visp/tests/test_map_repeats.py,sha256=
|
|
52
|
+
dkist_processing_visp/tests/test_make_movie_frames.py,sha256=huQ5n0YneHByKumM_Ye9tekqKeh-F-e6MQoudOP3S-g,2628
|
|
53
|
+
dkist_processing_visp/tests/test_map_repeats.py,sha256=9g3NnvSfn1OqxxYYxTFoOIi1UsCOa6mZjiuGkbxUvTg,7611
|
|
52
54
|
dkist_processing_visp/tests/test_parameters.py,sha256=h9EemGJf0b4ma0jLGd321untkhLkhwgo88mIRnSmxXs,4369
|
|
53
55
|
dkist_processing_visp/tests/test_parse.py,sha256=aFBbLBXdz2KJZy62gC1KC049FFfRKYqYVo-Y32mx_E4,20853
|
|
54
56
|
dkist_processing_visp/tests/test_quality.py,sha256=YW24VjEHoILseFIXZBp4-o7egT26mfT1lafzajVjXu8,6905
|
|
55
|
-
dkist_processing_visp/tests/test_science.py,sha256=
|
|
57
|
+
dkist_processing_visp/tests/test_science.py,sha256=rIoHu1hifNYj1niQaDkmyID5BQVzTEgZ5BhsdZOSaWk,23985
|
|
56
58
|
dkist_processing_visp/tests/test_solar.py,sha256=Bpkm59HI0GqrwM8LqIK7GR0pUUW0anboJkePQnofgmA,9471
|
|
57
59
|
dkist_processing_visp/tests/test_trial_create_quality_report.py,sha256=t3de9LMJOqrRaFXAvKV_5sotAfzDR8fZVIrFNRB2I_A,2779
|
|
58
60
|
dkist_processing_visp/tests/test_visp_constants.py,sha256=v8meAXdR6jJntaFtWR-N65Z-3ovr9Vf9_-BwAZv2RBc,1974
|
|
59
61
|
dkist_processing_visp/tests/test_workflows.py,sha256=qyWxagIDv-MmVN0u3KFswa5HdaHC6uGeJpvgxvPE30E,287
|
|
60
|
-
dkist_processing_visp/tests/test_write_l1.py,sha256=
|
|
62
|
+
dkist_processing_visp/tests/test_write_l1.py,sha256=ORNKUvF4RPjXc24HgOzyVsP0BCQ9izi54EqXpjLzHXQ,6574
|
|
61
63
|
dkist_processing_visp/tests/local_trial_workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
64
|
dkist_processing_visp/tests/local_trial_workflows/l0_cals_only.py,sha256=1TmvdAn80WKW-2DsuSg9iK6yQ3Urwm9yt5HlVv1dGWE,10649
|
|
63
|
-
dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py,sha256=
|
|
65
|
+
dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py,sha256=geU6Q8ULfIq4Jmj6ORq8mgXYlF8SYrvgZP-0u-4Uz24,17567
|
|
64
66
|
dkist_processing_visp/tests/local_trial_workflows/l0_solar_gain_as_science.py,sha256=cRrLklazesLBHPPbGzVjBXbEQKaCxQfgy1_AgWyZRyo,15820
|
|
65
67
|
dkist_processing_visp/tests/local_trial_workflows/l0_to_l1.py,sha256=cwEdvUGqqSpp4htrxlmyRN2sZBBvVOjomd66DEqTmTA,15191
|
|
66
|
-
dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py,sha256=
|
|
68
|
+
dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py,sha256=eSOuCn7c2DYATq3twu9Gqj7DrLR902p7b5vGlHZ3zxU,26750
|
|
67
69
|
dkist_processing_visp/workflows/__init__.py,sha256=1-GP9tOzjCxLJtyq0ry_x4dPdArfSso8Hxu65ydPpXQ,103
|
|
68
70
|
dkist_processing_visp/workflows/l0_processing.py,sha256=VzTjyS-ywhwATN1Hc17B7tdZOwu3Cv3dUv9l5Ux_vqY,3262
|
|
69
71
|
dkist_processing_visp/workflows/single_task_workflows.py,sha256=LK4dsshM0-lwy79WaMoTplyCxUyINnP9RU74MG_dhyc,33
|
|
@@ -85,7 +87,7 @@ docs/requirements_table.rst,sha256=_HIbwFpDooM5n0JjiDAbFozGfJuX13smtcoujLFN4Gk,2
|
|
|
85
87
|
docs/science_calibration.rst,sha256=VN_g7xSjN-nbXhlBaFnPCbNcsc_Qu0207jEUfRAjnBE,2939
|
|
86
88
|
docs/scientific_changelog.rst,sha256=01AWBSHg8zElnodCgAq-hMxhk9CkX5rtEENx4iz0sjI,300
|
|
87
89
|
licenses/LICENSE.rst,sha256=piZaQplkzOMmH1NXg6QIdo9wwo9pPCoHkvm2-DmH76E,1462
|
|
88
|
-
dkist_processing_visp-
|
|
89
|
-
dkist_processing_visp-
|
|
90
|
-
dkist_processing_visp-
|
|
91
|
-
dkist_processing_visp-
|
|
90
|
+
dkist_processing_visp-4.0.0.dist-info/METADATA,sha256=4S8qtlI-Cp12PpvTDgD0ZgSA2ZaaOp0QnHtC94RZ4Ak,29073
|
|
91
|
+
dkist_processing_visp-4.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
92
|
+
dkist_processing_visp-4.0.0.dist-info/top_level.txt,sha256=9GHSn-ZMGQxaRNGrPP3HNc5ZkE7ftzluO74Jz5vUSTE,46
|
|
93
|
+
dkist_processing_visp-4.0.0.dist-info/RECORD,,
|
|
File without changes
|
{dkist_processing_visp-3.6.2.dist-info → dkist_processing_visp-4.0.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|