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.
@@ -0,0 +1,530 @@
1
+ import configparser
2
+ import os
3
+
4
+ import numpy as np
5
+ import pandas as pd
6
+ import pyadps.utils.writenc as wr
7
+ from netCDF4 import date2num
8
+ from pyadps.utils import readrdi
9
+ from pyadps.utils.profile_test import side_lobe_beam_angle, manual_cut_bins
10
+ from pyadps.utils.profile_test import regrid2d, regrid3d
11
+ from pyadps.utils.signal_quality import (
12
+ default_mask,
13
+ ev_check,
14
+ false_target,
15
+ pg_check,
16
+ echo_check,
17
+ correlation_check,
18
+ )
19
+ from pyadps.utils.velocity_test import (
20
+ despike,
21
+ flatline,
22
+ velocity_cutoff,
23
+ magdec,
24
+ wmm2020api,
25
+ velocity_modifier,
26
+ )
27
+ from pyadps.utils.sensor_health import sound_speed_correction, tilt_sensor_check
28
+
29
+
30
+ def main():
31
+ # Get the config file
32
+ try:
33
+ filepath = input("Enter config file name: ")
34
+ if os.path.exists(filepath):
35
+ autoprocess(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
+ def autoprocess(config_file, binary_file_path=None):
46
+ # Load configuration
47
+ config = configparser.ConfigParser()
48
+
49
+ # Decode and parse the config file
50
+ # Check if config_file is a file-like object or a file path
51
+ if hasattr(config_file, "read"):
52
+ # If it's a file-like object, read its content
53
+ config_content = config_file.read().decode("utf-8")
54
+ else:
55
+ # If it's a file path, open the file and read its content
56
+ with open(config_file, "r", encoding="utf-8") as file:
57
+ config_content = file.read()
58
+ config.read_string(config_content)
59
+
60
+ if not binary_file_path:
61
+ input_file_name = config.get("FileSettings", "input_file_name")
62
+ input_file_path = config.get("FileSettings", "input_file_path")
63
+ full_input_file_path = os.path.join(input_file_path, input_file_name)
64
+ else:
65
+ full_input_file_path = binary_file_path
66
+
67
+ print("File reading started. Please wait for a few seconds ...")
68
+ ds = readrdi.ReadFile(full_input_file_path)
69
+ print("File reading complete.")
70
+
71
+ header = ds.fileheader
72
+ flobj = ds.fixedleader
73
+ vlobj = ds.variableleader
74
+ velocity = ds.velocity.data
75
+ echo = ds.echo.data
76
+ correlation = ds.correlation.data
77
+ pgood = ds.percentgood.data
78
+ roll = ds.variableleader.roll.data
79
+ pitch = ds.variableleader.pitch.data
80
+ sound = ds.variableleader.speed_of_sound.data
81
+ depth = ds.variableleader.depth_of_transducer.data
82
+ temperature = (
83
+ ds.variableleader.temperature.data * ds.variableleader.temperature.scale
84
+ )
85
+ salinity = ds.variableleader.salinity.data * ds.variableleader.salinity.scale
86
+ orientation = ds.fixedleader.system_configuration()["Beam Direction"]
87
+ ensembles = header.ensembles
88
+ cells = flobj.field()["Cells"]
89
+ beams = flobj.field()["Beams"]
90
+ cell_size = flobj.field()["Depth Cell Len"]
91
+ bin1dist = flobj.field()["Bin 1 Dist"]
92
+ beam_angle = int(flobj.system_configuration()["Beam Angle"])
93
+ fdata = flobj.fleader
94
+ vdata = vlobj.vleader
95
+ # depth = ds.variableleader.depth_of_transducer
96
+
97
+ # Initialize mask
98
+ mask = default_mask(ds)
99
+
100
+ # Debugging statement
101
+ x = np.arange(0, ensembles, 1)
102
+ y = np.arange(0, cells, 1)
103
+
104
+ axis_option = config.get("DownloadOptions", "axis_option")
105
+
106
+ # Sensor Test
107
+ isSensorTest = config.getboolean("SensorTest", "sensor_test")
108
+ if isSensorTest:
109
+ isDepthModified = config.getboolean("SensorTest", "is_depth_modified")
110
+ if isDepthModified:
111
+ depth_option = config.get("SensorTest", "depth_input_option")
112
+ if depth_option == "Fixed Value":
113
+ fixed_depth = config.getfloat("SensorTest", "fixed_depth")
114
+ depth = np.full(ensembles, fixed_depth)
115
+ depth *= 10
116
+ elif depth_option == "File Upload":
117
+ depth_file_path = config.get("SensorTest", "depth_file_path")
118
+ df = pd.read_csv(depth_file_path)
119
+ depth = np.squeeze(df)
120
+ if len(depth) != ensembles:
121
+ print("""
122
+ Error: Uploaded file ensembles and
123
+ actual ensembles mismatch
124
+ """)
125
+ else:
126
+ print("Depth file uploaded.")
127
+
128
+ isSalinityModified = config.getboolean("SensorTest", "is_salinity_modified")
129
+ if isSalinityModified:
130
+ salinity_option = config.get("SensorTest", "salinity_input_option")
131
+ if salinity_option == "Fixed Value":
132
+ fixed_salinity = config.getfloat("SensorTest", "fixed_salinity")
133
+ salinity = np.full(ensembles, fixed_salinity)
134
+ salinity *= 10
135
+ elif salinity_option == "File Upload":
136
+ salinity_file_path = config.get("SensorTest", "salinity_file_path")
137
+ df = pd.read_csv(salinity_file_path)
138
+ salinity = np.squeeze(df)
139
+ if len(salinity) != ensembles:
140
+ print("""
141
+ Error: Uploaded file ensembles and
142
+ actual ensembles mismatch
143
+ """)
144
+ else:
145
+ print("Salinity file uploaded.")
146
+
147
+ isTemperatureModified = config.getboolean(
148
+ "SensorTest", "is_temperature_modified"
149
+ )
150
+ if isTemperatureModified:
151
+ temperature_option = config.get("SensorTest", "temperature_input_option")
152
+ if temperature_option == "Fixed Value":
153
+ fixed_temperature = config.getfloat("SensorTest", "fixed_temperature")
154
+ temperature = np.full(ensembles, fixed_temperature)
155
+ temperature *= 10
156
+ elif temperature_option == "File Upload":
157
+ temperature_file_path = config.get(
158
+ "SensorTest", "temperature_file_path"
159
+ )
160
+ df = pd.read_csv(temperature_file_path)
161
+ temperature = np.squeeze(df)
162
+ if len(temperature) != ensembles:
163
+ print("""
164
+ Error: Uploaded file ensembles and
165
+ actual ensembles mismatch
166
+ """)
167
+ else:
168
+ print("Temperature file uploaded.")
169
+
170
+ isRollCheck = config.getboolean("SensorTest", "roll_check")
171
+ if isRollCheck:
172
+ roll_cutoff = config.getint("SensorTest", "roll_cutoff")
173
+ mask = tilt_sensor_check(roll, mask, cutoff=roll_cutoff)
174
+
175
+ isPitchCheck = config.getboolean("SensorTest", "pitch_check")
176
+ if isPitchCheck:
177
+ pitch_cutoff = config.getint("SensorTest", "pitch_cutoff")
178
+ mask = tilt_sensor_check(pitch, mask, cutoff=pitch_cutoff)
179
+
180
+ isVelocityModified = config.getboolean("SensorTest", "velocity_modified")
181
+ if isVelocityModified:
182
+ velocity = sound_speed_correction(
183
+ velocity, sound, temperature, salinity, depth
184
+ )
185
+
186
+ # QC Test
187
+ isQCTest = config.getboolean("QCTest", "qc_test")
188
+ if isQCTest:
189
+ isQCCheck = config.get("QCTest", "qc_check")
190
+ if isQCCheck:
191
+ ct = config.getint("QCTest", "correlation")
192
+ evt = config.getint("QCTest", "error_velocity")
193
+ et = config.getint("QCTest", "echo_intensity")
194
+ ft = config.getint("QCTest", "false_target")
195
+ is3Beam = config.getboolean("QCTest", "three_beam")
196
+ pgt = config.getint("QCTest", "percent_good")
197
+ orientation = config.get("QCTest", "orientation")
198
+
199
+ mask = pg_check(ds, mask, pgt, threebeam=is3Beam)
200
+ mask = correlation_check(ds, mask, ct)
201
+ mask = echo_check(ds, mask, et)
202
+ mask = ev_check(ds, mask, evt)
203
+ mask = false_target(ds, mask, ft, threebeam=True)
204
+
205
+ print("QC Check Complete.")
206
+
207
+ isBeamModified = config.getboolean("QCTest", "beam_modified")
208
+ if isBeamModified:
209
+ orientation = config.get("QCTest", "orientation")
210
+ print("Beam orientation changed.")
211
+
212
+ # Profile Test
213
+ endpoints = None
214
+ isProfileTest = config.getboolean("ProfileTest", "profile_test")
215
+ if isProfileTest:
216
+ isTrimEnds = config.getboolean("ProfileTest", "trim_ends_check")
217
+ if isTrimEnds:
218
+ start_index = config.getint("ProfileTest", "trim_start_ensemble")
219
+ end_index = config.getint("ProfileTest", "trim_end_ensemble")
220
+ if start_index > 0:
221
+ mask[:, :start_index] = 1
222
+
223
+ if end_index < x[-1]:
224
+ mask[:, end_index:] = 1
225
+
226
+ endpoints = np.array([start_index, end_index])
227
+
228
+ print("Trim Ends complete.")
229
+
230
+ isCutBins = config.getboolean("ProfileTest", "cutbins_sidelobe_check")
231
+ if isCutBins:
232
+ water_column_depth = config.getint("ProfileTest", "water_depth")
233
+ extra_cells = config.getint("ProfileTest", "extra_cells")
234
+ mask = side_lobe_beam_angle(
235
+ depth,
236
+ mask,
237
+ orientation=orientation,
238
+ water_column_depth=water_column_depth,
239
+ extra_cells=extra_cells,
240
+ cells=cells,
241
+ cell_size=cell_size,
242
+ bin1dist=bin1dist,
243
+ beam_angle=beam_angle,
244
+ )
245
+ print("Cutbins complete.")
246
+
247
+ # Manual Cut Bins
248
+ # isManual_cutbins = config.getboolean("ProfileTest", "manual_cutbins")
249
+ # if isManual_cutbins:
250
+ # raw_bins = config.get("ProfileTest", "manual_cut_bins")
251
+ # bin_groups = raw_bins.split("]")
252
+ #
253
+ # for group in bin_groups:
254
+ # if group.strip(): # Ignore empty parts
255
+ # # Clean and split the values
256
+ # clean_group = group.replace("[", "").strip()
257
+ # values = list(map(int, clean_group.split(",")))
258
+ # min_cell, max_cell, min_ensemble, max_ensemble = values
259
+ # mask = manual_cut_bins(
260
+ # mask, min_cell, max_cell, min_ensemble, max_ensemble
261
+ # )
262
+ #
263
+ # print("Manual cut bins applied.")
264
+
265
+ isRegrid = config.getboolean("ProfileTest", "regrid")
266
+ if isRegrid:
267
+ print("File regridding started. This will take a few seconds ...")
268
+
269
+ # regrid_option = config.get("ProfileTest", "regrid_option")
270
+ end_cell_option = config.get("ProfileTest", "end_cell_option")
271
+ interpolate = config.get("ProfileTest", "interpolate")
272
+ boundary = config.getint("ProfileTest", "boundary")
273
+ z, velocity = regrid3d(
274
+ depth,
275
+ velocity,
276
+ -32768,
277
+ trimends=endpoints,
278
+ orientation=orientation,
279
+ end_cell_option=end_cell_option,
280
+ method=interpolate,
281
+ boundary_limit=boundary,
282
+ cells=cells,
283
+ cell_size=cell_size,
284
+ bin1dist=bin1dist,
285
+ beams=beams,
286
+ )
287
+ z, echo = regrid3d(
288
+ depth,
289
+ echo,
290
+ -32768,
291
+ trimends=endpoints,
292
+ orientation=orientation,
293
+ method=interpolate,
294
+ boundary_limit=boundary,
295
+ cells=cells,
296
+ cell_size=cell_size,
297
+ bin1dist=bin1dist,
298
+ beams=beams,
299
+ )
300
+ z, correlation = regrid3d(
301
+ depth,
302
+ correlation,
303
+ -32768,
304
+ trimends=endpoints,
305
+ orientation=orientation,
306
+ method=interpolate,
307
+ boundary_limit=boundary,
308
+ cells=cells,
309
+ cell_size=cell_size,
310
+ bin1dist=bin1dist,
311
+ beams=beams,
312
+ )
313
+ z, pgood = regrid3d(
314
+ depth,
315
+ pgood,
316
+ -32768,
317
+ trimends=endpoints,
318
+ orientation=orientation,
319
+ method=interpolate,
320
+ boundary_limit=boundary,
321
+ cells=cells,
322
+ cell_size=cell_size,
323
+ bin1dist=bin1dist,
324
+ beams=beams,
325
+ )
326
+ z, mask = regrid2d(
327
+ depth,
328
+ mask,
329
+ 1,
330
+ trimends=endpoints,
331
+ orientation=orientation,
332
+ method=interpolate,
333
+ boundary_limit=boundary,
334
+ cells=cells,
335
+ cell_size=cell_size,
336
+ bin1dist=bin1dist,
337
+ )
338
+ depth = z
339
+
340
+ print("Regrid Complete.")
341
+
342
+ print("Profile Test complete.")
343
+
344
+ isVelocityTest = config.getboolean("VelocityTest", "velocity_test")
345
+ if isVelocityTest:
346
+ isMagneticDeclination = config.getboolean(
347
+ "VelocityTest", "magnetic_declination"
348
+ )
349
+ if isMagneticDeclination:
350
+ magmethod = config.get("VelocityTest", "magnet_method")
351
+ maglat = config.getfloat("VelocityTest", "magnet_latitude")
352
+ maglon = config.getfloat("VelocityTest", "magnet_longitude")
353
+ magdep = config.getfloat("VelocityTest", "magnet_depth")
354
+ magyear = config.getfloat("VelocityTest", "magnet_year")
355
+ year = int(magyear)
356
+ # mag = config.getfloat("VelocityTest", "mag")
357
+
358
+ if magmethod == "pygeomag":
359
+ mag = magdec(maglat, maglon, magdep, magyear)
360
+ elif magmethod.lower() == "api":
361
+ mag = wmm2020api(maglat, maglon, year)
362
+ elif magmethod.lower() == "manual":
363
+ mag = config.getint("VelocityTest", "magnet_user_input")
364
+ else:
365
+ mag = 0
366
+ velocity = velocity_modifier(velocity, mag)
367
+ print(f"Magnetic Declination applied. The value is {mag[0]} degrees.")
368
+
369
+ isCutOff = config.getboolean("VelocityTest", "cutoff")
370
+ if isCutOff:
371
+ maxu = config.getint("VelocityTest", "max_zonal_velocity")
372
+ maxv = config.getint("VelocityTest", "max_meridional_velocity")
373
+ maxw = config.getint("VelocityTest", "max_vertical_velocity")
374
+ mask = velocity_cutoff(velocity[0, :, :], mask, cutoff=maxu)
375
+ mask = velocity_cutoff(velocity[1, :, :], mask, cutoff=maxv)
376
+ mask = velocity_cutoff(velocity[2, :, :], mask, cutoff=maxw)
377
+ print("Maximum velocity cutoff applied.")
378
+
379
+ isDespike = config.getboolean("VelocityTest", "despike")
380
+ if isDespike:
381
+ despike_kernal = config.getint("VelocityTest", "despike_kernal_size")
382
+ despike_cutoff = config.getint("VelocityTest", "despike_cutoff")
383
+
384
+ mask = despike(
385
+ velocity[0, :, :],
386
+ mask,
387
+ kernal_size=despike_kernal,
388
+ cutoff=despike_cutoff,
389
+ )
390
+ mask = despike(
391
+ velocity[1, :, :],
392
+ mask,
393
+ kernal_size=despike_kernal,
394
+ cutoff=despike_cutoff,
395
+ )
396
+ print("Velocity data despiked.")
397
+
398
+ isFlatline = config.getboolean("VelocityTest", "flatline")
399
+ if isFlatline:
400
+ despike_kernal = config.getint("VelocityTest", "flatline_kernal_size")
401
+ despike_cutoff = config.getint("VelocityTest", "flatline_deviation")
402
+
403
+ mask = flatline(
404
+ velocity[0, :, :],
405
+ mask,
406
+ kernal_size=despike_kernal,
407
+ cutoff=despike_cutoff,
408
+ )
409
+ mask = flatline(
410
+ velocity[1, :, :],
411
+ mask,
412
+ kernal_size=despike_kernal,
413
+ cutoff=despike_cutoff,
414
+ )
415
+ mask = flatline(
416
+ velocity[2, :, :],
417
+ mask,
418
+ kernal_size=despike_kernal,
419
+ cutoff=despike_cutoff,
420
+ )
421
+
422
+ print("Flatlines in velocity removed.")
423
+
424
+ print("Velocity Test complete.")
425
+
426
+ # Apply mask to velocity data
427
+ isApplyMask = config.get("DownloadOptions", "apply_mask")
428
+ if isApplyMask:
429
+ velocity[:, mask == 1] = -32768
430
+ print("Mask Applied.")
431
+
432
+ # Create Depth axis if regrid not applied
433
+ if depth is None:
434
+ mean_depth = np.mean(vlobj.vleader["Depth of Transducer"]) / 10
435
+ mean_depth = np.trunc(mean_depth)
436
+ cells = flobj.field()["Cells"]
437
+ cell_size = flobj.field()["Depth Cell Len"] / 100
438
+ bin1dist = flobj.field()["Bin 1 Dist"] / 100
439
+ max_depth = mean_depth - bin1dist
440
+ min_depth = max_depth - cells * cell_size
441
+ depth = np.arange(-1 * max_depth, -1 * min_depth, cell_size)
442
+
443
+ print("WARNING: File not regrided. Depth axis created based on mean depth.")
444
+
445
+ # Create Time axis
446
+ year = vlobj.vleader["RTC Year"]
447
+ month = vlobj.vleader["RTC Month"]
448
+ day = vlobj.vleader["RTC Day"]
449
+ hour = vlobj.vleader["RTC Hour"]
450
+ minute = vlobj.vleader["RTC Minute"]
451
+ second = vlobj.vleader["RTC Second"]
452
+
453
+ year = year + 2000
454
+ date_df = pd.DataFrame(
455
+ {
456
+ "year": year,
457
+ "month": month,
458
+ "day": day,
459
+ "hour": hour,
460
+ "minute": minute,
461
+ "second": second,
462
+ }
463
+ )
464
+
465
+ date_raw = pd.to_datetime(date_df)
466
+ date_vlead = pd.to_datetime(date_df)
467
+ date_final = pd.to_datetime(date_df)
468
+
469
+ print("Time axis created.")
470
+
471
+ isAttributes = config.getboolean("DownloadOptions", "add_attributes_processed")
472
+ if isAttributes:
473
+ attributes = [att for att in config["DownloadOptions"]]
474
+ attributes = dict(config["DownloadOptions"].items())
475
+ del attributes["add_attributes_processed"]
476
+ else:
477
+ attributes = None
478
+
479
+ isWriteRawNC = config.getboolean("DownloadOptions", "download_raw_netcdf")
480
+ isWriteVleadNC = config.getboolean("DownloadOptions", "download_vlead_netcdf")
481
+ isWriteProcNC = config.getboolean("DownloadOptions", "download_processed_netcdf")
482
+ filepath = config.get("FileSettings", "output_file_path")
483
+
484
+ print(isWriteRawNC)
485
+ if isWriteRawNC:
486
+ filename = config.get("FileSettings", "output_file_name_raw_netcdf")
487
+ output_file_path = os.path.join(filepath, filename)
488
+ print(date_raw.shape)
489
+ wr.rawnc(
490
+ full_input_file_path,
491
+ output_file_path,
492
+ date_raw,
493
+ axis_option=axis_option,
494
+ attributes=attributes,
495
+ )
496
+
497
+ print("Raw file written.")
498
+
499
+ if isWriteVleadNC:
500
+ filename = config.get("FileSettings", "output_file_name_vlead_netcdf")
501
+ output_file_path = os.path.join(filepath, filename)
502
+ wr.vlead_nc(
503
+ full_input_file_path,
504
+ output_file_path,
505
+ date_vlead,
506
+ axis_option=axis_option,
507
+ attributes=attributes,
508
+ )
509
+
510
+ print("Vlead file written.")
511
+
512
+ depth1 = depth
513
+
514
+ if isWriteProcNC:
515
+ filename = config.get("FileSettings", "output_file_name_processed_netcdf")
516
+ output_file_path = os.path.join(filepath, filename)
517
+
518
+ wr.finalnc(
519
+ output_file_path,
520
+ depth1,
521
+ mask,
522
+ date_final,
523
+ velocity,
524
+ attributes=attributes, # Pass edited attributes
525
+ )
526
+ print("Processed file written.")
527
+
528
+
529
+ if __name__ == "__main__":
530
+ main()
@@ -0,0 +1,99 @@
1
+ [FileSettings]
2
+ # Input file settings
3
+ input_file_path = /home/user/data/
4
+ input_file_name = adcp_raw.000
5
+
6
+ # Output file settings. Do not enter file extension.
7
+ output_file_path = /home/nio/Videos/output/
8
+ output_file_name_raw = adcp_raw.nc
9
+ output_file_name_vlead = adcp_vlead.nc
10
+ output_file_name_processed = adcp_proc.nc
11
+
12
+ # Choose between 'netcdf' or 'csv' for the raw output format
13
+ output_format_raw = netcdf
14
+
15
+ # Choose between 'netcdf' or 'csv' for the processed output format
16
+ output_format_processed = csv
17
+
18
+ [DownloadOptions]
19
+ # Options to download raw and/or processed output files
20
+ axis_option = ensemble
21
+ download_raw = True
22
+ download_vlead = True
23
+ download_processed = True
24
+ apply_mask = True
25
+ download_mask = True
26
+
27
+ [SensorTest]
28
+ sensor_test = True
29
+ transducer_depth = 200
30
+
31
+
32
+ [QCTest]
33
+ # Enable or Disable QC Test (True/False)
34
+ qc_test = True
35
+ correlation = 64
36
+ error_velocity = 2000
37
+ echo_intensity = 40
38
+ false_target = 50
39
+ three_beam = True
40
+ percentage_good = 50
41
+ orientation = up
42
+
43
+
44
+ [ProfileTest]
45
+ # Enable or Disable Profile Test (True/False)
46
+ profile_test = True
47
+ trim_ends = True
48
+ trim_ends_start_index = 2
49
+ trim_ends_end_index = 17086
50
+ cut_bins = True
51
+ cut_bins_add_cells = 2
52
+ manual_cutbins = True
53
+ manual_cut_bins = [2,10,0,8000] [40,42,0,8000] [12,14,0,8000]
54
+ regrid = True
55
+ regrid_option = Cell
56
+ regrid_interpolation = nearest
57
+ transducer_depth = 200
58
+ water_column_depth = 200
59
+
60
+ [VelocityTest]
61
+ # Enable or Disable Velocity Test (True/False)
62
+ velocity_test = True
63
+ magnetic_declination = True
64
+ latitude = 0.0
65
+ longitude = 83.0
66
+ depth = 0
67
+ year = 2025
68
+ cutoff = True
69
+ max_zonal_velocity = 250
70
+ max_meridional_velocity = 250
71
+ max_vertical_velocity = 15
72
+ despike = True
73
+ despike_kernal_size = 5
74
+ despike_cutoff = 150
75
+ flatline = True
76
+ flatline_kernal_size = 13
77
+ flatline_deviation = 1
78
+
79
+ [Optional]
80
+ # Options to add attributes to netcdf file (True/False)
81
+ attributes = True
82
+ cruise_no. = Ship999
83
+ ship_name = RV Vessel Name
84
+ project_no. = GAP9999
85
+ water_depth_m = 1000
86
+ deployment_depth_m = 300
87
+ deployment_date = 12/10/2023
88
+ recovery_date = 8/10/2024
89
+ latitude = 15.0
90
+ longitude = 77.0
91
+ platform_type = Moored
92
+ participants = abcd, efgh, ijkl
93
+ file_created_by = xxxx
94
+ contact = abcd
95
+ comments = No comments
96
+
97
+
98
+
99
+
Binary file