RDI_PD0_IO.pl
changeset 45 5767cbe470a0
parent 44 b7654ea68af6
parent 43 b63fa355644c
child 47 494a76548e94
child 48 cdc74ebada81
equal deleted inserted replaced
44:b7654ea68af6 45:5767cbe470a0
     1 #======================================================================
     1 #======================================================================
     2 #                    R D I _ P D 0 _ I O . P L 
     2 #                    R D I _ P D 0 _ I O . P L 
     3 #                    doc: Sat Jan 18 14:54:43 2003
     3 #                    doc: Sat Jan 18 14:54:43 2003
     4 #                    dlm: Fri Feb  2 12:49:54 2018
     4 #                    dlm: Tue Jun 12 19:10:08 2018
     5 #                    (c) 2003 A.M. Thurnherr
     5 #                    (c) 2003 A.M. Thurnherr
     6 #					 uE-Info: 772 0 NIL 0 0 72 2 2 4 NIL ofnI
     6 #					 uE-Info: 115 72 NIL 0 0 72 2 2 4 NIL ofnI
     7 #======================================================================
     7 #======================================================================
     8 	
     8 	
     9 # Read RDI PD0 binary data files (*.[0-9][0-9][0-9])
     9 # Read RDI PD0 binary data files (*.[0-9][0-9][0-9])
    10 	
    10 	
    11 # HISTORY:
    11 # HISTORY:
    95 #				  - added support for RDI_PD0_IO::IGNORE_Y2K_CLOCK
    95 #				  - added support for RDI_PD0_IO::IGNORE_Y2K_CLOCK
    96 #	Dec  7, 2017: - added suppress_error to readHeader()
    96 #	Dec  7, 2017: - added suppress_error to readHeader()
    97 #	Dec 23, 2017: - BUG: could no longer read Anslope II raw files
    97 #	Dec 23, 2017: - BUG: could no longer read Anslope II raw files
    98 #				  - added support for patching ADCP time data
    98 #				  - added support for patching ADCP time data
    99 #				  - added support for RDI_PD0_IO::OVERRIDE_Y2K_CLOCK
    99 #				  - added support for RDI_PD0_IO::OVERRIDE_Y2K_CLOCK
       
   100 #	Feb  6, 2018: - added supprort for first_ens, last_ens in readData()
       
   101 #	Feb  7, 2018: - BUG: new params were not optional
       
   102 #				  - made read routines more permissive by allowing
       
   103 #					garbage between ensembles
       
   104 #	Mar 15, 2018: - BUG: WBPens() did not work for files with garbage
       
   105 #	Mar 16, 2018: - added skipped garbage warning
       
   106 #				  - BUG: garbage skipping did not work correctly for files w/o BT
       
   107 #	Mar 20, 2018: - BUG: garbage skipping STILL did not work correctly for files w/o BT
       
   108 #	Apr  9, 2018: - added last_bin argument to readData()
       
   109 #	Apr 10, 2018: - slight improvement (parameter range check)
       
   110 #	Apr 23, 2018: - make WBRens() work (again?) with BB150 data froim 1996
       
   111 #	Apr 24, 2018: - BUG: undefined lat_bin argument to readData() did not work
       
   112 #	Apr 30, 2018: - added support for repeated ensembles
       
   113 #				  - added warning on wrong ensemble length
       
   114 #	Jun  9, 2018: - removed double \n from warnings
       
   115 #	Jun 12, 2018: - BUG: IMPed files did not pass the garbage detection 
   100 	
   116 	
   101 # FIRMWARE VERSIONS:
   117 # FIRMWARE VERSIONS:
   102 #	It appears that different firmware versions generate different file
   118 #	It appears that different firmware versions generate different file
   103 #	structures. Currently (Sep 2005) these routines have been tested
   119 #	structures. Currently (Sep 2005) these routines have been tested
   104 #	with the following firmware versions (as reported by [listHdr]):
   120 #	with the following firmware versions (as reported by [listHdr]):
   117 #	- first ensemble uses default RDI DATA_SOURCE_ID because the LDEO_IX
   133 #	- first ensemble uses default RDI DATA_SOURCE_ID because the LDEO_IX
   118 #	  software assumes this
   134 #	  software assumes this
   119 #
   135 #
   120 #	- DATA_SOURCE_ID = 0x7F 					original TRDI PD0 file
   136 #	- DATA_SOURCE_ID = 0x7F 					original TRDI PD0 file
   121 #
   137 #
   122 #	- DATA_SOURCE_ID = 0xA0 | PATCHED_MASK		produced by IMP+LADP 
   138 #	- DATA_SOURCE_ID = 0xA0 | PATCHED_MASK		produced by IMP+LADCP, KVH+LADCP
   123 #		PATCHED_MASK & 0x04:						pitch value has been patched
   139 #		PATCHED_MASK & 0x04:						pitch value has been patched
   124 #		PATCHED_MASK & 0x02:						roll value has been patched
   140 #		PATCHED_MASK & 0x02:						roll value has been patched
   125 #		PATCHED_MASK & 0x01:						heading value has been patched
   141 #		PATCHED_MASK & 0x01:						heading value has been patched
   126 #			- PITCH & ROLL can be missing (0x8000 badval as in velocities)
   142 #			- PITCH & ROLL can be missing (0x8000 badval as in velocities)
   127 #			- HEADING can be missing (0xF000 badval, as 0x8000 is valid 327.68 heading)
   143 #			- HEADING can be missing (0xF000 badval, as 0x8000 is valid 327.68 heading)
   355 	my($BIT_errors) = 0;									# built-in-test errors
   371 	my($BIT_errors) = 0;									# built-in-test errors
   356 	
   372 	
   357 	my($FmtErr) = "%s: illegal %s Id 0x%04x at ensemble %d";
   373 	my($FmtErr) = "%s: illegal %s Id 0x%04x at ensemble %d";
   358 	
   374 	
   359 #----------------------------------------------------------------------
   375 #----------------------------------------------------------------------
   360 # skip to first valid ensemble (skip over initial garbage)
   376 # skip to next valid start of ensemble (skip over garbage)
   361 #----------------------------------------------------------------------
   377 #----------------------------------------------------------------------
   362 	
   378 	
   363 	sub skip_initial_trash(@)
   379 sub goto_next_ens(@)
   364 	{
   380 {
   365 		my($quiet) = @_;
   381 	my($fh,$return_skipped) = @_;							# if return_skipped not set, return file pos
   366 		my($buf,$dta);
   382 	my($buf,$dta);
   367 	
   383 
   368 		my($found) = 0; 									# zero consecutive 0x7f found
   384 	my($found) = 0; 										# zero consecutive 0x7f found
   369 		my($skipped) = 0;
   385 	my($skipped) = 0; my($garbage_start);
   370 		while ($found < 2) {
   386 	while ($found < 2) {
   371 			sysread(WBRF,$buf,1) == 1 || last;
   387 		sysread($fh,$buf,1) == 1 || last; 
   372 			($dta) = unpack('C',$buf);
   388 		($dta) = unpack('C',$buf);
   373 			if ($dta == 0x7f) {
   389 		if ($dta == 0x7f) {
   374 				$found++;
   390 			$found++;
   375 			} elsif ($found==1 && ($dta==0xE0 || ($dta&0xF0==0xA0 && $dta&0x0F<8))) {
   391 		} elsif ($found==1 &&
   376 				$found++;
   392 					($dta==0xE0	||									# from editPD0
   377 			} elsif ($found == 0) {
   393 					 (($dta&0xF0)==0xA0 && ($dta&0x0F)<8))) {		# from IMP+LADCP or KVH+LADCP
   378 				$skipped++;
   394 			$found++;
   379 			} else {
   395 		} elsif ($found == 0) {
   380 				$skipped += $found;
   396 			$garbage_start = sysseek($fh,0,1)-1 unless defined($garbage_start);
   381 				$found = 0;
   397 			$skipped++;
   382 			}
   398 		} else {											# here, found == 1 but 2nd byte was not found
   383 		}
   399 			$garbage_start = sysseek($fh,0,1)-$found unless defined($garbage_start);
   384 		die("$WBRcfn: no valid ensemble header found [$!]\n")
   400 			$skipped += $found;
   385 			if ($found < 2);
   401 			$found = 0;
   386 		printf(STDERR "WARNING: %d bytes of initial garbage\n",$skipped)
   402 		}
   387 			if ($skipped > 0 && !$quiet);
       
   388 		return sysseek(WBRF,-2,1);
       
   389 	}
   403 	}
       
   404 	my($fpos) = ($found < 2) ? undef : sysseek($fh,-2,1);
       
   405 	return $skipped if ($return_skipped);
       
   406 	
       
   407 	if ($skipped) {
       
   408 		if (eof($fh)) {
       
   409 #				04/18/18: disabled the following line of code because it is very common at
       
   410 #						  least with the older RDI instruments I am looking at in the context
       
   411 #						  of the SR1b repeat section analysis
       
   412 #			print(STDERR "WARNING (RDI_PD0_IO): PD0 file ends with $skipped garbage bytes\n");
       
   413 		} elsif ($garbage_start == 0) {
       
   414 			print(STDERR "WARNING (RDI_PD0_IO): PD0 file starts with $skipped garbage bytes\n");
       
   415 		} else {
       
   416 			print(STDERR "WARNING (RDI_PD0_IO): $skipped garbage bytes in PD0 file beginning at byte $garbage_start\n");
       
   417 		}
       
   418 	}
       
   419 	
       
   420 	return $fpos;
       
   421 }
   390 	
   422 	
   391 #----------------------------------------------------------------------
   423 #----------------------------------------------------------------------
   392 # readHeader(file_name,^dta) WBRhdr(^data)
   424 # readHeader(file_name,^dta) WBRhdr(^data)
   393 #	- read header data
   425 #	- read header data
   394 #	- also includes some data from 1st ens
   426 #	- also includes some data from 1st ens
   417     
   449     
   418 	#--------------------
   450 	#--------------------
   419 	# HEADER
   451 	# HEADER
   420 	#--------------------
   452 	#--------------------
   421 
   453 
   422 	skip_initial_trash();
   454 	my($skipped) = goto_next_ens(\*WBRF,1);	
       
   455    	printf(STDERR "WARNING: %d bytes of initial garbage\n",$skipped)
       
   456 		if ($skipped > 0);
       
   457 	
   423 	sysread(WBRF,$buf,6) == 6 || return undef;
   458 	sysread(WBRF,$buf,6) == 6 || return undef;
   424 	($hid,$did,$dta->{ENSEMBLE_BYTES},$dummy,$dta->{NUMBER_OF_DATA_TYPES})
   459 	($hid,$did,$dta->{ENSEMBLE_BYTES},$dummy,$dta->{NUMBER_OF_DATA_TYPES})
   425 		= unpack('CCvCC',$buf);
   460 		= unpack('CCvCC',$buf);
   426 	$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (hid)",$hid,0));
   461 	$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (hid)",$hid,0));
   427 	$did == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (did)",$did,0));
   462 	$did == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (did)",$did,0));
   453 	sysread(WBRF,$buf,2*$dta->{NUMBER_OF_DATA_TYPES})
   488 	sysread(WBRF,$buf,2*$dta->{NUMBER_OF_DATA_TYPES})
   454 		== 2*$dta->{NUMBER_OF_DATA_TYPES}
   489 		== 2*$dta->{NUMBER_OF_DATA_TYPES}
   455 			|| die("$WBRcfn: $!");
   490 			|| die("$WBRcfn: $!");
   456 	@WBRofs = unpack("v$dta->{NUMBER_OF_DATA_TYPES}",$buf);
   491 	@WBRofs = unpack("v$dta->{NUMBER_OF_DATA_TYPES}",$buf);
   457 #	for ($i=0; $i<$dta->{NUMBER_OF_DATA_TYPES}; $i++) {
   492 #	for ($i=0; $i<$dta->{NUMBER_OF_DATA_TYPES}; $i++) {
   458 #		printf(STDERR "\nWBRofs[$i] = %d",$WBRofs[$i]);
   493 #		printf(STDERR "WBRofs[$i] = %d",$WBRofs[$i]);
   459 #	}
   494 #	}
   460 
   495 
   461 
   496 
   462 	$dta->{HEADER_BYTES}					= $WBRofs[0];
   497 	$dta->{HEADER_BYTES}					= $WBRofs[0];
   463 	$dta->{FIXED_LEADER_BYTES}				= $WBRofs[1] - $WBRofs[0];
   498 	$dta->{FIXED_LEADER_BYTES}				= $WBRofs[1] - $WBRofs[0];
   696     
   731     
   697     return $dta;
   732     return $dta;
   698 }
   733 }
   699 
   734 
   700 #----------------------------------------------------------------------
   735 #----------------------------------------------------------------------
   701 # readData(file_name,^data) WBRens(nbins,fixed_leader_bytes,^data)
   736 # readData(file_name,^data[,first_ens,last_ens[,last_bin]]) 
   702 # 	- read all ensembles
   737 # 	- read ensembles
       
   738 #	- read all ensembles unless first_ens and last_ens are given
       
   739 #	- read all bins unless last_bin is given
   703 #----------------------------------------------------------------------
   740 #----------------------------------------------------------------------
   704 
   741 
   705 sub readData(@)
   742 sub readData(@)
   706 {
   743 {
   707 	my($fn,$dta) = @_;
   744 	my($fn,$dta,$fe,$le,$lb) = @_;
   708 	$WBRcfn = $fn;
   745 	$WBRcfn = $fn;
   709     open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
   746     open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
   710     WBRhdr($dta,1) || die("$WBRcfn: Insufficient Data\n");
   747     WBRhdr($dta,1) || die("$WBRcfn: Insufficient Data\n");
   711 	WBRens($dta->{N_BINS},$dta->{FIXED_LEADER_BYTES},
   748     $lb = $dta->{N_BINS}
   712 		   \@{$dta->{ENSEMBLE}});
   749 		unless (numberp($lb) && $lb>=1 && $lb<=$dta->{N_BINS});
       
   750 	WBRens($lb,$dta->{FIXED_LEADER_BYTES},\@{$dta->{ENSEMBLE}},$fe,$le);
   713 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
   751 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
   714 		if ($BIT_errors);
   752 		if ($BIT_errors);
   715 }
   753 }
   716 
   754 
   717 sub WBRens($$$)
   755 sub WBRens(@)
   718 {
   756 {
   719 	my($nbins,$fixed_leader_bytes,$E) = @_;
   757 	my($nbins,$fixed_leader_bytes,$E,$fe,$le) = @_;
   720 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs,@WBRofs);
   758 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs,@WBRofs);
   721 	my($ens,$ensNo,$dayStart,$ens_length,$hid,$did,$ndt);
   759 	my($ens,$ensNo,$dayStart,$ens_length,$hid,$did,$ndt,$el);
   722 
   760 
   723     sysseek(WBRF,0,0) || die("$WBRcfn: $!");
   761     sysseek(WBRF,0,0) || die("$WBRcfn: $!");
   724 	$start_ens = skip_initial_trash(1);
   762 ENSEMBLE:
   725 	for ($ens=0; 1; $ens++,$start_ens+=$ens_length+2) {
   763 	for ($ens=0; 1; $ens++) {
   726 #		print(STDERR "ens = $ens\n");
   764 		$start_ens = goto_next_ens(\*WBRF);
   727 #		print(STDERR "start_ens = $start_ens\n");
   765 		last unless defined($start_ens);
       
   766 
       
   767 		#----------------------------------------
       
   768 		# Handle first_ens and last_ens
       
   769 		#----------------------------------------
       
   770 
       
   771 		if (defined($fe) && $ens>0 && ${$E}[$ens-1]->{NUMBER}<$fe) {					# delete previous ensemble
       
   772 			pop(@{$E}); $ens--;
       
   773 		}
       
   774 
       
   775 		if (defined($le) && $ens>0 && ${$E}[$ens-1]->{NUMBER}>$le) {					# delete previous ensemble and finish
       
   776 			pop(@{$E}); $ens--;
       
   777 			last;
       
   778 		}
   728 
   779 
   729 		#----------------------------------------
   780 		#----------------------------------------
   730 		# Get ensemble length and # of data types 
   781 		# Get ensemble length and # of data types 
   731 		#----------------------------------------
   782 		#----------------------------------------
   732 
   783 
   733 		sysseek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
   784 		sysseek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
   734 		sysread(WBRF,$buf,6) == 6 || last;
   785 		sysread(WBRF,$buf,6) == 6 || last;
   735 		($hid,$did,$ens_length,$dummy,$ndt) = unpack('CCvCC',$buf);
   786 		($hid,$did,$el,$dummy,$ndt) = unpack('CCvCC',$buf);
   736 		$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,defined($ensNo)?$ensNo+1:0));
   787 		$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,defined($ensNo)?$ensNo+1:0));
   737 		${$E}[$ens]->{DATA_SOURCE_ID} = $did;
   788 		${$E}[$ens]->{DATA_SOURCE_ID} = $did;
   738 		if ($did == 0x7f) {
   789 		if ($did == 0x7f) {
   739 			${$E}[$ens]->{PRODUCER} = 'TRDI ADCP';
   790 			${$E}[$ens]->{PRODUCER} = 'TRDI ADCP';
   740 		} elsif ($did&0xF0 == 0xA0) {
   791 		} elsif ($did&0xF0 == 0xA0) {
   743 			${$E}[$ens]->{PRODUCER} = 'editPD0 (Thurnherr software)';
   794 			${$E}[$ens]->{PRODUCER} = 'editPD0 (Thurnherr software)';
   744 		} else {
   795 		} else {
   745 			${$E}[$ens]->{PRODUCER} = 'unknown';
   796 			${$E}[$ens]->{PRODUCER} = 'unknown';
   746 	    }
   797 	    }
   747 
   798 
   748 ##		printf(STDERR "\n$WBRcfn: WARNING: unexpected number of data types (%d, ens=$ens)\n",$ndt),last
   799 		if (defined($ens_length) && ($el != $ens_length)) {
       
   800 			print(STDERR "WARNING (RDI_PD0_IO): ensemble ${$E}[$#{$E}]->{NUMBER} skipped (unexpected length)\n");
       
   801 			pop(@{$E});
       
   802 			$ens--;
       
   803 			next;
       
   804 		}
       
   805 		
       
   806 		$ens_length = $el;
       
   807 
       
   808 ##		printf(STDERR "$WBRcfn: WARNING: unexpected number of data types (%d, ens=$ens)\n",$ndt),last
   749 ##				unless ($ndt == 6 || $ndt == 7);
   809 ##				unless ($ndt == 6 || $ndt == 7);
   750 		sysread(WBRF,$buf,2*$ndt) == 2*$ndt || die("$WBRcfn: $!");
   810 		sysread(WBRF,$buf,2*$ndt) == 2*$ndt || die("$WBRcfn: $!");
   751 		@WBRofs = unpack("v$ndt",$buf);
   811 		@WBRofs = unpack("v$ndt",$buf);
   752 		$fixed_leader_bytes = $WBRofs[1] - $WBRofs[0];
   812 		$fixed_leader_bytes = $WBRofs[1] - $WBRofs[0];
   753 #		print(STDERR "@WBRofs\n");
   813 #		print(STDERR "@WBRofs\n");
   761 		# final ensemble.
   821 		# final ensemble.
   762 
   822 
   763 		sysseek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
   823 		sysseek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
   764 		unless ((sysread(WBRF,$buf,$ens_length) == $ens_length) &&				# incomplete ensemble
   824 		unless ((sysread(WBRF,$buf,$ens_length) == $ens_length) &&				# incomplete ensemble
   765 				(sysread(WBRF,$cs,2) == 2)) {
   825 				(sysread(WBRF,$cs,2) == 2)) {
       
   826 #			print(STDERR "INCOMPLETE ENSEMBLE\n");
   766 			pop(@{$E});
   827 			pop(@{$E});
   767 			last;
   828 			last;
   768 		}
   829 		}
   769 
   830 
   770 		unless (unpack('%16C*',$buf) == unpack('v',$cs)) {						# bad checksum
   831 		unless (unpack('%16C*',$buf) == unpack('v',$cs)) {						# bad checksum
   771 			pop(@{$E});
   832 #			print(STDERR "BAD CHECKSUM\n");
   772 			last;
   833 			pop(@{$E}); $ens--;
   773 #			next;																# using this might make the code work
   834 			next;
   774 		}																		# for files with isolated bad ensembles
   835 		}		
   775 
   836 
   776 		#------------------------------
   837 		#------------------------------
   777 		# Variable Leader
   838 		# Variable Leader
   778 		#------------------------------
   839 		#------------------------------
   779 	
   840 	
   801 #			sysseek(WBRF,7,1) || die("$WBRcfn: $!");							# use Y2K RTC instead
   862 #			sysseek(WBRF,7,1) || die("$WBRcfn: $!");							# use Y2K RTC instead
   802 #		}
   863 #		}
   803 
   864 
   804 		sysread(WBRF,$buf,1) == 1 || die("$WBRcfn: $!");
   865 		sysread(WBRF,$buf,1) == 1 || die("$WBRcfn: $!");
   805 		$ensNo += unpack('C',$buf) << 16;
   866 		$ensNo += unpack('C',$buf) << 16;
       
   867 
       
   868 		for (my($i)=$ens; $i>0; $i--) {											# check for duplicate ens; e.g. 2018 S4P 24UL
       
   869 			if (${$E}[$i]->{NUMBER} == $ensNo) {									
       
   870 				print(STDERR "WARNING (RDI_PD0_IO): duplicate ensemble $ensNo skipped\n");
       
   871 				pop(@{$E});
       
   872 				$ens--;
       
   873 				next ENSEMBLE;
       
   874 			}
       
   875 		}
       
   876 			
   806 		${$E}[$ens]->{NUMBER} = $ensNo;
   877 		${$E}[$ens]->{NUMBER} = $ensNo;
   807 		
   878 		
   808 		sysread(WBRF,$buf,30) == 30 || die("$WBRcfn: $!");
   879 		sysread(WBRF,$buf,30) == 30 || die("$WBRcfn: $!");
   809 		(${$E}[$ens]->{BUILT_IN_TEST_ERROR},${$E}[$ens]->{SPEED_OF_SOUND},
   880 		(${$E}[$ens]->{BUILT_IN_TEST_ERROR},${$E}[$ens]->{SPEED_OF_SOUND},
   810 		 ${$E}[$ens]->{XDUCER_DEPTH},${$E}[$ens]->{HEADING},
   881 		 ${$E}[$ens]->{XDUCER_DEPTH},${$E}[$ens]->{HEADING},
   886 									 	  ${$E}[$ens]->{SECONDS});
   957 									 	  ${$E}[$ens]->{SECONDS});
   887 		${$E}[$ens]->{DAYNO}
   958 		${$E}[$ens]->{DAYNO}
   888 			= &_dayNo(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},${$E}[$ens]->{DAY},
   959 			= &_dayNo(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},${$E}[$ens]->{DAY},
   889 					  ${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},${$E}[$ens]->{SECONDS});
   960 					  ${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},${$E}[$ens]->{SECONDS});
   890 
   961 
   891 		# when analyzing an STA file from an OS75 SADCP (Poseidion),
   962 		# when analyzing an STA file from an OS75 SADCP (Poseidon),
   892 		# I noticed that there is no time information. This causes
   963 		# I noticed that there is no time information. This causes
   893 		# timegm to bomb. 
   964 		# timegm to bomb. 
   894 		if (${$E}[$ens]->{MONTH} == 0) {					# no time info
   965 		if (${$E}[$ens]->{MONTH} == 0) {					# no time info
   895 			${$E}[$ens]->{UNIX_TIME} = 0;
   966 			${$E}[$ens]->{UNIX_TIME} = 0;
   896 			${$E}[$ens]->{SECNO} = 0;
   967 			${$E}[$ens]->{SECNO} = 0;
   897         } else {
   968         } else {
   898 #			print(STDERR "\n[$ens]->${$E}[$ens]->{MINUTE}:${$E}[$ens]->{HOUR},${$E}[$ens]->{DAY},${$E}[$ens]->{MONTH},${$E}[$ens]->{YEAR}-<\n");
   969 #			print(STDERR "[$ens]->${$E}[$ens]->{MINUTE}:${$E}[$ens]->{HOUR},${$E}[$ens]->{DAY},${$E}[$ens]->{MONTH},${$E}[$ens]->{YEAR}-<\n");
   899 			${$E}[$ens]->{UNIX_TIME}
   970 			${$E}[$ens]->{UNIX_TIME}
   900 				= timegm(0,${$E}[$ens]->{MINUTE},
   971 				= timegm(0,${$E}[$ens]->{MINUTE},
   901 						   ${$E}[$ens]->{HOUR},
   972 						   ${$E}[$ens]->{HOUR},
   902 						   ${$E}[$ens]->{DAY},
   973 						   ${$E}[$ens]->{DAY},
   903 						   ${$E}[$ens]->{MONTH}-1,			# timegm jan==0!!!
   974 						   ${$E}[$ens]->{MONTH}-1,			# timegm jan==0!!!
   986 
  1057 
   987 		$id == 0x0400 ||
  1058 		$id == 0x0400 ||
   988 			die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,$ensNo));
  1059 			die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,$ensNo));
   989 
  1060 
   990 		for ($i=0,$bin=0; $bin<$nbins; $bin++) {
  1061 		for ($i=0,$bin=0; $bin<$nbins; $bin++) {
   991 #			printf(STDERR "%-GOOD($bin): ");
       
   992 			for ($beam=0; $beam<4; $beam++,$i++) {
  1062 			for ($beam=0; $beam<4; $beam++,$i++) {
   993 #				printf(STDERR "$dta[$i] ");
       
   994 				${$E}[$ens]->{PERCENT_GOOD}[$bin][$beam] = $dta[$i];
  1063 				${$E}[$ens]->{PERCENT_GOOD}[$bin][$beam] = $dta[$i];
   995 			}
  1064 			}
   996 #			printf(STDERR "\n");
       
   997 		}
  1065 		}
   998 
  1066 
   999 		#-----------------------------------------
  1067 		#-----------------------------------------
  1000 		# Bottom-Track Data
  1068 		# Bottom-Track Data
  1001 		#	- scan through remaining data types
  1069 		#	- scan through remaining data types
  1007 			sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
  1075 			sysread(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
  1008 			$id = unpack('v',$buf);
  1076 			$id = unpack('v',$buf);
  1009 			last if ($id == 0x0600);
  1077 			last if ($id == 0x0600);
  1010 		}
  1078 		}
  1011 
  1079 
  1012 		next if ($nxt == $ndt);													# no BT found => next ens
  1080 		if ($nxt == $ndt) {														# no BT found => next ens
       
  1081 #			sysseek(WBRF,4,1) || die("$WBRcfn: $!");							# skip over remainder of ensemble
       
  1082 			sysseek(WBRF,$start_ens+$ens_length+2,0) || die("$WBRcfn: $!");
       
  1083 			next;
       
  1084 		}
  1013 
  1085 
  1014 		sysseek(WBRF,14,1) || die("$WBRcfn: $!");								# BT range, velocity, corr, %-good, ...
  1086 		sysseek(WBRF,14,1) || die("$WBRcfn: $!");								# BT range, velocity, corr, %-good, ...
  1015 		sysread(WBRF,$buf,28) == 28 || die("$WBRcfn: $!");
  1087 		sysread(WBRF,$buf,28) == 28 || die("$WBRcfn: $!");
  1016 		@dta = unpack('v4v4C4C4C4',$buf);
  1088 		@dta = unpack('v4v4C4C4C4',$buf);
  1017 		for ($beam=0; $beam<4; $beam++) {
  1089 		for ($beam=0; $beam<4; $beam++) {
  1052 		for ($beam=0; $beam<4; $beam++) {
  1124 		for ($beam=0; $beam<4; $beam++) {
  1053 			${$E}[$ens]->{BT_RL_PERCENT_GOOD}[$beam] = $dta[12+$beam];
  1125 			${$E}[$ens]->{BT_RL_PERCENT_GOOD}[$beam] = $dta[12+$beam];
  1054 		}
  1126 		}
  1055 
  1127 
  1056 		sysseek(WBRF,2,1) || die("$WBRcfn: $!");								# BT signal strength & BT range high bytes
  1128 		sysseek(WBRF,2,1) || die("$WBRcfn: $!");								# BT signal strength & BT range high bytes
  1057 		sysread(WBRF,$buf,9) == 9 || die("$WBRcfn: $!");
  1129 #		sysread(WBRF,$buf,9) == 9 || die("$WBRcfn: $!");
  1058 		@dta = unpack('C4CC4',$buf);
  1130 		if (sysread(WBRF,$buf,9) == 9) {										# SR1b JR16 BB150 data files require this
  1059 		for ($beam=0; $beam<4; $beam++) {
  1131 			@dta = unpack('C4CC4',$buf);
  1060 			${$E}[$ens]->{BT_SIGNAL_STRENGTH}[$beam] = $dta[$beam];
  1132 			for ($beam=0; $beam<4; $beam++) {
  1061 		}
  1133 				${$E}[$ens]->{BT_SIGNAL_STRENGTH}[$beam] = $dta[$beam];
  1062 		${$E}[$ens]->{HIGH_GAIN} if    ($dta[4]);
  1134 			}
  1063 		${$E}[$ens]->{LOW_GAIN} unless ($dta[4]);
  1135 			${$E}[$ens]->{HIGH_GAIN} if    ($dta[4]);
  1064 		for ($beam=0; $beam<4; $beam++) {										# high bytes (1 byte per beam)
  1136 			${$E}[$ens]->{LOW_GAIN} unless ($dta[4]);
  1065 			${$E}[$ens]->{BT_RANGE}[$beam] += $dta[5+$beam] * 655.36
  1137 			for ($beam=0; $beam<4; $beam++) {									# high bytes (1 byte per beam)
  1066 				if ($dta[5+$beam]);
  1138 				${$E}[$ens]->{BT_RANGE}[$beam] += $dta[5+$beam] * 655.36
  1067 		}
  1139 					if ($dta[5+$beam]);
       
  1140 			}
       
  1141 #			sysseek(WBRF,8,1) || die("$WBRcfn: $!");							# remainder of ensemble
       
  1142         }
       
  1143         sysseek(WBRF,$start_ens+$ens_length+2,0) || die("$WBRcfn: $!");
  1068 	} # ens loop
  1144 	} # ens loop
  1069 }
  1145 }
  1070 
  1146 
  1071 #----------------------------------------------------------------------
  1147 #----------------------------------------------------------------------
  1072 # writeData(output_file_name,^data) WBPens(nbins,fixed_leader_bytes,^data)
  1148 # writeData(output_file_name,^data) WBPens(nbins,fixed_leader_bytes,^data)
  1114 
  1190 
  1115 sub WBPens($$$)
  1191 sub WBPens($$$)
  1116 {
  1192 {
  1117 	my($nbins,$fixed_leader_bytes,$dta) = @_;
  1193 	my($nbins,$fixed_leader_bytes,$dta) = @_;
  1118 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs,@WBPofs);
  1194 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs,@WBPofs);
  1119 	my($ens,$dayStart,$ens_length,$hid,$ndt);
  1195 	my($ens,$dayStart,$ens_length,$hid,$ndt,$el);
  1120 
  1196 
  1121 	for ($ens=$start_ens=0; $ens<=$#{$dta->{ENSEMBLE}}; $ens++,$start_ens+=$ens_length+2) {
  1197 #	for ($ens=$start_ens=0; $ens<=$#{$dta->{ENSEMBLE}}; $ens++,$start_ens+=$ens_length+2) {
       
  1198 	for ($ens=0; $ens<=$#{$dta->{ENSEMBLE}}; $ens++) {
       
  1199 		$start_ens = goto_next_ens(\*WBPF);
       
  1200 		die("ens = $ens\n") unless defined($start_ens);
  1122 
  1201 
  1123 		#------------------------------
  1202 		#------------------------------
  1124 		# Patch Header (Data Source Id)
  1203 		# Patch Header (Data Source Id)
  1125 		#------------------------------
  1204 		#------------------------------
  1126 
  1205 
  1132 		$buf = pack('C',$dta->{ENSEMBLE}[$ens]->{DATA_SOURCE_ID});
  1211 		$buf = pack('C',$dta->{ENSEMBLE}[$ens]->{DATA_SOURCE_ID});
  1133 		my($nw) = syswrite(WBPF,$buf,1);
  1212 		my($nw) = syswrite(WBPF,$buf,1);
  1134 		$nw == 1 || die("$WBPcfn: $nw bytes written ($!)");
  1213 		$nw == 1 || die("$WBPcfn: $nw bytes written ($!)");
  1135 
  1214 
  1136 		sysread(WBPF,$buf,4) == 4 || die("$WBPcfn: unexpected EOF");
  1215 		sysread(WBPF,$buf,4) == 4 || die("$WBPcfn: unexpected EOF");
  1137 		($ens_length,$dummy,$ndt) = unpack('vCC',$buf);
  1216 		($el,$dummy,$ndt) = unpack('vCC',$buf);
  1138 		printf(STDERR "\n$WBPcfn: WARNING: unexpected number of data types (%d, ens=$ens)\n",$ndt),last
  1217 		$ens--,next if (defined($ens_length) && ($el != $ens_length));
       
  1218 		$ens_length = $el;
       
  1219 		
       
  1220 		printf(STDERR "$WBPcfn: WARNING: unexpected number of data types (%d, ens=$ens)\n",$ndt),last
  1139 				unless ($ndt == 6 || $ndt == 7);
  1221 				unless ($ndt == 6 || $ndt == 7);
  1140 
  1222 
  1141 		sysread(WBPF,$buf,2*$ndt) == 2*$ndt || die("$WBPcfn: $!");
  1223 		sysread(WBPF,$buf,2*$ndt) == 2*$ndt || die("$WBPcfn: $!");
  1142 		@WBPofs = unpack("v$ndt",$buf);
  1224 		@WBPofs = unpack("v$ndt",$buf);
  1143 		$fixed_leader_bytes = $WBPofs[1] - $WBPofs[0];
  1225 		$fixed_leader_bytes = $WBPofs[1] - $WBPofs[0];
  1170 		$buf = pack('C',$EX);		
  1252 		$buf = pack('C',$EX);		
  1171 		sysseek(WBPF,$start_ens+$WBPofs[0]+25,0) || die("$WBPcfn: $!");
  1253 		sysseek(WBPF,$start_ens+$WBPofs[0]+25,0) || die("$WBPcfn: $!");
  1172 		syswrite(WBPF,$buf,1) == 1 || die("$WBPcfn: $!");
  1254 		syswrite(WBPF,$buf,1) == 1 || die("$WBPcfn: $!");
  1173 
  1255 
  1174 		#----------------------------------------------------------------------
  1256 		#----------------------------------------------------------------------
       
  1257 		# Variable Leader #0
       
  1258 		#	- read ensNo for debugging purposes
       
  1259 		#----------------------------------------------------------------------
       
  1260 
       
  1261 		sysseek(WBPF,$start_ens+$WBPofs[1]+2,0) || die("$WBPcfn: $!");
       
  1262 		sysread(WBPF,$buf,2) == 2 || die("$WBPcfn: $!");
       
  1263 		my($ensNo) = unpack("v",$buf);											# only lower two bytes!!!
       
  1264 		sysseek(WBPF,$start_ens+$WBPofs[1]+13,0) || die("$WBPcfn: $!");			# jump to high byte
       
  1265 		sysread(WBPF,$buf,1) == 1 || die("$WBPcfn: $!");
       
  1266 		$ensNo += unpack('C',$buf) << 16;
       
  1267 		die("ensNo = $ensNo (should be $dta->{ENSEMBLE}[$ens]->{NUMBER})\n")
       
  1268 			unless ($ensNo == $dta->{ENSEMBLE}[$ens]->{NUMBER});
       
  1269 
       
  1270 		#----------------------------------------------------------------------
  1175 		# Variable Leader #1
  1271 		# Variable Leader #1
  1176 		#	- if $RDI_PD0_IO::OVERRIDE_Y2K_CLOCK is set, the data from the pre-Y2K
  1272 		#	- if $RDI_PD0_IO::OVERRIDE_Y2K_CLOCK is set, the data from the pre-Y2K
  1177 		#	  clock are used to override the ADCP clock values; this allows
  1273 		#	  clock are used to override the ADCP clock values; this allows
  1178 		#	  a better time to be recorded by the data acquisition system
  1274 		#	  a better time to be recorded by the data acquisition system
  1179 		#	  without overwriting the main instrument clock data
  1275 		#	  without overwriting the main instrument clock data
  1180 		#----------------------------------------------------------------------
  1276 		#----------------------------------------------------------------------
  1181 
  1277 
  1182 		if ($RDI_PD0_IO::OVERRIDE_Y2K_CLOCK) {
  1278 		if ($RDI_PD0_IO::OVERRIDE_Y2K_CLOCK) {
  1183 			sysseek(WBPF,$start_ens+$WBPofs[1]+4,0) || die("$WBPcfn: $!");		# jump to RTC_YEAR
  1279 			sysseek(WBPF,$start_ens+$WBPofs[1]+4,0) || die("$WBPcfn: $!");		# jump to RTC_YEAR
  1184 			sysread(WBPF,$buf,7) == 7 || die("$WBRcfn: $!");					# read pre-Y2K clock
  1280 			sysread(WBPF,$buf,7) == 7 || die("$WBPcfn: $!");					# read pre-Y2K clock
  1185 			($dta->{ENSEMBLE}[$ens]->{YEAR},
  1281 			($dta->{ENSEMBLE}[$ens]->{YEAR},
  1186 			 $dta->{ENSEMBLE}[$ens]->{MONTH},
  1282 			 $dta->{ENSEMBLE}[$ens]->{MONTH},
  1187 			 $dta->{ENSEMBLE}[$ens]->{DAY},
  1283 			 $dta->{ENSEMBLE}[$ens]->{DAY},
  1188 			 $dta->{ENSEMBLE}[$ens]->{HOUR},
  1284 			 $dta->{ENSEMBLE}[$ens]->{HOUR},
  1189 			 $dta->{ENSEMBLE}[$ens]->{MINUTE},
  1285 			 $dta->{ENSEMBLE}[$ens]->{MINUTE},
  1193 			$dta->{ENSEMBLE}[$ens]->{YEAR} += ($dta->{ENSEMBLE}[$ens]->{YEAR} > 80) ? 1900 : 2000;
  1289 			$dta->{ENSEMBLE}[$ens]->{YEAR} += ($dta->{ENSEMBLE}[$ens]->{YEAR} > 80) ? 1900 : 2000;
  1194 		}
  1290 		}
  1195 		
  1291 		
  1196 		#----------------------------------------------------------------------
  1292 		#----------------------------------------------------------------------
  1197 		# Variable Leader #2
  1293 		# Variable Leader #2
       
  1294 		#   - read ensemble number for debugging purposes
  1198 		#	- patch everything from SPEED_OF_SOUND to TEMPERATURE
  1295 		#	- patch everything from SPEED_OF_SOUND to TEMPERATURE
  1199 		# 	- at one stage, IMP allowed for missing values in pitch/roll and heading;
  1296 		# 	- at one stage, IMP allowed for missing values in pitch/roll and heading;
  1200 		#	  on 12/23/2017 the corresponding code was disabled (replaced by assertion)
  1297 		#	  on 12/23/2017 the corresponding code was disabled (replaced by assertion)
  1201 		#----------------------------------------------------------------------
  1298 		#----------------------------------------------------------------------
  1202 
  1299