pyadps 0.2.0b0__py3-none-any.whl → 0.3.0b0__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 +96 -17
- pyadps/pages/02_View_Raw_Data.py +69 -33
- pyadps/pages/03_Download_Raw_File.py +4 -4
- pyadps/pages/04_Sensor_Health.py +892 -0
- pyadps/pages/05_QC_Test.py +478 -0
- pyadps/pages/06_Profile_Test.py +959 -0
- pyadps/pages/07_Velocity_Test.py +599 -0
- pyadps/pages/{07_Write_File.py → 08_Write_File.py} +127 -52
- pyadps/pages/09_Auto_process.py +62 -0
- pyadps/utils/__init__.py +2 -3
- pyadps/utils/autoprocess.py +129 -46
- pyadps/utils/metadata/config.ini +22 -4
- pyadps/utils/metadata/demo.000 +0 -0
- pyadps/utils/plotgen.py +499 -0
- pyadps/utils/profile_test.py +491 -126
- pyadps/utils/pyreadrdi.py +13 -6
- pyadps/utils/readrdi.py +78 -6
- pyadps/utils/script.py +21 -23
- pyadps/utils/sensor_health.py +120 -0
- pyadps/utils/signal_quality.py +343 -23
- pyadps/utils/velocity_test.py +75 -27
- pyadps/utils/writenc.py +8 -1
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0b0.dist-info}/METADATA +53 -22
- pyadps-0.3.0b0.dist-info/RECORD +33 -0
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0b0.dist-info}/WHEEL +1 -1
- pyadps/pages/04_QC_Test.py +0 -334
- pyadps/pages/05_Profile_Test.py +0 -575
- pyadps/pages/06_Velocity_Test.py +0 -341
- pyadps/utils/cutbin.py +0 -413
- pyadps/utils/regrid.py +0 -279
- pyadps-0.2.0b0.dist-info/RECORD +0 -31
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0b0.dist-info}/LICENSE +0 -0
- {pyadps-0.2.0b0.dist-info → pyadps-0.3.0b0.dist-info}/entry_points.txt +0 -0
pyadps/utils/pyreadrdi.py
CHANGED
@@ -320,7 +320,7 @@ def safe_read(bfile, num_bytes):
|
|
320
320
|
|
321
321
|
if len(readbytes) != num_bytes:
|
322
322
|
print(f"Unexpected end of file: fewer than {num_bytes} bytes were read.")
|
323
|
-
return (
|
323
|
+
return (None, ErrorCode.FILE_CORRUPTED)
|
324
324
|
else:
|
325
325
|
return (readbytes, ErrorCode.SUCCESS)
|
326
326
|
|
@@ -427,6 +427,7 @@ def fileheader(rdi_file):
|
|
427
427
|
error = ErrorCode.WRONG_RDIFILE_TYPE
|
428
428
|
print(bcolors.FAIL + error.message + bcolors.ENDC)
|
429
429
|
error_code = error.code
|
430
|
+
dummytuple = ([], [], [], [], [], ensemble, error_code)
|
430
431
|
return dummytuple
|
431
432
|
else:
|
432
433
|
if headerid[i] != 127 or sourceid[i] != 127:
|
@@ -442,8 +443,14 @@ def fileheader(rdi_file):
|
|
442
443
|
print(f"Ensembles reset to {i}" + bcolors.ENDC)
|
443
444
|
break
|
444
445
|
|
445
|
-
|
446
|
-
|
446
|
+
try:
|
447
|
+
data = unpack("H" * datatype[i], dbyte)
|
448
|
+
address_offset.append(data)
|
449
|
+
except:
|
450
|
+
error = ErrorCode.FILE_CORRUPTED
|
451
|
+
error_code = error.code
|
452
|
+
dummytuple = ([], [], [], [], [], ensemble, error_code)
|
453
|
+
return dummytuple
|
447
454
|
|
448
455
|
skip_array = [None] * datatype[i]
|
449
456
|
for dtype in range(datatype[i]):
|
@@ -553,7 +560,7 @@ def fixedleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=0):
|
|
553
560
|
fbyteskip = None
|
554
561
|
for count, item in enumerate(idarray[i]):
|
555
562
|
if item in (0, 1):
|
556
|
-
fbyteskip = offset[
|
563
|
+
fbyteskip = offset[0][count]
|
557
564
|
if fbyteskip == None:
|
558
565
|
error = ErrorCode.ID_NOT_FOUND
|
559
566
|
ensemble = i
|
@@ -703,7 +710,7 @@ def variableleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=
|
|
703
710
|
fbyteskip = None
|
704
711
|
for count, item in enumerate(idarray[i]):
|
705
712
|
if item in (128, 129):
|
706
|
-
fbyteskip = offset[
|
713
|
+
fbyteskip = offset[0][count]
|
707
714
|
if fbyteskip == None:
|
708
715
|
error = ErrorCode.ID_NOT_FOUND
|
709
716
|
ensemble = i
|
@@ -935,7 +942,7 @@ def datatype(
|
|
935
942
|
fbyteskip = None
|
936
943
|
for count, item in enumerate(idarray[0][:]):
|
937
944
|
if item in vid:
|
938
|
-
fbyteskip = offset[
|
945
|
+
fbyteskip = offset[0][count]
|
939
946
|
break
|
940
947
|
if fbyteskip is None:
|
941
948
|
print(
|
pyadps/utils/readrdi.py
CHANGED
@@ -435,7 +435,7 @@ def flead_dict(fid, dim=2):
|
|
435
435
|
"False Target Thresh": "int64",
|
436
436
|
"Spare 1": "int64",
|
437
437
|
"Transmit Lag Dist": "int64",
|
438
|
-
"CPU Serial No": "
|
438
|
+
"CPU Serial No": "int128",
|
439
439
|
"System Bandwidth": "int64",
|
440
440
|
"System Power": "int64",
|
441
441
|
"Spare 2": "int64",
|
@@ -447,9 +447,15 @@ def flead_dict(fid, dim=2):
|
|
447
447
|
counter = 1
|
448
448
|
for key, value in fname.items():
|
449
449
|
if dim == 2:
|
450
|
-
|
450
|
+
if key == "CPU Serial No":
|
451
|
+
flead[key] = np.uint64(fid[:][counter])
|
452
|
+
else:
|
453
|
+
flead[key] = np.int64(fid[:][counter])
|
451
454
|
elif dim == 1:
|
452
|
-
|
455
|
+
if key == "CPU Serial No":
|
456
|
+
flead[key] = np.uint64(fid[counter])
|
457
|
+
else:
|
458
|
+
flead[key] = np.int64(fid[counter])
|
453
459
|
else:
|
454
460
|
print("ERROR: Higher dimensions not allowed")
|
455
461
|
sys.exit()
|
@@ -505,6 +511,7 @@ class FixedLeader:
|
|
505
511
|
)
|
506
512
|
self.warning = pyreadrdi.ErrorCode.get_message(self.error)
|
507
513
|
|
514
|
+
self.data = np.uint64(self.data)
|
508
515
|
self.fleader = flead_dict(self.data)
|
509
516
|
self._initialize_from_dict(DotDict(json_file_path="flmeta.json"))
|
510
517
|
|
@@ -878,7 +885,7 @@ class VariableLeader:
|
|
878
885
|
setattr(getattr(self, key), "data", self.data[i])
|
879
886
|
i = i + 1
|
880
887
|
|
881
|
-
def
|
888
|
+
def bitresult(self):
|
882
889
|
"""
|
883
890
|
Extracts Bit Results from Variable Leader (Byte 13 & 14)
|
884
891
|
This field is part of the WorkHorse ADCP’s Built-in Test function.
|
@@ -957,8 +964,6 @@ class VariableLeader:
|
|
957
964
|
|
958
965
|
scale_factor = scale_list.get(fixclass["Frequency"])
|
959
966
|
|
960
|
-
print(fixclass["Frequency"])
|
961
|
-
|
962
967
|
channel["Xmit Voltage"] = adc1 * (scale_factor[0] / 1000000)
|
963
968
|
|
964
969
|
channel["Xmit Current"] = adc0 * (scale_factor[1] / 1000000)
|
@@ -975,6 +980,73 @@ class VariableLeader:
|
|
975
980
|
|
976
981
|
return channel
|
977
982
|
|
983
|
+
def error_status_word(self, esw=1):
|
984
|
+
bitset1 = ("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
|
+
bitset2 = ("Pinging",
|
993
|
+
"Not Used 1",
|
994
|
+
"Not Used 2",
|
995
|
+
"Not Used 3",
|
996
|
+
"Not Used 4",
|
997
|
+
"Not Used 5",
|
998
|
+
"Cold Wakeup occured",
|
999
|
+
"Unknown Wakeup occured")
|
1000
|
+
|
1001
|
+
bitset3 = ("Clock Read error occured",
|
1002
|
+
"Unexpected alarm",
|
1003
|
+
"Clock jump forward",
|
1004
|
+
"Clock jump backward",
|
1005
|
+
"Not Used 6",
|
1006
|
+
"Not Used 7",
|
1007
|
+
"Not Used 8",
|
1008
|
+
"Not Used 9")
|
1009
|
+
|
1010
|
+
bitset4 = ("Not Used 10",
|
1011
|
+
"Not Used 11",
|
1012
|
+
"Not Used 12",
|
1013
|
+
"Power Fail Unrecorded",
|
1014
|
+
"Spurious level 4 intr DSP",
|
1015
|
+
"Spurious level 5 intr UART",
|
1016
|
+
"Spurious level 6 intr CLOCK",
|
1017
|
+
"Level 7 interrup occured")
|
1018
|
+
|
1019
|
+
|
1020
|
+
|
1021
|
+
if esw == 1:
|
1022
|
+
bitset = bitset1
|
1023
|
+
errorarray = self.vleader["Error Status Word 1"]
|
1024
|
+
elif esw == 2:
|
1025
|
+
bitset = bitset2
|
1026
|
+
errorarray = self.vleader["Error Status Word 2"]
|
1027
|
+
elif esw == 3:
|
1028
|
+
bitset = bitset3
|
1029
|
+
errorarray = self.vleader["Error Status Word 3"]
|
1030
|
+
else:
|
1031
|
+
bitset = bitset4
|
1032
|
+
errorarray = self.vleader["Error Status Word 4"]
|
1033
|
+
|
1034
|
+
errorstatus = dict()
|
1035
|
+
# bitarray = np.zeros(32, dtype='str')
|
1036
|
+
|
1037
|
+
for item in bitset:
|
1038
|
+
errorstatus[item] = np.array([])
|
1039
|
+
|
1040
|
+
for data in errorarray:
|
1041
|
+
byte_split = format(data, "08b")
|
1042
|
+
bitposition=0
|
1043
|
+
for item in bitset:
|
1044
|
+
errorstatus[item] = np.append(errorstatus[item], byte_split[bitposition] )
|
1045
|
+
bitposition+=1
|
1046
|
+
|
1047
|
+
return errorstatus
|
1048
|
+
|
1049
|
+
|
978
1050
|
|
979
1051
|
class Velocity:
|
980
1052
|
"""
|
pyadps/utils/script.py
CHANGED
@@ -1,22 +1,19 @@
|
|
1
|
-
import sys
|
2
|
-
|
3
1
|
import matplotlib.pyplot as plt
|
4
2
|
import numpy as np
|
5
3
|
import pyadps.utils.readrdi as rd
|
6
|
-
from pyadps.utils.
|
7
|
-
from pyadps.utils.plotgen import plotmask, plotvar
|
4
|
+
from pyadps.utils.plotgen import CutBins
|
8
5
|
from pyadps.utils.profile_test import side_lobe_beam_angle, trim_ends
|
9
|
-
from pyadps.utils.
|
6
|
+
from pyadps.utils.profile_test import regrid2d, regrid3d
|
10
7
|
from pyadps.utils.signal_quality import (default_mask, ev_check, false_target,
|
11
|
-
pg_check,
|
12
|
-
from pyadps.utils.velocity_test import (despike, flatline,
|
13
|
-
|
8
|
+
pg_check, correlation_check, echo_check, qc_prompt)
|
9
|
+
from pyadps.utils.velocity_test import (despike, flatline, velocity_modifier,
|
10
|
+
wmm2020api, velocity_cutoff)
|
14
11
|
|
15
12
|
plt.style.use("seaborn-v0_8-darkgrid")
|
16
13
|
|
17
14
|
|
18
15
|
# Read data
|
19
|
-
filename = "/
|
16
|
+
filename = "./src/pyadps/utils/metadata/demo.000"
|
20
17
|
ds = rd.ReadFile(filename)
|
21
18
|
fl = ds.fixedleader
|
22
19
|
vl = ds.variableleader
|
@@ -34,7 +31,7 @@ cells = fl.field()["Cells"]
|
|
34
31
|
# sys.exit()
|
35
32
|
|
36
33
|
# Original mask created from velocity
|
37
|
-
mask = default_mask(
|
34
|
+
mask = default_mask(ds)
|
38
35
|
orig_mask = np.copy(mask)
|
39
36
|
|
40
37
|
# Default threshold
|
@@ -56,22 +53,22 @@ ft = qc_prompt(fl, "False Target Thresh")
|
|
56
53
|
# Apply threshold
|
57
54
|
values, counts = np.unique(mask, return_counts=True)
|
58
55
|
print(values, counts, np.round(counts * 100 / np.sum(counts)))
|
59
|
-
mask = pg_check(
|
60
|
-
mask =
|
61
|
-
mask =
|
62
|
-
mask = ev_check(
|
63
|
-
mask = false_target(
|
56
|
+
mask = pg_check(ds, mask, pgt)
|
57
|
+
mask = correlation_check(ds, mask, ct)
|
58
|
+
mask = echo_check(ds, mask, et)
|
59
|
+
mask = ev_check(ds, mask, evt)
|
60
|
+
mask = false_target(ds, mask, ft, threebeam=True)
|
64
61
|
|
65
62
|
|
66
63
|
########## PROFILE TEST #########
|
67
64
|
|
68
65
|
affirm = input("Would you like to trim the ends? [y/n]: ")
|
69
66
|
if affirm.lower() == "y":
|
70
|
-
mask = trim_ends(
|
67
|
+
mask = trim_ends(ds, mask)
|
71
68
|
|
72
69
|
affirm = input("Would you remove the surface backscatter bins? [y/n]: ")
|
73
70
|
if affirm.lower() == "y":
|
74
|
-
mask = side_lobe_beam_angle(
|
71
|
+
mask = side_lobe_beam_angle(ds, mask)
|
75
72
|
|
76
73
|
affirm = input("Would you like to manually select and mask data?")
|
77
74
|
if affirm.lower() == "y":
|
@@ -81,11 +78,11 @@ if affirm.lower() == "y":
|
|
81
78
|
|
82
79
|
affirm = input("Regrid the data based on pressure sensor? [y/n]:")
|
83
80
|
if affirm.lower() == "y":
|
84
|
-
z, vel = regrid3d(
|
85
|
-
z, echo_reg = regrid3d(
|
86
|
-
z, correlation_reg = regrid3d(
|
87
|
-
z, percentgood_reg = regrid3d(
|
88
|
-
z, mask = regrid2d(
|
81
|
+
z, vel = regrid3d(ds, vel, -32768)
|
82
|
+
z, echo_reg = regrid3d(ds, echo, -32768)
|
83
|
+
z, correlation_reg = regrid3d(ds, cor, -32768)
|
84
|
+
z, percentgood_reg = regrid3d(ds, pgood, -32768)
|
85
|
+
z, mask = regrid2d(ds, mask, -32768)
|
89
86
|
|
90
87
|
# affirm = input("Display original and revised mask files? [y/n]:")
|
91
88
|
# if affirm.lower() == "y":
|
@@ -107,7 +104,8 @@ if affirm.lower() == "y":
|
|
107
104
|
year = input("Year: ")
|
108
105
|
year = int(year)
|
109
106
|
|
110
|
-
|
107
|
+
mag = wmm2020api(lat, lon, year)
|
108
|
+
vel = velocity_modifier(vel, mag)
|
111
109
|
|
112
110
|
affirm = input("Apply velocity thresholds [y/n]: ")
|
113
111
|
if affirm.lower() == "y":
|
@@ -0,0 +1,120 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
|
4
|
+
def sound_speed_correction(
|
5
|
+
velocity: np.ndarray,
|
6
|
+
sound_speed: np.ndarray,
|
7
|
+
temperature: np.ndarray,
|
8
|
+
salinity: np.ndarray,
|
9
|
+
depth: np.ndarray,
|
10
|
+
horizontal: bool = True,
|
11
|
+
) -> np.ndarray:
|
12
|
+
"""
|
13
|
+
Corrects velocity measurements for variations in sound speed.
|
14
|
+
|
15
|
+
The function calculates the corrected sound speed based on temperature,
|
16
|
+
salinity, and depth using empirical equations. It then adjusts the velocity
|
17
|
+
measurements for the u and v components using the ratio of the original
|
18
|
+
and corrected sound speeds, while leaving the w component unchanged by default.
|
19
|
+
|
20
|
+
Parameter:
|
21
|
+
----------
|
22
|
+
velocity (numpy.ndarray): 4D array of velocity measurements in m/s with
|
23
|
+
components (u, v, w, error) along depth and time axes. Missing values
|
24
|
+
should be represented by -32768.
|
25
|
+
sound_speed (numpy.ndarray): 1D array of measured sound speed in m/s as a function of time.
|
26
|
+
temperature (numpy.ndarray): 1D array of temperature in degrees Celsius as a function of time.
|
27
|
+
salinity (numpy.ndarray): 1D array of salinity in PSU (Practical Salinity Units) as a function of time.
|
28
|
+
depth (numpy.ndarray): 1D array of transducer depth in meters as a function of time.
|
29
|
+
horizontal (bool): By default only horizontal velocities are corrected.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
--------
|
33
|
+
numpy.ndarray: 3D array of corrected velocity measurements as 32-bit integers.
|
34
|
+
The w component remains unchanged. Missing values (-32768) remain unchanged.
|
35
|
+
|
36
|
+
Notes:
|
37
|
+
------
|
38
|
+
The sound speed correction formula is derived empirically using the
|
39
|
+
equation (Urick, 1983).
|
40
|
+
"""
|
41
|
+
# Calculate corrected sound speed
|
42
|
+
sound_speed_corrected = (
|
43
|
+
1449.2
|
44
|
+
+ 4.6 * temperature
|
45
|
+
- 0.055 * temperature**2
|
46
|
+
+ 0.00029 * temperature**3
|
47
|
+
+ (1.34 - 0.01 * temperature) * (salinity - 35)
|
48
|
+
+ 0.016 * depth
|
49
|
+
)
|
50
|
+
|
51
|
+
sound_speed = sound_speed
|
52
|
+
sound_speed_corrected = sound_speed_corrected
|
53
|
+
# Separate u, v, and w components
|
54
|
+
u = velocity[0, :, :]
|
55
|
+
v = velocity[1, :, :]
|
56
|
+
w = velocity[2, :, :]
|
57
|
+
e = velocity[3, :, :]
|
58
|
+
|
59
|
+
# Correct u and v components
|
60
|
+
u_corrected = np.where(
|
61
|
+
u == -32768,
|
62
|
+
-32768.0,
|
63
|
+
u * sound_speed[np.newaxis, :] / sound_speed_corrected[np.newaxis, :],
|
64
|
+
)
|
65
|
+
|
66
|
+
v_corrected = np.where(
|
67
|
+
v == -32768,
|
68
|
+
-32768.0,
|
69
|
+
v * sound_speed[np.newaxis, :] / sound_speed_corrected[np.newaxis, :],
|
70
|
+
)
|
71
|
+
|
72
|
+
if not horizontal:
|
73
|
+
w_corrected = np.where(
|
74
|
+
w == -32768,
|
75
|
+
-32768.0,
|
76
|
+
w * sound_speed[np.newaxis, :] / sound_speed_corrected[np.newaxis, :],
|
77
|
+
)
|
78
|
+
else:
|
79
|
+
w_corrected = w
|
80
|
+
|
81
|
+
# Combine corrected components back into a 4D array
|
82
|
+
velocity_corrected = np.stack(
|
83
|
+
[
|
84
|
+
u_corrected.astype(np.int32),
|
85
|
+
v_corrected.astype(np.int32),
|
86
|
+
w_corrected.astype(np.int32),
|
87
|
+
e.astype(np.int32),
|
88
|
+
],
|
89
|
+
axis=0,
|
90
|
+
)
|
91
|
+
|
92
|
+
return velocity_corrected
|
93
|
+
|
94
|
+
|
95
|
+
def tilt_sensor_check(
|
96
|
+
tilt: np.ndarray, mask: np.ndarray, cutoff: int = 15
|
97
|
+
) -> np.ndarray:
|
98
|
+
"""
|
99
|
+
Updates the given 2D mask array based on the tilt sensor readings. If the tilt value in
|
100
|
+
the 1D tilt array exceeds the specified cutoff, the corresponding values in the mask are
|
101
|
+
set to 1.
|
102
|
+
|
103
|
+
Parameters
|
104
|
+
----------
|
105
|
+
tilt : np.ndarray
|
106
|
+
A 1D array of tilt sensor readings.
|
107
|
+
mask : np.ndarray
|
108
|
+
A 2D array where the tilt values are checked against the cutoff.
|
109
|
+
cutoff : int, optional
|
110
|
+
The tilt value threshold. Default is 15. If a tilt value exceeds this threshold,
|
111
|
+
the corresponding mask value is updated to 1.
|
112
|
+
|
113
|
+
Returns
|
114
|
+
-------
|
115
|
+
np.ndarray
|
116
|
+
A 2D array with updated mask values where tilt exceeds the cutoff.
|
117
|
+
"""
|
118
|
+
tilt = tilt * 0.01
|
119
|
+
updated_mask = np.where(tilt[:, np.newaxis] > cutoff, 1, mask.T)
|
120
|
+
return updated_mask.T
|