roc-film 1.13.4__py3-none-any.whl → 1.14.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|