pyadps 0.1.0b0__py3-none-any.whl → 0.1.1__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.
Files changed (50) hide show
  1. pyadps/Home_Page.py +11 -5
  2. pyadps/pages/01_Read_File.py +191 -16
  3. pyadps/pages/02_View_Raw_Data.py +69 -33
  4. pyadps/pages/03_Download_Raw_File.py +161 -60
  5. pyadps/pages/04_Sensor_Health.py +905 -0
  6. pyadps/pages/05_QC_Test.py +476 -0
  7. pyadps/pages/06_Profile_Test.py +971 -0
  8. pyadps/pages/07_Velocity_Test.py +600 -0
  9. pyadps/pages/08_Write_File.py +587 -0
  10. pyadps/pages/09_Auto_process.py +64 -0
  11. pyadps/pages/__pycache__/__init__.cpython-312.pyc +0 -0
  12. pyadps/utils/__init__.py +3 -3
  13. pyadps/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  14. pyadps/utils/__pycache__/autoprocess.cpython-312.pyc +0 -0
  15. pyadps/utils/__pycache__/cutbin.cpython-312.pyc +0 -0
  16. pyadps/utils/__pycache__/plotgen.cpython-312.pyc +0 -0
  17. pyadps/utils/__pycache__/profile_test.cpython-312.pyc +0 -0
  18. pyadps/utils/__pycache__/pyreadrdi.cpython-312.pyc +0 -0
  19. pyadps/utils/__pycache__/readrdi.cpython-312.pyc +0 -0
  20. pyadps/utils/__pycache__/regrid.cpython-312.pyc +0 -0
  21. pyadps/utils/__pycache__/script.cpython-312.pyc +0 -0
  22. pyadps/utils/__pycache__/sensor_health.cpython-312.pyc +0 -0
  23. pyadps/utils/__pycache__/signal_quality.cpython-312.pyc +0 -0
  24. pyadps/utils/__pycache__/velocity_test.cpython-312.pyc +0 -0
  25. pyadps/utils/__pycache__/writenc.cpython-312.pyc +0 -0
  26. pyadps/utils/autoprocess.py +548 -0
  27. pyadps/utils/metadata/config.ini +99 -0
  28. pyadps/utils/metadata/demo.000 +0 -0
  29. pyadps/utils/plotgen.py +505 -3
  30. pyadps/utils/profile_test.py +526 -145
  31. pyadps/utils/pyreadrdi.py +27 -17
  32. pyadps/utils/readrdi.py +167 -20
  33. pyadps/utils/script.py +197 -147
  34. pyadps/utils/sensor_health.py +120 -0
  35. pyadps/utils/signal_quality.py +344 -24
  36. pyadps/utils/velocity_test.py +103 -20
  37. pyadps/utils/writenc.py +223 -27
  38. {pyadps-0.1.0b0.dist-info → pyadps-0.1.1.dist-info}/METADATA +56 -24
  39. pyadps-0.1.1.dist-info/RECORD +47 -0
  40. {pyadps-0.1.0b0.dist-info → pyadps-0.1.1.dist-info}/WHEEL +1 -1
  41. pyadps-0.1.1.dist-info/entry_points.txt +5 -0
  42. pyadps/pages/04_QC_Test.py +0 -283
  43. pyadps/pages/05_Profile_Test.py +0 -389
  44. pyadps/pages/06_Velocity_Test.py +0 -293
  45. pyadps/pages/07_Write_File.py +0 -367
  46. pyadps/utils/cutbin.py +0 -413
  47. pyadps/utils/regrid.py +0 -122
  48. pyadps-0.1.0b0.dist-info/RECORD +0 -29
  49. pyadps-0.1.0b0.dist-info/entry_points.txt +0 -3
  50. {pyadps-0.1.0b0.dist-info → pyadps-0.1.1.dist-info}/LICENSE +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 (readbytes, ErrorCode.FILE_CORRUPTED)
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
- data = unpack("H" * datatype[i], dbyte)
446
- address_offset.append(data)
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[1][count]
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[1][count]
713
+ fbyteskip = offset[0][count]
707
714
  if fbyteskip == None:
