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.
Files changed (39) hide show
  1. pyadps/Home_Page.py +11 -5
  2. pyadps/pages/01_Read_File.py +623 -215
  3. pyadps/pages/02_View_Raw_Data.py +97 -41
  4. pyadps/pages/03_Download_Raw_File.py +200 -67
  5. pyadps/pages/04_Sensor_Health.py +905 -0
  6. pyadps/pages/05_QC_Test.py +493 -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 +623 -0
  10. pyadps/pages/09_Add-Ons.py +168 -0
  11. pyadps/utils/__init__.py +5 -3
  12. pyadps/utils/autoprocess.py +371 -80
  13. pyadps/utils/logging_utils.py +269 -0
  14. pyadps/utils/metadata/config.ini +22 -4
  15. pyadps/utils/metadata/demo.000 +0 -0
  16. pyadps/utils/metadata/flmeta.json +420 -420
  17. pyadps/utils/metadata/vlmeta.json +611 -565
  18. pyadps/utils/multifile.py +292 -0
  19. pyadps/utils/plotgen.py +505 -3
  20. pyadps/utils/profile_test.py +720 -125
  21. pyadps/utils/pyreadrdi.py +164 -92
  22. pyadps/utils/readrdi.py +436 -186
  23. pyadps/utils/script.py +197 -147
  24. pyadps/utils/sensor_health.py +120 -0
  25. pyadps/utils/signal_quality.py +472 -68
  26. pyadps/utils/velocity_test.py +79 -31
  27. pyadps/utils/writenc.py +222 -39
  28. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/METADATA +13 -14
  29. pyadps-0.3.0.dist-info/RECORD +35 -0
  30. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/WHEEL +1 -1
  31. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/entry_points.txt +1 -0
  32. pyadps/pages/04_QC_Test.py +0 -334
  33. pyadps/pages/05_Profile_Test.py +0 -575
  34. pyadps/pages/06_Velocity_Test.py +0 -341
  35. pyadps/pages/07_Write_File.py +0 -452
  36. pyadps/utils/cutbin.py +0 -413
  37. pyadps/utils/regrid.py +0 -279
  38. pyadps-0.2.1b0.dist-info/RECORD +0 -31
  39. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/LICENSE +0 -0
pyadps/utils/pyreadrdi.py CHANGED
@@ -3,17 +3,17 @@ pyreadrdi.py
3
3
 
4
4
  Module Overview
5
5
  ---------------
6
- This module provides functionalities to read and parse RDI ADCP files.
7
- It includes functions for reading file headers, fixed and variable leaders,
6
+ This module provides functionalities to read and parse RDI ADCP files.
7
+ It includes functions for reading file headers, fixed and variable leaders,
8
8
  and data types like velocity, correlation, echo intensity, and percent good.
9
- Currently reads only PD0 format.
9
+ Currently reads only PD0 format.
10
10
 
11
11
  Modules
12
12
  -------------------
13
13
  - fileheader: Function to read and parse the file header information.
14
14
  - fixedleader: Function to read and parse the fixed leader section of an RDI file.
15
15
  - variableleader: Function to read and parse the variable leader section of an RDI file.
16
- - datatype: Function to read and parse 3D data types.
16
+ - datatype: Function to read and parse 3D data types.
17
17
  - ErrorCode: Enum class to define and manage error codes for file operations.
18
18
 
19
19
  Creation Date
@@ -22,15 +22,15 @@ Creation Date
22
22
 
23
23
  Last Modified Date
24
24
  --------------
25
- 2024-09-01
25
+ 2025-10-01
26
26
 
27
27
  Version
28
28
  -------
29
- 0.2.0
29
+ 0.3.0
30
30
 
31
31
  Author
32
32
  ------
33
- [P. Amol] <your.email@example.com>
33
+ [P. Amol] <prakashamol@gmail.com>
34
34
 
35
35
  License
36
36
  -------
@@ -57,7 +57,7 @@ Examples
57
57
  >>> vel_data = datatype('example.rdi', "velocity")
58
58
  >>> vel_data = datatype('example.rdi', "echo", beam=4, cell=20)
59
59
 
