pyadps 0.2.1b0__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/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
@@ -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[1][count]
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": "int64",
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
- flead[key] = getattr(np, value)(fid[:][counter])
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
- flead[key] = getattr(np, value)(fid[counter])
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 bit_result(self):
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.cutbin import CutBins
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.regrid import regrid2d, regrid3d
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, qc_check, qc_prompt)
12
- from pyadps.utils.velocity_test import (despike, flatline,
13
- magnetic_declination, velocity_cutoff)
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 = "/home/amol/Desktop/BGS11000.000"
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(fl, vel)
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(pgood, mask, pgt)
60
- mask = qc_check(cor, mask, ct)
61
- mask = qc_check(echo, mask, et)
62
- mask = ev_check(vel[3, :, :], mask, evt)
63
- mask = false_target(echo, mask, ft, threebeam=True)
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(vl, mask)
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(fl, vl, mask)
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(fl, vl, vel, -32768)
85
- z, echo_reg = regrid3d(fl, vl, echo, -32768)
86
- z, correlation_reg = regrid3d(fl, vl, cor, -32768)
87
- z, percentgood_reg = regrid3d(fl, vl, pgood, -32768)
88
- z, mask = regrid2d(fl, vl, mask, -32768)
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
- velocity = magnetic_declination(vel, lat, lon, depth, year)
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