pyadps 0.3.3b0__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.
- pyadps/Home_Page.py +42 -0
- pyadps/__init__.py +8 -0
- pyadps/__main__.py +15 -0
- pyadps/pages/01_Read_File.py +458 -0
- pyadps/pages/02_View_Raw_Data.py +164 -0
- pyadps/pages/03_Download_Raw_File.py +298 -0
- pyadps/pages/04_Sensor_Health.py +905 -0
- pyadps/pages/05_QC_Test.py +476 -0
- pyadps/pages/06_Profile_Test.py +970 -0
- pyadps/pages/07_Velocity_Test.py +600 -0
- pyadps/pages/08_Write_File.py +574 -0
- pyadps/pages/09_Auto_process.py +62 -0
- pyadps/pages/__init__.py +0 -0
- pyadps/utils/__init__.py +12 -0
- pyadps/utils/autoprocess.py +530 -0
- pyadps/utils/metadata/config.ini +99 -0
- pyadps/utils/metadata/demo.000 +0 -0
- pyadps/utils/metadata/flmeta.json +422 -0
- pyadps/utils/metadata/vlmeta.json +567 -0
- pyadps/utils/plotgen.py +728 -0
- pyadps/utils/profile_test.py +556 -0
- pyadps/utils/pyreadrdi.py +969 -0
- pyadps/utils/readrdi.py +1610 -0
- pyadps/utils/script.py +201 -0
- pyadps/utils/sensor_health.py +120 -0
- pyadps/utils/signal_quality.py +455 -0
- pyadps/utils/velocity_test.py +200 -0
- pyadps/utils/writenc.py +339 -0
- pyadps-0.3.3b0.dist-info/LICENSE +8 -0
- pyadps-0.3.3b0.dist-info/METADATA +172 -0
- pyadps-0.3.3b0.dist-info/RECORD +33 -0
- pyadps-0.3.3b0.dist-info/WHEEL +4 -0
- pyadps-0.3.3b0.dist-info/entry_points.txt +5 -0
pyadps/utils/readrdi.py
ADDED
@@ -0,0 +1,1610 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
"""
|
4
|
+
RDI ADCP Binary File Reader
|
5
|
+
===========================
|
6
|
+
This module provides classes and functions to read and extract data from RDI Acoustic Doppler
|
7
|
+
Current Profiler (ADCP) binary files. The module supports Workhorse, Ocean Surveyor, and DVS ADCPs.
|
8
|
+
It allows for parsing of various data types such as Fixed Leader, Variable Leader, Velocity, Correlation,
|
9
|
+
Echo Intensity, Percent Good, and Status data.
|
10
|
+
|
11
|
+
Classes
|
12
|
+
-------
|
13
|
+
FileHeader
|
14
|
+
Extracts metadata from the ADCP file header.
|
15
|
+
FixedLeader
|
16
|
+
Handles the Fixed Leader Data that contains configuration and system settings.
|
17
|
+
VariableLeader
|
18
|
+
Extracts and processes the dynamic Variable Leader Data.
|
19
|
+
Velocity
|
20
|
+
Retrieves the velocity data from the ADCP file.
|
21
|
+
Correlation
|
22
|
+
Retrieves correlation data between ADCP beams.
|
23
|
+
Echo
|
24
|
+
Retrieves echo intensity data from the ADCP file.
|
25
|
+
PercentGood
|
26
|
+
Extracts the percentage of valid velocity data.
|
27
|
+
Status
|
28
|
+
Parses the status data from the ADCP.
|
29
|
+
ReadFile
|
30
|
+
Manages the entire data extraction process and unifies all data types.
|
31
|
+
|
32
|
+
Functions
|
33
|
+
---------
|
34
|
+
check_equal(array)
|
35
|
+
Utility function to check if all values in an array are equal.
|
36
|
+
error_code(code)
|
37
|
+
Maps an error code to a human-readable message.
|
38
|
+
|
39
|
+
Creation Date
|
40
|
+
--------------
|
41
|
+
2024-09-01
|
42
|
+
|
43
|
+
Last Modified Date
|
44
|
+
--------------
|
45
|
+
2024-09-05
|
46
|
+
|
47
|
+
Version
|
48
|
+
-------
|
49
|
+
0.3.0
|
50
|
+
|
51
|
+
Author
|
52
|
+
------
|
53
|
+
[P. Amol] <your.email@example.com>
|
54
|
+
|
55
|
+
License
|
56
|
+
-------
|
57
|
+
This module is licensed under the MIT License. See LICENSE file for details.
|
58
|
+
|
59
|
+
Dependencies
|
60
|
+
------------
|
61
|
+
- numpy : Used for array handling and mathematical operations.
|
62
|
+
- pyreadrdi : Python interface for reading RDI ADCP binary files (supports variableleader, fixedleader, etc.).
|
63
|
+
- sys : System-specific parameters and functions.
|
64
|
+
- DotDict : Utility class to handle dictionary-like objects with dot access.
|
65
|
+
|
66
|
+
Install the dependencies using:
|
67
|
+
pip install numpy
|
68
|
+
|
69
|
+
Usage
|
70
|
+
-----
|
71
|
+
Basic Example:
|
72
|
+
```python
|
73
|
+
from readrdi import ReadFile
|
74
|
+
|
75
|
+
# Initialize the ReadFile class
|
76
|
+
adcp_obj = ReadFile('path_to_your_rdi_file.000')
|
77
|
+
velocity_data = adcp_obj.velocity.data
|
78
|
+
pressure_data = adcp_obj.variableleader.pressure.data
|
79
|
+
|
80
|
+
# Individual data types can be accessed without reading the entire file
|
81
|
+
# An example to access fixed leader data
|
82
|
+
|
83
|
+
import readrdi as rd
|
84
|
+
|
85
|
+
fixed_leader_obj = rd.FixedLeader('path_to_your_rdi_file.000')
|
86
|
+
isfixedleader_uniform_dict = fixed_leader_obj.is_uniform()
|
87
|
+
|
88
|
+
# Access velocity data
|
89
|
+
velocity_data = adcp_file.velocity.data
|
90
|
+
|
91
|
+
# Check for warnings and errors
|
92
|
+
if adcp_obj.isWarning:
|
93
|
+
print("Warning: Some errors were encountered during data extraction.")
|
94
|
+
else:
|
95
|
+
print("Data extracted successfully.")
|
96
|
+
"""
|
97
|
+
|
98
|
+
import importlib.resources as pkg_resources
|
99
|
+
import json
|
100
|
+
import os
|
101
|
+
import sys
|
102
|
+
|
103
|
+
import numpy as np
|
104
|
+
import pandas as pd
|
105
|
+
from pyadps.utils import pyreadrdi
|
106
|
+
|
107
|
+
|
108
|
+
class DotDict:
|
109
|
+
"""
|
110
|
+
A dictionary-like class that allows access to dictionary items as attributes.
|
111
|
+
If initialized with a dictionary, it converts it into attributes.
|
112
|
+
If initialized without a dictionary, it loads one from a JSON file or initializes an empty dictionary.
|
113
|
+
|
114
|
+
Parameters
|
115
|
+
----------
|
116
|
+
dictionary : dict, optional
|
117
|
+
A dictionary to initialize the DotDict with. If not provided, a JSON file is loaded or an empty dictionary is created.
|
118
|
+
json_file_path : str, optional
|
119
|
+
Path to the JSON file to load the dictionary from, by default "data.json".
|
120
|
+
"""
|
121
|
+
|
122
|
+
def __init__(self, dictionary=None, json_file_path="data.json"):
|
123
|
+
if dictionary is None:
|
124
|
+
if pkg_resources.is_resource("pyadps.utils.metadata", json_file_path):
|
125
|
+
with pkg_resources.open_text(
|
126
|
+
"pyadps.utils.metadata", json_file_path
|
127
|
+
) as f:
|
128
|
+
dictionary = json.load(f)
|
129
|
+
# if os.path.exists(json_file_path):
|
130
|
+
# with open(json_file_path, "r") as file:
|
131
|
+
# dictionary = json.load(file)
|
132
|
+
else:
|
133
|
+
dictionary = {} # Initialize an empty dictionary if no JSON file is found
|
134
|
+
self._initialize_from_dict(dictionary)
|
135
|
+
|
136
|
+
def _initialize_from_dict(self, dictionary):
|
137
|
+
"""
|
138
|
+
Recursively initializes DotDict attributes from a given dictionary.
|
139
|
+
|
140
|
+
Parameters
|
141
|
+
----------
|
142
|
+
dictionary : dict
|
143
|
+
The dictionary to convert into attributes for the DotDict object.
|
144
|
+
"""
|
145
|
+
|
146
|
+
for key, value in dictionary.items():
|
147
|
+
if isinstance(value, dict):
|
148
|
+
value = DotDict(value)
|
149
|
+
setattr(self, key, value)
|
150
|
+
|
151
|
+
def __getattr__(self, key):
|
152
|
+
"""
|
153
|
+
Retrieves an attribute from the DotDict object by its key.
|
154
|
+
|
155
|
+
Parameters
|
156
|
+
----------
|
157
|
+
key : str
|
158
|
+
The attribute key to retrieve.
|
159
|
+
|
160
|
+
Returns
|
161
|
+
-------
|
162
|
+
value : any
|
163
|
+
The value corresponding to the given key.
|
164
|
+
"""
|
165
|
+
return self.__dict__.get(key)
|
166
|
+
|
167
|
+
|
168
|
+
def error_code(code):
|
169
|
+
if code == 0:
|
170
|
+
error_string = "Data type is healthy"
|
171
|
+
elif code == 1:
|
172
|
+
error_string = "End of file"
|
173
|
+
elif code == 2:
|
174
|
+
error_string = "File Corrupted (ID not recognized)"
|
175
|
+
elif code == 3:
|
176
|
+
error_string = "Wrong file type"
|
177
|
+
elif code == 4:
|
178
|
+
error_string = "Data type mismatch"
|
179
|
+
else:
|
180
|
+
error_string = "Unknown error"
|
181
|
+
return error_string
|
182
|
+
|
183
|
+
|
184
|
+
def check_equal(lst):
|
185
|
+
"""
|
186
|
+
Checks if all elements in the list are equal.
|
187
|
+
|
188
|
+
Parameters
|
189
|
+
----------
|
190
|
+
lst : list
|
191
|
+
A list of elements to check.
|
192
|
+
|
193
|
+
Returns
|
194
|
+
-------
|
195
|
+
bool
|
196
|
+
True if all elements in the list are equal, False otherwise.
|
197
|
+
"""
|
198
|
+
return np.all(np.array(lst) == lst[0])
|
199
|
+
|
200
|
+
|
201
|
+
class FileHeader:
|
202
|
+
"""
|
203
|
+
A class to handle the extraction and management of file header
|
204
|
+
data from an RDI ADCP binary data.
|
205
|
+
|
206
|
+
The `FileHeader` class uses the `fileheader` function (imported
|
207
|
+
from an external module) to extract various data sets from a
|
208
|
+
binary file. These data sets are assigned to instance variables within the class. The class provides methods
|
209
|
+
to perform operations on the extracted data, such as checking the file format and determining data types.
|
210
|
+
FileHeader class can be used for getting Header information from an
|
211
|
+
RDI ADCP File. The information can be accessed by the class instance
|
212
|
+
called 'header', which is a list.
|
213
|
+
|
214
|
+
|
215
|
+
Input:
|
216
|
+
----------
|
217
|
+
rdi_file = TYPE STRING
|
218
|
+
RDI ADCP binary file. The class can currently extract Workhorse,
|
219
|
+
Ocean Surveyor, and DVS files.
|
220
|
+
|
221
|
+
Attributes:
|
222
|
+
-------------------
|
223
|
+
filename = TYPE CHARACTER
|
224
|
+
Returns the input filename
|
225
|
+
ensembles = TYPE INTEGER
|
226
|
+
Total number of ensembles in the file
|
227
|
+
header = DICTIONARY(STRING, LIST(INTEGER))
|
228
|
+
KEYS: 'Header ID', 'Source ID', 'Bytes', 'Spare',
|
229
|
+
'Data Types', 'Address Offset'
|
230
|
+
|
231
|
+
Methods:
|
232
|
+
--------
|
233
|
+
datatypes(ens=0)
|
234
|
+
Lists out the data types for any one ensemble (default = 0)
|
235
|
+
check_file()
|
236
|
+
Checks if
|
237
|
+
1. system file size and calculated file size are same
|
238
|
+
2. no. of data types and no. of bytes are same for all ensembles
|
239
|
+
|
240
|
+
Example code to access the class
|
241
|
+
--------------------------------
|
242
|
+
myvar = FileHeader(rdi_file)
|
243
|
+
myvar.header['Header Id']
|
244
|
+
myvar.datatypes(ens=1)
|
245
|
+
myvar.check_file()
|
246
|
+
|
247
|
+
"""
|
248
|
+
|
249
|
+
def __init__(self, rdi_file):
|
250
|
+
"""
|
251
|
+
Initializes the FileHeader object, extracts header data, and assigns it to instance variables.
|
252
|
+
|
253
|
+
Parameters
|
254
|
+
----------
|
255
|
+
rdi_file : str
|
256
|
+
The RDI ADCP binary file to extract data from.
|
257
|
+
"""
|
258
|
+
self.filename = rdi_file
|
259
|
+
(
|
260
|
+
self.datatypes,
|
261
|
+
self.bytes,
|
262
|
+
self.byteskip,
|
263
|
+
self.address_offset,
|
264
|
+
self.dataid,
|
265
|
+
self.ensembles,
|
266
|
+
self.error,
|
267
|
+
) = pyreadrdi.fileheader(rdi_file)
|
268
|
+
self.warning = pyreadrdi.ErrorCode.get_message(self.error)
|
269
|
+
|
270
|
+
def data_types(self, ens=0):
|
271
|
+
"""
|
272
|
+
Finds the available data types for an ensemble.
|
273
|
+
|
274
|
+
Parameters
|
275
|
+
----------
|
276
|
+
ens : int, optional
|
277
|
+
Ensemble number to get data types for, by default 0 or the first ensemble.
|
278
|
+
|
279
|
+
Returns
|
280
|
+
-------
|
281
|
+
list
|
282
|
+
A list of data type names corresponding to the ensemble.
|
283
|
+
"""
|
284
|
+
|
285
|
+
data_id_array = self.dataid[ens]
|
286
|
+
id_name_array = list()
|
287
|
+
i = 0
|
288
|
+
|
289
|
+
for data_id in data_id_array:
|
290
|
+
# Checks dual mode IDs (BroadBand or NarrowBand)
|
291
|
+
# The first ID is generally the default ID
|
292
|
+
if data_id in (0, 1):
|
293
|
+
id_name = "Fixed Leader"
|
294
|
+
elif data_id in (128, 129):
|
295
|
+
id_name = "Variable Leader"
|
296
|
+
elif data_id in (256, 257):
|
297
|
+
id_name = "Velocity"
|
298
|
+
elif data_id in (512, 513):
|
299
|
+
id_name = "Correlation"
|
300
|
+
elif data_id in (768, 769):
|
301
|
+
id_name = "Echo"
|
302
|
+
elif data_id in (1024, 1025):
|
303
|
+
id_name = "Percent Good"
|
304
|
+
elif data_id == 1280:
|
305
|
+
id_name = "Status"
|
306
|
+
elif data_id == 1536:
|
307
|
+
id_name = "Bottom Track"
|
308
|
+
else:
|
309
|
+
id_name = "ID not Found"
|
310
|
+
|
311
|
+
id_name_array.append(id_name)
|
312
|
+
i += 1
|
313
|
+
|
314
|
+
return id_name_array
|
315
|
+
|
316
|
+
def check_file(self):
|
317
|
+
"""
|
318
|
+
Checks if the system file size matches the calculated file size and
|
319
|
+
verifies uniformity across bytes and data types for all ensembles.
|
320
|
+
|
321
|
+
Returns
|
322
|
+
-------
|
323
|
+
dict
|
324
|
+
A dictionary containing file size and uniformity checks.
|
325
|
+
"""
|
326
|
+
file_stats = os.stat(self.filename)
|
327
|
+
sys_file_size = file_stats.st_size
|
328
|
+
cal_file_size = sum((self.bytes).astype(int)) + 2 * len(self.bytes)
|
329
|
+
|
330
|
+
check = dict()
|
331
|
+
|
332
|
+
check["System File Size (B)"] = sys_file_size
|
333
|
+
check["Calculated File Size (B)"] = cal_file_size
|
334
|
+
check["File Size (MB)"] = cal_file_size / 1048576
|
335
|
+
|
336
|
+
if sys_file_size != cal_file_size:
|
337
|
+
check["File Size Match"] = False
|
338
|
+
else:
|
339
|
+
check["File Size Match"] = True
|
340
|
+
|
341
|
+
check["Byte Uniformity"] = check_equal(self.bytes.tolist())
|
342
|
+
check["Data Type Uniformity"] = check_equal(self.bytes.tolist())
|
343
|
+
|
344
|
+
return check
|
345
|
+
|
346
|
+
def print_check_file(self):
|
347
|
+
"""
|
348
|
+
Prints a summary of the file size check results, including system file size, calculated file size,
|
349
|
+
and warnings if discrepancies are found.
|
350
|
+
|
351
|
+
Returns
|
352
|
+
-------
|
353
|
+
None
|
354
|
+
"""
|
355
|
+
file_stats = os.stat(self.filename)
|
356
|
+
sys_file_size = file_stats.st_size
|
357
|
+
cal_file_size = sum(self.bytes) + 2 * len(self.bytes)
|
358
|
+
|
359
|
+
print("---------------RDI FILE SIZE CHECK-------------------")
|
360
|
+
print(f"System file size = {sys_file_size} B")
|
361
|
+
print(f"Calculated file size = {cal_file_size} B")
|
362
|
+
if sys_file_size != cal_file_size:
|
363
|
+
print("WARNING: The file sizes do not match")
|
364
|
+
else:
|
365
|
+
print(
|
366
|
+
"File size in MB (binary): % 8.2f MB\
|
367
|
+
\nFile sizes matches!"
|
368
|
+
% (cal_file_size / 1048576)
|
369
|
+
)
|
370
|
+
print("-----------------------------------------------------")
|
371
|
+
|
372
|
+
print(f"Total number of ensembles: {self.ensembles}")
|
373
|
+
|
374
|
+
if check_equal(self.bytes.tolist()):
|
375
|
+
print("No. of Bytes are same for all ensembles.")
|
376
|
+
else:
|
377
|
+
print("WARNING: No. of Bytes not equal for all ensembles.")
|
378
|
+
|
379
|
+
if check_equal(self.datatypes.tolist()):
|
380
|
+
print("No. of Data Types are same for all ensembles.")
|
381
|
+
else:
|
382
|
+
print("WARNING: No. of Data Types not equal for all ensembles.")
|
383
|
+
|
384
|
+
return
|
385
|
+
|
386
|
+
|
387
|
+
# FIXED LEADER CODES #
|
388
|
+
|
389
|
+
|
390
|
+
def flead_dict(fid, dim=2):
|
391
|
+
"""
|
392
|
+
Extracts Fixed Leader data from a file and assigns it a identifiable name.
|
393
|
+
|
394
|
+
Parameters
|
395
|
+
----------
|
396
|
+
fid : file object or array-like
|
397
|
+
The data source to extract Fixed Leader information from.
|
398
|
+
dim : int, optional
|
399
|
+
The dimension of the data, by default 2.
|
400
|
+
|
401
|
+
Returns
|
402
|
+
-------
|
403
|
+
dict
|
404
|
+
A dictionary containing Fixed Leader field and data.
|
405
|
+
"""
|
406
|
+
|
407
|
+
fname = {
|
408
|
+
"CPU Version": "int64",
|
409
|
+
"CPU Revision": "int64",
|
410
|
+
"System Config Code": "int64",
|
411
|
+
"Real Flag": "int64",
|
412
|
+
"Lag Length": "int64",
|
413
|
+
"Beams": "int64",
|
414
|
+
"Cells": "int64",
|
415
|
+
"Pings": "int64",
|
416
|
+
"Depth Cell Len": "int64",
|
417
|
+
"Blank Transmit": "int64",
|
418
|
+
"Signal Mode": "int64",
|
419
|
+
"Correlation Thresh": "int64",
|
420
|
+
"Code Reps": "int64",
|
421
|
+
"Percent Good Min": "int64",
|
422
|
+
"Error Velocity Thresh": "int64",
|
423
|
+
"TP Minute": "int64",
|
424
|
+
"TP Second": "int64",
|
425
|
+
"TP Hundredth": "int64",
|
426
|
+
"Coord Transform Code": "int64",
|
427
|
+
"Head Alignment": "int64",
|
428
|
+
"Head Bias": "int64",
|
429
|
+
"Sensor Source Code": "int64",
|
430
|
+
"Sensor Avail Code": "int64",
|
431
|
+
"Bin 1 Dist": "int64",
|
432
|
+
"Xmit Pulse Len": "int64",
|
433
|
+
"Ref Layer Avg": "int64",
|
434
|
+
"False Target Thresh": "int64",
|
435
|
+
"Spare 1": "int64",
|
436
|
+
"Transmit Lag Dist": "int64",
|
437
|
+
"CPU Serial No": "int128",
|
438
|
+
"System Bandwidth": "int64",
|
439
|
+
"System Power": "int64",
|
440
|
+
"Spare 2": "int64",
|
441
|
+
"Instrument No": "int64",
|
442
|
+
"Beam Angle": "int64",
|
443
|
+
}
|
444
|
+
|
445
|
+
flead = dict()
|
446
|
+
counter = 1
|
447
|
+
for key, value in fname.items():
|
448
|
+
if dim == 2:
|
449
|
+
if key == "CPU Serial No":
|
450
|
+
flead[key] = np.uint64(fid[:][counter])
|
451
|
+
else:
|
452
|
+
flead[key] = np.int64(fid[:][counter])
|
453
|
+
elif dim == 1:
|
454
|
+
if key == "CPU Serial No":
|
455
|
+
flead[key] = np.uint64(fid[counter])
|
456
|
+
else:
|
457
|
+
flead[key] = np.int64(fid[counter])
|
458
|
+
else:
|
459
|
+
print("ERROR: Higher dimensions not allowed")
|
460
|
+
sys.exit()
|
461
|
+
|
462
|
+
counter += 1
|
463
|
+
|
464
|
+
return flead
|
465
|
+
|
466
|
+
|
467
|
+
class FixedLeader:
|
468
|
+
"""
|
469
|
+
The class extracts Fixed Leader data from RDI File.
|
470
|
+
|
471
|
+
Fixed Leader data are non-dynamic or constants. They
|
472
|
+
contain hardware information and ADCP data that only
|
473
|
+
change based on certain ADCP input commands. The data,
|
474
|
+
generally, do not change within a file.
|
475
|
+
"""
|
476
|
+
|
477
|
+
def __init__(
|
478
|
+
self,
|
479
|
+
rdi_file,
|
480
|
+
byteskip=None,
|
481
|
+
offset=None,
|
482
|
+
idarray=None,
|
483
|
+
ensemble=0,
|
484
|
+
):
|
485
|
+
"""
|
486
|
+
Initializes the FixedLeader object, extracts Fixed Leader data, and stores it in a dictionary.
|
487
|
+
The optional parameters can be obtained from FileHeader.
|
488
|
+
|
489
|
+
Parameters
|
490
|
+
----------
|
491
|
+
rdi_file : str
|
492
|
+
The RDI ADCP binary file to extract data from.
|
493
|
+
byteskip : int, optional
|
494
|
+
Number of bytes to skip, by default None.
|
495
|
+
offset : int, optional
|
496
|
+
Offset value for data extraction, by default None.
|
497
|
+
idarray : array-like, optional
|
498
|
+
Array of IDs for data extraction, by default None.
|
499
|
+
ensemble : int, optional
|
500
|
+
Ensemble number to start extraction from, by default 0.
|
501
|
+
"""
|
502
|
+
self.filename = rdi_file
|
503
|
+
|
504
|
+
self.data, self.ensembles, self.error = pyreadrdi.fixedleader(
|
505
|
+
self.filename,
|
506
|
+
byteskip=byteskip,
|
507
|
+
offset=offset,
|
508
|
+
idarray=idarray,
|
509
|
+
ensemble=ensemble,
|
510
|
+
)
|
511
|
+
self.warning = pyreadrdi.ErrorCode.get_message(self.error)
|
512
|
+
|
513
|
+
self.data = np.uint64(self.data)
|
514
|
+
self.fleader = flead_dict(self.data)
|
515
|
+
self._initialize_from_dict(DotDict(json_file_path="flmeta.json"))
|
516
|
+
|
517
|
+
def _initialize_from_dict(self, dotdict):
|
518
|
+
"""
|
519
|
+
Initializes the FixedLeader object from a DotDict.
|
520
|
+
|
521
|
+
Parameters
|
522
|
+
----------
|
523
|
+
dotdict : DotDict
|
524
|
+
A DotDict object containing metadata for the Fixed Leader.
|
525
|
+
"""
|
526
|
+
i = 1
|
527
|
+
for key, value in dotdict.__dict__.items():
|
528
|
+
setattr(self, key, value)
|
529
|
+
setattr(getattr(self, key), "data", self.data[i])
|
530
|
+
i = i + 1
|
531
|
+
|
532
|
+
def field(self, ens=0):
|
533
|
+
"""
|
534
|
+
Returns Fixed Leader dictionary pairs for a single ensemble.
|
535
|
+
|
536
|
+
Parameters
|
537
|
+
----------
|
538
|
+
ens : int, optional
|
539
|
+
Ensemble number to extract, by default 0 or the first ensemble.
|
540
|
+
|
541
|
+
Returns
|
542
|
+
-------
|
543
|
+
dict
|
544
|
+
A dictionary of Fixed Leader data for the specified ensemble.
|
545
|
+
"""
|
546
|
+
|
547
|
+
f1 = np.array(self.data)
|
548
|
+
return flead_dict(f1[:, ens], dim=1)
|
549
|
+
|
550
|
+
def is_uniform(self):
|
551
|
+
"""
|
552
|
+
Checks whether Fixed Leader data fields are uniform across ensembles.
|
553
|
+
|
554
|
+
Returns
|
555
|
+
-------
|
556
|
+
dict
|
557
|
+
A dictionary indicating uniformity of each Fixed Leader data field.
|
558
|
+
"""
|
559
|
+
output = dict()
|
560
|
+
for key, value in self.fleader.items():
|
561
|
+
output[key] = check_equal(value)
|
562
|
+
return output
|
563
|
+
|
564
|
+
def system_configuration(self, ens=0):
|
565
|
+
"""
|
566
|
+
Extracts and interprets the system configuration from the Fixed Leader data.
|
567
|
+
|
568
|
+
Parameters
|
569
|
+
----------
|
570
|
+
ens : int, optional
|
571
|
+
Ensemble number to extract system configuration for, by default 0.
|
572
|
+
|
573
|
+
Returns
|
574
|
+
-------
|
575
|
+
dict
|
576
|
+
A dictionary containing system configuration details.
|
577
|
+
|
578
|
+
List of Returnable Keys
|
579
|
+
-----------------------
|
580
|
+
Returns values for the following keys
|
581
|
+
No| Keys | Possible values
|
582
|
+
---------------------------------
|
583
|
+
1 | "Frequency" | ['75 kHz', '150 kHz', '300 kHz',
|
584
|
+
'600 kHz', '1200 kHz', '2400 kHz',
|
585
|
+
'38 kHz']
|
586
|
+
2 | "Beam Pattern" | ['Concave', 'Convex']
|
587
|
+
3 | "Sensor Configuration" | ['#1', '#2', '#3']
|
588
|
+
4 | "XDCR HD" | ['Not Attached', 'Attached']
|
589
|
+
5 | "Beam Direction" | ['Up', 'Down']
|
590
|
+
6 | "Beam Angle | [15, 20, 30, 25, 45]
|
591
|
+
7 | "Janus Configuration" | ["4 Beam", "5 Beam CFIG DEMOD",
|
592
|
+
"5 Beam CFIG 2 DEMOD"]
|
593
|
+
"""
|
594
|
+
|
595
|
+
binary_bits = format(self.fleader["System Config Code"][ens], "016b")
|
596
|
+
# convert integer to binary format
|
597
|
+
# In '016b': 0 adds extra zeros to the binary string
|
598
|
+
# : 16 is the total number of binary bits
|
599
|
+
# : b is used to convert integer to binary format
|
600
|
+
# : Add '#' to get python binary format ('#016b')
|
601
|
+
sys_cfg = dict()
|
602
|
+
|
603
|
+
freq_code = {
|
604
|
+
"000": "75-kHz",
|
605
|
+
"001": "150-kHz",
|
606
|
+
"010": "300-kHz",
|
607
|
+
"011": "600-kHz",
|
608
|
+
"100": "1200-kHz",
|
609
|
+
"101": "2400-kHz",
|
610
|
+
"110": "38-kHz",
|
611
|
+
}
|
612
|
+
|
613
|
+
beam_code = {"0": "Concave", "1": "Convex"}
|
614
|
+
|
615
|
+
sensor_code = {
|
616
|
+
"00": "#1",
|
617
|
+
"01": "#2",
|
618
|
+
"10": "#3",
|
619
|
+
"11": "Sensor configuration not found",
|
620
|
+
}
|
621
|
+
|
622
|
+
xdcr_code = {"0": "Not attached", "1": "Attached"}
|
623
|
+
|
624
|
+
dir_code = {"0": "Down", "1": "Up"}
|
625
|
+
|
626
|
+
angle_code = {
|
627
|
+
"0000": "15",
|
628
|
+
"0001": "20",
|
629
|
+
"0010": "30",
|
630
|
+
"0011": "Other beam angle",
|
631
|
+
"0111": "25",
|
632
|
+
"1100": "45",
|
633
|
+
}
|
634
|
+
|
635
|
+
janus_code = {
|
636
|
+
"0100": "4 Beam",
|
637
|
+
"0101": "5 Beam CFIG DEMOD",
|
638
|
+
"1111": "5 Beam CFIG 2 DEMOD",
|
639
|
+
}
|
640
|
+
|
641
|
+
bit_group = binary_bits[13:16]
|
642
|
+
sys_cfg["Frequency"] = freq_code.get(bit_group, "Frequency not found")
|
643
|
+
|
644
|
+
bit_group = binary_bits[12]
|
645
|
+
sys_cfg["Beam Pattern"] = beam_code.get(bit_group)
|
646
|
+
|
647
|
+
bit_group = binary_bits[10:12]
|
648
|
+
sys_cfg["Sensor Configuration"] = sensor_code.get(bit_group)
|
649
|
+
|
650
|
+
bit_group = binary_bits[9]
|
651
|
+
sys_cfg["XDCR HD"] = xdcr_code.get(bit_group)
|
652
|
+
|
653
|
+
bit_group = binary_bits[8]
|
654
|
+
sys_cfg["Beam Direction"] = dir_code.get(bit_group)
|
655
|
+
|
656
|
+
bit_group = binary_bits[4:8]
|
657
|
+
sys_cfg["Beam Angle"] = angle_code.get(bit_group, "Angle not found")
|
658
|
+
|
659
|
+
bit_group = binary_bits[0:4]
|
660
|
+
sys_cfg["Janus Configuration"] = janus_code.get(
|
661
|
+
bit_group, "Janus cfg. not found"
|
662
|
+
)
|
663
|
+
|
664
|
+
return sys_cfg
|
665
|
+
|
666
|
+
def ex_coord_trans(self, ens=0):
|
667
|
+
"""
|
668
|
+
Extracts the coordinate transformation configuration from the Fixed Leader data.
|
669
|
+
|
670
|
+
Parameters
|
671
|
+
----------
|
672
|
+
ens : int, optional
|
673
|
+
Ensemble number to extract transformation configuration for, by default 0.
|
674
|
+
|
675
|
+
Returns
|
676
|
+
-------
|
677
|
+
dict
|
678
|
+
A dictionary of coordinate transformation details.
|
679
|
+
"""
|
680
|
+
|
681
|
+
bit_group = format(self.fleader["Coord Transform Code"][ens], "08b")
|
682
|
+
transform = dict()
|
683
|
+
|
684
|
+
trans_code = {
|
685
|
+
"00": "Beam Coordinates",
|
686
|
+
"01": "Instrument Coordinates",
|
687
|
+
"10": "Ship Coordinates",
|
688
|
+
"11": "Earth Coordinates",
|
689
|
+
}
|
690
|
+
|
691
|
+
bool_code = {"1": True, "0": False}
|
692
|
+
|
693
|
+
transform["Coordinates"] = trans_code.get(bit_group[3:5])
|
694
|
+
transform["Tilt Correction"] = bool_code.get(bit_group[5])
|
695
|
+
transform["Three-Beam Solution"] = bool_code.get(bit_group[6])
|
696
|
+
transform["Bin Mapping"] = bool_code.get(bit_group[7])
|
697
|
+
|
698
|
+
return transform
|
699
|
+
|
700
|
+
def ez_sensor(self, ens=0, field="source"):
|
701
|
+
"""
|
702
|
+
Checks for available or selected sensors from the Fixed Leader.
|
703
|
+
|
704
|
+
Parameters
|
705
|
+
----------
|
706
|
+
ens : int, optional
|
707
|
+
Ensemble number to extract sensor information for, by default 0.
|
708
|
+
field : str, optional
|
709
|
+
Sensor field to extract ('source' or 'avail'), by default "source".
|
710
|
+
|
711
|
+
Returns
|
712
|
+
-------
|
713
|
+
dict
|
714
|
+
A dictionary of sensor availability or source selection.
|
715
|
+
|
716
|
+
"""
|
717
|
+
if field == "source":
|
718
|
+
bit_group = format(self.fleader["Sensor Source Code"][ens], "08b")
|
719
|
+
elif field == "avail":
|
720
|
+
bit_group = format(self.fleader["Sensor Avail Code"][ens], "08b")
|
721
|
+
else:
|
722
|
+
sys.exit("ERROR (function ez_sensor): Enter valid argument.")
|
723
|
+
|
724
|
+
sensor = dict()
|
725
|
+
|
726
|
+
bool_code = {"1": True, "0": False}
|
727
|
+
|
728
|
+
sensor["Sound Speed"] = bool_code.get(bit_group[1])
|
729
|
+
sensor["Depth Sensor"] = bool_code.get(bit_group[2])
|
730
|
+
sensor["Heading Sensor"] = bool_code.get(bit_group[3])
|
731
|
+
sensor["Pitch Sensor"] = bool_code.get(bit_group[4])
|
732
|
+
sensor["Roll Sensor"] = bool_code.get(bit_group[5])
|
733
|
+
sensor["Conductivity Sensor"] = bool_code.get(bit_group[6])
|
734
|
+
sensor["Temperature Sensor"] = bool_code.get(bit_group[7])
|
735
|
+
|
736
|
+
return sensor
|
737
|
+
|
738
|
+
|
739
|
+
# VARIABLE LEADER CODES #
|
740
|
+
def vlead_dict(vid):
|
741
|
+
"""
|
742
|
+
Extracts Variable Leader data from a file and assigns it a identifiable name.
|
743
|
+
|
744
|
+
Parameters
|
745
|
+
----------
|
746
|
+
fid : file object or array-like
|
747
|
+
The data source to extract Fixed Leader information from.
|
748
|
+
|
749
|
+
Returns
|
750
|
+
-------
|
751
|
+
dict
|
752
|
+
A dictionary containing Variable Leader field and data.
|
753
|
+
"""
|
754
|
+
|
755
|
+
vname = {
|
756
|
+
"RDI Ensemble": "int16",
|
757
|
+
"RTC Year": "int16",
|
758
|
+
"RTC Month": "int16",
|
759
|
+
"RTC Day": "int16",
|
760
|
+
"RTC Hour": "int16",
|
761
|
+
"RTC Minute": "int16",
|
762
|
+
"RTC Second": "int16",
|
763
|
+
"RTC Hundredth": "int16",
|
764
|
+
"Ensemble MSB": "int16",
|
765
|
+
"Bit Result": "int16",
|
766
|
+
"Speed of Sound": "int16",
|
767
|
+
"Depth of Transducer": "int16",
|
768
|
+
"Heading": "int32",
|
769
|
+
"Pitch": "int16",
|
770
|
+
"Roll": "int16",
|
771
|
+
"Salinity": "int16",
|
772
|
+
"Temperature": "int16",
|
773
|
+
"MPT Minute": "int16",
|
774
|
+
"MPT Second": "int16",
|
775
|
+
"MPT Hundredth": "int16",
|
776
|
+
"Hdg Std Dev": "int16",
|
777
|
+
"Pitch Std Dev": "int16",
|
778
|
+
"Roll Std Dev": "int16",
|
779
|
+
"ADC Channel 0": "int16",
|
780
|
+
"ADC Channel 1": "int16",
|
781
|
+
"ADC Channel 2": "int16",
|
782
|
+
"ADC Channel 3": "int16",
|
783
|
+
"ADC Channel 4": "int16",
|
784
|
+
"ADC Channel 5": "int16",
|
785
|
+
"ADC Channel 6": "int16",
|
786
|
+
"ADC Channel 7": "int16",
|
787
|
+
"Error Status Word 1": "int16",
|
788
|
+
"Error Status Word 2": "int16",
|
789
|
+
"Error Status Word 3": "int16",
|
790
|
+
"Error Status Word 4": "int16",
|
791
|
+
"Reserved": "int16",
|
792
|
+
"Pressure": "int32",
|
793
|
+
"Pressure Variance": "int32",
|
794
|
+
"Spare": "int16",
|
795
|
+
"Y2K Century": "int16",
|
796
|
+
"Y2K Year": "int16",
|
797
|
+
"Y2K Month": "int16",
|
798
|
+
"Y2K Day": "int16",
|
799
|
+
"Y2K Hour": "int16",
|
800
|
+
"Y2K Minute": "int16",
|
801
|
+
"Y2K Second": "int16",
|
802
|
+
"Y2K Hundredth": "int16",
|
803
|
+
}
|
804
|
+
|
805
|
+
vlead = dict()
|
806
|
+
|
807
|
+
counter = 1
|
808
|
+
for key, value in vname.items():
|
809
|
+
# vlead[key] = getattr(np, value)(vid[:][counter])
|
810
|
+
vlead[key] = vid[:][counter]
|
811
|
+
counter += 1
|
812
|
+
|
813
|
+
return vlead
|
814
|
+
|
815
|
+
|
816
|
+
class VariableLeader:
|
817
|
+
"""
|
818
|
+
The class extracts Variable Leader Data.
|
819
|
+
|
820
|
+
Variable Leader data refers to the dynamic ADCP data
|
821
|
+
(from clocks/sensors) that change with each ping. The
|
822
|
+
WorkHorse ADCP always sends Variable Leader data as output
|
823
|
+
data (LSBs first).
|
824
|
+
|
825
|
+
Parameters
|
826
|
+
----------
|
827
|
+
rdi_file : str
|
828
|
+
RDI ADCP binary file. The class can currently extract Workhorse,
|
829
|
+
Ocean Surveyor, and DVS files.
|
830
|
+
"""
|
831
|
+
|
832
|
+
def __init__(
|
833
|
+
self,
|
834
|
+
rdi_file,
|
835
|
+
byteskip=None,
|
836
|
+
offset=None,
|
837
|
+
idarray=None,
|
838
|
+
ensemble=0,
|
839
|
+
):
|
840
|
+
"""
|
841
|
+
Initializes the VariableLeader object and extracts data from the RDI ADCP binary file.
|
842
|
+
|
843
|
+
Parameters
|
844
|
+
----------
|
845
|
+
rdi_file : str
|
846
|
+
The RDI ADCP binary file to extract data from.
|
847
|
+
byteskip : int, optional
|
848
|
+
Number of bytes to skip, by default None.
|
849
|
+
offset : int, optional
|
850
|
+
Offset value for data extraction, by default None.
|
851
|
+
idarray : array-like, optional
|
852
|
+
Array of IDs for data extraction, by default None.
|
853
|
+
ensemble : int, optional
|
854
|
+
Ensemble number to start extraction from, by default 0.
|
855
|
+
"""
|
856
|
+
self.filename = rdi_file
|
857
|
+
|
858
|
+
# Extraction starts here
|
859
|
+
self.data, self.ensembles, self.error = pyreadrdi.variableleader(
|
860
|
+
self.filename,
|
861
|
+
byteskip=byteskip,
|
862
|
+
offset=offset,
|
863
|
+
idarray=idarray,
|
864
|
+
ensemble=ensemble,
|
865
|
+
)
|
866
|
+
self.warning = pyreadrdi.ErrorCode.get_message(self.error)
|
867
|
+
|
868
|
+
# self.vdict = DotDict()
|
869
|
+
self.vleader = vlead_dict(self.data)
|
870
|
+
self._initialize_from_dict(DotDict(json_file_path="vlmeta.json"))
|
871
|
+
|
872
|
+
def _initialize_from_dict(self, dotdict):
|
873
|
+
"""
|
874
|
+
Initializes the VariableLeader object attributes from a DotDict.
|
875
|
+
|
876
|
+
Parameters
|
877
|
+
----------
|
878
|
+
dotdict : DotDict
|
879
|
+
A DotDict object containing metadata for the Variable Leader.
|
880
|
+
"""
|
881
|
+
i = 1
|
882
|
+
for key, value in dotdict.__dict__.items():
|
883
|
+
setattr(self, key, value)
|
884
|
+
setattr(getattr(self, key), "data", self.data[i])
|
885
|
+
i = i + 1
|
886
|
+
|
887
|
+
def bitresult(self):
|
888
|
+
"""
|
889
|
+
Extracts Bit Results from Variable Leader (Byte 13 & 14)
|
890
|
+
This field is part of the WorkHorse ADCP’s Built-in Test function.
|
891
|
+
A zero code indicates a successful BIT result.
|
892
|
+
|
893
|
+
Note: Byte 14 used for future use.
|
894
|
+
|
895
|
+
Returns
|
896
|
+
-------
|
897
|
+
dict
|
898
|
+
A dictionary of test field results.
|
899
|
+
|
900
|
+
"""
|
901
|
+
tfname = {
|
902
|
+
"Reserved #1": "int16",
|
903
|
+
"Reserved #2": "int16",
|
904
|
+
"Reserved #3": "int16",
|
905
|
+
"DEMOD 1 Error": "int16",
|
906
|
+
"DEMOD 0 Error": "int16",
|
907
|
+
"Reserved #4": "int16",
|
908
|
+
"Timing Card Error": "int16",
|
909
|
+
"Reserved #5": "int16",
|
910
|
+
}
|
911
|
+
|
912
|
+
test_field = dict()
|
913
|
+
bit_array = self.vleader["Bit Result"]
|
914
|
+
|
915
|
+
# The bit result is read as single 16 bits variable instead of
|
916
|
+
# two 8-bits variable (Byte 13 & 14). The data is written in
|
917
|
+
# little endian format. Therefore, the Byte 14 comes before Byte 13.
|
918
|
+
|
919
|
+
for key, value in tfname.items():
|
920
|
+
test_field[key] = np.array([], dtype=value)
|
921
|
+
|
922
|
+
for item in bit_array:
|
923
|
+
bit_group = format(item, "016b")
|
924
|
+
bitpos = 8
|
925
|
+
for key, value in tfname.items():
|
926
|
+
bitappend = getattr(np, value)(bit_group[bitpos])
|
927
|
+
test_field[key] = np.append(test_field[key], bitappend)
|
928
|
+
bitpos += 1
|
929
|
+
|
930
|
+
return test_field
|
931
|
+
|
932
|
+
def adc_channel(self, offset=-0.20):
|
933
|
+
"""
|
934
|
+
Extracts ADC Channel data and computes values for Xmit Voltage, Xmit Current,
|
935
|
+
and Ambient Temperature using system configuration.
|
936
|
+
|
937
|
+
Parameters
|
938
|
+
----------
|
939
|
+
offset : float, optional
|
940
|
+
Offset value for temperature calculation, by default -0.20.
|
941
|
+
|
942
|
+
Returns
|
943
|
+
-------
|
944
|
+
dict
|
945
|
+
A dictionary of channel data including Xmit Voltage, Xmit Current, and Ambient Temperature.
|
946
|
+
"""
|
947
|
+
# -----------CODE INCOMPLETE-------------- #
|
948
|
+
channel = dict()
|
949
|
+
scale_list = {
|
950
|
+
"75-kHz": [2092719, 43838],
|
951
|
+
"150-kHz": [592157, 11451],
|
952
|
+
"300-kHz": [592157, 11451],
|
953
|
+
"600-kHz": [380667, 11451],
|
954
|
+
"1200-kHz": [253765, 11451],
|
955
|
+
"2400-kHz": [253765, 11451],
|
956
|
+
}
|
957
|
+
|
958
|
+
adc0 = self.vleader["ADC Channel 0"]
|
959
|
+
adc1 = self.vleader["ADC Channel 1"]
|
960
|
+
adc2 = self.vleader["ADC Channel 2"]
|
961
|
+
|
962
|
+
fixclass = FixedLeader(self.filename).system_configuration()
|
963
|
+
|
964
|
+
scale_factor = scale_list.get(fixclass["Frequency"])
|
965
|
+
|
966
|
+
channel["Xmit Voltage"] = adc1 * (scale_factor[0] / 1000000)
|
967
|
+
|
968
|
+
channel["Xmit Current"] = adc0 * (scale_factor[1] / 1000000)
|
969
|
+
|
970
|
+
# Coefficients for temperature equation
|
971
|
+
a0 = 9.82697464e1
|
972
|
+
a1 = -5.86074151382e-3
|
973
|
+
a2 = 1.60433886495e-7
|
974
|
+
a3 = -2.32924716883e-12
|
975
|
+
|
976
|
+
channel["Ambient Temperature"] = (
|
977
|
+
offset + ((a3 * adc2 + a2) * adc2 + a1) * adc2 + a0
|
978
|
+
)
|
979
|
+
|
980
|
+
return channel
|
981
|
+
|
982
|
+
def error_status_word(self, esw=1):
|
983
|
+
bitset1 = (
|
984
|
+
"Bus Error exception",
|
985
|
+
"Address Error exception",
|
986
|
+
"Zero Divide exception",
|
987
|
+
"Emulator exception",
|
988
|
+
"Unassigned exception",
|
989
|
+
"Watchdog restart occurred",
|
990
|
+
"Batter Saver Power",
|
991
|
+
)
|
992
|
+
|
993
|
+
bitset2 = (
|
994
|
+
"Pinging",
|
995
|
+
"Not Used 1",
|
996
|
+
"Not Used 2",
|
997
|
+
"Not Used 3",
|
998
|
+
"Not Used 4",
|
999
|
+
"Not Used 5",
|
1000
|
+
"Cold Wakeup occured",
|
1001
|
+
"Unknown Wakeup occured",
|
1002
|
+
)
|
1003
|
+
|
1004
|
+
bitset3 = (
|
1005
|
+
"Clock Read error occured",
|
1006
|
+
"Unexpected alarm",
|
1007
|
+
"Clock jump forward",
|
1008
|
+
"Clock jump backward",
|
1009
|
+
"Not Used 6",
|
1010
|
+
"Not Used 7",
|
1011
|
+
"Not Used 8",
|
1012
|
+
"Not Used 9",
|
1013
|
+
)
|
1014
|
+
|
1015
|
+
bitset4 = (
|
1016
|
+
"Not Used 10",
|
1017
|
+
"Not Used 11",
|
1018
|
+
"Not Used 12",
|
1019
|
+
"Power Fail Unrecorded",
|
1020
|
+
"Spurious level 4 intr DSP",
|
1021
|
+
"Spurious level 5 intr UART",
|
1022
|
+
"Spurious level 6 intr CLOCK",
|
1023
|
+
"Level 7 interrup occured",
|
1024
|
+
)
|
1025
|
+
|
1026
|
+
if esw == 1:
|
1027
|
+
bitset = bitset1
|
1028
|
+
errorarray = self.vleader["Error Status Word 1"]
|
1029
|
+
elif esw == 2:
|
1030
|
+
bitset = bitset2
|
1031
|
+
errorarray = self.vleader["Error Status Word 2"]
|
1032
|
+
elif esw == 3:
|
1033
|
+
bitset = bitset3
|
1034
|
+
errorarray = self.vleader["Error Status Word 3"]
|
1035
|
+
else:
|
1036
|
+
bitset = bitset4
|
1037
|
+
errorarray = self.vleader["Error Status Word 4"]
|
1038
|
+
|
1039
|
+
errorstatus = dict()
|
1040
|
+
# bitarray = np.zeros(32, dtype='str')
|
1041
|
+
|
1042
|
+
for item in bitset:
|
1043
|
+
errorstatus[item] = np.array([])
|
1044
|
+
|
1045
|
+
for data in errorarray:
|
1046
|
+
byte_split = format(data, "08b")
|
1047
|
+
bitposition = 0
|
1048
|
+
for item in bitset:
|
1049
|
+
errorstatus[item] = np.append(
|
1050
|
+
errorstatus[item], byte_split[bitposition]
|
1051
|
+
)
|
1052
|
+
bitposition += 1
|
1053
|
+
|
1054
|
+
return errorstatus
|
1055
|
+
|
1056
|
+
|
1057
|
+
class Velocity:
|
1058
|
+
"""
|
1059
|
+
The class extracts velocity data from RDI ADCP files.
|
1060
|
+
|
1061
|
+
Parameters
|
1062
|
+
----------
|
1063
|
+
filename : str
|
1064
|
+
The RDI ADCP binary file to extract data from.
|
1065
|
+
cell : int, optional
|
1066
|
+
Cell number to extract, by default 0.
|
1067
|
+
beam : int, optional
|
1068
|
+
Beam number to extract, by default 0.
|
1069
|
+
byteskip : int, optional
|
1070
|
+
Number of bytes to skip, by default None.
|
1071
|
+
offset : int, optional
|
1072
|
+
Offset value for data extraction, by default None.
|
1073
|
+
idarray : array-like, optional
|
1074
|
+
Array of IDs for data extraction, by default None.
|
1075
|
+
ensemble : int, optional
|
1076
|
+
Ensemble number to start extraction from, by default 0.
|
1077
|
+
"""
|
1078
|
+
|
1079
|
+
def __init__(
|
1080
|
+
self,
|
1081
|
+
filename,
|
1082
|
+
cell=0,
|
1083
|
+
beam=0,
|
1084
|
+
byteskip=None,
|
1085
|
+
offset=None,
|
1086
|
+
idarray=None,
|
1087
|
+
ensemble=0,
|
1088
|
+
):
|
1089
|
+
self.filename = filename
|
1090
|
+
error = 0
|
1091
|
+
data, ens, cell, beam, error = pyreadrdi.datatype(
|
1092
|
+
self.filename,
|
1093
|
+
"velocity",
|
1094
|
+
cell=cell,
|
1095
|
+
beam=beam,
|
1096
|
+
byteskip=byteskip,
|
1097
|
+
offset=offset,
|
1098
|
+
idarray=idarray,
|
1099
|
+
ensemble=ensemble,
|
1100
|
+
)
|
1101
|
+
self.warning = pyreadrdi.ErrorCode.get_message(error)
|
1102
|
+
|
1103
|
+
self.data = data
|
1104
|
+
self.error = error
|
1105
|
+
self.ensembles = ens
|
1106
|
+
self.cells = cell
|
1107
|
+
self.beams = beam
|
1108
|
+
|
1109
|
+
self.unit = "mm/s"
|
1110
|
+
self.missing_value = "-32768"
|
1111
|
+
self.scale_factor = 1
|
1112
|
+
self.valid_min = -32768
|
1113
|
+
self.valid_max = 32768
|
1114
|
+
|
1115
|
+
|
1116
|
+
class Correlation:
|
1117
|
+
"""
|
1118
|
+
The class extracts correlation data from RDI ADCP files.
|
1119
|
+
|
1120
|
+
Parameters
|
1121
|
+
----------
|
1122
|
+
filename : str
|
1123
|
+
The RDI ADCP binary file to extract data from.
|
1124
|
+
cell : int, optional
|
1125
|
+
Cell number to extract, by default 0.
|
1126
|
+
beam : int, optional
|
1127
|
+
Beam number to extract, by default 0.
|
1128
|
+
byteskip : int, optional
|
1129
|
+
Number of bytes to skip, by default None.
|
1130
|
+
offset : int, optional
|
1131
|
+
Offset value for data extraction, by default None.
|
1132
|
+
idarray : array-like, optional
|
1133
|
+
Array of IDs for data extraction, by default None.
|
1134
|
+
ensemble : int, optional
|
1135
|
+
Ensemble number to start extraction from, by default 0.
|
1136
|
+
"""
|
1137
|
+
|
1138
|
+
def __init__(
|
1139
|
+
self,
|
1140
|
+
filename,
|
1141
|
+
cell=0,
|
1142
|
+
beam=0,
|
1143
|
+
byteskip=None,
|
1144
|
+
offset=None,
|
1145
|
+
idarray=None,
|
1146
|
+
ensemble=0,
|
1147
|
+
):
|
1148
|
+
self.filename = filename
|
1149
|
+
error = 0
|
1150
|
+
data, ens, cell, beam, error = pyreadrdi.datatype(
|
1151
|
+
self.filename,
|
1152
|
+
"correlation",
|
1153
|
+
cell=cell,
|
1154
|
+
beam=beam,
|
1155
|
+
byteskip=byteskip,
|
1156
|
+
offset=offset,
|
1157
|
+
idarray=idarray,
|
1158
|
+
ensemble=ensemble,
|
1159
|
+
)
|
1160
|
+
self.warning = pyreadrdi.ErrorCode.get_message(error)
|
1161
|
+
|
1162
|
+
self.data = data
|
1163
|
+
self.error = error
|
1164
|
+
self.ensembles = ens
|
1165
|
+
self.cells = cell
|
1166
|
+
self.beams = beam
|
1167
|
+
|
1168
|
+
self.unit = ""
|
1169
|
+
self.scale_factor = 1
|
1170
|
+
self.valid_min = 0
|
1171
|
+
self.valid_max = 255
|
1172
|
+
self.long_name = "Correlation Magnitude"
|
1173
|
+
|
1174
|
+
|
1175
|
+
class Echo:
|
1176
|
+
"""
|
1177
|
+
The class extracts echo intensity data from RDI ADCP files.
|
1178
|
+
|
1179
|
+
Parameters
|
1180
|
+
----------
|
1181
|
+
filename : str
|
1182
|
+
The RDI ADCP binary file to extract data from.
|
1183
|
+
cell : int, optional
|
1184
|
+
Cell number to extract, by default 0.
|
1185
|
+
beam : int, optional
|
1186
|
+
Beam number to extract, by default 0.
|
1187
|
+
byteskip : int, optional
|
1188
|
+
Number of bytes to skip, by default None.
|
1189
|
+
offset : int, optional
|
1190
|
+
Offset value for data extraction, by default None.
|
1191
|
+
idarray : array-like, optional
|
1192
|
+
Array of IDs for data extraction, by default None.
|
1193
|
+
ensemble : int, optional
|
1194
|
+
Ensemble number to start extraction from, by default 0.
|
1195
|
+
"""
|
1196
|
+
|
1197
|
+
def __init__(
|
1198
|
+
self,
|
1199
|
+
filename,
|
1200
|
+
cell=0,
|
1201
|
+
beam=0,
|
1202
|
+
byteskip=None,
|
1203
|
+
offset=None,
|
1204
|
+
idarray=None,
|
1205
|
+
ensemble=0,
|
1206
|
+
):
|
1207
|
+
self.filename = filename
|
1208
|
+
error = 0
|
1209
|
+
data, ens, cell, beam, error = pyreadrdi.datatype(
|
1210
|
+
self.filename,
|
1211
|
+
"echo",
|
1212
|
+
cell=cell,
|
1213
|
+
beam=beam,
|
1214
|
+
byteskip=byteskip,
|
1215
|
+
offset=offset,
|
1216
|
+
idarray=idarray,
|
1217
|
+
ensemble=ensemble,
|
1218
|
+
)
|
1219
|
+
self.warning = pyreadrdi.ErrorCode.get_message(error)
|
1220
|
+
|
1221
|
+
self.data = data
|
1222
|
+
self.error = error
|
1223
|
+
self.ensembles = ens
|
1224
|
+
self.cells = cell
|
1225
|
+
self.beams = beam
|
1226
|
+
|
1227
|
+
self.unit = "counts"
|
1228
|
+
self.scale_factor = "0.45"
|
1229
|
+
self.valid_min = 0
|
1230
|
+
self.valid_max = 255
|
1231
|
+
self.long_name = "Echo Intensity"
|
1232
|
+
|
1233
|
+
|
1234
|
+
class PercentGood:
|
1235
|
+
"""
|
1236
|
+
The class extracts Percent Good data from RDI ADCP files.
|
1237
|
+
|
1238
|
+
Parameters
|
1239
|
+
----------
|
1240
|
+
filename : str
|
1241
|
+
The RDI ADCP binary file to extract data from.
|
1242
|
+
cell : int, optional
|
1243
|
+
Cell number to extract, by default 0.
|
1244
|
+
beam : int, optional
|
1245
|
+
Beam number to extract, by default 0.
|
1246
|
+
byteskip : int, optional
|
1247
|
+
Number of bytes to skip, by default None.
|
1248
|
+
offset : int, optional
|
1249
|
+
Offset value for data extraction, by default None.
|
1250
|
+
idarray : array-like, optional
|
1251
|
+
Array of IDs for data extraction, by default None.
|
1252
|
+
ensemble : int, optional
|
1253
|
+
Ensemble number to start extraction from, by default 0.
|
1254
|
+
"""
|
1255
|
+
|
1256
|
+
def __init__(
|
1257
|
+
self,
|
1258
|
+
filename,
|
1259
|
+
cell=0,
|
1260
|
+
beam=0,
|
1261
|
+
byteskip=None,
|
1262
|
+
offset=None,
|
1263
|
+
idarray=None,
|
1264
|
+
ensemble=0,
|
1265
|
+
):
|
1266
|
+
self.filename = filename
|
1267
|
+
error = 0
|
1268
|
+
data, ens, cell, beam, error = pyreadrdi.datatype(
|
1269
|
+
self.filename,
|
1270
|
+
"percent good",
|
1271
|
+
cell=cell,
|
1272
|
+
beam=beam,
|
1273
|
+
byteskip=byteskip,
|
1274
|
+
offset=offset,
|
1275
|
+
idarray=idarray,
|
1276
|
+
ensemble=ensemble,
|
1277
|
+
)
|
1278
|
+
self.warning = pyreadrdi.ErrorCode.get_message(error)
|
1279
|
+
|
1280
|
+
self.data = data
|
1281
|
+
self.error = error
|
1282
|
+
self.ensembles = ens
|
1283
|
+
self.cells = cell
|
1284
|
+
self.beams = beam
|
1285
|
+
|
1286
|
+
self.unit = "percent"
|
1287
|
+
self.valid_min = 0
|
1288
|
+
self.valid_max = 100
|
1289
|
+
self.long_name = "Percent Good"
|
1290
|
+
|
1291
|
+
|
1292
|
+
class Status:
|
1293
|
+
"""
|
1294
|
+
The class extracts Status data from RDI ADCP files.
|
1295
|
+
|
1296
|
+
Parameters
|
1297
|
+
----------
|
1298
|
+
filename : str
|
1299
|
+
The RDI ADCP binary file to extract data from.
|
1300
|
+
cell : int, optional
|
1301
|
+
Cell number to extract, by default 0.
|
1302
|
+
beam : int, optional
|
1303
|
+
Beam number to extract, by default 0.
|
1304
|
+
byteskip : int, optional
|
1305
|
+
Number of bytes to skip, by default None.
|
1306
|
+
offset : int, optional
|
1307
|
+
Offset value for data extraction, by default None.
|
1308
|
+
idarray : array-like, optional
|
1309
|
+
Array of IDs for data extraction, by default None.
|
1310
|
+
ensemble : int, optional
|
1311
|
+
Ensemble number to start extraction from, by default 0.
|
1312
|
+
"""
|
1313
|
+
|
1314
|
+
def __init__(
|
1315
|
+
self,
|
1316
|
+
filename,
|
1317
|
+
cell=0,
|
1318
|
+
beam=0,
|
1319
|
+
byteskip=None,
|
1320
|
+
offset=None,
|
1321
|
+
idarray=None,
|
1322
|
+
ensemble=0,
|
1323
|
+
):
|
1324
|
+
self.filename = filename
|
1325
|
+
error = 0
|
1326
|
+
data, ens, cell, beam, error = pyreadrdi.datatype(
|
1327
|
+
self.filename,
|
1328
|
+
"status",
|
1329
|
+
cell=cell,
|
1330
|
+
beam=beam,
|
1331
|
+
byteskip=byteskip,
|
1332
|
+
offset=offset,
|
1333
|
+
idarray=idarray,
|
1334
|
+
ensemble=ensemble,
|
1335
|
+
)
|
1336
|
+
self.warning = pyreadrdi.ErrorCode.get_message(error)
|
1337
|
+
|
1338
|
+
self.data = data
|
1339
|
+
self.error = error
|
1340
|
+
self.ensembles = ens
|
1341
|
+
self.cells = cell
|
1342
|
+
self.beams = beam
|
1343
|
+
|
1344
|
+
self.unit = ""
|
1345
|
+
self.valid_min = 0
|
1346
|
+
self.valid_max = 1
|
1347
|
+
self.long_name = "Status Data Format"
|
1348
|
+
|
1349
|
+
|
1350
|
+
class ReadFile:
|
1351
|
+
"""
|
1352
|
+
Class to read and extract data from RDI ADCP binary files, organizing data types like
|
1353
|
+
Fixed Leader, Variable Leader, Velocity, and others.
|
1354
|
+
|
1355
|
+
Parameters
|
1356
|
+
----------
|
1357
|
+
filename : str
|
1358
|
+
The RDI ADCP binary file to be read.
|
1359
|
+
"""
|
1360
|
+
|
1361
|
+
def __init__(self, filename):
|
1362
|
+
"""
|
1363
|
+
Initializes the ReadFile object and extracts data from the RDI ADCP binary file.
|
1364
|
+
"""
|
1365
|
+
self.fileheader = FileHeader(filename)
|
1366
|
+
datatype_array = self.fileheader.data_types()
|
1367
|
+
error_array = {"Fileheader": self.fileheader.error}
|
1368
|
+
warning_array = {"Fileheader": self.fileheader.warning}
|
1369
|
+
ensemble_array = {"Fileheader": self.fileheader.ensembles}
|
1370
|
+
|
1371
|
+
byteskip = self.fileheader.byteskip
|
1372
|
+
offset = self.fileheader.address_offset
|
1373
|
+
idarray = self.fileheader.dataid
|
1374
|
+
ensemble = self.fileheader.ensembles
|
1375
|
+
|
1376
|
+
self.fixedleader = FixedLeader(
|
1377
|
+
filename,
|
1378
|
+
byteskip=byteskip,
|
1379
|
+
offset=offset,
|
1380
|
+
idarray=idarray,
|
1381
|
+
ensemble=ensemble,
|
1382
|
+
)
|
1383
|
+
error_array["Fixed Leader"] = self.fixedleader.error
|
1384
|
+
warning_array["Fixed Leader"] = self.fixedleader.warning
|
1385
|
+
ensemble_array["Fixed Leader"] = self.fixedleader.ensembles
|
1386
|
+
cells = self.fixedleader.fleader["Cells"][0]
|
1387
|
+
beams = self.fixedleader.fleader["Beams"][0]
|
1388
|
+
ensemble = self.fixedleader.ensembles
|
1389
|
+
|
1390
|
+
self.variableleader = VariableLeader(
|
1391
|
+
filename,
|
1392
|
+
byteskip=byteskip,
|
1393
|
+
offset=offset,
|
1394
|
+
idarray=idarray,
|
1395
|
+
ensemble=ensemble,
|
1396
|
+
)
|
1397
|
+
error_array["Variable Leader"] = self.variableleader.error
|
1398
|
+
warning_array["Variable Leader"] = self.variableleader.warning
|
1399
|
+
ensemble_array["Variable Leader"] = self.variableleader.ensembles
|
1400
|
+
ensemble = self.fixedleader.ensembles
|
1401
|
+
|
1402
|
+
if "Velocity" in datatype_array:
|
1403
|
+
self.velocity = Velocity(
|
1404
|
+
filename,
|
1405
|
+
cell=cells,
|
1406
|
+
beam=beams,
|
1407
|
+
byteskip=byteskip,
|
1408
|
+
offset=offset,
|
1409
|
+
idarray=idarray,
|
1410
|
+
ensemble=ensemble,
|
1411
|
+
)
|
1412
|
+
error_array["Velocity"] = self.velocity.error
|
1413
|
+
warning_array["Velocity"] = self.velocity.warning
|
1414
|
+
ensemble_array["Velocity"] = self.velocity.ensembles
|
1415
|
+
|
1416
|
+
if "Correlation" in datatype_array:
|
1417
|
+
self.correlation = Correlation(
|
1418
|
+
filename,
|
1419
|
+
cell=cells,
|
1420
|
+
beam=beams,
|
1421
|
+
byteskip=byteskip,
|
1422
|
+
offset=offset,
|
1423
|
+
idarray=idarray,
|
1424
|
+
ensemble=ensemble,
|
1425
|
+
)
|
1426
|
+
error_array["Correlation"] = self.correlation.error
|
1427
|
+
warning_array["Correlation"] = self.correlation.warning
|
1428
|
+
ensemble_array["Correlation"] = self.correlation.ensembles
|
1429
|
+
|
1430
|
+
if "Echo" in datatype_array:
|
1431
|
+
self.echo = Echo(
|
1432
|
+
filename,
|
1433
|
+
cell=cells,
|
1434
|
+
beam=beams,
|
1435
|
+
byteskip=byteskip,
|
1436
|
+
offset=offset,
|
1437
|
+
idarray=idarray,
|
1438
|
+
ensemble=ensemble,
|
1439
|
+
)
|
1440
|
+
error_array["Echo"] = self.echo.error
|
1441
|
+
warning_array["Echo"] = self.echo.warning
|
1442
|
+
ensemble_array["Echo"] = self.echo.ensembles
|
1443
|
+
|
1444
|
+
if "Percent Good" in datatype_array:
|
1445
|
+
self.percentgood = PercentGood(
|
1446
|
+
filename,
|
1447
|
+
cell=cells,
|
1448
|
+
beam=beams,
|
1449
|
+
byteskip=byteskip,
|
1450
|
+
offset=offset,
|
1451
|
+
idarray=idarray,
|
1452
|
+
ensemble=ensemble,
|
1453
|
+
)
|
1454
|
+
error_array["Percent Good"] = self.percentgood.error
|
1455
|
+
warning_array["Percent Good"] = self.percentgood.warning
|
1456
|
+
ensemble_array["Percent Good"] = self.percentgood.ensembles
|
1457
|
+
|
1458
|
+
if "Status" in datatype_array:
|
1459
|
+
self.status = Status(
|
1460
|
+
filename,
|
1461
|
+
cell=cells,
|
1462
|
+
beam=beams,
|
1463
|
+
byteskip=byteskip,
|
1464
|
+
offset=offset,
|
1465
|
+
idarray=idarray,
|
1466
|
+
ensemble=ensemble,
|
1467
|
+
)
|
1468
|
+
error_array["Status"] = self.status.error
|
1469
|
+
warning_array["Status"] = self.status.warning
|
1470
|
+
ensemble_array["Status"] = self.status.ensembles
|
1471
|
+
|
1472
|
+
# Add Time Axis
|
1473
|
+
year = self.variableleader.vleader["RTC Year"]
|
1474
|
+
month = self.variableleader.vleader["RTC Month"]
|
1475
|
+
day = self.variableleader.vleader["RTC Day"]
|
1476
|
+
hour = self.variableleader.vleader["RTC Hour"]
|
1477
|
+
minute = self.variableleader.vleader["RTC Minute"]
|
1478
|
+
second = self.variableleader.vleader["RTC Second"]
|
1479
|
+
year = year + 2000
|
1480
|
+
date_df = pd.DataFrame(
|
1481
|
+
{
|
1482
|
+
"year": year,
|
1483
|
+
"month": month,
|
1484
|
+
"day": day,
|
1485
|
+
"hour": hour,
|
1486
|
+
"minute": minute,
|
1487
|
+
"second": second,
|
1488
|
+
}
|
1489
|
+
)
|
1490
|
+
self.time = pd.to_datetime(date_df)
|
1491
|
+
|
1492
|
+
# Depth
|
1493
|
+
# Create a depth axis with mean depth in 'm'
|
1494
|
+
cell1 = self.fixedleader.field()["Cells"]
|
1495
|
+
bin1dist1 = self.fixedleader.field()["Bin 1 Dist"] / 100
|
1496
|
+
depth_cell_len1 = self.fixedleader.field()["Depth Cell Len"] / 100
|
1497
|
+
beam_direction1 = self.fixedleader.system_configuration()["Beam Direction"]
|
1498
|
+
mean_depth = np.mean(self.variableleader.vleader["Depth of Transducer"]) / 10
|
1499
|
+
mean_depth = np.trunc(mean_depth)
|
1500
|
+
if beam_direction1.lower() == "up":
|
1501
|
+
sgn = -1
|
1502
|
+
else:
|
1503
|
+
sgn = 1
|
1504
|
+
first_depth = mean_depth + sgn * bin1dist1
|
1505
|
+
last_depth = first_depth + sgn * cell1 * depth_cell_len1
|
1506
|
+
z = np.arange(first_depth, last_depth, sgn * depth_cell_len1)
|
1507
|
+
self.depth = z
|
1508
|
+
|
1509
|
+
# Add all attributes/method/data from FixedLeader and VariableLeader
|
1510
|
+
self._copy_attributes_from_var()
|
1511
|
+
|
1512
|
+
# Error Codes and Warnings
|
1513
|
+
self.error_codes = error_array
|
1514
|
+
self.warnings = warning_array
|
1515
|
+
self.ensemble_array = ensemble_array
|
1516
|
+
self.ensemble_value_array = np.array(list(self.ensemble_array.values()))
|
1517
|
+
|
1518
|
+
self.isEnsembleEqual = check_equal(self.ensemble_value_array)
|
1519
|
+
self.isFixedEnsemble = False
|
1520
|
+
|
1521
|
+
ec = np.array(list(self.error_codes.values()))
|
1522
|
+
|
1523
|
+
if np.all(ec == 0):
|
1524
|
+
self.isWarning = False
|
1525
|
+
else:
|
1526
|
+
self.isWarning = True
|
1527
|
+
|
1528
|
+
# Add additional attributes
|
1529
|
+
# Ensemble
|
1530
|
+
dtens = self.ensemble_value_array
|
1531
|
+
minens = np.min(dtens)
|
1532
|
+
self.ensembles = minens
|
1533
|
+
|
1534
|
+
# Add attribute that lists all variables/functions
|
1535
|
+
self.list_vars = vars(self).keys()
|
1536
|
+
|
1537
|
+
def _copy_attributes_from_var(self):
|
1538
|
+
for attr_name, attr_value in self.variableleader.__dict__.items():
|
1539
|
+
# Copy each attribute of var into self
|
1540
|
+
setattr(self, attr_name, attr_value)
|
1541
|
+
for attr_name, attr_value in self.fixedleader.__dict__.items():
|
1542
|
+
# Copy each attribute of var into self
|
1543
|
+
setattr(self, attr_name, attr_value)
|
1544
|
+
|
1545
|
+
def __getattr__(self, name):
|
1546
|
+
# Delegate attribute/method access to self.var if not found in self
|
1547
|
+
if hasattr(self.variableleader, name):
|
1548
|
+
return getattr(self.variableleader, name)
|
1549
|
+
if hasattr(self.fixedleader, name):
|
1550
|
+
return getattr(self.fixedleader, name)
|
1551
|
+
raise AttributeError(
|
1552
|
+
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
1553
|
+
)
|
1554
|
+
|
1555
|
+
def fixensemble(self, min_cutoff=0):
|
1556
|
+
"""
|
1557
|
+
Fixes the ensemble size across all data types in the file if they differ.
|
1558
|
+
|
1559
|
+
Parameters
|
1560
|
+
----------
|
1561
|
+
min_cutoff : int, optional
|
1562
|
+
Minimum number of ensembles to consider when fixing, by default 0.
|
1563
|
+
|
1564
|
+
Returns
|
1565
|
+
-------
|
1566
|
+
None
|
1567
|
+
"""
|
1568
|
+
datatype_array = self.fileheader.data_types()
|
1569
|
+
# Check if the number of ensembles in a data type
|
1570
|
+
# is less than min_cutoff.
|
1571
|
+
# Some data type can have zero ensembles
|
1572
|
+
dtens = self.ensemble_value_array
|
1573
|
+
new_array = dtens[dtens > min_cutoff]
|
1574
|
+
minens = np.min(new_array)
|
1575
|
+
|
1576
|
+
if not self.isEnsembleEqual:
|
1577
|
+
self.fileheader.ensembles = minens
|
1578
|
+
self.fileheader.datatypes = self.fileheader.datatypes[:minens]
|
1579
|
+
self.fileheader.bytes = self.fileheader.bytes[:minens]
|
1580
|
+
self.fileheader.byteskip = self.fileheader.byteskip[:minens]
|
1581
|
+
self.fileheader.address_offset = self.fileheader.address_offset[:minens, :]
|
1582
|
+
self.fileheader.dataid = self.fileheader.dataid[:minens, :]
|
1583
|
+
if "Fixed Leader" in datatype_array:
|
1584
|
+
self.fixedleader.data = self.fixedleader.data[:, :minens]
|
1585
|
+
self.fixedleader.ensembles = minens
|
1586
|
+
if "Variable Leader" in datatype_array:
|
1587
|
+
self.variableleader.data = self.variableleader.data[:, :minens]
|
1588
|
+
self.variableleader.ensembles = minens
|
1589
|
+
if "Velocity" in datatype_array:
|
1590
|
+
self.velocity.data = self.velocity.data[:, :, :minens]
|
1591
|
+
self.velocity.ensembles = minens
|
1592
|
+
if "Correlation" in datatype_array:
|
1593
|
+
self.correlation.data = self.correlation.data[:, :, :minens]
|
1594
|
+
self.correlation.ensembles = minens
|
1595
|
+
if "Echo" in datatype_array:
|
1596
|
+
self.echo.data = self.echo.data[:, :, :minens]
|
1597
|
+
self.echo.ensembles = minens
|
1598
|
+
if "Percent Good" in datatype_array:
|
1599
|
+
self.percentgood.data = self.percentgood.data[:, :, :minens]
|
1600
|
+
self.percentgood.ensembles = minens
|
1601
|
+
if "Status" in datatype_array:
|
1602
|
+
self.status.data = self.status.data[:, :, :minens]
|
1603
|
+
self.status.ensembles = minens
|
1604
|
+
print(f"Ensembles fixed to {minens}. All data types have same ensembles.")
|
1605
|
+
else:
|
1606
|
+
print(
|
1607
|
+
"WARNING: No response was initiated. All data types have same ensemble."
|
1608
|
+
)
|
1609
|
+
|
1610
|
+
self.isFixedEnsemble = True
|