60
- Other add-on functions and classes inlcude bcolors, safe_open, and ErrorCode.
60
+ Other add-on functions and classes inlcude bcolors, safe_open, and ErrorCode.
61
61
  Examples (add-on)
62
62
  -------------------
63
63
  >>> error = ErrorCode.FILE_NOT_FOUND
@@ -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
 
@@ -403,65 +403,85 @@ def fileheader(rdi_file):
403
403
  bfile.seek(0, 0)
404
404
  bskip = i = 0
405
405
  hid = [None] * 5
406
- while byt := bfile.read(6):
407
- hid[0], hid[1], hid[2], hid[3], hid[4] = unpack("<BBHBB", byt)
408
- headerid = np.append(headerid, np.int8(hid[0]))
409
- sourceid = np.append(sourceid, np.int16(hid[1]))
410
- byte = np.append(byte, np.int16(hid[2]))
411
- spare = np.append(spare, np.int16(hid[3]))
412
- datatype = np.append(datatype, np.int16(hid[4]))
413
-
414
- # dbyte = bfile.read(2 * datatype[i])
415
- dbyte, error = safe_read(bfile, 2 * datatype[i])
416
- if dbyte is None:
406
+ try:
407
+ while byt := bfile.read(6):
408
+ hid[0], hid[1], hid[2], hid[3], hid[4] = unpack("<BBHBB", byt)
409
+ headerid = np.append(headerid, np.int8(hid[0]))
410
+ sourceid = np.append(sourceid, np.int16(hid[1]))
411
+ byte = np.append(byte, np.int16(hid[2]))
412
+ spare = np.append(spare, np.int16(hid[3]))
413
+ datatype = np.append(datatype, np.int16(hid[4]))
414
+
415
+ # dbyte = bfile.read(2 * datatype[i])
416
+ dbyte, error = safe_read(bfile, 2 * datatype[i])
417
+ if dbyte is None:
418
+ if i == 0:
419
+ error_code = error.code
420
+ dummytuple = ([], [], [], [], [], ensemble, error_code)
421
+ return dummytuple
422
+ else:
423
+ break
424
+
425
+ # Check for id and datatype errors
417
426
  if i == 0:
418
- error_code = error.code
419
- dummytuple = ([], [], [], [], [], ensemble, error_code)
420
- return dummytuple
427
+ if headerid[0] != 127 or sourceid[0] != 127:
428
+ error = ErrorCode.WRONG_RDIFILE_TYPE
429
+ print(bcolors.FAIL + error.message + bcolors.ENDC)
430
+ error_code = error.code
431
+ dummytuple = ([], [], [], [], [], ensemble, error_code)
432
+ return dummytuple
421
433
  else:
422
- break
434
+ if headerid[i] != 127 or sourceid[i] != 127:
435
+ error = ErrorCode.ID_NOT_FOUND
436
+ print(bcolors.FAIL + error.message)
437
+ print(f"Ensembles reset to {i}" + bcolors.ENDC)
438
+ break
423
439
 
424
- # Check for id and datatype errors
425
- if i == 0:
426
- if headerid[0] != 127 or sourceid[0] != 127:
427
- error = ErrorCode.WRONG_RDIFILE_TYPE
428
- print(bcolors.FAIL + error.message + bcolors.ENDC)
440
+ if datatype[i] != datatype[i - 1]:
441
+ error = ErrorCode.DATATYPE_MISMATCH
442
+ print(bcolors.FAIL + error.message)
443
+ print(f"Data Types for ensemble {i} is {datatype[i - 1]}.")
444
+ print(f"Data Types for ensemble {i + 1} is {datatype[i]}.")
445
+ print(f"Ensembles reset to {i}" + bcolors.ENDC)
446
+ break
447
+
448
+ try:
449
+ data = unpack("H" * datatype[i], dbyte)
450
+ address_offset.append(data)
451
+ except:
452
+ error = ErrorCode.FILE_CORRUPTED
429
453
  error_code = error.code
454
+ dummytuple = ([], [], [], [], [], ensemble, error_code)
430
455
  return dummytuple
