roc-film 1.13.4__py3-none-any.whl → 1.14.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.
- roc/__init__.py +2 -1
- roc/film/__init__.py +2 -2
- roc/film/commands.py +372 -323
- roc/film/config/__init__.py +0 -1
- roc/film/constants.py +101 -65
- roc/film/descriptor.json +126 -95
- roc/film/exceptions.py +28 -27
- roc/film/tasks/__init__.py +16 -16
- roc/film/tasks/cat_solo_hk.py +86 -74
- roc/film/tasks/cdf_postpro.py +438 -309
- roc/film/tasks/check_dds.py +39 -45
- roc/film/tasks/db_to_anc_bia_sweep_table.py +381 -0
- roc/film/tasks/dds_to_l0.py +232 -180
- roc/film/tasks/export_solo_coord.py +147 -0
- roc/film/tasks/file_handler.py +91 -75
- roc/film/tasks/l0_to_hk.py +117 -103
- roc/film/tasks/l0_to_l1_bia_current.py +38 -30
- roc/film/tasks/l0_to_l1_bia_sweep.py +417 -329
- roc/film/tasks/l0_to_l1_sbm.py +250 -208
- roc/film/tasks/l0_to_l1_surv.py +185 -130
- roc/film/tasks/make_daily_tm.py +40 -37
- roc/film/tasks/merge_tcreport.py +77 -71
- roc/film/tasks/merge_tmraw.py +102 -89
- roc/film/tasks/parse_dds_xml.py +21 -20
- roc/film/tasks/set_l0_utc.py +51 -49
- roc/film/tests/cdf_compare.py +565 -0
- roc/film/tests/hdf5_compare.py +84 -62
- roc/film/tests/test_dds_to_l0.py +93 -51
- roc/film/tests/test_dds_to_tc.py +8 -11
- roc/film/tests/test_dds_to_tm.py +8 -10
- roc/film/tests/test_film.py +161 -116
- roc/film/tests/test_l0_to_hk.py +64 -36
- roc/film/tests/test_l0_to_l1_bia.py +10 -14
- roc/film/tests/test_l0_to_l1_sbm.py +14 -19
- roc/film/tests/test_l0_to_l1_surv.py +68 -41
- roc/film/tests/test_metadata.py +21 -20
- roc/film/tests/tests.py +743 -396
- roc/film/tools/__init__.py +5 -5
- roc/film/tools/dataset_tasks.py +34 -2
- roc/film/tools/file_helpers.py +390 -269
- roc/film/tools/l0.py +402 -324
- roc/film/tools/metadata.py +147 -127
- roc/film/tools/skeleton.py +12 -17
- roc/film/tools/tools.py +109 -92
- roc/film/tools/xlsx2skt.py +161 -139
- {roc_film-1.13.4.dist-info → roc_film-1.14.0.dist-info}/LICENSE +127 -125
- roc_film-1.14.0.dist-info/METADATA +60 -0
- roc_film-1.14.0.dist-info/RECORD +50 -0
- {roc_film-1.13.4.dist-info → roc_film-1.14.0.dist-info}/WHEEL +1 -1
- roc/film/tasks/l0_to_anc_bia_sweep_table.py +0 -348
- roc_film-1.13.4.dist-info/METADATA +0 -120
- roc_film-1.13.4.dist-info/RECORD +0 -48
roc/film/tools/l0.py
CHANGED
@@ -9,11 +9,18 @@ from poppy.core.logger import logger
|
|
9
9
|
from roc.idb.parsers.idb_parser import IDBParser
|
10
10
|
from roc.rpl.constants import VALID_PACKET
|
11
11
|
|
12
|
-
from roc.film import
|
12
|
+
from roc.film import (
|
13
|
+
L0ProdFailure,
|
14
|
+
MIN_DATETIME,
|
15
|
+
MAX_DATETIME,
|
16
|
+
PACKET_TYPE,
|
17
|
+
TIME_L0_STRFORMAT,
|
18
|
+
TIME_ISO_STRFORMAT,
|
19
|
+
)
|
13
20
|
from roc.film.tools.file_helpers import is_packet
|
14
21
|
from roc.film.tools import valid_data_version, Map, decode
|
15
22
|
|
16
|
-
__all__ = [
|
23
|
+
__all__ = ["L0"]
|
17
24
|
|
18
25
|
|
19
26
|
class L0:
|
@@ -22,24 +29,30 @@ class L0:
|
|
22
29
|
# Class attributes
|
23
30
|
h5py_str_dtype = h5py.special_dtype(vlen=str)
|
24
31
|
|
25
|
-
l0_file_prefix =
|
26
|
-
|
27
|
-
L0_FIELDS = {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
32
|
+
l0_file_prefix = "solo_L0_rpw"
|
33
|
+
|
34
|
+
L0_FIELDS = {
|
35
|
+
"TM": [
|
36
|
+
"utc_time",
|
37
|
+
"binary",
|
38
|
+
"idb",
|
39
|
+
"compressed",
|
40
|
+
"data_field_header",
|
41
|
+
"packet_header",
|
42
|
+
"source_data",
|
43
|
+
],
|
44
|
+
"TC": [
|
45
|
+
"utc_time",
|
46
|
+
"binary",
|
47
|
+
"idb",
|
48
|
+
"tc_ack_state",
|
49
|
+
"unique_id",
|
50
|
+
"sequence_name",
|
51
|
+
"packet_header",
|
52
|
+
"data_field_header",
|
53
|
+
"application_data",
|
54
|
+
],
|
55
|
+
}
|
43
56
|
|
44
57
|
def __init__(self, filepath=None):
|
45
58
|
self.filepath = filepath
|
@@ -67,7 +80,7 @@ class L0:
|
|
67
80
|
@staticmethod
|
68
81
|
def extract_header(filepath):
|
69
82
|
if filepath and os.path.isfile(filepath):
|
70
|
-
with h5py.File(filepath,
|
83
|
+
with h5py.File(filepath, "r") as file:
|
71
84
|
header = {key: val for key, val in file.attrs.items()}
|
72
85
|
else:
|
73
86
|
header = {}
|
@@ -83,9 +96,9 @@ class L0:
|
|
83
96
|
:return: data version as a string of the form "XX", where XX are 2 digit integers
|
84
97
|
"""
|
85
98
|
try:
|
86
|
-
return os.path.splitext(os.path.basename(filepath))[0].split(
|
99
|
+
return os.path.splitext(os.path.basename(filepath))[0].split("_")[4][1:]
|
87
100
|
except Exception:
|
88
|
-
return
|
101
|
+
return ""
|
89
102
|
|
90
103
|
@staticmethod
|
91
104
|
def version_from_header(filepath):
|
@@ -97,15 +110,13 @@ class L0:
|
|
97
110
|
:return: data version as a string of the form "XX", where XX are 2 digit integers
|
98
111
|
"""
|
99
112
|
try:
|
100
|
-
with h5py.File(filepath,
|
101
|
-
return valid_data_version(file.attrs[
|
113
|
+
with h5py.File(filepath, "r") as file:
|
114
|
+
return valid_data_version(file.attrs["Data_version"])
|
102
115
|
except Exception:
|
103
|
-
return
|
116
|
+
return ""
|
104
117
|
|
105
118
|
@staticmethod
|
106
|
-
def to_hdf5(filepath,
|
107
|
-
packet_parser=None,
|
108
|
-
metadata={}):
|
119
|
+
def to_hdf5(filepath, packet_parser=None, metadata={}):
|
109
120
|
"""
|
110
121
|
Given an PacketParser object like and the target on which to put the packets data,
|
111
122
|
generate the L0 HDF5 file in format with all the necessary meta information.
|
@@ -114,21 +125,21 @@ class L0:
|
|
114
125
|
"""
|
115
126
|
|
116
127
|
if os.path.isfile(filepath):
|
117
|
-
action =
|
128
|
+
action = "a"
|
118
129
|
else:
|
119
|
-
action =
|
130
|
+
action = "w"
|
120
131
|
|
121
132
|
# Create instance of L0 class
|
122
133
|
l0 = L0()
|
123
134
|
|
124
135
|
try:
|
125
136
|
with h5py.File(filepath, action) as hdf5:
|
126
|
-
|
127
137
|
# Add l0 root metadata
|
128
138
|
for key, val in metadata.items():
|
129
139
|
if key in hdf5.attrs:
|
130
|
-
logger.debug(
|
131
|
-
|
140
|
+
logger.debug(
|
141
|
+
f"{key} L0 metadata already defined: {hdf5.attrs[key]}!"
|
142
|
+
)
|
132
143
|
hdf5.attrs[key] = val
|
133
144
|
|
134
145
|
# write packets data into the file
|
@@ -136,11 +147,11 @@ class L0:
|
|
136
147
|
l0._to_hdf5(hdf5, packet_parser)
|
137
148
|
|
138
149
|
except Exception:
|
139
|
-
logger.exception(f
|
150
|
+
logger.exception(f"{filepath} production has failed!")
|
140
151
|
raise L0ProdFailure()
|
141
152
|
|
142
153
|
if not L0.is_l0(filepath):
|
143
|
-
logger.exception(f
|
154
|
+
logger.exception(f"{filepath} not saved correctly!")
|
144
155
|
raise L0ProdFailure()
|
145
156
|
|
146
157
|
return filepath
|
@@ -156,23 +167,22 @@ class L0:
|
|
156
167
|
|
157
168
|
# create a group for TM and an other for TC packets
|
158
169
|
# and initialize counters
|
159
|
-
if
|
160
|
-
tm = self.hdf5.create_group(
|
170
|
+
if "TM" not in self.hdf5.keys():
|
171
|
+
tm = self.hdf5.create_group("TM")
|
161
172
|
tm_count = 0
|
162
173
|
else:
|
163
|
-
tm = self.hdf5[
|
164
|
-
tm_count = tm.attrs[
|
174
|
+
tm = self.hdf5["TM"]
|
175
|
+
tm_count = tm.attrs["COUNT"]
|
165
176
|
|
166
|
-
if
|
167
|
-
tc = self.hdf5.create_group(
|
177
|
+
if "TC" not in self.hdf5.keys():
|
178
|
+
tc = self.hdf5.create_group("TC")
|
168
179
|
tc_count = 0
|
169
180
|
else:
|
170
|
-
tc = self.hdf5[
|
171
|
-
tc_count = tc.attrs[
|
181
|
+
tc = self.hdf5["TC"]
|
182
|
+
tc_count = tc.attrs["COUNT"]
|
172
183
|
|
173
184
|
# loop over registered packets
|
174
185
|
for packet in packet_parser.packet.manager.instances:
|
175
|
-
|
176
186
|
# if the packet is empty do not try to add it
|
177
187
|
# same thing if it is a compressed one
|
178
188
|
if packet.is_compressed or packet.counter == 0:
|
@@ -182,7 +192,9 @@ class L0:
|
|
182
192
|
criterion = packet.status == VALID_PACKET
|
183
193
|
new_counter = len(packet.status[criterion])
|
184
194
|
if not packet.counter == new_counter:
|
185
|
-
logger.warning(
|
195
|
+
logger.warning(
|
196
|
+
f"{packet.name} has {packet.counter - new_counter} invalid packets"
|
197
|
+
)
|
186
198
|
# Loop over packet attributes
|
187
199
|
for key, val in packet.__dict__.items():
|
188
200
|
try:
|
@@ -195,7 +207,7 @@ class L0:
|
|
195
207
|
packet.counter = len(packet.status)
|
196
208
|
|
197
209
|
# send the packet to the good type
|
198
|
-
logger.debug(
|
210
|
+
logger.debug("Insert {0} into L0 file".format(packet))
|
199
211
|
if packet.type == 0:
|
200
212
|
# call the method to store into hdf5 for each packet
|
201
213
|
tm_count += self.packet_to_hdf5(packet, tm)
|
@@ -204,8 +216,8 @@ class L0:
|
|
204
216
|
tc_count += self.packet_to_hdf5(packet, tc, tc=True)
|
205
217
|
|
206
218
|
# Set TM / TC packet counters
|
207
|
-
tm.attrs[
|
208
|
-
tc.attrs[
|
219
|
+
tm.attrs["COUNT"] = tm_count
|
220
|
+
tc.attrs["COUNT"] = tc_count
|
209
221
|
|
210
222
|
return tm, tc
|
211
223
|
|
@@ -227,85 +239,95 @@ class L0:
|
|
227
239
|
# Store some metadata of current packet
|
228
240
|
# as H5 datasets in L0 packet group
|
229
241
|
# Mapping dict of the attributes to add
|
230
|
-
attrs_dict = {
|
231
|
-
|
242
|
+
attrs_dict = {
|
243
|
+
"SRDB_ID": (packet.__dict__, "srdb_id"),
|
244
|
+
"PACKET_CATEGORY": (packet.__dict__, "category"),
|
245
|
+
}
|
232
246
|
self.add_packet_attrs(packet, attrs_dict, packet_group)
|
233
247
|
|
234
248
|
# Store number of packets
|
235
249
|
try:
|
236
|
-
if
|
237
|
-
packet_count = packet_group.attrs[
|
250
|
+
if "COUNT" in packet_group.attrs:
|
251
|
+
packet_count = packet_group.attrs["COUNT"]
|
238
252
|
else:
|
239
253
|
packet_count = 0
|
240
254
|
|
241
|
-
packet_group.attrs[
|
255
|
+
packet_group.attrs["COUNT"] = packet_count + packet.counter
|
242
256
|
except Exception:
|
243
|
-
logger.warning(f
|
257
|
+
logger.warning(f"No counter found for packet {packet.name}")
|
244
258
|
|
245
259
|
# packet_header, data_field_header, source_data ou application_data
|
246
260
|
|
247
261
|
# inside it, create groups for the packet_header,
|
248
262
|
# data_field_header and the source_data/application_data
|
249
|
-
self._packet_to_hdf5(
|
250
|
-
self._packet_to_hdf5(
|
251
|
-
packet.data_header, packet_group)
|
263
|
+
self._packet_to_hdf5("packet_header", packet.header, packet_group)
|
264
|
+
self._packet_to_hdf5("data_field_header", packet.data_header, packet_group)
|
252
265
|
|
253
266
|
if tc:
|
254
|
-
self._packet_to_hdf5(
|
267
|
+
self._packet_to_hdf5("application_data", packet.data, packet_group)
|
255
268
|
|
256
269
|
# Add TC specific info:
|
257
270
|
# array of Ack. TC acceptance/execution state
|
258
|
-
self._packet_to_hdf5(
|
259
|
-
|
260
|
-
|
261
|
-
|
271
|
+
self._packet_to_hdf5(
|
272
|
+
"tc_ack_state",
|
273
|
+
packet.tc_ack_state.astype(self.h5py_str_dtype),
|
274
|
+
packet_group,
|
275
|
+
)
|
262
276
|
|
263
277
|
# unique ID as a string
|
264
|
-
self._packet_to_hdf5(
|
265
|
-
|
266
|
-
|
278
|
+
self._packet_to_hdf5(
|
279
|
+
"unique_id", packet.unique_id.astype(self.h5py_str_dtype), packet_group
|
280
|
+
)
|
267
281
|
|
268
282
|
# sequence name ID as a string
|
269
|
-
self._packet_to_hdf5(
|
270
|
-
|
271
|
-
|
272
|
-
|
283
|
+
self._packet_to_hdf5(
|
284
|
+
"sequence_name",
|
285
|
+
packet.sequence_name.astype(self.h5py_str_dtype),
|
286
|
+
packet_group,
|
287
|
+
)
|
273
288
|
|
274
289
|
else:
|
275
|
-
self._packet_to_hdf5(
|
290
|
+
self._packet_to_hdf5("source_data", packet.data, packet_group)
|
276
291
|
|
277
292
|
# Add binary
|
278
|
-
self._packet_to_hdf5(
|
279
|
-
|
280
|
-
|
293
|
+
self._packet_to_hdf5(
|
294
|
+
"binary", packet.binary.astype(self.h5py_str_dtype), packet_group
|
295
|
+
)
|
281
296
|
|
282
297
|
# Add idb_source and idb_version
|
283
|
-
self._packet_to_hdf5(
|
284
|
-
|
285
|
-
|
298
|
+
self._packet_to_hdf5(
|
299
|
+
"idb", packet.idb.astype(self.h5py_str_dtype), packet_group
|
300
|
+
)
|
286
301
|
|
287
302
|
# save utc time as string
|
288
303
|
# Note: datetime64 could be also saved as int64
|
289
|
-
self._packet_to_hdf5(
|
290
|
-
|
291
|
-
|
292
|
-
|
304
|
+
self._packet_to_hdf5(
|
305
|
+
"utc_time",
|
306
|
+
np.datetime_as_string(packet.utc_time, timezone="UTC").astype(
|
307
|
+
self.h5py_str_dtype
|
308
|
+
),
|
309
|
+
packet_group,
|
310
|
+
)
|
293
311
|
|
294
312
|
# Save compression status (only if any compressed packet)
|
295
|
-
if not np.all(packet.compressed == 0) or
|
296
|
-
self._packet_to_hdf5(
|
297
|
-
packet.compressed,
|
298
|
-
packet_group)
|
313
|
+
if not np.all(packet.compressed == 0) or "compressed" in packet_group.keys():
|
314
|
+
self._packet_to_hdf5("compressed", packet.compressed, packet_group)
|
299
315
|
# Mapping dict of the attributes to add for compressed packets
|
300
|
-
packet_name_c = packet.name +
|
301
|
-
attrs_dict = {
|
302
|
-
|
303
|
-
|
304
|
-
|
316
|
+
packet_name_c = packet.name + "_C"
|
317
|
+
attrs_dict = {
|
318
|
+
"SRDB_ID_C": (
|
319
|
+
self.packet_parser.palisade_metadata[packet_name_c],
|
320
|
+
"srdb_id",
|
321
|
+
),
|
322
|
+
"PACKET_CATEGORY_C": (
|
323
|
+
self.packet_parser.palisade_metadata[packet_name_c],
|
324
|
+
"packet_category",
|
325
|
+
),
|
326
|
+
}
|
305
327
|
self.add_packet_attrs(packet, attrs_dict, packet_group)
|
306
328
|
|
307
329
|
# Return number of packets
|
308
|
-
return packet_group.attrs[
|
330
|
+
return packet_group.attrs["COUNT"]
|
309
331
|
|
310
332
|
def _packet_to_hdf5(self, name, data, group):
|
311
333
|
"""
|
@@ -337,16 +359,19 @@ class L0:
|
|
337
359
|
if delta_size > 0:
|
338
360
|
# If delta size is positive, it means that we need to increase
|
339
361
|
# the size of dataset array along axis 1
|
340
|
-
logger.debug(
|
362
|
+
logger.debug(
|
363
|
+
f"Resizing axis 1 of {name} from {dataset.shape[1]} to {dataset.shape[1] + delta_size}"
|
364
|
+
)
|
341
365
|
dataset.resize(dataset.shape[1] + delta_size, axis=1)
|
342
366
|
|
343
367
|
# Than save 2D array data
|
344
|
-
dataset[old_dset_shape[0]:, :data.shape[
|
345
|
-
|
368
|
+
dataset[old_dset_shape[0] :, : data.shape[1]] = data[
|
369
|
+
:, : data.shape[1]
|
370
|
+
]
|
346
371
|
|
347
372
|
else:
|
348
373
|
# Saving data along axis 0 if dim = 1
|
349
|
-
dataset[old_dset_shape[0]:] = data[:]
|
374
|
+
dataset[old_dset_shape[0] :] = data[:]
|
350
375
|
else:
|
351
376
|
# Else if the dataset does not exist, create it and insert input data
|
352
377
|
# Make sure that dataset is resizable (None == unlimited)
|
@@ -357,8 +382,9 @@ class L0:
|
|
357
382
|
maxshape = (None, None)
|
358
383
|
|
359
384
|
# Create and fill new chunked dataset
|
360
|
-
dataset = group.create_dataset(
|
361
|
-
|
385
|
+
dataset = group.create_dataset(
|
386
|
+
name, data.shape, dtype=data.dtype, maxshape=maxshape, chunks=True
|
387
|
+
)
|
362
388
|
dataset[:] = data
|
363
389
|
|
364
390
|
# in this case, this is a part of the record array, so treating
|
@@ -405,7 +431,9 @@ class L0:
|
|
405
431
|
:param filepath: Path of the file to check
|
406
432
|
:return: True if input file exists and has a valid RPW L0 file name, False otherwise
|
407
433
|
"""
|
408
|
-
return os.path.isfile(filepath) and L0.l0_file_prefix in os.path.basename(
|
434
|
+
return os.path.isfile(filepath) and L0.l0_file_prefix in os.path.basename(
|
435
|
+
filepath
|
436
|
+
)
|
409
437
|
|
410
438
|
@staticmethod
|
411
439
|
def set_attr(l0_attrs, metadata):
|
@@ -421,21 +449,24 @@ class L0:
|
|
421
449
|
|
422
450
|
for key, val in metadata.items():
|
423
451
|
if key in l0_attrs:
|
424
|
-
logger.debug(
|
425
|
-
|
452
|
+
logger.debug(
|
453
|
+
f"{key} L0 attribute already defined: Replace by {l0_attrs[key]}"
|
454
|
+
)
|
426
455
|
l0_attrs[key] = val
|
427
456
|
|
428
457
|
@staticmethod
|
429
|
-
def l0_to_packet_list(
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
458
|
+
def l0_to_packet_list(
|
459
|
+
files,
|
460
|
+
include=[],
|
461
|
+
exclude=[],
|
462
|
+
start_time=MIN_DATETIME,
|
463
|
+
end_time=MAX_DATETIME,
|
464
|
+
packet_type=PACKET_TYPE,
|
465
|
+
no_header=False,
|
466
|
+
no_data=False,
|
467
|
+
ascending=True,
|
468
|
+
to_dataframe=False,
|
469
|
+
):
|
439
470
|
"""
|
440
471
|
Parse list of input RPW L0 files
|
441
472
|
and return list of TM/TC packets
|
@@ -457,7 +488,6 @@ class L0:
|
|
457
488
|
import scipy.sparse as sparse
|
458
489
|
|
459
490
|
def load_param(current_group, current_df):
|
460
|
-
|
461
491
|
if not isinstance(current_group, h5py.Group):
|
462
492
|
return False
|
463
493
|
|
@@ -476,15 +506,14 @@ class L0:
|
|
476
506
|
total_sample_num = 0
|
477
507
|
file_num = len(files)
|
478
508
|
for file_idx, file in enumerate(files):
|
479
|
-
|
480
|
-
|
481
|
-
with h5py.File(file, 'r') as l0:
|
509
|
+
logger.debug(f"Opening {file}... ({file_idx + 1}/{file_num})")
|
510
|
+
with h5py.File(file, "r") as l0:
|
482
511
|
for current_type in packet_type:
|
483
512
|
for current_packet in l0[current_type].keys():
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
logger.debug(f
|
513
|
+
if (current_packet in exclude) or (
|
514
|
+
include and current_packet not in include
|
515
|
+
):
|
516
|
+
logger.debug(f"{current_packet} excluded")
|
488
517
|
continue
|
489
518
|
# else:
|
490
519
|
# logger.debug(f'{current_packet} included')
|
@@ -492,87 +521,108 @@ class L0:
|
|
492
521
|
# Initialize DataFrame instance for the current packet
|
493
522
|
current_df = pd.DataFrame()
|
494
523
|
|
495
|
-
current_times = decode(
|
496
|
-
current_packet][
|
524
|
+
current_times = decode(
|
525
|
+
l0[current_type][current_packet]["utc_time"][()]
|
526
|
+
)
|
497
527
|
|
498
|
-
current_binary = decode(
|
499
|
-
current_packet][
|
528
|
+
current_binary = decode(
|
529
|
+
l0[current_type][current_packet]["binary"][()]
|
530
|
+
)
|
500
531
|
|
501
532
|
# Get number of
|
502
533
|
current_nsamp = len(current_times)
|
503
|
-
current_counter = l0[current_type][
|
504
|
-
|
534
|
+
current_counter = l0[current_type][current_packet].attrs[
|
535
|
+
"COUNT"
|
536
|
+
]
|
505
537
|
try:
|
506
538
|
assert current_nsamp == current_counter
|
507
539
|
except Exception:
|
508
|
-
logger.warning(
|
540
|
+
logger.warning(
|
541
|
+
f"Something goes wrong for {current_packet} COUNT in {file}"
|
542
|
+
)
|
509
543
|
else:
|
510
|
-
logger.debug(
|
544
|
+
logger.debug(
|
545
|
+
f"{current_counter} {current_packet} found in {file}"
|
546
|
+
)
|
511
547
|
|
512
548
|
# Store packet utc time values
|
513
|
-
current_df[
|
549
|
+
current_df["utc_time"] = current_times
|
514
550
|
# Convert utc_time strings into datetime objects
|
515
|
-
current_df[
|
516
|
-
lambda x: datetime.strptime(x, TIME_L0_STRFORMAT)
|
551
|
+
current_df["utc_time"] = current_df["utc_time"].apply(
|
552
|
+
lambda x: datetime.strptime(x, TIME_L0_STRFORMAT)
|
553
|
+
)
|
517
554
|
|
518
555
|
# Convert binary hexa packet data to string hexa
|
519
|
-
current_df[
|
520
|
-
current_df[
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
current_df[
|
525
|
-
|
556
|
+
current_df["binary"] = current_binary
|
557
|
+
current_df["palisade_id"] = [current_packet] * current_nsamp
|
558
|
+
current_df["srdb_id"] = [
|
559
|
+
l0[current_type][current_packet].attrs["SRDB_ID"]
|
560
|
+
] * current_nsamp
|
561
|
+
current_df["category"] = [
|
562
|
+
l0[current_type][current_packet].attrs["PACKET_CATEGORY"]
|
563
|
+
] * current_nsamp
|
526
564
|
|
527
565
|
# Get IDB source/version
|
528
|
-
current_idb = l0[current_type][
|
529
|
-
|
530
|
-
current_df[
|
531
|
-
current_df['idb_version'] = decode(current_idb[:, 1])
|
566
|
+
current_idb = l0[current_type][current_packet]["idb"][()]
|
567
|
+
current_df["idb_source"] = decode(current_idb[:, 0])
|
568
|
+
current_df["idb_version"] = decode(current_idb[:, 1])
|
532
569
|
|
533
570
|
# Case of compressed TM packets
|
534
|
-
if
|
571
|
+
if "compressed" in l0[current_type][current_packet].keys():
|
535
572
|
# Add compressed array to the current dataframe
|
536
|
-
current_df[
|
537
|
-
|
573
|
+
current_df["compressed"] = l0[current_type][current_packet][
|
574
|
+
"compressed"
|
575
|
+
][()]
|
538
576
|
|
539
577
|
# Be sure that compressed TM have the right
|
540
578
|
# PALISADE_ID, SDRB_ID and CATEGORY
|
541
|
-
where_compressed =
|
542
|
-
current_df.loc[where_compressed,
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
current_df[
|
560
|
-
|
579
|
+
where_compressed = current_df.compressed == "1"
|
580
|
+
current_df.loc[where_compressed, "srdb_id"] = l0[
|
581
|
+
current_type
|
582
|
+
][current_packet].attrs["SRDB_ID_C"]
|
583
|
+
current_df.loc[where_compressed, "category"] = l0[
|
584
|
+
current_type
|
585
|
+
][current_packet].attrs["PACKET_CATEGORY_C"]
|
586
|
+
current_df.loc[where_compressed, "palisade_id"] = (
|
587
|
+
current_packet + "_C"
|
588
|
+
)
|
589
|
+
|
590
|
+
if current_type == "TM":
|
591
|
+
data_grp = "source_data"
|
592
|
+
elif current_type == "TC":
|
593
|
+
data_grp = "application_data"
|
594
|
+
current_df["unique_id"] = decode(
|
595
|
+
l0[current_type][current_packet]["unique_id"][()]
|
596
|
+
)
|
597
|
+
current_df["sequence_name"] = decode(
|
598
|
+
l0[current_type][current_packet]["sequence_name"][()]
|
599
|
+
)
|
600
|
+
current_tc_state = l0[current_type][current_packet][
|
601
|
+
"tc_ack_state"
|
602
|
+
][()]
|
603
|
+
current_df["tc_acc_state"] = decode(current_tc_state[:, 0])
|
604
|
+
current_df["tc_exe_state"] = decode(current_tc_state[:, 1])
|
561
605
|
|
562
606
|
if not no_header:
|
563
|
-
load_param(
|
564
|
-
|
565
|
-
|
566
|
-
|
607
|
+
load_param(
|
608
|
+
l0[current_type][current_packet]["packet_header"],
|
609
|
+
current_df,
|
610
|
+
)
|
611
|
+
load_param(
|
612
|
+
l0[current_type][current_packet]["data_field_header"],
|
613
|
+
current_df,
|
614
|
+
)
|
567
615
|
|
568
616
|
if not no_data:
|
569
|
-
load_param(
|
570
|
-
data_grp], current_df
|
617
|
+
load_param(
|
618
|
+
l0[current_type][current_packet][data_grp], current_df
|
619
|
+
)
|
571
620
|
|
572
621
|
# Filter by start_time/end_time
|
573
|
-
current_mask =
|
622
|
+
current_mask = current_df["utc_time"] >= start_time
|
574
623
|
current_mask = current_mask & (
|
575
|
-
current_df[
|
624
|
+
current_df["utc_time"] <= end_time
|
625
|
+
)
|
576
626
|
current_df = current_df.loc[current_mask]
|
577
627
|
# Get actual number of samples
|
578
628
|
current_nsamp = len(current_df.index)
|
@@ -583,38 +633,42 @@ class L0:
|
|
583
633
|
if current_packet not in packet_data:
|
584
634
|
packet_data[current_packet] = current_df
|
585
635
|
else:
|
586
|
-
packet_data[current_packet] = pd.concat(
|
587
|
-
current_packet], current_df]
|
636
|
+
packet_data[current_packet] = pd.concat(
|
637
|
+
[packet_data[current_packet], current_df]
|
638
|
+
)
|
588
639
|
|
589
640
|
logger.debug(
|
590
|
-
f
|
641
|
+
f"{current_nsamp} samples extracted from {current_packet} (total samples extracted: {total_sample_num})"
|
642
|
+
)
|
591
643
|
|
592
644
|
# Free memory
|
593
645
|
current_df = None
|
594
646
|
|
595
|
-
logger.info(
|
596
|
-
|
647
|
+
logger.info(
|
648
|
+
f"RPW packet loading completed ({total_sample_num} samples extracted)"
|
649
|
+
)
|
597
650
|
|
598
651
|
if to_dataframe:
|
599
|
-
logger.debug(
|
652
|
+
logger.debug("Return packet data as a Pandas DataFrame object")
|
600
653
|
return packet_data
|
601
654
|
|
602
|
-
logger.info(
|
655
|
+
logger.info("Converting to list of RPW packets...")
|
603
656
|
packet_list = [None] * total_sample_num
|
604
657
|
counter = 0
|
605
658
|
for key, val in packet_data.items():
|
606
|
-
current_samples = val.to_dict(
|
659
|
+
current_samples = val.to_dict("Records")
|
607
660
|
current_nsamp = len(current_samples)
|
608
|
-
packet_list[counter:counter + current_nsamp] = current_samples
|
661
|
+
packet_list[counter : counter + current_nsamp] = current_samples
|
609
662
|
counter += current_nsamp
|
610
|
-
perc =
|
611
|
-
logger.debug(
|
663
|
+
perc = "{: .2f}".format(100.0 * counter / total_sample_num)
|
664
|
+
logger.debug(
|
665
|
+
f"{current_nsamp} {key} samples added to the packet list ({perc}% completed)"
|
666
|
+
)
|
612
667
|
|
613
668
|
# Sort by ascending packet creation times
|
614
669
|
if ascending:
|
615
|
-
logger.info(
|
616
|
-
|
617
|
-
key = 'utc_time'
|
670
|
+
logger.info("Sorting packet list by ascending packet creation time...")
|
671
|
+
key = "utc_time"
|
618
672
|
sorted_packet_list = sorted(packet_list, key=lambda i: i[key])
|
619
673
|
else:
|
620
674
|
sorted_packet_list = packet_list
|
@@ -630,20 +684,27 @@ class L0:
|
|
630
684
|
:return: True if valid, False otherwise
|
631
685
|
"""
|
632
686
|
|
633
|
-
for mandatory_key in [
|
634
|
-
|
635
|
-
|
636
|
-
|
687
|
+
for mandatory_key in [
|
688
|
+
"palisade_id",
|
689
|
+
"srdb_id",
|
690
|
+
"utc_time",
|
691
|
+
"binary",
|
692
|
+
"category",
|
693
|
+
"idb_version",
|
694
|
+
"idb_source",
|
695
|
+
]:
|
637
696
|
if not packet.get(mandatory_key):
|
638
697
|
return False
|
639
698
|
|
640
|
-
if packet[
|
699
|
+
if packet["palisade_id"].startswith("TM"):
|
641
700
|
pass
|
642
|
-
elif packet[
|
643
|
-
for mandatory_key in [
|
644
|
-
|
645
|
-
|
646
|
-
|
701
|
+
elif packet["palisade_id"].startswith("TC"):
|
702
|
+
for mandatory_key in [
|
703
|
+
"unique_id",
|
704
|
+
"sequence_name",
|
705
|
+
"tc_acc_state",
|
706
|
+
"tc_exe_state",
|
707
|
+
]:
|
647
708
|
if not packet.get(mandatory_key):
|
648
709
|
return False
|
649
710
|
else:
|
@@ -652,11 +713,13 @@ class L0:
|
|
652
713
|
return True
|
653
714
|
|
654
715
|
@staticmethod
|
655
|
-
def l0_to_raw(
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
716
|
+
def l0_to_raw(
|
717
|
+
l0_file_list,
|
718
|
+
expected_packet_list=[],
|
719
|
+
start_time=None,
|
720
|
+
end_time=None,
|
721
|
+
increasing_time=True,
|
722
|
+
):
|
660
723
|
"""
|
661
724
|
Extract packet data from a list of L0 files.
|
662
725
|
|
@@ -676,59 +739,63 @@ class L0:
|
|
676
739
|
l0_file_list = [l0_file_list]
|
677
740
|
|
678
741
|
if len(l0_file_list) == 0:
|
679
|
-
logger.warning(
|
680
|
-
return {
|
742
|
+
logger.warning("Input list of L0 file is empty!")
|
743
|
+
return {"packet_list": []}
|
681
744
|
|
682
745
|
# loop over L0 file(s) to get data and save packet data
|
683
746
|
for i, l0_file in enumerate(l0_file_list):
|
684
|
-
logger.debug(f
|
685
|
-
with h5py.File(l0_file,
|
686
|
-
|
747
|
+
logger.debug(f"Parsing {l0_file}...")
|
748
|
+
with h5py.File(l0_file, "r") as l0:
|
687
749
|
# Get L0 time minimum/maximum
|
688
750
|
l0_time_min = datetime.strptime(
|
689
|
-
l0.attrs[
|
751
|
+
l0.attrs["TIME_MIN"], TIME_ISO_STRFORMAT
|
752
|
+
)
|
690
753
|
l0_time_max = datetime.strptime(
|
691
|
-
l0.attrs[
|
754
|
+
l0.attrs["TIME_MAX"], TIME_ISO_STRFORMAT
|
755
|
+
)
|
692
756
|
# If start_time/end_time passed, then filter L0 data outside
|
693
757
|
# time range
|
694
758
|
if start_time and l0_time_max < start_time:
|
695
|
-
logger.info(
|
759
|
+
logger.info(
|
760
|
+
f"{l0_file} time max. is less than {start_time}, skip it"
|
761
|
+
)
|
696
762
|
continue
|
697
763
|
|
698
764
|
if end_time and l0_time_min > end_time:
|
699
|
-
logger.info(
|
765
|
+
logger.info(
|
766
|
+
f"{l0_file} time min. is greater than {end_time}, skip it"
|
767
|
+
)
|
700
768
|
continue
|
701
769
|
|
702
|
-
file_id = l0.attrs[
|
703
|
-
creation_date = l0.attrs[
|
770
|
+
file_id = l0.attrs["File_ID"]
|
771
|
+
creation_date = l0.attrs["Generation_date"]
|
704
772
|
|
705
773
|
# if expected_packet_list not passed as an input
|
706
774
|
# then load all packets
|
707
775
|
if not expected_packet_list:
|
708
776
|
l0_expected_packet_list = []
|
709
|
-
if
|
710
|
-
l0_expected_packet_list.extend(list(l0[
|
711
|
-
if
|
712
|
-
l0_expected_packet_list.extend(list(l0[
|
777
|
+
if "TM" in l0.keys():
|
778
|
+
l0_expected_packet_list.extend(list(l0["TM"].keys()))
|
779
|
+
if "TC" in l0.keys():
|
780
|
+
l0_expected_packet_list.extend(list(l0["TC"].keys()))
|
713
781
|
else:
|
714
782
|
l0_expected_packet_list = expected_packet_list
|
715
783
|
|
716
784
|
# Loop over list of expected packets
|
717
785
|
for expected_packet in l0_expected_packet_list:
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
current_data_group = 'application_data'
|
786
|
+
if expected_packet.startswith("TM"):
|
787
|
+
current_type = "TM"
|
788
|
+
current_data_group = "source_data"
|
789
|
+
elif expected_packet.startswith("TC"):
|
790
|
+
current_type = "TC"
|
791
|
+
current_data_group = "application_data"
|
725
792
|
else:
|
726
|
-
logger.warning(f
|
793
|
+
logger.warning(f"Unknown packet type: {expected_packet}")
|
727
794
|
continue
|
728
795
|
|
729
796
|
# Check that packet(s) are found in the present l0 file
|
730
797
|
if not is_packet(expected_packet, l0[current_type]):
|
731
|
-
logger.debug(f
|
798
|
+
logger.debug(f"No {expected_packet} packet found in {l0_file}")
|
732
799
|
# if not continue
|
733
800
|
continue
|
734
801
|
|
@@ -739,16 +806,16 @@ class L0:
|
|
739
806
|
current_packet_data = current_packet[current_data_group]
|
740
807
|
|
741
808
|
# Get list of TC execution UTC times
|
742
|
-
utc_time_list = decode(current_packet[
|
809
|
+
utc_time_list = decode(current_packet["utc_time"][()])
|
743
810
|
|
744
811
|
# Get number of packets
|
745
812
|
current_packet_num = len(utc_time_list)
|
746
813
|
|
747
814
|
# Loop over packets in l0
|
748
|
-
logger.debug(
|
749
|
-
|
815
|
+
logger.debug(
|
816
|
+
f"Extracting {current_packet_num} {expected_packet} packets..."
|
817
|
+
)
|
750
818
|
for j, utc_time in enumerate(utc_time_list):
|
751
|
-
|
752
819
|
try:
|
753
820
|
# Put current packet parameter values into a
|
754
821
|
# dictionary
|
@@ -763,93 +830,101 @@ class L0:
|
|
763
830
|
|
764
831
|
# Get current packet header
|
765
832
|
current_packet_header = L0.l0_packet_to_dotdict(
|
766
|
-
l0[current_type][expected_packet][
|
833
|
+
l0[current_type][expected_packet]["packet_header"], j
|
834
|
+
)
|
767
835
|
|
768
836
|
# Get current packet data field header
|
769
837
|
current_packet_data_header = L0.l0_packet_to_dotdict(
|
770
|
-
l0[current_type][expected_packet][
|
838
|
+
l0[current_type][expected_packet]["data_field_header"], j
|
839
|
+
)
|
771
840
|
|
772
841
|
# Compute packet APID
|
773
|
-
current_apid = IDBParser.compute_apid(
|
774
|
-
|
842
|
+
current_apid = IDBParser.compute_apid(
|
843
|
+
current_packet_header["process_id"],
|
844
|
+
current_packet_header["packet_category"],
|
845
|
+
)
|
775
846
|
|
776
847
|
# Get some packet attributes in L0
|
777
848
|
try:
|
778
|
-
srdb_id = current_packet.attrs[
|
779
|
-
packet_cat = current_packet.attrs[
|
780
|
-
'PACKET_CATEGORY']
|
849
|
+
srdb_id = current_packet.attrs["SRDB_ID"]
|
850
|
+
packet_cat = current_packet.attrs["PACKET_CATEGORY"]
|
781
851
|
except Exception:
|
782
|
-
logger.warning(f
|
852
|
+
logger.warning(f"Attributes missing in {l0_file}!")
|
783
853
|
srdb_id = None
|
784
854
|
packet_cat = None
|
785
855
|
|
786
856
|
# Get compression flag
|
787
857
|
try:
|
788
|
-
current_compressed = current_packet[
|
789
|
-
'compressed'][:]
|
858
|
+
current_compressed = current_packet["compressed"][:]
|
790
859
|
except Exception:
|
791
860
|
current_compressed = None
|
792
861
|
|
793
862
|
# Add current packet parameters to lists
|
794
863
|
# utc_time, name of packet and parameters
|
795
864
|
current_packet_fields = {
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
865
|
+
"utc_time": L0.l0_utc_time_to_dt(utc_time),
|
866
|
+
"palisade_id": expected_packet,
|
867
|
+
"srdb_id": srdb_id,
|
868
|
+
"data": parameters,
|
869
|
+
"type": current_type,
|
870
|
+
"category": packet_cat,
|
871
|
+
"binary": decode(current_packet["binary"][j]),
|
872
|
+
"idb_source": decode(current_packet["idb"][j][0]),
|
873
|
+
"idb_version": decode(current_packet["idb"][j][1]),
|
874
|
+
"length": current_packet_header["packet_length"] + 7,
|
875
|
+
"apid": current_apid,
|
876
|
+
"header": current_packet_header,
|
877
|
+
"data_header": current_packet_data_header,
|
878
|
+
"compressed": current_compressed,
|
810
879
|
}
|
811
880
|
|
812
881
|
# If TC or TM, add also specific attributes
|
813
|
-
if current_type ==
|
814
|
-
current_packet_fields[
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
current_packet_fields[
|
821
|
-
|
822
|
-
|
823
|
-
current_packet_fields[
|
824
|
-
|
825
|
-
|
826
|
-
|
882
|
+
if current_type == "TC":
|
883
|
+
current_packet_fields["unique_id"] = current_packet[
|
884
|
+
"unique_id"
|
885
|
+
][j]
|
886
|
+
current_packet_fields["sequence_name"] = current_packet[
|
887
|
+
"sequence_name"
|
888
|
+
][j]
|
889
|
+
current_packet_fields["ack_acc_state"] = current_packet[
|
890
|
+
"tc_ack_state"
|
891
|
+
][j][0]
|
892
|
+
current_packet_fields["ack_exe_state"] = current_packet[
|
893
|
+
"tc_ack_state"
|
894
|
+
][j][1]
|
895
|
+
elif current_type == "TM":
|
896
|
+
current_packet_fields["sync_flag"] = (
|
897
|
+
current_packet_data_header["time"][2]
|
898
|
+
)
|
899
|
+
current_packet_fields["obt_time"] = (
|
900
|
+
f"1/"
|
901
|
+
f"{current_packet_data_header['time'][0]}:"
|
827
902
|
f"{current_packet_data_header['time'][1]}"
|
903
|
+
)
|
828
904
|
|
829
905
|
output_packet_list.append(current_packet_fields)
|
830
906
|
|
831
907
|
output_packet_num = len(output_packet_list)
|
832
908
|
if output_packet_num == 0:
|
833
|
-
logger.info(
|
834
|
-
return {
|
909
|
+
logger.info("No packet has been returned")
|
910
|
+
return {"packet_list": []}
|
835
911
|
else:
|
836
|
-
logger.debug(f
|
912
|
+
logger.debug(f"{output_packet_num} packets returned")
|
837
913
|
# Sort list by increasing time
|
838
914
|
if increasing_time:
|
839
915
|
output_packet_list = sorted(
|
840
|
-
output_packet_list,
|
841
|
-
|
842
|
-
k: k['utc_time'])
|
916
|
+
output_packet_list, key=lambda k: k["utc_time"]
|
917
|
+
)
|
843
918
|
|
844
919
|
# FIXME - Strange output combining data from one L0
|
845
920
|
# (l0_time_min, l0_time_max, file_id, creation_date)
|
846
921
|
# and for several L0 (packet_list)
|
847
922
|
l0_data = {
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
923
|
+
"time_min": l0_time_min,
|
924
|
+
"time_max": l0_time_max,
|
925
|
+
"file_id": file_id,
|
926
|
+
"creation_date": creation_date,
|
927
|
+
"packet_list": output_packet_list,
|
853
928
|
}
|
854
929
|
|
855
930
|
return l0_data
|
@@ -874,18 +949,19 @@ class L0:
|
|
874
949
|
l0_file_list = [l0_file_list]
|
875
950
|
|
876
951
|
if len(l0_file_list) == 0:
|
877
|
-
logger.warning(
|
952
|
+
logger.warning("Input list of L0 file is empty!")
|
878
953
|
return output_file_list
|
879
954
|
|
880
955
|
# loop over L0 file(s) to filter by time range
|
881
956
|
for i, l0_file in enumerate(l0_file_list):
|
882
|
-
with h5py.File(l0_file,
|
883
|
-
|
957
|
+
with h5py.File(l0_file, "r") as l0:
|
884
958
|
# Get L0 time minimum/maximum
|
885
959
|
l0_time_min = datetime.strptime(
|
886
|
-
l0.attrs[
|
960
|
+
l0.attrs["TIME_MIN"], TIME_ISO_STRFORMAT
|
961
|
+
)
|
887
962
|
l0_time_max = datetime.strptime(
|
888
|
-
l0.attrs[
|
963
|
+
l0.attrs["TIME_MAX"], TIME_ISO_STRFORMAT
|
964
|
+
)
|
889
965
|
# If start_time/end_time passed, then filter L0 data outside
|
890
966
|
# time range
|
891
967
|
if start_time and l0_time_max < start_time:
|
@@ -930,13 +1006,10 @@ class L0:
|
|
930
1006
|
"""
|
931
1007
|
# Nanoseconds not supported by datetime and must removed from input l0
|
932
1008
|
# utc time
|
933
|
-
return datetime.strptime(decode(l0_utc_time)[:-4] +
|
934
|
-
TIME_ISO_STRFORMAT)
|
1009
|
+
return datetime.strptime(decode(l0_utc_time)[:-4] + "Z", TIME_ISO_STRFORMAT)
|
935
1010
|
|
936
1011
|
@staticmethod
|
937
|
-
def count_packets(l0_file,
|
938
|
-
tm=True,
|
939
|
-
tc=True):
|
1012
|
+
def count_packets(l0_file, tm=True, tc=True):
|
940
1013
|
"""
|
941
1014
|
Count number of Packet elements in the input RPW L0 file.
|
942
1015
|
|
@@ -948,13 +1021,12 @@ class L0:
|
|
948
1021
|
# Initialize output
|
949
1022
|
packet_num = 0
|
950
1023
|
|
951
|
-
with h5py.File(l0_file,
|
952
|
-
tm_num = l0[
|
953
|
-
tc_num = l0[
|
1024
|
+
with h5py.File(l0_file, "r") as l0:
|
1025
|
+
tm_num = l0["TM"].attrs["COUNT"]
|
1026
|
+
tc_num = l0["TC"].attrs["COUNT"]
|
954
1027
|
|
955
1028
|
if not tm and not tc:
|
956
|
-
logger.warning(
|
957
|
-
'At least tm and/or tc keyword must be set to True')
|
1029
|
+
logger.warning("At least tm and/or tc keyword must be set to True")
|
958
1030
|
return packet_num
|
959
1031
|
|
960
1032
|
if tm:
|
@@ -965,9 +1037,7 @@ class L0:
|
|
965
1037
|
return packet_num
|
966
1038
|
|
967
1039
|
@staticmethod
|
968
|
-
def order_by_utc(l0_file,
|
969
|
-
unique=False,
|
970
|
-
update_time_minmax=False):
|
1040
|
+
def order_by_utc(l0_file, unique=False, update_time_minmax=False):
|
971
1041
|
"""
|
972
1042
|
Order data in an input RPW L0 HDF5 file
|
973
1043
|
by packet creation UTC times.
|
@@ -978,6 +1048,7 @@ class L0:
|
|
978
1048
|
root attributes with actual values.
|
979
1049
|
:return:
|
980
1050
|
"""
|
1051
|
+
|
981
1052
|
# Internal function to sort a dataset according to input indices
|
982
1053
|
def _sort_dataset(group, item, unique_idx, sorted_idx):
|
983
1054
|
# If not dtype.names, then we have a dataset
|
@@ -988,15 +1059,14 @@ class L0:
|
|
988
1059
|
# Only update dataset if no empty data
|
989
1060
|
if current_dataset.shape[0] > 0:
|
990
1061
|
# Extract sorted/unique dataset array
|
991
|
-
sorted_dataset = (current_dataset[()][unique_idx])[
|
992
|
-
sorted_idx]
|
1062
|
+
sorted_dataset = (current_dataset[()][unique_idx])[sorted_idx]
|
993
1063
|
|
994
1064
|
# If there duplicate values, then resize L0 HDF5 dataset
|
995
1065
|
if sorted_dataset.shape[0] < current_dataset.shape[0]:
|
996
|
-
delta_shape = current_dataset.shape[
|
997
|
-
|
998
|
-
|
999
|
-
|
1066
|
+
delta_shape = current_dataset.shape[0] - sorted_dataset.shape[0]
|
1067
|
+
current_dataset.resize(
|
1068
|
+
current_dataset.shape[0] - delta_shape, axis=0
|
1069
|
+
)
|
1000
1070
|
|
1001
1071
|
# Then update it with sorted indices
|
1002
1072
|
current_dataset[...] = sorted_dataset
|
@@ -1007,54 +1077,61 @@ class L0:
|
|
1007
1077
|
_sort_dataset(group[item], field, unique_idx, sorted_idx)
|
1008
1078
|
|
1009
1079
|
# Initialize time_min / time_max
|
1010
|
-
time_min =
|
1011
|
-
time_max =
|
1080
|
+
time_min = "9999-12-31T23:59:59.999999999Z"
|
1081
|
+
time_max = "0000-01-01T00:00:00.000000000Z"
|
1012
1082
|
|
1013
1083
|
# Open L0 file
|
1014
|
-
with h5py.File(l0_file,
|
1015
|
-
|
1084
|
+
with h5py.File(l0_file, "a") as l0:
|
1016
1085
|
# Loop over TM/TC groups in L0
|
1017
|
-
for cat in [
|
1086
|
+
for cat in ["TM", "TC"]:
|
1018
1087
|
# Check that TM/TC group exists
|
1019
1088
|
if cat in l0.keys():
|
1020
1089
|
# Loop over each TM packet in L0
|
1021
1090
|
new_packet_count = []
|
1022
1091
|
for packet_name in l0[cat].keys():
|
1023
|
-
|
1024
1092
|
# Get packet utc_time values
|
1025
|
-
original_utc_time = l0[cat][
|
1026
|
-
packet_name]['utc_time'][()]
|
1093
|
+
original_utc_time = l0[cat][packet_name]["utc_time"][()]
|
1027
1094
|
|
1028
1095
|
if unique:
|
1029
1096
|
# If unique input keyword is True,
|
1030
|
-
if cat ==
|
1097
|
+
if cat == "TM":
|
1031
1098
|
# then if TM
|
1032
1099
|
# apply the uniqueness of the tuple (packet,
|
1033
1100
|
# binary) over packets in l0 file
|
1034
|
-
binary = l0[cat][
|
1035
|
-
packet_name]['binary'][()]
|
1101
|
+
binary = l0[cat][packet_name]["binary"][()]
|
1036
1102
|
binary, unique_idx = np.unique(
|
1037
|
-
binary, return_index=True
|
1103
|
+
binary, return_index=True
|
1104
|
+
)
|
1038
1105
|
utc_time = original_utc_time[unique_idx]
|
1039
1106
|
else:
|
1040
1107
|
# then if TC
|
1041
1108
|
# apply the uniqueness of the tuple (packet,
|
1042
1109
|
# utc_time) over packets in l0 file
|
1043
1110
|
utc_time, unique_idx = np.unique(
|
1044
|
-
original_utc_time, return_index=True
|
1111
|
+
original_utc_time, return_index=True
|
1112
|
+
)
|
1045
1113
|
|
1046
1114
|
# Update packet COUNT attribute
|
1047
|
-
l0[cat][packet_name].attrs[
|
1048
|
-
'COUNT'] = utc_time.shape[0]
|
1115
|
+
l0[cat][packet_name].attrs["COUNT"] = utc_time.shape[0]
|
1049
1116
|
new_packet_count.append(utc_time.shape[0])
|
1050
1117
|
|
1051
1118
|
else:
|
1052
1119
|
unique_idx = np.arange(original_utc_time.shape[0])
|
1053
1120
|
|
1054
|
-
if
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1121
|
+
if (
|
1122
|
+
cat == "TM"
|
1123
|
+
and utc_time.shape[0] != original_utc_time.shape[0]
|
1124
|
+
):
|
1125
|
+
duplicated_times = [
|
1126
|
+
item
|
1127
|
+
for item, count in collections.Counter(
|
1128
|
+
original_utc_time
|
1129
|
+
).items()
|
1130
|
+
if count > 1
|
1131
|
+
]
|
1132
|
+
logger.warning(
|
1133
|
+
f"There are duplicated times ({duplicated_times}) for packet {packet_name} in {l0_file}!"
|
1134
|
+
)
|
1058
1135
|
|
1059
1136
|
# Code block to get indices of duplicated elements
|
1060
1137
|
# duplicated_indices = []
|
@@ -1068,7 +1145,8 @@ class L0:
|
|
1068
1145
|
for item in L0.L0_FIELDS[cat]:
|
1069
1146
|
if item in l0[cat][packet_name].keys():
|
1070
1147
|
_sort_dataset(
|
1071
|
-
l0[cat][packet_name], item, unique_idx, sorted_idx
|
1148
|
+
l0[cat][packet_name], item, unique_idx, sorted_idx
|
1149
|
+
)
|
1072
1150
|
|
1073
1151
|
if np.min(utc_time):
|
1074
1152
|
time_min = min([decode(np.min(utc_time)), time_min])
|
@@ -1078,9 +1156,9 @@ class L0:
|
|
1078
1156
|
# If unique is True, then update the COUNT attribute of
|
1079
1157
|
# TM/TC group
|
1080
1158
|
if unique:
|
1081
|
-
l0[cat].attrs[
|
1159
|
+
l0[cat].attrs["COUNT"] = sum(new_packet_count)
|
1082
1160
|
|
1083
1161
|
if update_time_minmax:
|
1084
1162
|
# Save microsecond resolution
|
1085
|
-
l0.attrs[
|
1086
|
-
l0.attrs[
|
1163
|
+
l0.attrs["TIME_MIN"] = time_min[:-4] + "Z"
|
1164
|
+
l0.attrs["TIME_MAX"] = time_max[:-4] + "Z"
|