pyadps 0.1.1__py3-none-any.whl → 0.1.3__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/pages/01_Read_File.py +3 -2
- pyadps/pages/03_Download_Raw_File.py +69 -24
- pyadps/pages/05_QC_Test.py +20 -3
- pyadps/pages/06_Profile_Test.py +10 -10
- pyadps/pages/08_Write_File.py +26 -5
- pyadps/utils/autoprocess.py +10 -5
- pyadps/utils/metadata/flmeta.json +420 -420
- pyadps/utils/plotgen.py +1 -1
- pyadps/utils/profile_test.py +228 -2
- pyadps/utils/pyreadrdi.py +112 -83
- pyadps/utils/readrdi.py +34 -3
- pyadps/utils/signal_quality.py +16 -4
- pyadps/utils/writenc.py +101 -52
- {pyadps-0.1.1.dist-info → pyadps-0.1.3.dist-info}/METADATA +1 -1
- pyadps-0.1.3.dist-info/RECORD +33 -0
- pyadps/pages/__pycache__/__init__.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/autoprocess.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/cutbin.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/plotgen.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/profile_test.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/pyreadrdi.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/readrdi.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/regrid.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/script.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/sensor_health.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/signal_quality.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/velocity_test.cpython-312.pyc +0 -0
- pyadps/utils/__pycache__/writenc.cpython-312.pyc +0 -0
- pyadps-0.1.1.dist-info/RECORD +0 -47
- {pyadps-0.1.1.dist-info → pyadps-0.1.3.dist-info}/LICENSE +0 -0
- {pyadps-0.1.1.dist-info → pyadps-0.1.3.dist-info}/WHEEL +0 -0
- {pyadps-0.1.1.dist-info → pyadps-0.1.3.dist-info}/entry_points.txt +0 -0
pyadps/utils/plotgen.py
CHANGED
pyadps/utils/profile_test.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import numpy as np
|
2
2
|
import scipy as sp
|
3
|
-
from pyadps.utils.readrdi import ReadFile
|
3
|
+
from pyadps.utils.readrdi import ReadFile, check_equal
|
4
4
|
from .plotgen import PlotEnds
|
5
5
|
|
6
6
|
|
@@ -214,6 +214,217 @@ def manual_cut_bins(mask, min_cell, max_cell, min_ensemble, max_ensemble):
|
|
214
214
|
return mask
|
215
215
|
|
216
216
|
|
217
|
+
def modifiedRegrid2d(
|
218
|
+
ds,
|
219
|
+
data,
|
220
|
+
fill_value,
|
221
|
+
end_cell_option="cell",
|
222
|
+
trimends=None,
|
223
|
+
method="nearest",
|
224
|
+
orientation="default",
|
225
|
+
boundary_limit=0,
|
226
|
+
cells=None,
|
227
|
+
cell_size=None,
|
228
|
+
bin1dist=None,
|
229
|
+
):
|
230
|
+
"""
|
231
|
+
Modified Regrids 2D data onto a new grid based on specified parameters.
|
232
|
+
The function is capable of handling data with non-uniform number of cells
|
233
|
+
and Depth cell length.
|
234
|
+
|
235
|
+
Parameters:
|
236
|
+
-----------
|
237
|
+
ds : pyadps.dataset or numpy.ndarray
|
238
|
+
If pyadps dataframe is loaded, the data from the fixed and variable leader
|
239
|
+
is automatically obtained. This includes the depth of the transducer and other relevant information
|
240
|
+
for trimming the data.
|
241
|
+
|
242
|
+
If numpy.ndarray is loaded, the value should contain the transducer_depth.
|
243
|
+
In such cases provide cells, cell_size, and bin 1 distance.
|
244
|
+
Orientiation should be either 'up' or 'down' and not 'default'.
|
245
|
+
|
246
|
+
|
247
|
+
data : array-like
|
248
|
+
The 2D data array to be regridded.
|
249
|
+
|
250
|
+
fill_value : scalar
|
251
|
+
The value used to fill missing or undefined grid points.
|
252
|
+
|
253
|
+
end_cell_option : str or float, optional, default="cell"
|
254
|
+
The depth of the last bin or boundary for the grid.
|
255
|
+
Options include:
|
256
|
+
- "cell" : Calculates the depth of the default last bin for the grid.
|
257
|
+
Truncates to surface for upward ADCP.
|
258
|
+
- "surface": The data is gridded till the surface
|
259
|
+
- "manual": User-defined depth for the grid.
|
260
|
+
Use boundary_limit option to provide the value.
|
261
|
+
otherwise, a specific numerical depth value can be provided.
|
262
|
+
|
263
|
+
trimends : tuple of floats, optional, default=None
|
264
|
+
If provided, defines the ensemble range (start, end) for
|
265
|
+
calculating the maximum/minimum transducer depth.
|
266
|
+
Helps avoiding the deployment or retrieval data.
|
267
|
+
E.g. (10, 3000)
|
268
|
+
|
269
|
+
method : str, optional, default="nearest"
|
270
|
+
The interpolation method to use for regridding based
|
271
|
+
on scipy.interpolate.interp1d.
|
272
|
+
Options include:
|
273
|
+
- "nearest" : Nearest neighbor interpolation.
|
274
|
+
- "linear" : Linear interpolation.
|
275
|
+
- "cubic" : Cubic interpolation.
|
276
|
+
|
277
|
+
orientation : str, optional, default="up"
|
278
|
+
Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
|
279
|
+
- "up" : Regrid upwards (for upward-looking ADCP).
|
280
|
+
- "down" : Regrid downwards (for downward-looking ADCP).
|
281
|
+
|
282
|
+
boundary_limit : float, optional, default=0
|
283
|
+
The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
|
284
|
+
|
285
|
+
cells: int, optional
|
286
|
+
Number of cells
|
287
|
+
|
288
|
+
cell_size: int, optional
|
289
|
+
Cell size or depth cell length in cm
|
290
|
+
|
291
|
+
bin1dist: int, optional
|
292
|
+
Distance from the first bin in cm
|
293
|
+
|
294
|
+
|
295
|
+
Returns:
|
296
|
+
--------
|
297
|
+
z: regridded depth
|
298
|
+
regridded_data : array-like
|
299
|
+
The regridded 2D data array, based on the specified method,
|
300
|
+
orientation, and other parameters.
|
301
|
+
|
302
|
+
Notes:
|
303
|
+
------
|
304
|
+
- If `end_cell_option == boundary`, then `boundary_limit` is used to regrid the data.
|
305
|
+
- This function allows for flexible regridding of 2D data to fit a new grid, supporting different interpolation methods.
|
306
|
+
- The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
|
307
|
+
"""
|
308
|
+
|
309
|
+
if isinstance(ds, ReadFile) or ds.__class__.__name__ == "ReadFile":
|
310
|
+
flobj = ds.fixedleader
|
311
|
+
vlobj = ds.variableleader
|
312
|
+
# Get values and convert to 'm'
|
313
|
+
bin1dist = flobj.field()["Bin 1 Dist"] / 100
|
314
|
+
transdepth = vlobj.vleader["Depth of Transducer"] / 10
|
315
|
+
cell_size = flobj.field()["Depth Cell Len"] / 100
|
316
|
+
cells = flobj.field()["Cells"]
|
317
|
+
ensembles = flobj.ensembles
|
318
|
+
if orientation.lower() == "default":
|
319
|
+
orientation = flobj.system_configuration()["Beam Direction"]
|
320
|
+
|
321
|
+
elif isinstance(ds, np.ndarray) and np.squeeze(ds).ndim == 1:
|
322
|
+
transdepth = ds / 10
|
323
|
+
ensembles = np.size(ds)
|
324
|
+
|
325
|
+
if cells is None:
|
326
|
+
raise ValueError("Input must include number of cells.")
|
327
|
+
|
328
|
+
if cell_size is None:
|
329
|
+
raise ValueError("Input must include cell size.")
|
330
|
+
else:
|
331
|
+
cell_size = cell_size / 100
|
332
|
+
|
333
|
+
if bin1dist is None:
|
334
|
+
raise ValueError("Input must include bin 1 distance.")
|
335
|
+
else:
|
336
|
+
bin1dist = bin1dist / 100
|
337
|
+
|
338
|
+
if orientation.lower() != "up" and orientation.lower() != "down":
|
339
|
+
raise ValueError("Orientation must be `up` or `down`.")
|
340
|
+
else:
|
341
|
+
raise ValueError("Input must be a 1-D numpy array or a PyADPS instance")
|
342
|
+
|
343
|
+
if orientation.lower() == "up":
|
344
|
+
sgn = -1
|
345
|
+
else:
|
346
|
+
sgn = 1
|
347
|
+
|
348
|
+
# Create a regular grid
|
349
|
+
|
350
|
+
# Find depth of first cell
|
351
|
+
depth = transdepth + sgn * bin1dist
|
352
|
+
# print("depth: ", depth)
|
353
|
+
|
354
|
+
# Find the maximum and minimum depth for first cell for upward
|
355
|
+
# looking ADCP (minimum and maximum for downward looking)
|
356
|
+
if trimends is not None:
|
357
|
+
max_depth = abs(np.min(sgn * depth[trimends[0] : trimends[1]]))
|
358
|
+
min_depth = abs(np.max(sgn * depth[trimends[0] : trimends[1]]))
|
359
|
+
else:
|
360
|
+
max_depth = abs(np.min(sgn * depth))
|
361
|
+
min_depth = abs(np.max(sgn * depth))
|
362
|
+
|
363
|
+
# FIRST CELL
|
364
|
+
# Convert the first cell depth to the first regular grid depth
|
365
|
+
depthfirstcell = max_depth - max_depth % min(cell_size)
|
366
|
+
# print("depthfirstcell: ", depthfirstcell)
|
367
|
+
|
368
|
+
# LAST CELL
|
369
|
+
# Convert the last cell depth to last regular grid depth
|
370
|
+
if end_cell_option.lower() == "surface":
|
371
|
+
# Added one additional negative cell to accomodate 0 m.
|
372
|
+
depthlastcell = sgn * min(cell_size)
|
373
|
+
# print("depthlastcell: ", depthlastcell)
|
374
|
+
elif end_cell_option.lower() == "cell":
|
375
|
+
min_depth_regrid = min_depth - sgn * min_depth % min(cell_size)
|
376
|
+
depthlastcell = min_depth_regrid + sgn * (max(cells) + 1) * min(cell_size)
|
377
|
+
# print("depthlastcell: ", depthlastcell)
|
378
|
+
# Check if this is required. Use 'surface' option
|
379
|
+
if depthlastcell < 0:
|
380
|
+
depthlastcell = sgn * min(cell_size)
|
381
|
+
elif end_cell_option.lower() == "manual":
|
382
|
+
if sgn < 0 and boundary_limit > depthfirstcell:
|
383
|
+
print(
|
384
|
+
"ERROR: For upward looking ADCP, boundary limit should be less than transducer depth"
|
385
|
+
)
|
386
|
+
return
|
387
|
+
if sgn > 0 and boundary_limit < depthfirstcell:
|
388
|
+
print(
|
389
|
+
"ERROR: For downward looking ADCP, boundary limit should be greater than transducer depth"
|
390
|
+
)
|
391
|
+
return
|
392
|
+
# Set the last grid cell depth
|
393
|
+
depthlastcell = boundary_limit
|
394
|
+
else:
|
395
|
+
print("ERROR: `end_cell_option` not recognized.")
|
396
|
+
return
|
397
|
+
|
398
|
+
# Negative used for upward and positive for downward.
|
399
|
+
z = np.arange(sgn * depthfirstcell, sgn * depthlastcell, min(cell_size))
|
400
|
+
regbins = len(z)
|
401
|
+
|
402
|
+
regridded_data = np.zeros((regbins, ensembles))
|
403
|
+
|
404
|
+
# Create original depth array
|
405
|
+
for i, d in enumerate(depth):
|
406
|
+
n = d + sgn * cell_size[i] * cells[i]
|
407
|
+
# np.arange may include unexpected elements due to floating-point
|
408
|
+
# precision issues at the stopping point. Changed to np.linspace.
|
409
|
+
#
|
410
|
+
# depth_bins = np.arange(sgn*d, sgn*n, cell_size)
|
411
|
+
depth_bins = np.linspace(sgn * d, sgn * n, max(cells))
|
412
|
+
# print("depth_bins: ", depth_bins, "len: ", len(depth_bins))
|
413
|
+
# print("data:", data, "len:", len(data))
|
414
|
+
# print("i: ", i)
|
415
|
+
f = sp.interpolate.interp1d(
|
416
|
+
depth_bins,
|
417
|
+
data[:, i],
|
418
|
+
kind=method,
|
419
|
+
fill_value=fill_value,
|
420
|
+
bounds_error=False,
|
421
|
+
)
|
422
|
+
gridz = f(z)
|
423
|
+
|
424
|
+
regridded_data[:, i] = gridz
|
425
|
+
|
426
|
+
return abs(z), regridded_data
|
427
|
+
|
217
428
|
def regrid2d(
|
218
429
|
ds,
|
219
430
|
data,
|
@@ -305,6 +516,11 @@ def regrid2d(
|
|
305
516
|
"""
|
306
517
|
|
307
518
|
if isinstance(ds, ReadFile) or ds.__class__.__name__ == "ReadFile":
|
519
|
+
if not (check_equal(ds.fleader['Cells']) or check_equal(ds.fleader['Depth Cell Len'])):
|
520
|
+
print("\033[93m Warning: The number of cells or depth cell length are not equal. Using the modifiedRegrid2d function, which may take some time.\033[0m")
|
521
|
+
return modifiedRegrid2d(ds, data, fill_value, end_cell_option, trimends, method, orientation,
|
522
|
+
boundary_limit, cells, cell_size, bin1dist)
|
523
|
+
|
308
524
|
flobj = ds.fixedleader
|
309
525
|
vlobj = ds.variableleader
|
310
526
|
# Get values and convert to 'm'
|
@@ -322,11 +538,21 @@ def regrid2d(
|
|
322
538
|
|
323
539
|
if cells is None:
|
324
540
|
raise ValueError("Input must include number of cells.")
|
541
|
+
else:
|
542
|
+
if not check_equal(cells):
|
543
|
+
print("\033[93m Warning: The number of cells or depth cell length are not equal. Using the modifiedRegrid2d function, which may take some time.\033[0m")
|
544
|
+
return modifiedRegrid2d(ds, data, fill_value, end_cell_option, trimends, method, orientation,
|
545
|
+
boundary_limit, cells, cell_size, bin1dist)
|
546
|
+
cells = cells[0]
|
325
547
|
|
326
548
|
if cell_size is None:
|
327
549
|
raise ValueError("Input must include cell size.")
|
328
550
|
else:
|
329
|
-
|
551
|
+
if not check_equal(cell_size):
|
552
|
+
# print("\033[93m Warning: The number of cells or depth cell length are not equal. Using the modifiedRegrid2d function, which may take some time.\033[0m")
|
553
|
+
return modifiedRegrid2d(ds, data, fill_value, end_cell_option, trimends, method, orientation,
|
554
|
+
boundary_limit, cells, cell_size, bin1dist)
|
555
|
+
cell_size = cell_size[0] / 100
|
330
556
|
|
331
557
|
if bin1dist is None:
|
332
558
|
raise ValueError("Input must include bin 1 distance.")
|
pyadps/utils/pyreadrdi.py
CHANGED
@@ -3,17 +3,17 @@ pyreadrdi.py
|
|
3
3
|
|
4
4
|
Module Overview
|
5
5
|
---------------
|
6
|
-
This module provides functionalities to read and parse RDI ADCP files.
|
7
|
-
It includes functions for reading file headers, fixed and variable leaders,
|
6
|
+
This module provides functionalities to read and parse RDI ADCP files.
|
7
|
+
It includes functions for reading file headers, fixed and variable leaders,
|
8
8
|
and data types like velocity, correlation, echo intensity, and percent good.
|
9
|
-
Currently reads only PD0 format.
|
9
|
+
Currently reads only PD0 format.
|
10
10
|
|
11
11
|
Modules
|
12
12
|
-------------------
|
13
13
|
- fileheader: Function to read and parse the file header information.
|
14
14
|
- fixedleader: Function to read and parse the fixed leader section of an RDI file.
|
15
15
|
- variableleader: Function to read and parse the variable leader section of an RDI file.
|
16
|
-
- datatype: Function to read and parse 3D data types.
|
16
|
+
- datatype: Function to read and parse 3D data types.
|
17
17
|
- ErrorCode: Enum class to define and manage error codes for file operations.
|
18
18
|
|
19
19
|
Creation Date
|
@@ -57,7 +57,7 @@ Examples
|
|
57
57
|
>>> vel_data = datatype('example.rdi', "velocity")
|
58
58
|
>>> vel_data = datatype('example.rdi', "echo", beam=4, cell=20)
|
59
59
|
|
60
|
-
Other add-on functions and classes inlcude bcolors, safe_open, and ErrorCode.
|
60
|
+
Other add-on functions and classes inlcude bcolors, safe_open, and ErrorCode.
|
61
61
|
Examples (add-on)
|
62
62
|
-------------------
|
63
63
|
>>> error = ErrorCode.FILE_NOT_FOUND
|
@@ -403,72 +403,85 @@ def fileheader(rdi_file):
|
|
403
403
|
bfile.seek(0, 0)
|
404
404
|
bskip = i = 0
|
405
405
|
hid = [None] * 5
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
406
|
+
try:
|
407
|
+
while byt := bfile.read(6):
|
408
|
+
hid[0], hid[1], hid[2], hid[3], hid[4] = unpack("<BBHBB", byt)
|
409
|
+
headerid = np.append(headerid, np.int8(hid[0]))
|
410
|
+
sourceid = np.append(sourceid, np.int16(hid[1]))
|
411
|
+
byte = np.append(byte, np.int16(hid[2]))
|
412
|
+
spare = np.append(spare, np.int16(hid[3]))
|
413
|
+
datatype = np.append(datatype, np.int16(hid[4]))
|
414
|
+
|
415
|
+
# dbyte = bfile.read(2 * datatype[i])
|
416
|
+
dbyte, error = safe_read(bfile, 2 * datatype[i])
|
417
|
+
if dbyte is None:
|
418
|
+
if i == 0:
|
419
|
+
error_code = error.code
|
420
|
+
dummytuple = ([], [], [], [], [], ensemble, error_code)
|
421
|
+
return dummytuple
|
422
|
+
else:
|
423
|
+
break
|
424
|
+
|
425
|
+
# Check for id and datatype errors
|
417
426
|
if i == 0:
|
418
|
-
|
419
|
-
|
420
|
-
|
427
|
+
if headerid[0] != 127 or sourceid[0] != 127:
|
428
|
+
error = ErrorCode.WRONG_RDIFILE_TYPE
|
429
|
+
print(bcolors.FAIL + error.message + bcolors.ENDC)
|
430
|
+
error_code = error.code
|
431
|
+
dummytuple = ([], [], [], [], [], ensemble, error_code)
|
432
|
+
return dummytuple
|
421
433
|
else:
|
422
|
-
|
434
|
+
if headerid[i] != 127 or sourceid[i] != 127:
|
435
|
+
error = ErrorCode.ID_NOT_FOUND
|
436
|
+
print(bcolors.FAIL + error.message)
|
437
|
+
print(f"Ensembles reset to {i}" + bcolors.ENDC)
|
438
|
+
break
|
439
|
+
|
440
|
+
if datatype[i] != datatype[i - 1]:
|
441
|
+
error = ErrorCode.DATATYPE_MISMATCH
|
442
|
+
print(bcolors.FAIL + error.message)
|
443
|
+
print(f"Data Types for ensemble {i} is {datatype[i - 1]}.")
|
444
|
+
print(f"Data Types for ensemble {i + 1} is {datatype[i]}.")
|
445
|
+
print(f"Ensembles reset to {i}" + bcolors.ENDC)
|
446
|
+
break
|
423
447
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
448
|
+
try:
|
449
|
+
data = unpack("H" * datatype[i], dbyte)
|
450
|
+
address_offset.append(data)
|
451
|
+
except:
|
452
|
+
error = ErrorCode.FILE_CORRUPTED
|
429
453
|
error_code = error.code
|
430
454
|
dummytuple = ([], [], [], [], [], ensemble, error_code)
|
431
455
|
return dummytuple
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
readbyte, byteorder="little", signed=False
|
462
|
-
)
|
463
|
-
|
464
|
-
dataid.append(skip_array)
|
465
|
-
# bytekip is the number of bytes to skip to reach
|
466
|
-
# an ensemble from beginning of file.
|
467
|
-
# ?? Should byteskip be from current position ??
|
468
|
-
bskip = int(bskip) + int(byte[i]) + 2
|
469
|
-
bfile.seek(bskip, 0)
|
470
|
-
byteskip = np.append(byteskip, np.int32(bskip))
|
471
|
-
i += 1
|
456
|
+
|
457
|
+
skip_array = [None] * datatype[i]
|
458
|
+
for dtype in range(datatype[i]):
|
459
|
+
bseek = int(bskip) + int(address_offset[i][dtype])
|
460
|
+
bfile.seek(bseek, 0)
|
461
|
+
readbyte = bfile.read(2)
|
462
|
+
skip_array[dtype] = int.from_bytes(
|
463
|
+
readbyte, byteorder="little", signed=False
|
464
|
+
)
|
465
|
+
|
466
|
+
dataid.append(skip_array)
|
467
|
+
# bytekip is the number of bytes to skip to reach
|
468
|
+
# an ensemble from beginning of file.
|
469
|
+
# ?? Should byteskip be from current position ??
|
470
|
+
bskip = int(bskip) + int(byte[i]) + 2
|
471
|
+
bfile.seek(bskip, 0)
|
472
|
+
byteskip = np.append(byteskip, np.int32(bskip))
|
473
|
+
i += 1
|
474
|
+
except (ValueError, StructError, OverflowError) as e:
|
475
|
+
# except:
|
476
|
+
print(bcolors.WARNING + "WARNING: The file is broken.")
|
477
|
+
print(
|
478
|
+
f"Function `fileheader` unable to extract data for ensemble {i + 1}. Total ensembles reset to {i}."
|
479
|
+
)
|
480
|
+
print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
|
481
|
+
print(f" Error Type: {type(e).__name__}")
|
482
|
+
print(f" Error Details: {e}")
|
483
|
+
error = ErrorCode.FILE_CORRUPTED
|
484
|
+
ensemble = i
|
472
485
|
|
473
486
|
ensemble = i
|
474
487
|
bfile.close()
|
@@ -612,13 +625,14 @@ def fixedleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=0):
|
|
612
625
|
|
613
626
|
bfile.seek(byteskip[i], 0)
|
614
627
|
|
615
|
-
except (ValueError, StructError) as e:
|
628
|
+
except (ValueError, StructError, OverflowError) as e:
|
616
629
|
print(bcolors.WARNING + "WARNING: The file is broken.")
|
617
630
|
print(
|
618
631
|
f"Function `fixedleader` unable to extract data for ensemble {i + 1}. Total ensembles reset to {i}."
|
619
632
|
)
|
620
|
-
print("Details from struct function
|
621
|
-
print(f"
|
633
|
+
print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
|
634
|
+
print(f" Error Type: {type(e).__name__}")
|
635
|
+
print(f" Error Details: {e}")
|
622
636
|
error = ErrorCode.FILE_CORRUPTED
|
623
637
|
ensemble = i
|
624
638
|
|
@@ -794,13 +808,14 @@ def variableleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=
|
|
794
808
|
|
795
809
|
bfile.seek(byteskip[i], 0)
|
796
810
|
|
797
|
-
except (ValueError, StructError) as e:
|
811
|
+
except (ValueError, StructError, OverflowError) as e:
|
798
812
|
print(bcolors.WARNING + "WARNING: The file is broken.")
|
799
813
|
print(
|
800
814
|
f"Function `variableleader` unable to extract data for ensemble {i + 1}. Total ensembles reset to {i}."
|
801
815
|
)
|
802
|
-
print("Details from struct function
|
803
|
-
print(f"
|
816
|
+
print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
|
817
|
+
print(f" Error Type: {type(e).__name__}")
|
818
|
+
print(f" Error Details: {e}")
|
804
819
|
error = ErrorCode.FILE_CORRUPTED
|
805
820
|
ensemble = i
|
806
821
|
|
@@ -910,7 +925,9 @@ def datatype(
|
|
910
925
|
# Velocity is 16 bits and all others are 8 bits.
|
911
926
|
# Create empty array for the chosen variable name.
|
912
927
|
if var_name == "velocity":
|
913
|
-
var_array = np.full(
|
928
|
+
var_array = np.full(
|
929
|
+
(int(max(beam)), int(max(cell)), ensemble), -32768, dtype="int16"
|
930
|
+
)
|
914
931
|
bitstr = "<h"
|
915
932
|
bitint = 2
|
916
933
|
else: # inserted
|
@@ -957,16 +974,28 @@ def datatype(
|
|
957
974
|
return (var_array, error.code)
|
958
975
|
|
959
976
|
# READ DATA
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
977
|
+
i = 0
|
978
|
+
try:
|
979
|
+
for i in range(ensemble):
|
980
|
+
bfile.seek(fbyteskip[i], 1)
|
981
|
+
bdata = bfile.read(2)
|
982
|
+
for cno in range(int(cell[i])):
|
983
|
+
for bno in range(int(beam[i])):
|
984
|
+
bdata = bfile.read(bitint)
|
985
|
+
varunpack = unpack(bitstr, bdata)
|
986
|
+
var_array[bno][cno][i] = varunpack[0]
|
987
|
+
bfile.seek(byteskip[i], 0)
|
988
|
+
bfile.close()
|
989
|
+
except (ValueError, StructError, OverflowError) as e:
|
990
|
+
print(bcolors.WARNING + "WARNING: The file is broken.")
|
991
|
+
print(
|
992
|
+
f"Function `datatype` unable to extract {var_name} for ensemble {i + 1}. Total ensembles reset to {i}."
|
993
|
+
)
|
994
|
+
print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
|
995
|
+
print(f" Error Type: {type(e).__name__}")
|
996
|
+
print(f" Error Details: {e}")
|
997
|
+
error = ErrorCode.FILE_CORRUPTED
|
998
|
+
ensemble = i
|
970
999
|
|
971
|
-
data = var_array
|
1000
|
+
data = var_array[:, :, :ensemble]
|
972
1001
|
return (data, ensemble, cell, beam, error_code)
|
pyadps/utils/readrdi.py
CHANGED
@@ -103,6 +103,7 @@ import sys
|
|
103
103
|
import numpy as np
|
104
104
|
import pandas as pd
|
105
105
|
from pyadps.utils import pyreadrdi
|
106
|
+
from pyadps.utils.pyreadrdi import bcolors
|
106
107
|
|
107
108
|
|
108
109
|
class DotDict:
|
@@ -773,7 +774,7 @@ def vlead_dict(vid):
|
|
773
774
|
"MPT Minute": "int16",
|
774
775
|
"MPT Second": "int16",
|
775
776
|
"MPT Hundredth": "int16",
|
776
|
-
"
|
777
|
+
"Head Std Dev": "int16",
|
777
778
|
"Pitch Std Dev": "int16",
|
778
779
|
"Roll Std Dev": "int16",
|
779
780
|
"ADC Channel 0": "int16",
|
@@ -1358,7 +1359,7 @@ class ReadFile:
|
|
1358
1359
|
The RDI ADCP binary file to be read.
|
1359
1360
|
"""
|
1360
1361
|
|
1361
|
-
def __init__(self, filename):
|
1362
|
+
def __init__(self, filename, is_fix_ensemble=True):
|
1362
1363
|
"""
|
1363
1364
|
Initializes the ReadFile object and extracts data from the RDI ADCP binary file.
|
1364
1365
|
"""
|
@@ -1534,6 +1535,10 @@ class ReadFile:
|
|
1534
1535
|
# Add attribute that lists all variables/functions
|
1535
1536
|
self.list_vars = list(vars(self).keys())
|
1536
1537
|
|
1538
|
+
# By default fix ensemble
|
1539
|
+
if is_fix_ensemble and not self.isEnsembleEqual:
|
1540
|
+
self.fixensemble()
|
1541
|
+
|
1537
1542
|
def _copy_attributes_from_var(self):
|
1538
1543
|
for attr_name, attr_value in self.variableleader.__dict__.items():
|
1539
1544
|
# Copy each attribute of var into self
|
@@ -1552,6 +1557,18 @@ class ReadFile:
|
|
1552
1557
|
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
1553
1558
|
)
|
1554
1559
|
|
1560
|
+
def resize_fixedleader(self, newshape):
|
1561
|
+
for key in self.fixedleader.fleader:
|
1562
|
+
attr_name = key.lower().replace(" ", "_")
|
1563
|
+
attr_obj = getattr(self.fixedleader, attr_name)
|
1564
|
+
attr_obj.data = attr_obj.data[:newshape]
|
1565
|
+
|
1566
|
+
def resize_variableleader(self, newshape):
|
1567
|
+
for key in self.variableleader.vleader:
|
1568
|
+
attr_name = key.lower().replace(" ", "_")
|
1569
|
+
attr_obj = getattr(self.variableleader, attr_name)
|
1570
|
+
attr_obj.data = attr_obj.data[:newshape]
|
1571
|
+
|
1555
1572
|
def fixensemble(self, min_cutoff=0):
|
1556
1573
|
"""
|
1557
1574
|
Fixes the ensemble size across all data types in the file if they differ.
|
@@ -1582,10 +1599,18 @@ class ReadFile:
|
|
1582
1599
|
self.fileheader.dataid = self.fileheader.dataid[:minens, :]
|
1583
1600
|
if "Fixed Leader" in datatype_array:
|
1584
1601
|
self.fixedleader.data = self.fixedleader.data[:, :minens]
|
1602
|
+
self.fixedleader.fleader = {
|
1603
|
+
k: v[:minens] for k, v in self.fixedleader.fleader.items()
|
1604
|
+
}
|
1585
1605
|
self.fixedleader.ensembles = minens
|
1606
|
+
self.resize_fixedleader(minens)
|
1586
1607
|
if "Variable Leader" in datatype_array:
|
1587
1608
|
self.variableleader.data = self.variableleader.data[:, :minens]
|
1609
|
+
self.variableleader.vleader = {
|
1610
|
+
k: v[:minens] for k, v in self.variableleader.vleader.items()
|
1611
|
+
}
|
1588
1612
|
self.variableleader.ensembles = minens
|
1613
|
+
self.resize_variableleader(minens)
|
1589
1614
|
if "Velocity" in datatype_array:
|
1590
1615
|
self.velocity.data = self.velocity.data[:, :, :minens]
|
1591
1616
|
self.velocity.ensembles = minens
|
@@ -1601,7 +1626,13 @@ class ReadFile:
|
|
1601
1626
|
if "Status" in datatype_array:
|
1602
1627
|
self.status.data = self.status.data[:, :, :minens]
|
1603
1628
|
self.status.ensembles = minens
|
1604
|
-
|
1629
|
+
|
1630
|
+
self.time = self.time[:minens]
|
1631
|
+
print(
|
1632
|
+
bcolors.OKBLUE
|
1633
|
+
+ f"Ensembles fixed to {minens}. All data types have same ensembles."
|
1634
|
+
+ bcolors.ENDC
|
1635
|
+
)
|
1605
1636
|
else:
|
1606
1637
|
print(
|
1607
1638
|
"WARNING: No response was initiated. All data types have same ensemble."
|