431
- else:
432
- if headerid[i] != 127 or sourceid[i] != 127:
433
- error = ErrorCode.ID_NOT_FOUND
434
- print(bcolors.FAIL + error.message + bcolors.ENDC)
435
- break
436
-
437
- if datatype[i] != datatype[i - 1]:
438
- error = ErrorCode.DATATYPE_MISMATCH
439
- print(bcolors.FAIL + error.message)
440
- print(f"Data Types for ensemble {i} is {datatype[i - 1]}.")
441
- print(f"Data Types for ensemble {i + 1} is {datatype[i]}.")
442
- print(f"Ensembles reset to {i}" + bcolors.ENDC)
443
- break
444
-
445
- data = unpack("H" * datatype[i], dbyte)
446
- address_offset.append(data)
447
-
448
- skip_array = [None] * datatype[i]
449
- for dtype in range(datatype[i]):
450
- bseek = int(bskip) + int(address_offset[i][dtype])
451
- bfile.seek(bseek, 0)
452
- readbyte = bfile.read(2)
453
- skip_array[dtype] = int.from_bytes(
454
- readbyte, byteorder="little", signed=False
455
- )
456
-
457
- dataid.append(skip_array)
458
- # bytekip is the number of bytes to skip to reach
459
- # an ensemble from beginning of file.
460
- # ?? Should byteskip be from current position ??
461
- bskip = int(bskip) + int(byte[i]) + 2
462
- bfile.seek(bskip, 0)
463
- byteskip = np.append(byteskip, np.int32(bskip))
464
- i += 1
456
+
457
+ skip_array = [None] * datatype[i]
458
+ for dtype in range(datatype[i]):
459
+ bseek = int(bskip) + int(address_offset[i][dtype])
460
+ bfile.seek(bseek, 0)
461
+ readbyte = bfile.read(2)
462
+ skip_array[dtype] = int.from_bytes(
463
+ readbyte, byteorder="little", signed=False
464
+ )
465
+
466
+ dataid.append(skip_array)
467
+ # bytekip is the number of bytes to skip to reach
468
+ # an ensemble from beginning of file.
469
+ # ?? Should byteskip be from current position ??
470
+ bskip = int(bskip) + int(byte[i]) + 2
471
+ bfile.seek(bskip, 0)
472
+ byteskip = np.append(byteskip, np.int32(bskip))
473
+ i += 1
474
+ except (ValueError, StructError, OverflowError) as e:
475
+ # except:
476
+ print(bcolors.WARNING + "WARNING: The file is broken.")
477
+ print(
478
+ f"Function `fileheader` unable to extract data for ensemble {i + 1}. Total ensembles reset to {i}."
479
+ )
480
+ print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
481
+ print(f" Error Type: {type(e).__name__}")
482
+ print(f" Error Details: {e}")
483
+ error = ErrorCode.FILE_CORRUPTED
484
+ ensemble = i
465
485
 
466
486
  ensemble = i
467
487
  bfile.close()
@@ -548,13 +568,24 @@ def fixedleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=0):
548
568
  error.code = error_code
549
569
  error.message = error.get_message(error.code)
550
570
 
571
+ # Note: When processing data from ADCPs with older firmware,
572
+ # the instrument serial number may be missing. As a result,
573
+ # garbage value is recorded, which sometimes is too large for a standard 64-bit integer.
574
+ # The following variables are defined to replace garbage value with a missing value.
575
+ # Flag to track if a missing serial number is detected
576
+ is_serial_missing = False
577
+ # Define the maximum value for a standard signed int64
578
+ INT64_MAX = 2**63 - 1
579
+ # Define a missing value flag (0 is a safe unsigned integer choice)
580
+ MISSING_VALUE_FLAG = 0
581
+
551
582
  bfile.seek(0, 0)
552
583
  for i in range(ensemble):
553
584
  fbyteskip = None
554
585
  for count, item in enumerate(idarray[i]):
555
586
  if item in (0, 1):
556
- fbyteskip = offset[1][count]
557
- if fbyteskip == None:
587
+ fbyteskip = offset[0][count]
588
+ if fbyteskip is None:
558
589
  error = ErrorCode.ID_NOT_FOUND
559
590
  ensemble = i
560
591
  print(bcolors.WARNING + error.message)