708
715
  error = ErrorCode.ID_NOT_FOUND
709
716
  ensemble = i
@@ -883,7 +890,7 @@ def datatype(
883
890
 
884
891
  # These arguments are outputs of fixedleader function.
885
892
  # Makes the code faster if the fixedheader function is already executed.
886
- if cell == 0 or beam == 0:
893
+ if isinstance(cell, (np.integer, int)) or isinstance(beam, (np.integer, int)):
887
894
  flead, ensemble, fl_error_code = fixedleader(
888
895
  filename,
889
896
  byteskip=byteskip,
@@ -891,22 +898,23 @@ def datatype(
891
898
  idarray=idarray,
892
899
  ensemble=ensemble,
893
900
  )
894
- cell = int(flead[7][0])
895
- beam = int(flead[6][0])
901
+ cell = []
902
+ beam = []
903
+ cell = flead[7][:]
904
+ beam = flead[6][:]
896
905
  if fl_error_code != 0:
897
906
  error_code = fl_error_code
898
907
  else:
899
- cell = int(cell)
900
- beam = int(beam)
901
-
908
+ cell = cell
909
+ beam = beam
902
910
  # Velocity is 16 bits and all others are 8 bits.
903
911
  # Create empty array for the chosen variable name.
904
912
  if var_name == "velocity":
905
- var_array = np.zeros((beam, cell, ensemble), dtype="int16")
913
+ var_array = np.full((int(max(beam)), int(max(cell)), ensemble), -32768, dtype="int16")
906
914
  bitstr = "<h"
907
915
  bitint = 2
908
916
  else: # inserted
909
- var_array = np.zeros((beam, cell, ensemble), dtype="uint8")
917
+ var_array = np.zeros((int(max(beam)), int(max(cell)), ensemble), dtype="uint8")
910
918
  bitstr = "<B"
911
919
  bitint = 1
912
920
  # -----------------------------
@@ -935,7 +943,9 @@ def datatype(
935
943
  fbyteskip = None
936
944
  for count, item in enumerate(idarray[0][:]):
937
945
  if item in vid:
938
- fbyteskip = offset[1][count]
946
+ fbyteskip = []
947
+ for i in range(ensemble):
948
+ fbyteskip.append(int(offset[i][count]))
939
949
  break
940
950
  if fbyteskip is None:
941
951
  print(
@@ -948,10 +958,10 @@ def datatype(
948
958
 
949
959
  # READ DATA
950
960
  for i in range(ensemble):
951
- bfile.seek(fbyteskip, 1)
961
+ bfile.seek(fbyteskip[i], 1)
952
962
  bdata = bfile.read(2)
953
- for cno in range(cell):
954
- for bno in range(beam):
963
+ for cno in range(int(cell[i])):
964
+ for bno in range(int(beam[i])):
955
965
  bdata = bfile.read(bitint)
956
966
  varunpack = unpack(bitstr, bdata)
957
967
  var_array[bno][cno][i] = varunpack[0]
pyadps/utils/readrdi.py CHANGED
@@ -3,9 +3,9 @@
3
3
  """
4
4
  RDI ADCP Binary File Reader
5
5
  ===========================
6
- This module provides classes and functions to read and extract data from RDI Acoustic Doppler
7
- Current Profiler (ADCP) binary files. The module supports Workhorse, Ocean Surveyor, and DVS ADCPs.
8
- It allows for parsing of various data types such as Fixed Leader, Variable Leader, Velocity, Correlation,
6
+ This module provides classes and functions to read and extract data from RDI Acoustic Doppler
7
+ Current Profiler (ADCP) binary files. The module supports Workhorse, Ocean Surveyor, and DVS ADCPs.
8
+ It allows for parsing of various data types such as Fixed Leader, Variable Leader, Velocity, Correlation,
9
9
  Echo Intensity, Percent Good, and Status data.
10
10
 
11
11
  Classes
@@ -28,7 +28,7 @@ Status
28
28
  Parses the status data from the ADCP.
29
29
  ReadFile
30
30
  Manages the entire data extraction process and unifies all data types.
31
-
31
+
32
32
  Functions
33
33
  ---------
34
34
  check_equal(array)
@@ -101,6 +101,7 @@ import os
101
101
  import sys
102
102
 
103
103
  import numpy as np
104
+ import pandas as pd
104
105
  from pyadps.utils import pyreadrdi
105
106
 
106
107
 
@@ -129,9 +130,7 @@ class DotDict:
129
130
  # with open(json_file_path, "r") as file:
130
131
  # dictionary = json.load(file)
131
132
  else:
132
- dictionary = (
133
- {}
134
- ) # Initialize an empty dictionary if no JSON file is found
133
+ dictionary = {} # Initialize an empty dictionary if no JSON file is found
135
134
  self._initialize_from_dict(dictionary)
136
135
 
137
136
  def _initialize_from_dict(self, dictionary):
@@ -326,7 +325,7 @@ class FileHeader:
326
325
  """
327
326
  file_stats = os.stat(self.filename)
328
327
  sys_file_size = file_stats.st_size
329
- cal_file_size = sum(self.bytes) + 2 * len(self.bytes)
328
+ cal_file_size = sum((self.bytes).astype(int)) + 2 * len(self.bytes)
330
329
 
331
330
  check = dict()
332
331
 
@@ -435,7 +434,7 @@ def flead_dict(fid, dim=2):
435
434
  "False Target Thresh": "int64",
436
435
  "Spare 1": "int64",
437
436
  "Transmit Lag Dist": "int64",
438
- "CPU Serial No": "int64",
437
+ "CPU Serial No": "int128",
439
438
  "System Bandwidth": "int64",
440
439
  "System Power": "int64",
441
440
  "Spare 2": "int64",
@@ -447,9 +446,15 @@ def flead_dict(fid, dim=2):
447
446
  counter = 1
448
447
  for key, value in fname.items():
449
448
  if dim == 2:
450
- flead[key] = getattr(np, value)(fid[:][counter])
449
+ if key == "CPU Serial No":
450
+ flead[key] = np.uint64(fid[:][counter])
451
+ else:
452
+ flead[key] = np.int64(fid[:][counter])
451
453
  elif dim == 1:
452
- flead[key] = getattr(np, value)(fid[counter])
454
+ if key == "CPU Serial No":
455
+ flead[key] = np.uint64(fid[counter])
456
+ else:
457
+ flead[key] = np.int64(fid[counter])
453
458
  else:
454
459
  print("ERROR: Higher dimensions not allowed")
455
460
  sys.exit()
@@ -505,6 +510,7 @@ class FixedLeader:
505
510
  )
506
511
  self.warning = pyreadrdi.ErrorCode.get_message(self.error)
507
512
 
513
+ self.data = np.uint64(self.data)
508
514
  self.fleader = flead_dict(self.data)
509
515
  self._initialize_from_dict(DotDict(json_file_path="flmeta.json"))
510
516
 
@@ -800,7 +806,8 @@ def vlead_dict(vid):
800
806
 
801
807
  counter = 1
802
808
  for key, value in vname.items():
803
- vlead[key] = getattr(np, value)(vid[:][counter])
809
+ # vlead[key] = getattr(np, value)(vid[:][counter])
810
+ vlead[key] = vid[:][counter]
804
811
  counter += 1
805
812
 
806
813
  return vlead
@@ -877,7 +884,7 @@ class VariableLeader:
877
884
  setattr(getattr(self, key), "data", self.data[i])
878
885
  i = i + 1
879
886
 
880
- def bit_result(self):
887
+ def bitresult(self):
881
888
  """
882
889
  Extracts Bit Results from Variable Leader (Byte 13 & 14)
883
890
  This field is part of the WorkHorse ADCP’s Built-in Test function.
@@ -956,8 +963,6 @@ class VariableLeader:
956
963
 
957
964
  scale_factor = scale_list.get(fixclass["Frequency"])
958
965
 
959
- print(fixclass["Frequency"])
960
-
961
966
  channel["Xmit Voltage"] = adc1 * (scale_factor[0] / 1000000)
962
967
 
963
968
  channel["Xmit Current"] = adc0 * (scale_factor[1] / 1000000)
@@ -974,6 +979,80 @@ class VariableLeader:
974
979
 
975
980
  return channel
976
981
 
982
+ def error_status_word(self, esw=1):
983
+ bitset1 = (
984
+ "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
+
993
+ bitset2 = (
994
+ "Pinging",
995
+ "Not Used 1",
996
+ "Not Used 2",
997
+ "Not Used 3",
998
+ "Not Used 4",
999
+ "Not Used 5",
1000
+ "Cold Wakeup occured",
1001
+ "Unknown Wakeup occured",
1002
+ )
1003
+
1004
+ bitset3 = (
1005
+ "Clock Read error occured",
1006
+ "Unexpected alarm",
1007
+ "Clock jump forward",
1008
+ "Clock jump backward",
1009
+ "Not Used 6",
1010
+ "Not Used 7",
1011
+ "Not Used 8",
1012
+ "Not Used 9",
1013
+ )
1014
+
1015
+ bitset4 = (
1016
+ "Not Used 10",
1017
+ "Not Used 11",
1018
+ "Not Used 12",
1019
+ "Power Fail Unrecorded",
1020
+ "Spurious level 4 intr DSP",
1021
+ "Spurious level 5 intr UART",
1022
+ "Spurious level 6 intr CLOCK",
1023
+ "Level 7 interrup occured",
1024
+ )
1025
+
1026
+ if esw == 1:
1027
+ bitset = bitset1
1028
+ errorarray = self.vleader["Error Status Word 1"]
1029
+ elif esw == 2:
1030
+ bitset = bitset2
1031
+ errorarray = self.vleader["Error Status Word 2"]
1032
+ elif esw == 3:
1033
+ bitset = bitset3
1034
+ errorarray = self.vleader["Error Status Word 3"]
1035
+ else:
1036
+ bitset = bitset4
1037
+ errorarray = self.vleader["Error Status Word 4"]
1038
+
1039
+ errorstatus = dict()
1040
+ # bitarray = np.zeros(32, dtype='str')
1041
+
1042
+ for item in bitset:
1043
+ errorstatus[item] = np.array([])
1044
+
1045
+ for data in errorarray:
1046
+ byte_split = format(data, "08b")
1047
+ bitposition = 0
1048
+ for item in bitset:
1049
+ errorstatus[item] = np.append(
1050
+ errorstatus[item], byte_split[bitposition]
1051
+ )
1052
+ bitposition += 1
1053
+
1054
+ return errorstatus
1055
+
977
1056
 
978
1057
  class Velocity:
979
1058
  """
@@ -1027,7 +1106,7 @@ class Velocity:
1027
1106
  self.cells = cell
1028
1107
  self.beams = beam
1029
1108
 
1030
- self.units = "mm/s"
1109
+ self.unit = "mm/s"
1031
1110
  self.missing_value = "-32768"
1032
1111
  self.scale_factor = 1
1033
1112
  self.valid_min = -32768
@@ -1086,7 +1165,7 @@ class Correlation:
1086
1165
  self.cells = cell
1087
1166
  self.beams = beam
1088
1167
 
1089
- self.units = ""
1168
+ self.unit = ""
1090
1169
  self.scale_factor = 1
1091
1170
  self.valid_min = 0
1092
1171
  self.valid_max = 255
@@ -1145,7 +1224,7 @@ class Echo:
1145
1224
  self.cells = cell
1146
1225
  self.beams = beam
1147
1226
 
1148
- self.units = "counts"
1227
+ self.unit = "counts"
1149
1228
  self.scale_factor = "0.45"
1150
1229
  self.valid_min = 0
1151
1230
  self.valid_max = 255
@@ -1204,7 +1283,7 @@ class PercentGood:
1204
1283
  self.cells = cell
1205
1284
  self.beams = beam
1206
1285
 
1207
- self.units = "percent"
1286
+ self.unit = "percent"
1208
1287
  self.valid_min = 0
1209
1288
  self.valid_max = 100
1210
1289
  self.long_name = "Percent Good"
@@ -1262,7 +1341,7 @@ class Status:
1262
1341
  self.cells = cell
1263
1342
  self.beams = beam
1264
1343
 
1265
- self.units = ""
1344
+ self.unit = ""
1266
1345
  self.valid_min = 0
1267
1346
  self.valid_max = 1
1268
1347
  self.long_name = "Status Data Format"
@@ -1390,6 +1469,47 @@ class ReadFile:
1390
1469
  warning_array["Status"] = self.status.warning
1391
1470
  ensemble_array["Status"] = self.status.ensembles
1392
1471
 
1472
+ # Add Time Axis
1473
+ year = self.variableleader.vleader["RTC Year"]
1474
+ month = self.variableleader.vleader["RTC Month"]
1475
+ day = self.variableleader.vleader["RTC Day"]
1476
+ hour = self.variableleader.vleader["RTC Hour"]
1477
+ minute = self.variableleader.vleader["RTC Minute"]
1478
+ second = self.variableleader.vleader["RTC Second"]
1479
+ year = year + 2000
1480
+ date_df = pd.DataFrame(
1481
+ {
1482
+ "year": year,
1483
+ "month": month,
1484
+ "day": day,
1485
+ "hour": hour,
1486
+ "minute": minute,
1487
+ "second": second,
1488
+ }
1489
+ )
1490
+ self.time = pd.to_datetime(date_df)
1491
+
1492
+ # Depth
1493
+ # Create a depth axis with mean depth in 'm'
1494
+ cell1 = self.fixedleader.field()["Cells"]
1495
+ bin1dist1 = self.fixedleader.field()["Bin 1 Dist"] / 100
1496
+ depth_cell_len1 = self.fixedleader.field()["Depth Cell Len"] / 100
1497
+ beam_direction1 = self.fixedleader.system_configuration()["Beam Direction"]
1498
+ mean_depth = np.mean(self.variableleader.vleader["Depth of Transducer"]) / 10
1499
+ mean_depth = np.trunc(mean_depth)
1500
+ if beam_direction1.lower() == "up":
1501
+ sgn = -1
1502
+ else:
1503
+ sgn = 1
1504
+ first_depth = mean_depth + sgn * bin1dist1
1505
+ last_depth = first_depth + sgn * cell1 * depth_cell_len1
1506
+ z = np.arange(first_depth, last_depth, sgn * depth_cell_len1)
1507
+ self.depth = z
1508
+
1509
+ # Add all attributes/method/data from FixedLeader and VariableLeader
1510
+ self._copy_attributes_from_var()
1511
+
1512
+ # Error Codes and Warnings
1393
1513
  self.error_codes = error_array
1394
1514
  self.warnings = warning_array
1395
1515
  self.ensemble_array = ensemble_array
@@ -1405,6 +1525,33 @@ class ReadFile:
1405
1525
  else:
1406
1526
  self.isWarning = True
1407
1527
 
1528
+ # Add additional attributes
1529
+ # Ensemble
1530
+ dtens = self.ensemble_value_array
1531
+ minens = np.min(dtens)
1532
+ self.ensembles = minens
1533
+
1534
+ # Add attribute that lists all variables/functions
1535
+ self.list_vars = list(vars(self).keys())
1536
+
1537
+ def _copy_attributes_from_var(self):
1538
+ for attr_name, attr_value in self.variableleader.__dict__.items():
1539
+ # Copy each attribute of var into self
1540
+ setattr(self, attr_name, attr_value)
1541
+ for attr_name, attr_value in self.fixedleader.__dict__.items():
1542
+ # Copy each attribute of var into self
1543
+ setattr(self, attr_name, attr_value)
1544
+
1545
+ def __getattr__(self, name):
1546
+ # Delegate attribute/method access to self.var if not found in self
1547
+ if hasattr(self.variableleader, name):
1548
+ return getattr(self.variableleader, name)
1549
+ if hasattr(self.fixedleader, name):
1550
+ return getattr(self.fixedleader, name)
1551
+ raise AttributeError(
1552
+ f"'{self.__class__.__name__}' object has no attribute '{name}'"
1553
+ )
1554
+
1408
1555
  def fixensemble(self, min_cutoff=0):
1409
1556
  """
1410
1557
  Fixes the ensemble size across all data types in the file if they differ.