imap-processing 0.16.2__py3-none-any.whl → 0.18.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.

Potentially problematic release.


This version of imap-processing might be problematic. Click here for more details.

Files changed (110) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/ccsds/excel_to_xtce.py +12 -0
  3. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -6
  4. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +35 -0
  5. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +35 -0
  6. imap_processing/cdf/config/imap_codice_l2_variable_attrs.yaml +24 -0
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +8 -8
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +1 -1
  9. imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +163 -100
  10. imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +398 -415
  11. imap_processing/cdf/config/imap_ialirt_l1_variable_attrs.yaml +97 -54
  12. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +9 -9
  13. imap_processing/cdf/config/imap_idex_l2b_variable_attrs.yaml +233 -57
  14. imap_processing/cdf/config/imap_idex_l2c_variable_attrs.yaml +16 -90
  15. imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +30 -0
  16. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -1
  17. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +19 -0
  18. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +20 -0
  19. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +39 -0
  20. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +168 -0
  21. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +103 -2
  22. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +91 -11
  23. imap_processing/cdf/utils.py +7 -1
  24. imap_processing/cli.py +42 -13
  25. imap_processing/codice/codice_l1a.py +125 -78
  26. imap_processing/codice/codice_l1b.py +1 -1
  27. imap_processing/codice/codice_l2.py +0 -9
  28. imap_processing/codice/constants.py +481 -498
  29. imap_processing/hi/hi_l1a.py +4 -4
  30. imap_processing/hi/hi_l1b.py +2 -2
  31. imap_processing/hi/packet_definitions/TLM_HI_COMBINED_SCI.xml +218 -38
  32. imap_processing/hit/hit_utils.py +2 -2
  33. imap_processing/hit/l0/decom_hit.py +4 -3
  34. imap_processing/hit/l1a/hit_l1a.py +64 -24
  35. imap_processing/hit/l1b/constants.py +5 -0
  36. imap_processing/hit/l1b/hit_l1b.py +18 -16
  37. imap_processing/hit/l2/constants.py +1 -1
  38. imap_processing/hit/l2/hit_l2.py +4 -4
  39. imap_processing/ialirt/constants.py +21 -0
  40. imap_processing/ialirt/generate_coverage.py +188 -0
  41. imap_processing/ialirt/l0/parse_mag.py +62 -5
  42. imap_processing/ialirt/l0/process_swapi.py +1 -1
  43. imap_processing/ialirt/l0/process_swe.py +23 -7
  44. imap_processing/ialirt/utils/constants.py +22 -16
  45. imap_processing/ialirt/utils/create_xarray.py +42 -19
  46. imap_processing/idex/idex_constants.py +8 -5
  47. imap_processing/idex/idex_l2b.py +554 -58
  48. imap_processing/idex/idex_l2c.py +30 -196
  49. imap_processing/lo/l0/lo_apid.py +1 -0
  50. imap_processing/lo/l0/lo_star_sensor.py +48 -0
  51. imap_processing/lo/l1a/lo_l1a.py +74 -30
  52. imap_processing/lo/packet_definitions/lo_xtce.xml +5359 -106
  53. imap_processing/mag/constants.py +1 -0
  54. imap_processing/mag/l0/decom_mag.py +9 -6
  55. imap_processing/mag/l0/mag_l0_data.py +46 -0
  56. imap_processing/mag/l1d/__init__.py +0 -0
  57. imap_processing/mag/l1d/mag_l1d.py +133 -0
  58. imap_processing/mag/l1d/mag_l1d_data.py +588 -0
  59. imap_processing/mag/l2/__init__.py +0 -0
  60. imap_processing/mag/l2/mag_l2.py +25 -20
  61. imap_processing/mag/l2/mag_l2_data.py +191 -130
  62. imap_processing/quality_flags.py +20 -2
  63. imap_processing/spice/geometry.py +25 -3
  64. imap_processing/spice/pointing_frame.py +1 -1
  65. imap_processing/spice/spin.py +4 -0
  66. imap_processing/spice/time.py +51 -0
  67. imap_processing/swapi/l1/swapi_l1.py +12 -2
  68. imap_processing/swapi/l2/swapi_l2.py +59 -14
  69. imap_processing/swapi/swapi_utils.py +1 -1
  70. imap_processing/swe/l1b/swe_l1b.py +11 -4
  71. imap_processing/swe/l2/swe_l2.py +111 -17
  72. imap_processing/ultra/constants.py +49 -1
  73. imap_processing/ultra/l0/decom_tools.py +28 -14
  74. imap_processing/ultra/l0/decom_ultra.py +225 -15
  75. imap_processing/ultra/l0/ultra_utils.py +281 -8
  76. imap_processing/ultra/l1a/ultra_l1a.py +77 -8
  77. imap_processing/ultra/l1b/cullingmask.py +3 -3
  78. imap_processing/ultra/l1b/de.py +53 -15
  79. imap_processing/ultra/l1b/extendedspin.py +26 -2
  80. imap_processing/ultra/l1b/lookup_utils.py +171 -50
  81. imap_processing/ultra/l1b/quality_flag_filters.py +14 -0
  82. imap_processing/ultra/l1b/ultra_l1b_culling.py +198 -5
  83. imap_processing/ultra/l1b/ultra_l1b_extended.py +304 -66
  84. imap_processing/ultra/l1c/helio_pset.py +54 -7
  85. imap_processing/ultra/l1c/spacecraft_pset.py +9 -1
  86. imap_processing/ultra/l1c/ultra_l1c.py +2 -0
  87. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +106 -109
  88. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +3 -3
  89. imap_processing/ultra/utils/ultra_l1_utils.py +13 -1
  90. imap_processing/utils.py +20 -42
  91. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/METADATA +2 -2
  92. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/RECORD +95 -103
  93. imap_processing/lo/l0/data_classes/star_sensor.py +0 -98
  94. imap_processing/lo/l0/utils/lo_base.py +0 -57
  95. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +0 -526
  96. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +0 -526
  97. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +0 -526
  98. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +0 -524
  99. imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +0 -32769
  100. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
  101. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +0 -2
  102. imap_processing/ultra/lookup_tables/dps_grid45_compressed.cdf +0 -0
  103. imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +0 -4097
  104. imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +0 -2050
  105. imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +0 -4097
  106. imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +0 -2050
  107. imap_processing/ultra/lookup_tables/yadjust.csv +0 -257
  108. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/LICENSE +0 -0
  109. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/WHEEL +0 -0
  110. {imap_processing-0.16.2.dist-info → imap_processing-0.18.0.dist-info}/entry_points.txt +0 -0