@@ -596,6 +627,14 @@ def fixedleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=0):
596
627
  (fid[27][i], fid[28][i], fid[29][i]) = unpack("<BBH", bdata[38:42])
597
628
  # CPU board serial number (Big Endian)
598
629
  (fid[30][i]) = unpack(">Q", bdata[42:50])[0]
630
+ # Check for overflow only once to set the flag
631
+ if not is_serial_missing and fid[30][i] > INT64_MAX:
632
+ print(
633
+ bcolors.WARNING
634
+ + "WARNING: Missing serial number detected (old firmware). Flagging for replacement."
635
+ + bcolors.ENDC
636
+ )
637
+ is_serial_missing = True
599
638
  # (fid[30][i], fid[31][i])= struct.unpack('>II', packed_data)
600
639
  # fid[30][i] = int.from_bytes(bdata[42:50], byteorder="big", signed=False)
601
640
  # System bandwidth, system power & Spare
@@ -605,13 +644,14 @@ def fixedleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=0):
605
644
 
606
645
  bfile.seek(byteskip[i], 0)
607
646
 
608
- except (ValueError, StructError) as e:
647
+ except (ValueError, StructError, OverflowError) as e:
609
648
  print(bcolors.WARNING + "WARNING: The file is broken.")
610
649
  print(
611
650
  f"Function `fixedleader` unable to extract data for ensemble {i + 1}. Total ensembles reset to {i}."
612
651
  )
613
- print("Details from struct function:")
614
- print(f"An error occurred: {e}" + bcolors.ENDC)
652
+ print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
653
+ print(f" Error Type: {type(e).__name__}")
654
+ print(f" Error Details: {e}")
615
655
  error = ErrorCode.FILE_CORRUPTED
616
656
  ensemble = i
617
657
 
@@ -625,6 +665,20 @@ def fixedleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=0):
625
665
  ensemble = i
626
666
  bfile.close()
627
667
  error_code = error.code
668
+
669
+ if is_serial_missing:
670
+ print(
671
+ bcolors.OKBLUE
672
+ + "INFO: Replacing entire serial number array with missing value flag."
673
+ + bcolors.ENDC
674
+ )
675
+ # If Serial No. is missing, flag all data after Serial No.
676
+ fid[30] = [MISSING_VALUE_FLAG] * ensemble # Serial No.
677
+ fid[31] = [MISSING_VALUE_FLAG] * ensemble # System Bandwidth
678
+ fid[32] = [MISSING_VALUE_FLAG] * ensemble # System Power
679
+ fid[33] = [MISSING_VALUE_FLAG] * ensemble # Spare 2
680
+ fid[34] = [MISSING_VALUE_FLAG] * ensemble # Instrument No
681
+ fid[35] = [MISSING_VALUE_FLAG] * ensemble # Beam Angle
628
682
  fid = np.array(fid)
629
683
  data = fid[:, :ensemble]
630
684
  return (data, ensemble, error_code)
@@ -703,7 +757,7 @@ def variableleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=
703
757
  fbyteskip = None
704
758
  for count, item in enumerate(idarray[i]):
705
759
  if item in (128, 129):
706
- fbyteskip = offset[1][count]
760
+ fbyteskip = offset[0][count]
707
761
  if fbyteskip == None:
708
762
  error = ErrorCode.ID_NOT_FOUND
709
763
  ensemble = i
