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/Home_Page.py
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
import streamlit as st
|
2
|
+
|
3
|
+
|
4
|
+
def main():
|
5
|
+
st.set_page_config(
|
6
|
+
page_title="ADCP Data Processing Software",
|
7
|
+
page_icon=":world_map:️",
|
8
|
+
layout="wide",
|
9
|
+
initial_sidebar_state="auto",
|
10
|
+
menu_items={
|
11
|
+
"Get Help": "http://github.com/p-amol/adps",
|
12
|
+
"Report a bug": "http://github.com/adps/issues",
|
13
|
+
"About": "# Python ADCP Data Processing Software (PyADPS)",
|
14
|
+
},
|
15
|
+
)
|
16
|
+
|
17
|
+
"""
|
18
|
+
# **Python ADCP Data Processing Software (pyadps)**
|
19
|
+
`pyadps` is a software for processing Teledyne RDI Acoustic Doppler Current Profiler (ADCP) PD0 files. Currently the software can process the data from Workhorse ADCPs.
|
20
|
+
|
21
|
+
## Features
|
22
|
+
|
23
|
+
* Access RDI ADCP binary files using Python 3
|
24
|
+
* Convert RDI binary files to netcdf
|
25
|
+
* Process ADCP data
|
26
|
+
|
27
|
+
## Contribute
|
28
|
+
Issue Tracker: http://github.com/adps/issues
|
29
|
+
Source Code: http://github.com/p-amol/adps
|
30
|
+
|
31
|
+
## Support
|
32
|
+
If you are having issues, please let us know.
|
33
|
+
We have a mailing list located at: adps-python@google-groups.com
|
34
|
+
|
35
|
+
## License
|
36
|
+
The project is licensed under the MIT license.
|
37
|
+
|
38
|
+
"""
|
39
|
+
|
40
|
+
|
41
|
+
if __name__ == "__main__":
|
42
|
+
main()
|
pyadps/__init__.py
ADDED
pyadps/__main__.py
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# pyadps/__main__.py
|
2
|
+
|
3
|
+
import subprocess
|
4
|
+
import os
|
5
|
+
|
6
|
+
def main():
|
7
|
+
# Get the absolute path to Home_Page.py
|
8
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
9
|
+
home_page_path = os.path.join(current_dir, 'Home_Page.py')
|
10
|
+
|
11
|
+
# Run the Streamlit app
|
12
|
+
subprocess.run(["streamlit", "run", home_page_path])
|
13
|
+
|
14
|
+
if __name__ == "__main__":
|
15
|
+
main()
|
@@ -0,0 +1,458 @@
|
|
1
|
+
import os
|
2
|
+
import tempfile
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
import pandas as pd
|
6
|
+
import streamlit as st
|
7
|
+
import utils.readrdi as rd
|
8
|
+
from utils.signal_quality import default_mask
|
9
|
+
from utils.readrdi import ReadFile
|
10
|
+
|
11
|
+
# To make the page wider if the user presses the reload button.
|
12
|
+
st.set_page_config(layout="wide")
|
13
|
+
|
14
|
+
"""
|
15
|
+
Streamlit page to load ADCP binary file and display File Header
|
16
|
+
and Fixed Leader data
|
17
|
+
"""
|
18
|
+
|
19
|
+
if "fname" not in st.session_state:
|
20
|
+
st.session_state.fname = "No file selected"
|
21
|
+
|
22
|
+
if "rawfilename" not in st.session_state:
|
23
|
+
st.session_state.rawfilename = "rawfile.nc"
|
24
|
+
|
25
|
+
if "vleadfilename" not in st.session_state:
|
26
|
+
st.session_state.vleadfilename = "vlead.nc"
|
27
|
+
|
28
|
+
|
29
|
+
################ Functions #######################
|
30
|
+
@st.cache_data()
|
31
|
+
def file_access(uploaded_file):
|
32
|
+
"""
|
33
|
+
Function creates temporary directory to store the uploaded file.
|
34
|
+
The path of the file is returned
|
35
|
+
|
36
|
+
Args:
|
37
|
+
uploaded_file (string): Name of the uploaded file
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
path (string): Path of the uploaded file
|
41
|
+
"""
|
42
|
+
temp_dir = tempfile.mkdtemp()
|
43
|
+
path = os.path.join(temp_dir, uploaded_file.name)
|
44
|
+
with open(path, "wb") as f:
|
45
|
+
f.write(uploaded_file.getvalue())
|
46
|
+
return path
|
47
|
+
|
48
|
+
|
49
|
+
def color_bool(val):
|
50
|
+
"""
|
51
|
+
Takes a scalar and returns a string with
|
52
|
+
the css color property.
|
53
|
+
"""
|
54
|
+
if isinstance(val, bool):
|
55
|
+
if val:
|
56
|
+
color = "green"
|
57
|
+
else:
|
58
|
+
color = "red"
|
59
|
+
else:
|
60
|
+
color = "orange"
|
61
|
+
return "color: %s" % color
|
62
|
+
|
63
|
+
|
64
|
+
def color_bool2(val):
|
65
|
+
"""
|
66
|
+
Takes a scalar and returns a string with
|
67
|
+
the css color property. The following colors
|
68
|
+
are assinged for the string
|
69
|
+
"True": green,
|
70
|
+
"False": red
|
71
|
+
Any other string: orange
|
72
|
+
|
73
|
+
Args:
|
74
|
+
val (string): Any string data
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
The input string with css color property added
|
78
|
+
"""
|
79
|
+
if val == "True" or val == "Data type is healthy":
|
80
|
+
color = "green"
|
81
|
+
elif val == "False":
|
82
|
+
color = "red"
|
83
|
+
# elif val in st.session_state.ds.warnings.values():
|
84
|
+
# color = "orange"
|
85
|
+
else:
|
86
|
+
color = "orange"
|
87
|
+
return "color: %s" % color
|
88
|
+
|
89
|
+
|
90
|
+
@st.cache_data
|
91
|
+
def read_file(filepath):
|
92
|
+
ds = rd.ReadFile(st.session_state.fpath)
|
93
|
+
if not ds.isEnsembleEqual:
|
94
|
+
ds.fixensemble()
|
95
|
+
st.session_state.ds = ds
|
96
|
+
# return ds
|
97
|
+
|
98
|
+
|
99
|
+
uploaded_file = st.file_uploader("Upload RDI ADCP Binary File", type="000")
|
100
|
+
|
101
|
+
if uploaded_file is not None:
|
102
|
+
# st.cache_data.clear
|
103
|
+
|
104
|
+
# Get path
|
105
|
+
st.session_state.fpath = file_access(uploaded_file)
|
106
|
+
# Get data
|
107
|
+
read_file(st.session_state.fpath)
|
108
|
+
ds = st.session_state.ds
|
109
|
+
head = ds.fileheader
|
110
|
+
flead = ds.fixedleader
|
111
|
+
vlead = ds.variableleader
|
112
|
+
velocity = ds.velocity.data
|
113
|
+
correlation = ds.correlation.data
|
114
|
+
echo = ds.echo.data
|
115
|
+
pgood = ds.percentgood.data
|
116
|
+
beamdir = ds.fixedleader.system_configuration()["Beam Direction"]
|
117
|
+
|
118
|
+
st.session_state.fname = uploaded_file.name
|
119
|
+
st.session_state.head = ds.fileheader
|
120
|
+
st.session_state.flead = ds.fixedleader
|
121
|
+
st.session_state.vlead = ds.variableleader
|
122
|
+
st.session_state.velocity = ds.velocity.data
|
123
|
+
st.session_state.echo = ds.echo.data
|
124
|
+
st.session_state.correlation = ds.correlation.data
|
125
|
+
st.session_state.pgood = ds.percentgood.data
|
126
|
+
st.session_state.beam_direction = beamdir
|
127
|
+
st.session_state.sound_speed = ds.variableleader.speed_of_sound.data
|
128
|
+
st.session_state.depth = ds.variableleader.depth_of_transducer.data
|
129
|
+
st.session_state.temperature = (
|
130
|
+
ds.variableleader.temperature.data * ds.variableleader.temperature.scale
|
131
|
+
)
|
132
|
+
st.session_state.salinity = (
|
133
|
+
ds.variableleader.salinity.data * ds.variableleader.salinity.scale
|
134
|
+
)
|
135
|
+
|
136
|
+
# st.session_state.flead = flead
|
137
|
+
# st.session_state.vlead = vlead
|
138
|
+
# st.session_state.head = head
|
139
|
+
# st.session_state.velocity = velocity
|
140
|
+
# st.session_state.echo = echo
|
141
|
+
# st.session_state.correlation = correlation
|
142
|
+
# st.session_state.pgood = pgood
|
143
|
+
st.write("You selected `%s`" % st.session_state.fname)
|
144
|
+
|
145
|
+
elif "flead" in st.session_state:
|
146
|
+
st.write("You selected `%s`" % st.session_state.fname)
|
147
|
+
else:
|
148
|
+
# reset the cache and resources if the user press reload button.
|
149
|
+
st.cache_data.clear()
|
150
|
+
st.cache_resource.clear()
|
151
|
+
st.stop()
|
152
|
+
|
153
|
+
########## TIME AXIS ##############
|
154
|
+
|
155
|
+
# Time axis is extracted and stored as Pandas datetime
|
156
|
+
year = st.session_state.vlead.vleader["RTC Year"]
|
157
|
+
month = st.session_state.vlead.vleader["RTC Month"]
|
158
|
+
day = st.session_state.vlead.vleader["RTC Day"]
|
159
|
+
hour = st.session_state.vlead.vleader["RTC Hour"]
|
160
|
+
minute = st.session_state.vlead.vleader["RTC Minute"]
|
161
|
+
second = st.session_state.vlead.vleader["RTC Second"]
|
162
|
+
|
163
|
+
# Recent ADCP binary files have Y2K compliant clock. The Century
|
164
|
+
# is stored in`RTC Century`. As all files may not have this clock
|
165
|
+
# we have added 2000 to the year.
|
166
|
+
# CHECKS:
|
167
|
+
# Are all our data Y2K compliant?
|
168
|
+
# Should we give users the options to correct the data?
|
169
|
+
|
170
|
+
year = year + 2000
|
171
|
+
date_df = pd.DataFrame(
|
172
|
+
{
|
173
|
+
"year": year,
|
174
|
+
"month": month,
|
175
|
+
"day": day,
|
176
|
+
"hour": hour,
|
177
|
+
"minute": minute,
|
178
|
+
"second": second,
|
179
|
+
}
|
180
|
+
)
|
181
|
+
|
182
|
+
st.session_state.date = pd.to_datetime(date_df)
|
183
|
+
st.session_state.date1 = pd.to_datetime(date_df)
|
184
|
+
st.session_state.date2 = pd.to_datetime(date_df)
|
185
|
+
st.session_state.ensemble_axis = np.arange(0, st.session_state.head.ensembles, 1)
|
186
|
+
st.session_state.axis_option = "time"
|
187
|
+
|
188
|
+
|
189
|
+
# ---------- Initialize all options -------------
|
190
|
+
# ------------------------
|
191
|
+
# Page: Download Raw File
|
192
|
+
# ------------------------
|
193
|
+
# Widgets
|
194
|
+
st.session_state.add_attributes_DRW = "No"
|
195
|
+
st.session_state.axis_option_DRW = "time"
|
196
|
+
st.session_state.rawnc_download_DRW = False
|
197
|
+
st.session_state.vleadnc_download_DRW = False
|
198
|
+
st.session_state.rawcsv_option_DRW = "Velocity"
|
199
|
+
st.session_state.rawcsv_beam_DRW = 1
|
200
|
+
st.session_state.rawcsv_download_DRW = False
|
201
|
+
|
202
|
+
# ------------------
|
203
|
+
# Page: Sensor Test
|
204
|
+
# ------------------
|
205
|
+
st.session_state.isSensorTest = False
|
206
|
+
st.session_state.isFirstSensorVisit = True
|
207
|
+
|
208
|
+
# -- Tab 1: Depth Correction
|
209
|
+
st.session_state.isDepthModified_ST = False
|
210
|
+
# Widgets
|
211
|
+
# Options: "Fixed Value", "File Upload"
|
212
|
+
st.session_state.depthoption_ST = "Fixed Value"
|
213
|
+
st.session_state.isFixedDepth_ST = False
|
214
|
+
st.session_state.fixeddepth_ST = 0
|
215
|
+
st.session_state.isUploadDepth_ST = False
|
216
|
+
|
217
|
+
# -- Tab 2: Salinity Correction
|
218
|
+
st.session_state.isSalinityModified_ST = False
|
219
|
+
# Widgets
|
220
|
+
st.session_state.salinityoption_ST = "Fixed Value"
|
221
|
+
st.session_state.isFixedSalinity_ST = False
|
222
|
+
st.session_state.fixedsalinity_ST = 35
|
223
|
+
st.session_state.isUploadSalinity_ST = False
|
224
|
+
|
225
|
+
# -- Tab 3: Temperature Correction
|
226
|
+
st.session_state.isTemperatureModified_ST = False
|
227
|
+
# Widgets
|
228
|
+
st.session_state.temperatureoption_ST = "Fixed Value"
|
229
|
+
st.session_state.isFixedTemperature_ST = False
|
230
|
+
st.session_state.fixedtemperature_ST = 0
|
231
|
+
st.session_state.isUploadTemperature_ST = False
|
232
|
+
|
233
|
+
# -- Tab 7: Pitch, Roll, Velocity Correction
|
234
|
+
st.session_state.isRollCheck_ST = False
|
235
|
+
st.session_state.isPitchCheck_ST = False
|
236
|
+
st.session_state.isVelocityModifiedSound_ST = False
|
237
|
+
# Widgets
|
238
|
+
st.session_state.roll_cutoff_ST = 359
|
239
|
+
st.session_state.pitch_cutoff_ST = 359
|
240
|
+
|
241
|
+
# ------------------
|
242
|
+
# Page: QC Test
|
243
|
+
# ------------------
|
244
|
+
# Global Test
|
245
|
+
st.session_state.isQCTest = False
|
246
|
+
st.session_state.isFirstQCVisit = True
|
247
|
+
|
248
|
+
# Tab 2: Apply QC
|
249
|
+
st.session_state.isQCCheck_QCT = False
|
250
|
+
# Widgets
|
251
|
+
st.session_state.ct_QCT = 64
|
252
|
+
st.session_state.et_QCT = 0
|
253
|
+
st.session_state.evt_QCT = 2000
|
254
|
+
st.session_state.ft_QCT = 50
|
255
|
+
st.session_state.is3beam_QCT = True
|
256
|
+
st.session_state.pgt_QCT = 0
|
257
|
+
|
258
|
+
# Data Modifications
|
259
|
+
st.session_state.isBeamModified_QCT = False
|
260
|
+
# Widgets
|
261
|
+
st.session_state.beam_direction_QCT = st.session_state.beam_direction
|
262
|
+
|
263
|
+
# ------------------
|
264
|
+
# Page: Profile Test
|
265
|
+
# ------------------
|
266
|
+
st.session_state.isProfileTest = False
|
267
|
+
st.session_state.isFirstProfileVisit = True
|
268
|
+
|
269
|
+
# Tab1: Trim Ends
|
270
|
+
st.session_state.isTrimEndsCheck_PT = False
|
271
|
+
# Widgets
|
272
|
+
st.session_state.start_ens_PT = 0
|
273
|
+
st.session_state.end_ens_PT = st.session_state.head.ensembles
|
274
|
+
|
275
|
+
# Tab2: Cutbins - Sidelobe
|
276
|
+
st.session_state.isCutBinSideLobeCheck_PT = False
|
277
|
+
st.session_state.extra_cells_PT = 0
|
278
|
+
st.session_state.water_depth_PT = 0
|
279
|
+
|
280
|
+
# Tab3: Cutbins - Manual
|
281
|
+
st.session_state.isCutBinManualCheck_PT = False
|
282
|
+
|
283
|
+
# Tab4: Regrid
|
284
|
+
st.session_state.isRegridCheck_PT = False
|
285
|
+
st.session_state.end_cell_option_PT = "Cell"
|
286
|
+
st.session_state.interpolate_PT = "nearest"
|
287
|
+
st.session_state.manualdepth_PT = 0
|
288
|
+
|
289
|
+
# ------------------
|
290
|
+
# Page: Velocity Test
|
291
|
+
# ------------------
|
292
|
+
# Global Test
|
293
|
+
st.session_state.isVelocityTest = False
|
294
|
+
# Check if visiting the page first time
|
295
|
+
st.session_state.isFirstVelocityVisit = True
|
296
|
+
# Local Tests:
|
297
|
+
# Tab1: Magnetic Declination
|
298
|
+
st.session_state.isMagnetCheck_VT = False
|
299
|
+
# OPTIONS: pygeomag, API, Manual
|
300
|
+
st.session_state.magnet_method_VT = "pygeomag"
|
301
|
+
st.session_state.magnet_lat_VT = 0
|
302
|
+
st.session_state.magnet_lon_VT = 0
|
303
|
+
st.session_state.magnet_year_VT = 2025
|
304
|
+
st.session_state.magnet_depth_VT = 0
|
305
|
+
st.session_state.magnet_user_input_VT = 0
|
306
|
+
|
307
|
+
# Tab2: Velocity Cutoff
|
308
|
+
st.session_state.isCutoffCheck_VT = False
|
309
|
+
st.session_state.maxuvel_VT = 250
|
310
|
+
st.session_state.maxvvel_VT = 250
|
311
|
+
st.session_state.maxwvel_VT = 15
|
312
|
+
|
313
|
+
# Tab3: Despike
|
314
|
+
st.session_state.isDespikeCheck_VT = False
|
315
|
+
st.session_state.despike_kernal_VT = 5
|
316
|
+
st.session_state.despike_cutoff_VT = 3
|
317
|
+
|
318
|
+
# Tab4: Flatline
|
319
|
+
st.session_state.isFlatlineCheck_VT = False
|
320
|
+
st.session_state.flatline_kernal_VT = 5
|
321
|
+
st.session_state.flatline_cutoff_VT = 3
|
322
|
+
|
323
|
+
# ------------------
|
324
|
+
# Page: Write File
|
325
|
+
# ------------------
|
326
|
+
st.session_state.isWriteFile = True
|
327
|
+
st.session_state.isAttributes = False
|
328
|
+
st.session_state.mask_data_WF = "Yes"
|
329
|
+
# FileTypes: NetCDF, CSV
|
330
|
+
st.session_state.file_type_WF = "NetCDF"
|
331
|
+
st.session_state.isProcessedNetcdfDownload_WF = True
|
332
|
+
st.session_state.isProcessedCSVDownload_WF = False
|
333
|
+
|
334
|
+
# MASK DATA
|
335
|
+
# The velocity data has missing values due to the cutoff
|
336
|
+
# criteria used before deployment. The `default_mask` uses
|
337
|
+
# the velocity to create a mask. This mask file is stored
|
338
|
+
# in the session_state.
|
339
|
+
#
|
340
|
+
# WARNING: Never Change `st.session_state.orig_mask` in the code!
|
341
|
+
#
|
342
|
+
if "orig_mask" not in st.session_state:
|
343
|
+
ds = st.session_state.ds
|
344
|
+
st.session_state.orig_mask = default_mask(ds)
|
345
|
+
|
346
|
+
# ----------------------
|
347
|
+
# Page returning options
|
348
|
+
# ----------------------
|
349
|
+
# This checks if we have returned back to the page after saving the data
|
350
|
+
st.session_state.isSensorPageReturn = False
|
351
|
+
st.session_state.isQCPageReturn = False
|
352
|
+
st.session_state.isProfilePageReturn = False
|
353
|
+
st.session_state.isVelocityPageReturn = False
|
354
|
+
|
355
|
+
########## FILE HEADER ###############
|
356
|
+
st.header("File Header", divider="blue")
|
357
|
+
st.write(
|
358
|
+
"""
|
359
|
+
Header information is the first item sent by the ADCP. You may check the file size, total ensembles, and available data types. The function also checks if the total bytes and data types are uniform for all ensembles.
|
360
|
+
"""
|
361
|
+
)
|
362
|
+
|
363
|
+
left1, right1 = st.columns(2)
|
364
|
+
with left1:
|
365
|
+
check_button = st.button("Check File Health")
|
366
|
+
if check_button:
|
367
|
+
cf = st.session_state.head.check_file()
|
368
|
+
if (
|
369
|
+
cf["File Size Match"]
|
370
|
+
and cf["Byte Uniformity"]
|
371
|
+
and cf["Data Type Uniformity"]
|
372
|
+
):
|
373
|
+
st.write("Your file appears healthy! :sunglasses:")
|
374
|
+
else:
|
375
|
+
st.write("Your file appears corrupted! :worried:")
|
376
|
+
|
377
|
+
cf["File Size (MB)"] = "{:,.2f}".format(cf["File Size (MB)"])
|
378
|
+
st.write(f"Total no. of Ensembles: :green[{st.session_state.head.ensembles}]")
|
379
|
+
df = pd.DataFrame(cf.items(), columns=pd.array(["Check", "Details"]))
|
380
|
+
df = df.astype("str")
|
381
|
+
st.write(df.style.map(color_bool2, subset="Details"))
|
382
|
+
# st.write(df)
|
383
|
+
with right1:
|
384
|
+
datatype_button = st.button("Display Data Types")
|
385
|
+
if datatype_button:
|
386
|
+
st.write(
|
387
|
+
pd.DataFrame(
|
388
|
+
st.session_state.head.data_types(),
|
389
|
+
columns=pd.array(["Available Data Types"]),
|
390
|
+
)
|
391
|
+
)
|
392
|
+
|
393
|
+
if st.session_state.ds.isWarning:
|
394
|
+
st.write(
|
395
|
+
"""
|
396
|
+
Warnings detected while reading. Data sets may still be available for processing.
|
397
|
+
Click `Display Warning` to display warnings for each data types.
|
398
|
+
"""
|
399
|
+
)
|
400
|
+
warning_button = st.button("Display Warnings")
|
401
|
+
df2 = pd.DataFrame(
|
402
|
+
st.session_state.ds.warnings.items(),
|
403
|
+
columns=pd.array(["Data Type", "Warnings"]),
|
404
|
+
)
|
405
|
+
if warning_button:
|
406
|
+
st.write(df2.style.map(color_bool2, subset=["Warnings"]))
|
407
|
+
|
408
|
+
############ FIXED LEADER #############
|
409
|
+
|
410
|
+
st.header("Fixed Leader (Static Variables)", divider="blue")
|
411
|
+
st.write(
|
412
|
+
"""
|
413
|
+
Fixed Leader data refers to the non-dynamic WorkHorse ADCP data like the hardware information and the thresholds. Typically, values remain constant over time. They only change when you change certain commands, although there are occasional exceptions. You can confirm this using the :blue[**Fleader Uniformity Check**]. Click :blue[**Fixed Leader**] to display the values for the first ensemble.
|
414
|
+
"""
|
415
|
+
)
|
416
|
+
|
417
|
+
|
418
|
+
flead_check_button = st.button("Fleader Uniformity Check")
|
419
|
+
if flead_check_button:
|
420
|
+
st.write("The following variables are non-uniform:")
|
421
|
+
for keys, values in st.session_state.flead.is_uniform().items():
|
422
|
+
if not values:
|
423
|
+
st.markdown(f":blue[**- {keys}**]")
|
424
|
+
st.write("Displaying all static variables")
|
425
|
+
df = pd.DataFrame(st.session_state.flead.is_uniform(), index=[0]).T
|
426
|
+
st.write(df.style.map(color_bool))
|
427
|
+
|
428
|
+
flead_button = st.button("Fixed Leader")
|
429
|
+
if flead_button:
|
430
|
+
# Pandas array should have all elements with same data type.
|
431
|
+
# Except Sl. no., which is np.uint64, rest are np.int64.
|
432
|
+
# Convert all datatype to uint64
|
433
|
+
fl_dict = st.session_state.flead.field().items()
|
434
|
+
new_dict = {}
|
435
|
+
for key, value in fl_dict:
|
436
|
+
new_dict[key] = value.astype(np.uint64)
|
437
|
+
|
438
|
+
df = pd.DataFrame(
|
439
|
+
{
|
440
|
+
"Fields": new_dict.keys(),
|
441
|
+
"Values": new_dict.values(),
|
442
|
+
}
|
443
|
+
)
|
444
|
+
st.dataframe(df, use_container_width=True)
|
445
|
+
|
446
|
+
left, centre, right = st.columns(3)
|
447
|
+
with left:
|
448
|
+
st.dataframe(st.session_state.flead.system_configuration())
|
449
|
+
|
450
|
+
with centre:
|
451
|
+
st.dataframe(st.session_state.flead.ez_sensor())
|
452
|
+
# st.write(output)
|
453
|
+
with right:
|
454
|
+
# st.write(st.session_state.flead.ex_coord_trans())
|
455
|
+
df = pd.DataFrame(st.session_state.flead.ex_coord_trans(), index=[0]).T
|
456
|
+
df = df.astype("str")
|
457
|
+
st.write((df.style.map(color_bool2)))
|
458
|
+
# st.dataframe(df)
|
@@ -0,0 +1,164 @@
|
|
1
|
+
import matplotlib.pyplot as plt
|
2
|
+
import numpy as np
|
3
|
+
import pandas as pd
|
4
|
+
import plotly.express as px
|
5
|
+
import plotly.graph_objects as go
|
6
|
+
import streamlit as st
|
7
|
+
from plotly_resampler import FigureResampler
|
8
|
+
|
9
|
+
if "flead" not in st.session_state:
|
10
|
+
st.write(":red[Please Select Data!]")
|
11
|
+
st.stop()
|
12
|
+
|
13
|
+
# Load data
|
14
|
+
fdata = st.session_state.flead.fleader
|
15
|
+
vdata = st.session_state.vlead.vleader
|
16
|
+
velocity = st.session_state.velocity
|
17
|
+
echo = st.session_state.echo
|
18
|
+
correlation = st.session_state.correlation
|
19
|
+
pgood = st.session_state.pgood
|
20
|
+
|
21
|
+
x = np.arange(0, st.session_state.head.ensembles, 1)
|
22
|
+
y = np.arange(0, fdata["Cells"][0], 1)
|
23
|
+
|
24
|
+
X, Y = np.meshgrid(x, y)
|
25
|
+
|
26
|
+
|
27
|
+
@st.cache_data
|
28
|
+
def fillplot_matplotlib(data):
|
29
|
+
fig, ax = plt.subplots()
|
30
|
+
cs = ax.contourf(X, Y, data)
|
31
|
+
fig.colorbar(cs)
|
32
|
+
st.pyplot(fig)
|
33
|
+
|
34
|
+
|
35
|
+
@st.cache_data
|
36
|
+
def fillplot_plotly(data, colorscale="balance", title="Data", xaxis="time"):
|
37
|
+
if xaxis == "time":
|
38
|
+
xdata = st.session_state.date
|
39
|
+
elif xaxis == "ensemble":
|
40
|
+
xdata = x
|
41
|
+
else:
|
42
|
+
xdata = x
|
43
|
+
fig = FigureResampler(go.Figure())
|
44
|
+
data1 = np.where(data == -32768, np.nan, data)
|
45
|
+
fig.add_trace(
|
46
|
+
go.Heatmap(
|
47
|
+
z=data1[:, 0:-1],
|
48
|
+
x=xdata,
|
49
|
+
y=y,
|
50
|
+
colorscale=colorscale,
|
51
|
+
hoverongaps=False,
|
52
|
+
)
|
53
|
+
)
|
54
|
+
fig.update_layout(
|
55
|
+
xaxis=dict(showline=True, mirror=True),
|
56
|
+
yaxis=dict(showline=True, mirror=True),
|
57
|
+
title_text=title,
|
58
|
+
)
|
59
|
+
st.plotly_chart(fig)
|
60
|
+
|
61
|
+
|
62
|
+
@st.cache_data
|
63
|
+
def lineplot(data, title, xaxis="time"):
|
64
|
+
if xaxis == "time":
|
65
|
+
df = pd.DataFrame({"date": st.session_state.date, title: data})
|
66
|
+
fig = px.line(df, x="date", y=title)
|
67
|
+
else:
|
68
|
+
df = pd.DataFrame({"ensemble": x, title: data})
|
69
|
+
fig = px.line(df, x="ensemble", y=title)
|
70
|
+
|
71
|
+
st.plotly_chart(fig)
|
72
|
+
|
73
|
+
|
74
|
+
# Introduction
|
75
|
+
st.header("View Raw Data", divider="orange")
|
76
|
+
st.write(
|
77
|
+
"""
|
78
|
+
Displays all variables available in the raw file. No processing has been carried out.
|
79
|
+
Data might be missing because of the quality-check criteria used before deployment.\n
|
80
|
+
Either `time` or `ensemble` axis can be chosen as the abscissa (x-axis).
|
81
|
+
The ordinate (y-axis) for the heatmap is `bins` as the depth correction is not applied.
|
82
|
+
"""
|
83
|
+
)
|
84
|
+
xbutton = st.radio("Select an x-axis to plot", ["time", "ensemble"], horizontal=True)
|
85
|
+
|
86
|
+
|
87
|
+
tab1, tab2, tab3, tab4 = st.tabs(["Primary Data", "Variable Leader", "Fixed Leader", "Advanced"])
|
88
|
+
|
89
|
+
with tab3:
|
90
|
+
# Fixed Leader Plots
|
91
|
+
st.header("Fixed Leader", divider="blue")
|
92
|
+
fbutton = st.radio("Select a dynamic variable to plot:", fdata.keys(), horizontal=True)
|
93
|
+
lineplot(fdata[fbutton], fbutton, xaxis=str(xbutton))
|
94
|
+
|
95
|
+
with tab2:
|
96
|
+
# Variable Leader Plots
|
97
|
+
st.header("Variable Leader", divider="blue")
|
98
|
+
vbutton = st.radio("Select a dynamic variable to plot:", vdata.keys(), horizontal=True)
|
99
|
+
lineplot(vdata[vbutton], vbutton, xaxis=str(xbutton))
|
100
|
+
|
101
|
+
with tab1:
|
102
|
+
st.header("Velocity, Echo Intensity, Correlation & Percent Good", divider="blue")
|
103
|
+
|
104
|
+
|
105
|
+
def call_plot(varname, beam, xaxis="time"):
|
106
|
+
if varname == "Velocity":
|
107
|
+
fillplot_plotly(velocity[beam - 1, :, :], title=varname, xaxis=xaxis)
|
108
|
+
elif varname == "Echo":
|
109
|
+
fillplot_plotly(echo[beam - 1, :, :], title=varname, xaxis=xaxis)
|
110
|
+
elif varname == "Correlation":
|
111
|
+
fillplot_plotly(correlation[beam - 1, :, :], title=varname, xaxis=xaxis)
|
112
|
+
elif varname == "Percent Good":
|
113
|
+
fillplot_plotly(pgood[beam - 1, :, :], title=varname, xaxis=xaxis)
|
114
|
+
|
115
|
+
|
116
|
+
var_option = st.selectbox(
|
117
|
+
"Select a data type", ("Velocity", "Echo", "Correlation", "Percent Good")
|
118
|
+
)
|
119
|
+
beam = st.radio("Select beam", (1, 2, 3, 4), horizontal=True)
|
120
|
+
call_plot(var_option, beam, xaxis=str(xbutton))
|
121
|
+
|
122
|
+
|
123
|
+
with tab4:
|
124
|
+
st.header("Advanced Data", divider="blue")
|
125
|
+
adv_option = st.selectbox(
|
126
|
+
"Select a data type", ("Bit Result",
|
127
|
+
"ADC Channel",
|
128
|
+
"Error Status Word 1",
|
129
|
+
"Error Status Word 2",
|
130
|
+
"Error Status Word 3",
|
131
|
+
"Error Status Word 4"))
|
132
|
+
if adv_option == "Bit Result":
|
133
|
+
bitdata = st.session_state.vlead.bitresult()
|
134
|
+
st.subheader("BIT Result", divider="orange")
|
135
|
+
st.write("""
|
136
|
+
This field contains the results of Workhorse ADCPs builtin test functions.
|
137
|
+
A zero indicates a successful BIT result.
|
138
|
+
""")
|
139
|
+
bitbutton = st.radio("Select a dynamic variable to plot:", bitdata.keys(), horizontal=True)
|
140
|
+
lineplot(bitdata[bitbutton], bitbutton, xaxis=str(xbutton))
|
141
|
+
|
142
|
+
elif adv_option == "ADC Channel":
|
143
|
+
adcdata = st.session_state.vlead.adc_channel()
|
144
|
+
st.subheader("ADC Channel", divider="orange")
|
145
|
+
adcbutton = st.radio("Select a dynamic variable to plot:", adcdata.keys(), horizontal=True)
|
146
|
+
lineplot(adcdata[adcbutton], adcbutton, xaxis=str(xbutton))
|
147
|
+
elif adv_option == "Error Status Word 1":
|
148
|
+
errordata1 = st.session_state.vlead.error_status_word(esw=1)
|
149
|
+
st.subheader("Error Status Word", divider="orange")
|
150
|
+
errorbutton = st.radio("Select a dynamic variable to plot:", errordata1.keys(), horizontal=True)
|
151
|
+
lineplot(errordata1[errorbutton], errorbutton, xaxis=str(xbutton))
|
152
|
+
elif adv_option == "Error Status Word 2":
|
153
|
+
errordata2 = st.session_state.vlead.error_status_word(esw=2)
|
154
|
+
errorbutton = st.radio("Select a dynamic variable to plot:", errordata2.keys(), horizontal=True)
|
155
|
+
lineplot(errordata2[errorbutton], errorbutton, xaxis=str(xbutton))
|
156
|
+
elif adv_option == "Error Status Word 3":
|
157
|
+
errordata3 = st.session_state.vlead.error_status_word(esw=3)
|
158
|
+
errorbutton = st.radio("Select a dynamic variable to plot:", errordata3.keys(), horizontal=True)
|
159
|
+
lineplot(errordata3[errorbutton], errorbutton, xaxis=str(xbutton))
|
160
|
+
elif adv_option == "Error Status Word 4":
|
161
|
+
errordata4 = st.session_state.vlead.error_status_word(esw=4)
|
162
|
+
errorbutton = st.radio("Select a dynamic variable to plot:", errordata4.keys(), horizontal=True)
|
163
|
+
lineplot(errordata4[errorbutton], errorbutton, xaxis=str(xbutton))
|
164
|
+
|