@@ -131,6 +131,7 @@ MAX_FINE_TIME = np.iinfo(np.uint16).max # maximum 16 bit unsigned int
131
131
  AXIS_COUNT = 3
132
132
  RANGE_BIT_WIDTH = 2
133
133
  MAX_COMPRESSED_VECTOR_BITS = 60
134
+ FILLVAL = -1e31
134
135
 
135
136
 
136
137
  def vectors_per_second_from_string(vecsec_string: str) -> dict:
@@ -43,8 +43,9 @@ def decom_packets(packet_file_path: str | Path) -> dict[str, list[MagL0]]:
43
43
 
44
44
  packet_definition = definitions.XtcePacketDefinition(xtce_document)
45
45
 
46
- norm_data = []
47
- burst_data = []
46
+ # Store in a dict for de-duplication. Only the keys are returned as a list.
47
+ norm_dict: dict[MagL0, None] = {}
48
+ burst_dict: dict[MagL0, None] = {}
48
49
 
49
50
  with open(packet_file_path, "rb") as binary_data:
50
51
  mag_packets = packet_definition.packet_generator(binary_data)
@@ -53,12 +54,14 @@ def decom_packets(packet_file_path: str | Path) -> dict[str, list[MagL0]]:
53
54
  apid = packet["PKT_APID"]
54
55
  if apid in (Mode.BURST, Mode.NORMAL):
55
56
  values = [item.raw_value for item in packet.user_data.values()]
57
+ mag_l0 = MagL0(CcsdsData(packet.header), *values)
56
58
  if apid == Mode.NORMAL:
57
- norm_data.append(MagL0(CcsdsData(packet.header), *values))
58
- else:
59
- burst_data.append(MagL0(CcsdsData(packet.header), *values))
59
+ if mag_l0 not in norm_dict:
60
+ norm_dict[mag_l0] = None
61
+ elif mag_l0 not in burst_dict:
62
+ burst_dict[mag_l0] = None
60
63
 