@@ -787,13 +841,14 @@ def variableleader(rdi_file, byteskip=None, offset=None, idarray=None, ensemble=
787
841
 
788
842
  bfile.seek(byteskip[i], 0)
789
843
 
790
- except (ValueError, StructError) as e:
844
+ except (ValueError, StructError, OverflowError) as e:
791
845
  print(bcolors.WARNING + "WARNING: The file is broken.")
792
846
  print(
793
847
  f"Function `variableleader` unable to extract data for ensemble {i + 1}. Total ensembles reset to {i}."
794
848
  )
795
- print("Details from struct function:")
796
- print(f"An error occurred: {e}" + bcolors.ENDC)
849
+ print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
850
+ print(f" Error Type: {type(e).__name__}")
851
+ print(f" Error Details: {e}")
797
852
  error = ErrorCode.FILE_CORRUPTED
798
853
  ensemble = i
799
854
 
@@ -883,7 +938,7 @@ def datatype(
883
938
 
884
939
  # These arguments are outputs of fixedleader function.
885
940
  # Makes the code faster if the fixedheader function is already executed.
886
- if cell == 0 or beam == 0:
941
+ if isinstance(cell, (np.integer, int)) or isinstance(beam, (np.integer, int)):
887
942
  flead, ensemble, fl_error_code = fixedleader(
888
943
  filename,
889
944
  byteskip=byteskip,
@@ -891,22 +946,25 @@ def datatype(
891
946
  idarray=idarray,
892
947
  ensemble=ensemble,
893
948
  )
894
- cell = int(flead[7][0])
895
- beam = int(flead[6][0])
949
+ cell = []
950
+ beam = []
951
+ cell = flead[7][:]
952
+ beam = flead[6][:]
896
953
  if fl_error_code != 0:
897
954
  error_code = fl_error_code
898
955
  else:
899
- cell = int(cell)
900
- beam = int(beam)
901
-
956
+ cell = cell
957
+ beam = beam
902
958
  # Velocity is 16 bits and all others are 8 bits.
903
959
  # Create empty array for the chosen variable name.
904
960
  if var_name == "velocity":
905
- var_array = np.zeros((beam, cell, ensemble), dtype="int16")
961
+ var_array = np.full(
962
+ (int(max(beam)), int(max(cell)), ensemble), -32768, dtype="int16"
963
+ )
906
964
  bitstr = "<h"
907
965
  bitint = 2
908
966
  else: # inserted
909
- var_array = np.zeros((beam, cell, ensemble), dtype="uint8")
967
+ var_array = np.zeros((int(max(beam)), int(max(cell)), ensemble), dtype="uint8")
910
968
  bitstr = "<B"
911
969
  bitint = 1
912
970
  # -----------------------------
@@ -935,7 +993,9 @@ def datatype(
935
993
  fbyteskip = None
936
994
  for count, item in enumerate(idarray[0][:]):
937
995
  if item in vid:
938
- fbyteskip = offset[1][count]
996
+ fbyteskip = []
997
+ for i in range(ensemble):
998
+ fbyteskip.append(int(offset[i][count]))
939
999
  break
940
1000
  if fbyteskip is None:
941
1001
  print(
@@ -947,16 +1007,28 @@ def datatype(
947
1007
  return (var_array, error.code)
948
1008
 
949
1009
  # READ DATA
950
- for i in range(ensemble):
951
- bfile.seek(fbyteskip, 1)
952
- bdata = bfile.read(2)
953
- for cno in range(cell):
954
- for bno in range(beam):
955
- bdata = bfile.read(bitint)
956
- varunpack = unpack(bitstr, bdata)
957
- var_array[bno][cno][i] = varunpack[0]
958
- bfile.seek(byteskip[i], 0)
959
- bfile.close()
1010
+ i = 0
1011
+ try:
1012
+ for i in range(ensemble):
1013
+ bfile.seek(fbyteskip[i], 1)
1014
+ bdata = bfile.read(2)
1015
+ for cno in range(int(cell[i])):
1016
+ for bno in range(int(beam[i])):
1017
+ bdata = bfile.read(bitint)
1018
+ varunpack = unpack(bitstr, bdata)
1019
+ var_array[bno][cno][i] = varunpack[0]
1020
+ bfile.seek(byteskip[i], 0)
1021
+ bfile.close()
1022
+ except (ValueError, StructError, OverflowError) as e:
1023
+ print(bcolors.WARNING + "WARNING: The file is broken.")
1024
+ print(
1025
+ f"Function `datatype` unable to extract {var_name} for ensemble {i + 1}. Total ensembles reset to {i}."
1026
+ )
1027
+ print(bcolors.UNDERLINE + "Details from struct function" + bcolors.ENDC)
1028
+ print(f" Error Type: {type(e).__name__}")
1029
+ print(f" Error Details: {e}")
1030
+ error = ErrorCode.FILE_CORRUPTED
1031
+ ensemble = i
960
1032
 
961
- data = var_array
1033
+ data = var_array[:, :, :ensemble]
962
1034
  return (data, ensemble, cell, beam, error_code)