pyadps 0.2.1b0__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyadps/Home_Page.py +11 -5
- pyadps/pages/01_Read_File.py +623 -215
- pyadps/pages/02_View_Raw_Data.py +97 -41
- pyadps/pages/03_Download_Raw_File.py +200 -67
- pyadps/pages/04_Sensor_Health.py +905 -0
- pyadps/pages/05_QC_Test.py +493 -0
- pyadps/pages/06_Profile_Test.py +971 -0
- pyadps/pages/07_Velocity_Test.py +600 -0
- pyadps/pages/08_Write_File.py +623 -0
- pyadps/pages/09_Add-Ons.py +168 -0
- pyadps/utils/__init__.py +5 -3
- pyadps/utils/autoprocess.py +371 -80
- pyadps/utils/logging_utils.py +269 -0
- pyadps/utils/metadata/config.ini +22 -4
- pyadps/utils/metadata/demo.000 +0 -0
- pyadps/utils/metadata/flmeta.json +420 -420
- pyadps/utils/metadata/vlmeta.json +611 -565
- pyadps/utils/multifile.py +292 -0
- pyadps/utils/plotgen.py +505 -3
- pyadps/utils/profile_test.py +720 -125
- pyadps/utils/pyreadrdi.py +164 -92
- pyadps/utils/readrdi.py +436 -186
- pyadps/utils/script.py +197 -147
- pyadps/utils/sensor_health.py +120 -0
- pyadps/utils/signal_quality.py +472 -68
- pyadps/utils/velocity_test.py +79 -31
- pyadps/utils/writenc.py +222 -39
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/METADATA +13 -14
- pyadps-0.3.0.dist-info/RECORD +35 -0
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/WHEEL +1 -1
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/entry_points.txt +1 -0
- 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/pages/07_Write_File.py +0 -452
- pyadps/utils/cutbin.py +0 -413
- pyadps/utils/regrid.py +0 -279
- pyadps-0.2.1b0.dist-info/RECORD +0 -31
- {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/LICENSE +0 -0
pyadps/utils/script.py
CHANGED
@@ -1,155 +1,205 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
import os
|
3
2
|
import matplotlib.pyplot as plt
|
4
3
|
import numpy as np
|
5
4
|
import pyadps.utils.readrdi as rd
|
6
|
-
from pyadps.utils.
|
7
|
-
from pyadps.utils.plotgen import plotmask, plotvar
|
5
|
+
from pyadps.utils.plotgen import CutBins
|
8
6
|
from pyadps.utils.profile_test import side_lobe_beam_angle, trim_ends
|
9
|
-
from pyadps.utils.
|
10
|
-
from pyadps.utils.signal_quality import (
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
from pyadps.utils.profile_test import regrid2d, regrid3d
|
8
|
+
from pyadps.utils.signal_quality import (
|
9
|
+
default_mask,
|
10
|
+
ev_check,
|
11
|
+
false_target,
|
12
|
+
pg_check,
|
13
|
+
correlation_check,
|
14
|
+
echo_check,
|
15
|
+
qc_prompt,
|
16
|
+
)
|
17
|
+
from pyadps.utils.velocity_test import (
|
18
|
+
despike,
|
19
|
+
flatline,
|
20
|
+
velocity_modifier,
|
21
|
+
wmm2020api,
|
22
|
+
velocity_cutoff,
|
23
|
+
)
|
24
|
+
|
25
|
+
import pyadps.utils.writenc as wr
|
14
26
|
|
15
27
|
plt.style.use("seaborn-v0_8-darkgrid")
|
16
28
|
|
17
29
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
affirm
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
30
|
+
def main():
|
31
|
+
# Get the config file
|
32
|
+
try:
|
33
|
+
filepath = input("Enter file name: ")
|
34
|
+
if os.path.exists(filepath):
|
35
|
+
run_script(filepath)
|
36
|
+
else:
|
37
|
+
print("File not found!")
|
38
|
+
except Exception as e:
|
39
|
+
import traceback
|
40
|
+
|
41
|
+
print("Error: Unable to process the data.")
|
42
|
+
traceback.print_exc()
|
43
|
+
|
44
|
+
|
45
|
+
# filename = "./metadata/demo.000"
|
46
|
+
|
47
|
+
|
48
|
+
def run_script(filename):
|
49
|
+
ds = rd.ReadFile(filename)
|
50
|
+
fl = ds.fixedleader
|
51
|
+
vl = ds.variableleader
|
52
|
+
vel = ds.velocity.data
|
53
|
+
echo = ds.echo.data
|
54
|
+
cor = ds.correlation.data
|
55
|
+
pgood = ds.percentgood.data
|
56
|
+
time = ds.time
|
57
|
+
beamdir = ds.fixedleader.system_configuration()["Beam Direction"]
|
58
|
+
|
59
|
+
# Data pressure = vl.vleader["Pressure"]
|
60
|
+
# beam_angle = int(fl.system_configuration()["Beam Angle"])
|
61
|
+
# blank_size = fl.field()["Blank Transmit"]
|
62
|
+
cell_size = fl.field()["Depth Cell Len"] / 100
|
63
|
+
cells = fl.field()["Cells"]
|
64
|
+
bin1dist = fl.field()["Bin 1 Dist"] / 100
|
65
|
+
|
66
|
+
mean_depth = np.mean(ds.variableleader.depth_of_transducer.data) / 10
|
67
|
+
mean_depth = np.trunc(mean_depth)
|
68
|
+
if beamdir.lower() == "up":
|
69
|
+
sgn = -1
|
70
|
+
else:
|
71
|
+
sgn = 1
|
72
|
+
|
73
|
+
first_depth = mean_depth + sgn * bin1dist
|
74
|
+
last_depth = first_depth + sgn * cells * cell_size
|
75
|
+
z = np.arange(first_depth, last_depth, sgn * cell_size)
|
76
|
+
|
77
|
+
# Original mask created from velocity
|
78
|
+
mask = default_mask(ds)
|
79
|
+
orig_mask = np.copy(mask)
|
80
|
+
|
81
|
+
# Default threshold
|
82
|
+
ct = fl.field()["Correlation Thresh"]
|
83
|
+
et = 0
|
84
|
+
pgt = fl.field()["Percent Good Min"]
|
85
|
+
evt = fl.field()["Error Velocity Thresh"]
|
86
|
+
ft = fl.field()["False Target Thresh"]
|
87
|
+
|
88
|
+
# Get the threshold values
|
89
|
+
ct = qc_prompt(ds, "Correlation Thresh")
|
90
|
+
evt = qc_prompt(ds, "Error Velocity Thresh")
|
91
|
+
pgt = qc_prompt(ds, "Percent Good Min")
|
92
|
+
et = qc_prompt(ds, "Echo Intensity Thresh", echo)
|
93
|
+
ft = qc_prompt(ds, "False Target Thresh")
|
94
|
+
|
95
|
+
# Apply threshold
|
96
|
+
# values, counts = np.unique(mask, return_counts=True)
|
97
|
+
# print(values, counts, np.round(counts * 100 / np.sum(counts)))
|
98
|
+
print("Applying Thresholds")
|
99
|
+
mask = pg_check(ds, mask, pgt)
|
100
|
+
mask = correlation_check(ds, mask, ct)
|
101
|
+
mask = echo_check(ds, mask, et)
|
102
|
+
mask = ev_check(ds, mask, evt)
|
103
|
+
mask = false_target(ds, mask, ft, threebeam=True)
|
104
|
+
|
105
|
+
########## PROFILE TEST #########
|
106
|
+
|
107
|
+
affirm = input("Would you like to trim the ends? [y/n]: ")
|
108
|
+
if affirm.lower() == "y":
|
109
|
+
mask = trim_ends(ds, mask)
|
110
|
+
|
111
|
+
affirm = input("Would you remove the surface backscatter bins? [y/n]: ")
|
112
|
+
if affirm.lower() == "y":
|
113
|
+
mask = side_lobe_beam_angle(ds, mask)
|
114
|
+
|
115
|
+
affirm = input("Would you like to manually select and mask data? [y/n]: ")
|
116
|
+
if affirm.lower() == "y":
|
117
|
+
manual = CutBins(echo[0, :, :], mask)
|
118
|
+
plt.show()
|
119
|
+
mask = manual.mask()
|
120
|
+
|
121
|
+
affirm = input("Regrid the data based on pressure sensor? [y/n]:")
|
122
|
+
if affirm.lower() == "y":
|
123
|
+
z, vel = regrid3d(ds, vel, -32768)
|
124
|
+
z, echo_reg = regrid3d(ds, echo, -32768)
|
125
|
+
z, correlation_reg = regrid3d(ds, cor, -32768)
|
126
|
+
z, percentgood_reg = regrid3d(ds, pgood, -32768)
|
127
|
+
z, mask = regrid2d(ds, mask, -32768)
|
128
|
+
|
129
|
+
########## VELOCITY TEST ##########
|
130
|
+
affirm = input("Apply correction for magnetic declination? [y/n]:")
|
131
|
+
if affirm.lower() == "y":
|
132
|
+
lat = input("Enter Latitude: ")
|
133
|
+
lat = float(lat)
|
134
|
+
|
135
|
+
lon = input("Enter Longitude: ")
|
136
|
+
lon = float(lon)
|
137
|
+
|
138
|
+
depth = input("Enter Depth (m): ")
|
139
|
+
depth = float(depth)
|
140
|
+
|
141
|
+
year = input("Year: ")
|
142
|
+
year = int(year)
|
143
|
+
|
144
|
+
mag = wmm2020api(lat, lon, year)
|
145
|
+
vel = velocity_modifier(vel, mag)
|
146
|
+
|
147
|
+
affirm = input("Apply velocity thresholds [y/n]: ")
|
148
|
+
if affirm.lower() == "y":
|
149
|
+
maxuvel = input("Enter maximum zonal velocity: ")
|
150
|
+
maxuvel = float(maxuvel)
|
151
|
+
|
152
|
+
maxvvel = input("Enter maximum meridional velocity: ")
|
153
|
+
maxvvel = float(maxvvel)
|
154
|
+
|
155
|
+
maxwvel = input("Enter maximum vertical velocity: ")
|
156
|
+
maxwvel = float(maxwvel)
|
157
|
+
mask = velocity_cutoff(vel[0, :, :], mask, cutoff=maxuvel)
|
158
|
+
mask = velocity_cutoff(vel[1, :, :], mask, cutoff=maxvvel)
|
159
|
+
mask = velocity_cutoff(vel[2, :, :], mask, cutoff=maxwvel)
|
160
|
+
|
161
|
+
affirm = input("Despike the data? [y/n]: ")
|
162
|
+
if affirm.lower() == "y":
|
163
|
+
despike_kernel = input("Enter despike kernel size:")
|
164
|
+
despike_kernel = int(despike_kernel)
|
165
|
+
|
166
|
+
despike_cutoff = input("Enter despike cutoff (mm/s): ")
|
167
|
+
despike_cutoff = float(despike_cutoff)
|
168
|
+
|
169
|
+
mask = despike(
|
170
|
+
vel[0, :, :], mask, kernel_size=despike_kernel, cutoff=despike_cutoff
|
171
|
+
)
|
172
|
+
mask = despike(
|
173
|
+
vel[1, :, :], mask, kernel_size=despike_kernel, cutoff=despike_cutoff
|
174
|
+
)
|
175
|
+
|
176
|
+
affirm = input("Remove flatlines? [y/n]: ")
|
177
|
+
if affirm.lower() == "y":
|
178
|
+
flatline_kernel = input("Enter despike kernel size:")
|
179
|
+
flatline_kernel = int(flatline_kernel)
|
180
|
+
flatline_cutoff = input("Enter Flatline deviation: [y/n]")
|
181
|
+
flatlineL_cutoff = int(flatline_cutoff)
|
182
|
+
mask = flatline(
|
183
|
+
vel[0, :, :], mask, kernel_size=flatline_kernel, cutoff=flatline_cutoff
|
184
|
+
)
|
185
|
+
mask = flatline(
|
186
|
+
vel[1, :, :], mask, kernel_size=flatline_kernel, cutoff=flatline_cutoff
|
187
|
+
)
|
188
|
+
mask = flatline(
|
189
|
+
vel[2, :, :], mask, kernel_size=flatline_kernel, cutoff=flatline_cutoff
|
190
|
+
)
|
191
|
+
apply_mask = input("Apply mask? [y/n]: ")
|
192
|
+
if apply_mask.lower() == "y":
|
193
|
+
for i in range(4):
|
194
|
+
vel[i, :, :] = np.where(mask == 0, vel[i, :, :], -32768)
|
195
|
+
|
196
|
+
outfilepath = input("Enter output file name (*nc): ")
|
197
|
+
if os.path.exists(outfilepath):
|
198
|
+
if os.path.isfile(outfilepath):
|
199
|
+
print(f"The file already exists: {outfilepath}")
|
200
|
+
else:
|
201
|
+
wr.finalnc(outfilepath, z, mask, time, vel)
|
202
|
+
|
203
|
+
|
204
|
+
if __name__ == "__main__":
|
205
|
+
main()
|
@@ -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
|