61
- return {"norm": norm_data, "burst": burst_data}
64
+ return {"norm": list(norm_dict.keys()), "burst": list(burst_dict.keys())}
62
65
 
63
66
 
64
67
  def generate_dataset(
@@ -31,6 +31,9 @@ class MagL0:
31
31
  """
32
32
  Data class for MAG Level 0 data.
33
33
 
34
+ No attributes should be updated after creation. This class acts as a snapshot of the
35
+ packet data, and is read-only.
36
+
34
37
  Attributes
35
38
  ----------
36
39
  ccsds_header: CcsdsData
@@ -116,3 +119,46 @@ class MagL0:
116
119
 
117
120
  self.PRI_VECSEC = 2**self.PRI_VECSEC
118
121
  self.SEC_VECSEC = 2**self.SEC_VECSEC
122
+
123
+ def __eq__(self, other: object) -> bool:
124
+ """
125
+ Compare two MagL0 objects for equality.
126
+
127
+ Two objects are said to be equal if the SHCOARSE, APID, and SRC_SEQ_CTR are
128
+ equal.
129
+
130
+ Parameters
131
+ ----------
132
+ other : object
133
+ The other MagL0 object to compare against.
134
+
135
+ Returns
136
+ -------
137
+ bool
138
+ True if the objects are equal, False otherwise.
139
+ """
140
+ if not isinstance(other, MagL0):
141
+ return NotImplemented
142
+
143
+ return (
144
+ self.SHCOARSE == other.SHCOARSE
145
+ and self.ccsds_header.PKT_APID == other.ccsds_header.PKT_APID
146
+ and self.ccsds_header.SRC_SEQ_CTR == other.ccsds_header.SRC_SEQ_CTR
147
+ )
148
+
149
+ def __hash__(self) -> int:
150
+ """
151
+ Return a hash of the MagL0 object for use in sets.
152
+
153
+ Returns
154
+ -------
155
+ int
156
+ The hash value of the MagL0 object.
157
+ """
158
+ return hash(
159
+ (
160
+ self.SHCOARSE,
161
+ self.ccsds_header.PKT_APID,
162
+ self.ccsds_header.SRC_SEQ_CTR,
163
+ )
164
+ )
File without changes
@@ -0,0 +1,133 @@
1
+ """Module for generating Level 1d magnetic field data."""
2
+
3
+ import numpy as np
4
+ import xarray as xr
5
+
6
+ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
7
+ from imap_processing.mag.constants import DataMode
8
+ from imap_processing.mag.l1d.mag_l1d_data import MagL1d, MagL1dConfiguration
9
+ from imap_processing.mag.l2.mag_l2_data import ValidFrames
10
+
11
+
12
+ def mag_l1d(
13
+ science_data: list[xr.Dataset],
14
+ calibration_dataset: xr.Dataset,
15
+ day_to_process: np.datetime64,
16
+ ) -> list[xr.Dataset]:
17
+ """
18
+ Generate Level 1d magnetic field data from Level 1b/1c data.
19
+
20
+ Both norm and burst mode are calculated at the same time. Normal mode MAGO and MAGI
21
+ L1C data is required, burst mode MAGO and MAGI L1B data is optional.
22
+
23
+ Parameters
24
+ ----------
25
+ science_data : list[xr.Dataset]
26
+ The list of input datasets containing the MAG L1C and L1B data. This is required
27
+ to have at least one normal mode dataset for MAGo and MAGi, and optionally
28
+ burst mode datasets for MAGo and MAGi. There cannot be duplicates, so two
29
+ norm-mago files is invalid.
30
+ calibration_dataset : xr.Dataset
31
+ The calibration dataset to use for processing. Generated from multiple L1D
32
+ ancillary files using MagAncillaryCombiner class.
33
+ day_to_process : np.datetime64
34
+ The day to process, in np.datetime64[D] format. This is used to select the
35
+ correct ancillary parameters and to remove excessive data from the output.
36
+
37
+ Returns
38
+ -------
39
+ list[xr.Dataset]
40
+ A list containing the generated Level 1d dataset(s).
41
+ """
42
+ input_magi_norm = None
43
+ input_mago_norm = None
44
+ input_magi_burst = None
45
+ input_mago_burst = None
46
+ for dataset in science_data:
47
+ source = dataset.attrs.get("Logical_source", "")
48
+ if "norm-magi" in source:
49
+ input_magi_norm = dataset
50
+ elif "norm-mago" in source:
51
+ input_mago_norm = dataset
52
+ elif "burst-magi" in source:
53
+ input_magi_burst = dataset
54
+ elif "burst-mago" in source:
55
+ input_mago_burst = dataset
56
+ else:
57
+ raise ValueError(f"Input data has invalid logical source {source}")
58
+
59
+ if input_magi_norm is None or input_mago_norm is None:
60
+ raise ValueError(
61
+ "Both MAGo and MAGi normal mode datasets are required for L1d processing."
62
+ )
63
+
64
+ day: np.datetime64 = day_to_process.astype("datetime64[D]")
65
+
66
+ output_datasets = []
67
+
68
+ # Read configuration out of file
69
+ config = MagL1dConfiguration(calibration_dataset, day)
70
+
71
+ # Only the first 3 components are used for L1d
72
+ mago_vectors = input_mago_norm["vectors"].data[:, :3]
73
+ magi_vectors = input_magi_norm["vectors"].data[:, :3]
74
+
75
+ # TODO: verify that MAGO is primary sensor for all vectors before applying
76
+ # gradiometry
77
+
78
+ # TODO: L1D attributes
79
+ attributes = ImapCdfAttributes()
80
+ attributes.add_instrument_global_attrs("mag")
81
+ attributes.add_instrument_variable_attrs("mag", "l2")
82
+
83
+ l1d_norm = MagL1d(
84
+ vectors=mago_vectors,
85
+ epoch=input_mago_norm["epoch"].data,
86
+ range=input_mago_norm["vectors"].data[:, 3],
87
+ global_attributes={},
88
+ quality_flags=np.zeros(len(input_mago_norm["epoch"].data)),
89
+ quality_bitmask=np.zeros(len(input_mago_norm["epoch"].data)),
90
+ data_mode=DataMode.NORM,
91
+ magi_vectors=magi_vectors,
92
+ magi_range=input_magi_norm["vectors"].data[:, 3],
93
+ magi_epoch=input_magi_norm["epoch"].data,
94
+ config=config,
95
+ day=day,
96
+ )
97
+
98
+ l1d_norm.rotate_frame(ValidFrames.SRF)
99
+ norm_srf_dataset = l1d_norm.generate_dataset(attributes, day_to_process)
100
+ l1d_norm.rotate_frame(ValidFrames.DSRF)
101
+ norm_dsrf_dataset = l1d_norm.generate_dataset(attributes, day_to_process)
102
+ output_datasets.append(norm_srf_dataset)
103
+ output_datasets.append(norm_dsrf_dataset)
104
+
105
+ if input_mago_burst is not None and input_magi_burst is not None:
106
+ # If burst data is provided, use it to create the burst L1d dataset
107
+ mago_burst_vectors = input_mago_burst["vectors"].data[:, :3]
108
+ magi_burst_vectors = input_magi_burst["vectors"].data[:, :3]
109
+
110
+ l1d_burst = MagL1d(
111
+ vectors=mago_burst_vectors,
112
+ epoch=input_mago_burst["epoch"].data,
113
+ range=input_mago_burst["vectors"].data[:, 3],
114
+ global_attributes={},
115
+ quality_flags=np.zeros(len(input_mago_burst["epoch"].data)),
116
+ quality_bitmask=np.zeros(len(input_mago_burst["epoch"].data)),
117
+ data_mode=DataMode.BURST,
118
+ magi_vectors=magi_burst_vectors,
119
+ magi_range=input_magi_burst["vectors"].data[:, 3],
120
+ magi_epoch=input_magi_burst["epoch"].data,
121
+ config=config,
122
+ spin_offsets=l1d_norm.spin_offsets,
123
+ day=day,
124
+ )
125
+ l1d_burst.rotate_frame(ValidFrames.SRF)
126
+ burst_srf_dataset = l1d_burst.generate_dataset(attributes, day_to_process)
127
+ l1d_burst.rotate_frame(ValidFrames.DSRF)
128
+ burst_dsrf_dataset = l1d_burst.generate_dataset(attributes, day_to_process)
129
+ output_datasets.append(burst_srf_dataset)
130
+ output_datasets.append(burst_dsrf_dataset)
131
+
132
+ # TODO: Output ancillary files
133
+ return output_datasets