RDI_BB_Read.pl
changeset 10 c835cd613f3e
parent 6 603221e51c6f
child 12 0f89b1523648
equal deleted inserted replaced
9:9470ce05c10d 10:c835cd613f3e
     1 #======================================================================
     1 #======================================================================
     2 #                    R D I _ B B _ R E A D . P L 
     2 #                    R D I _ B B _ R E A D . P L 
     3 #                    doc: Sat Jan 18 14:54:43 2003
     3 #                    doc: Sat Jan 18 14:54:43 2003
     4 #                    dlm: Thu May 12 10:50:34 2011
     4 #                    dlm: Mon Mar 25 21:38:37 2013
     5 #                    (c) 2003 A.M. Thurnherr
     5 #                    (c) 2003 A.M. Thurnherr
     6 #                    uE-Info: 48 61 NIL 0 0 72 0 2 4 NIL ofnI
     6 #                    uE-Info: 632 0 NIL 0 0 72 74 2 4 NIL ofnI
     7 #======================================================================
     7 #======================================================================
     8 
     8 
     9 # Read RDI BroadBand Binary Data Files (*.[0-9][0-9][0-9])
     9 # Read RDI BroadBand Binary Data Files (*.[0-9][0-9][0-9])
    10 
    10 
    11 # HISTORY:
    11 # HISTORY:
    44 #	Jun  4, 2008: - BUG: BB150 code was not considered on Sep 18, 2007
    44 #	Jun  4, 2008: - BUG: BB150 code was not considered on Sep 18, 2007
    45 #	Aug 15, 2010: - downgraded "unexpected number of data types" from error to warning
    45 #	Aug 15, 2010: - downgraded "unexpected number of data types" from error to warning
    46 #				  - BUG: WBRcfn had not been set correctly
    46 #				  - BUG: WBRcfn had not been set correctly
    47 #				  - modified to allow processing files without time info
    47 #				  - modified to allow processing files without time info
    48 #	May 12, 2011: - added code to report built-in-test errors
    48 #	May 12, 2011: - added code to report built-in-test errors
       
    49 #	Mar 19, 2013: - added support for WH600 data file (58 fixed leader bytes)
       
    50 #	Mar 20, 2013: - removed DATA_FORMAT stuff
       
    51 #				  - added support for BT data in subset of ensembles
    49 
    52 
    50 # FIRMWARE VERSIONS:
    53 # FIRMWARE VERSIONS:
    51 #	It appears that different firmware versions generate different file
    54 #	It appears that different firmware versions generate different file
    52 #	structures. Currently (Sep 2005) these routines have been tested
    55 #	structures. Currently (Sep 2005) these routines have been tested
    53 #	with the following firmware versions (as reported by [listhdr]):
    56 #	with the following firmware versions (as reported by [listHdr]):
    54 #
    57 #
    55 #	Firmw.	DATA_FORMAT(_VARIANT)	Owner 	Cruise	FIXED_LEADER_LENGTH
    58 #	Firmw.	DATA_FORMAT(_VARIANT)	Owner 	Cruise	FIXED_LEADER_LENGTH
    56 #------------------------------------------------------------
    59 #------------------------------------------------------------
    57 #	05.52	BB150 (1)				UH 		CLIVAR/P16S 42
    60 #	05.52	BB150 (1)				UH 		CLIVAR/P16S 42
    58 #	16.12	WH300 (1)				FSU 	A0304		53
    61 #	16.12	WH300 (1)				FSU 	A0304		53
    89 #	  indicate power failures on both FSU and LDEO slave instruments...
    92 #	  indicate power failures on both FSU and LDEO slave instruments...
    90 
    93 
    91 # &readData() returns perl obj (ref to anonymous hash) with the following
    94 # &readData() returns perl obj (ref to anonymous hash) with the following
    92 # structure:
    95 # structure:
    93 #
    96 #
    94 #	DATA_FORMAT						string		BB150, WH300
       
    95 #	DATA_FORMAT_VARIANT				scalar		?
       
    96 #	NUMBER_OF_DATA_TYPES			scalar		6 (no BT) or 7
    97 #	NUMBER_OF_DATA_TYPES			scalar		6 (no BT) or 7
    97 #	ENSEMBLE_BYTES					scalar		?, number of bytes w/o checksum
    98 #	ENSEMBLE_BYTES					scalar		?, number of bytes w/o checksum
    98 #	HEADER_BYTES					scalar		?
    99 #	HEADER_BYTES					scalar		?
    99 #	FIXED_LEADER_BYTES				scalar		?
   100 #	FIXED_LEADER_BYTES				scalar		42 for BB150; 53 for WH300, 58 for WH600, 59 for WH300(Nash)
   100 #	VARIABLE_LEADER_BYTES			scalar		?
   101 #	VARIABLE_LEADER_BYTES			scalar		?
   101 #	VELOCITY_DATA_BYTES				scalar		?
   102 #	VELOCITY_DATA_BYTES				scalar		?
   102 #	CORRELATION_DATA_BYTES			scalar		?
   103 #	CORRELATION_DATA_BYTES			scalar		?
   103 #	ECHO_INTENSITY_DATA_BYTES		scalar		?
   104 #	ECHO_INTENSITY_DATA_BYTES		scalar		?
   104 #	PERCENT_GOOD_DATA_BYTES			scalar		?
   105 #	PERCENT_GOOD_DATA_BYTES			scalar		?
   277 #----------------------------------------------------------------------
   278 #----------------------------------------------------------------------
   278 # Read Data
   279 # Read Data
   279 #----------------------------------------------------------------------
   280 #----------------------------------------------------------------------
   280 
   281 
   281 my($WBRcfn);							# current file name
   282 my($WBRcfn);							# current file name
   282 my(@WBRofs);							# data type offsets
       
   283 my($BIT_errors) = 0;					# built-in-test errors
   283 my($BIT_errors) = 0;					# built-in-test errors
   284 
   284 
   285 my($FmtErr) = "%s: illegal %s Id 0x%04x at ensemble %d";
   285 my($FmtErr) = "%s: illegal %s Id 0x%04x at ensemble %d";
   286 
   286 
   287 sub WBRhdr($)
   287 sub WBRhdr($)
   288 {
   288 {
   289 	my($dta) = @_;
   289 	my($dta) = @_;
   290 	my($buf,$hid,$did,$Ndt,$B,$W,$i,$dummy,$id);
   290 	my($buf,$hid,$did,$Ndt,$B,$W,$i,$dummy,$id,@WBRofs);
   291 	my($B1,$B2,$B3,$B4,$B5,$B6,$B7,$W1,$W2,$W3,$W4,$W5);
   291 	my($B1,$B2,$B3,$B4,$B5,$B6,$B7,$W1,$W2,$W3,$W4,$W5);
   292 	
   292 	
   293 	#--------------------
   293 	#--------------------
   294 	# HEADER
   294 	# HEADER
   295 	#--------------------
   295 	#--------------------
   296 
   296 
   297 	read(WBRF,$buf,6) == 6 || die("$WBRcfn: $!\n");
   297 	read(WBRF,$buf,6) == 6 || die("$WBRcfn: $!");
   298 	($hid,$did,$dta->{ENSEMBLE_BYTES},$dummy,$dta->{NUMBER_OF_DATA_TYPES})
   298 	($hid,$did,$dta->{ENSEMBLE_BYTES},$dummy,$dta->{NUMBER_OF_DATA_TYPES})
   299 		= unpack('CCvCC',$buf);
   299 		= unpack('CCvCC',$buf);
   300 	$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,0));
   300 	$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,0));
   301 	$did == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Data Source",$did,0));
   301 	$did == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Data Source",$did,0));
   302 	printf(STDERR "\n$WBRcfn: WARNING: unexpected number of data types (%d)\n",
   302 	printf(STDERR "\n$WBRcfn: WARNING: unexpected number of data types (%d)\n",
   305 					$dta->{NUMBER_OF_DATA_TYPES} == 7);
   305 					$dta->{NUMBER_OF_DATA_TYPES} == 7);
   306 	$dta->{BT_PRESENT} = ($dta->{NUMBER_OF_DATA_TYPES} == 7);
   306 	$dta->{BT_PRESENT} = ($dta->{NUMBER_OF_DATA_TYPES} == 7);
   307 					  
   307 					  
   308 	read(WBRF,$buf,2*$dta->{NUMBER_OF_DATA_TYPES})
   308 	read(WBRF,$buf,2*$dta->{NUMBER_OF_DATA_TYPES})
   309 		== 2*$dta->{NUMBER_OF_DATA_TYPES}
   309 		== 2*$dta->{NUMBER_OF_DATA_TYPES}
   310 			|| die("$WBRcfn: $!\n");
   310 			|| die("$WBRcfn: $!");
   311 	@WBRofs = unpack("v$dta->{NUMBER_OF_DATA_TYPES}",$buf);
   311 	@WBRofs = unpack("v$dta->{NUMBER_OF_DATA_TYPES}",$buf);
   312 
   312 
   313 	$dta->{HEADER_BYTES} 					= $WBRofs[0];
   313 	$dta->{HEADER_BYTES} 					= $WBRofs[0];
   314 	$dta->{FIXED_LEADER_BYTES} 				= $WBRofs[1] - $WBRofs[0];
   314 	$dta->{FIXED_LEADER_BYTES} 				= $WBRofs[1] - $WBRofs[0];
   315 	$dta->{VARIABLE_LEADER_BYTES}			= $WBRofs[2] - $WBRofs[1];
   315 	$dta->{VARIABLE_LEADER_BYTES}			= $WBRofs[2] - $WBRofs[1];
   321 		$dta->{BT_DATA_BYTES}				= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[6];
   321 		$dta->{BT_DATA_BYTES}				= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[6];
   322 	} else {
   322 	} else {
   323 		$dta->{PERCENT_GOOD_DATA_BYTES}		= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[5];
   323 		$dta->{PERCENT_GOOD_DATA_BYTES}		= $dta->{ENSEMBLE_BYTES} - 4 - $WBRofs[5];
   324 	}
   324 	}
   325 
   325 
       
   326 	if ($dta->{FIXED_LEADER_BYTES} == 42) {				# Eric Firing's old instrument I used in 2004
       
   327 		$dta->{INSTRUMENT_TYPE} = 'BB150';
       
   328 	} elsif ($dta->{FIXED_LEADER_BYTES} == 53) {		# old firmware: no serial numbers
       
   329 		$dta->{INSTRUMENT_TYPE} = 'Workhorse';	
       
   330 	} elsif ($dta->{FIXED_LEADER_BYTES} == 59) {		# new firmware: with serial numbers
       
   331 		$dta->{INSTRUMENT_TYPE} = 'Workhorse';
       
   332     } elsif ($dta->{FIXED_LEADER_BYTES} == 58) {		# DVL
       
   333 		$dta->{INSTRUMENT_TYPE} = 'Explorer';
       
   334     } 
       
   335 
   326 #	for ($i=0; $i<$dta->{NUMBER_OF_DATA_TYPES}; $i++) {
   336 #	for ($i=0; $i<$dta->{NUMBER_OF_DATA_TYPES}; $i++) {
   327 #		printf(STDERR "\nWBRofs[$i] = %d",$WBRofs[$i]);
   337 #		printf(STDERR "\nWBRofs[$i] = %d",$WBRofs[$i]);
   328 #	}
   338 #	}
   329 
   339 
   330 	$dta->{DATA_FORMAT_VARIANT} = 1;
       
   331 	if ($dta->{FIXED_LEADER_BYTES} == 53 || $dta->{FIXED_LEADER_BYTES} == 59) {
       
   332 		$dta->{DATA_FORMAT} = 'WH300';
       
   333 		$dta->{DATA_FORMAT_VARIANT} = 2 if ($dta->{FIXED_LEADER_BYTES} == 59);
       
   334 	} elsif ($dta->{FIXED_LEADER_BYTES} == 42) {
       
   335 		$dta->{DATA_FORMAT} = 'BB150';
       
   336     } else {
       
   337     	printf(STDERR "\n$WBRcfn: WARNING: unknown data format (%d FIXED_LEADER_BYTES)\n",
       
   338 			$dta->{FIXED_LEADER_BYTES}
       
   339 		);
       
   340 		$dta->{DATA_FORMAT} = 'unknown';
       
   341     }
       
   342 
       
   343 	#----------------------------------
   340 	#----------------------------------
   344 	# Check Data Format of 1st Ensemble
   341 	# Check Data Format of 1st Ensemble
   345 	#----------------------------------
   342 	#----------------------------------
   346 
   343 
   347 	seek(WBRF,$WBRofs[1],0) || die("$WBRcfn: $!");
   344 	seek(WBRF,$WBRofs[1],0) || die("$WBRcfn: $!");
   348 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   345 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   349 	$id = unpack('v',$buf);
   346 	$id = unpack('v',$buf);
   350 	$id == 0x0080 || die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,1));
   347 	$id == 0x0080 || die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,1));
   351 
   348 
   352 	seek(WBRF,$WBRofs[2],0) || die("$WBRcfn: $!");
   349 	seek(WBRF,$WBRofs[2],0) || die("$WBRcfn: $!");
   353 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   350 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   354 	$id = unpack('v',$buf);
   351 	$id = unpack('v',$buf);
   355 	$id == 0x0100 || die(sprintf($FmtErr,$WBRcfn,"Velocity Data",$id,1));
   352 	$id == 0x0100 || die(sprintf($FmtErr,$WBRcfn,"Velocity Data",$id,1));
   356 
   353 
   357 	seek(WBRF,$WBRofs[3],0) || die("$WBRcfn: $!");
   354 	seek(WBRF,$WBRofs[3],0) || die("$WBRcfn: $!");
   358 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   355 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   359 	$id = unpack('v',$buf);
   356 	$id = unpack('v',$buf);
   360 	$id == 0x0200 || die(sprintf($FmtErr,$WBRcfn,"Correlation Data",$id,1));
   357 	$id == 0x0200 || die(sprintf($FmtErr,$WBRcfn,"Correlation Data",$id,1));
   361     
   358     
   362 	seek(WBRF,$WBRofs[4],0) || die("$WBRcfn: $!");
   359 	seek(WBRF,$WBRofs[4],0) || die("$WBRcfn: $!");
   363 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   360 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   364 	$id = unpack('v',$buf);
   361 	$id = unpack('v',$buf);
   365 	$id == 0x0300 || die(sprintf($FmtErr,$WBRcfn,"Echo Intensity",$id,1));
   362 	$id == 0x0300 || die(sprintf($FmtErr,$WBRcfn,"Echo Intensity",$id,1));
   366 
   363 
   367 	seek(WBRF,$WBRofs[5],0) || die("$WBRcfn: $!");
   364 	seek(WBRF,$WBRofs[5],0) || die("$WBRcfn: $!");
   368 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   365 	read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   369 	$id = unpack('v',$buf);
   366 	$id = unpack('v',$buf);
   370 	$id == 0x0400 || die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,1));
   367 	$id == 0x0400 || die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,1));
   371 
   368 
   372 	if ($dta->{BT_PRESENT}) {
   369 	if ($dta->{BT_PRESENT}) {
   373 		seek(WBRF,$WBRofs[6],0) || die("$WBRcfn: $!");
   370 		seek(WBRF,$WBRofs[6],0) || die("$WBRcfn: $!");
   374 		read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   371 		read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   375 		$id = unpack('v',$buf);
   372 		$id = unpack('v',$buf);
   376 		$id == 0x0600 || die(sprintf($FmtErr,$WBRcfn,"Bottom Track",$id,1));
   373 		$id == 0x0600 || die(sprintf($FmtErr,$WBRcfn,"Bottom Track",$id,1));
   377     }
   374     }
   378 
   375 
   379 	#--------------------
   376 	#--------------------
   380 	# FIXED LEADER
   377 	# FIXED LEADER
   381 	#--------------------
   378 	#--------------------
   382 
   379 
   383 	seek(WBRF,$WBRofs[0],0) || die("$WBRcfn: $!");
   380 	seek(WBRF,$WBRofs[0],0) || die("$WBRcfn: $!");
   384 	read(WBRF,$buf,42) == 42 || die("$WBRcfn: $!\n");
   381 	read(WBRF,$buf,42) == 42 || die("$WBRcfn: $!");
   385 	($id,$dta->{CPU_FW_VER},$dta->{CPU_FW_REV},$B1,$B2,$dummy,$dummy,$dummy,
   382 	($id,$dta->{CPU_FW_VER},$dta->{CPU_FW_REV},$B1,$B2,$dummy,$dummy,$dummy,
   386 	 $dta->{N_BINS},$dta->{PINGS_PER_ENSEMBLE},$dta->{BIN_LENGTH},
   383 	 $dta->{N_BINS},$dta->{PINGS_PER_ENSEMBLE},$dta->{BIN_LENGTH},
   387 	 $dta->{BLANKING_DISTANCE},$dummy,$dta->{MIN_CORRELATION},
   384 	 $dta->{BLANKING_DISTANCE},$dummy,$dta->{MIN_CORRELATION},
   388 	 $dta->{N_CODE_REPETITIONS},$dta->{MIN_PERCENT_GOOD},
   385 	 $dta->{N_CODE_REPETITIONS},$dta->{MIN_PERCENT_GOOD},
   389 	 $dta->{MAX_ERROR_VELOCITY},$dta->{TIME_BETWEEN_PINGS},$B3,$B4,$B5,
   386 	 $dta->{MAX_ERROR_VELOCITY},$dta->{TIME_BETWEEN_PINGS},$B3,$B4,$B5,
   452 
   449 
   453     $dta->{FALSE_TARGET_THRESHOLD} = undef
   450     $dta->{FALSE_TARGET_THRESHOLD} = undef
   454 		if ($dta->{FALSE_TARGET_THRESHOLD} == 255);
   451 		if ($dta->{FALSE_TARGET_THRESHOLD} == 255);
   455     $dta->{TRANSMIT_LAG_DISTANCE} /= 100;
   452     $dta->{TRANSMIT_LAG_DISTANCE} /= 100;
   456 
   453 
   457 	if ($dta->{DATA_FORMAT} eq 'WH300') {
   454 	if ($dta->{INSTRUMENT_TYPE} eq 'Workhorse') {
   458 		read(WBRF,$buf,11) == 11 || die("$WBRcfn: $!\n");
   455 		read(WBRF,$buf,11) == 11 || die("$WBRcfn: $!");
   459 		($W1,$W2,$W3,$W4,$W5,$dta->{TRANSMIT_POWER}) = 
   456 		($W1,$W2,$W3,$W4,$W5,$dta->{TRANSMIT_POWER}) = 
   460 			unpack('vvvvvC',$buf);
   457 			unpack('vvvvvC',$buf);
   461 
   458 
   462 		$dta->{CPU_SERIAL_NUMBER} = sprintf("%04X%04X%04X%04X",$W1,$W2,$W3,$W4);
   459 		$dta->{CPU_SERIAL_NUMBER} = sprintf("%04X%04X%04X%04X",$W1,$W2,$W3,$W4);
   463 	
   460 	
   464 		$dta->{NARROW_BANDWIDTH} = ($W5 == 1);
   461 		$dta->{NARROW_BANDWIDTH} = ($W5 == 1);
   465 		$dta->{WIDE_BANDWIDTH}	 = ($W5 == 0);
   462 		$dta->{WIDE_BANDWIDTH}	 = ($W5 == 0);
   466 	    $dta->{TRANSMIT_POWER_HIGH} = ($dta->{TRANSMIT_POWER} == 255);
   463 	    $dta->{TRANSMIT_POWER_HIGH} = ($dta->{TRANSMIT_POWER} == 255);
   467 	}
   464 
       
   465 		if ($dta->{FIXED_LEADER_BYTES} == 59) {					# new style with serial number
       
   466 			read(WBRF,$buf,6) == 6 || die("$WBRcfn: $!");
       
   467 			($dummy,$dta->{SERIAL_NUMBER},$dummy) =				# last bytes is beam angle, but that info has
       
   468 				unpack('CVC',$buf);								# already been provided above
       
   469 		}
       
   470     }
       
   471 
       
   472 	if ($dta->{INSTRUMENT_TYPE} eq 'Explorer') {
       
   473 		read(WBRF,$buf,16) == 16 || die("$WBRcfn: $!");
       
   474 		($dummy,$dummy,$W5,$dummy,$dta->{SERIAL_NUMBER}) = 
       
   475 			unpack('VVvvV',$buf);
       
   476 		$dta->{NARROW_BANDWIDTH} = ($W5 == 1);
       
   477 		$dta->{WIDE_BANDWIDTH}	 = ($W5 == 0);
       
   478     }
   468 
   479 
   469 	#-----------------------
   480 	#-----------------------
   470 	# 1st ENSEMBLE, BT Setup
   481 	# 1st ENSEMBLE, BT Setup
   471 	#-----------------------
   482 	#-----------------------
   472 
   483 
   473 	if ($dta->{BT_PRESENT}) {
   484 	if ($dta->{BT_PRESENT}) {
   474 		seek(WBRF,$WBRofs[6],0) || die("$WBRcfn: $!");
   485 		seek(WBRF,$WBRofs[6],0) || die("$WBRcfn: $!");
   475 		read(WBRF,$buf,12) == 12 || die("$WBRcfn: $!\n");
   486 		read(WBRF,$buf,12) == 12 || die("$WBRcfn: $!");
   476 		($id,$dta->{BT_PINGS_PER_ENSEMBLE},$dta->{BT_DELAY_BEFORE_REACQUIRE},
   487 		($id,$dta->{BT_PINGS_PER_ENSEMBLE},$dta->{BT_DELAY_BEFORE_REACQUIRE},
   477 		 $dta->{BT_MIN_CORRELATION},$dta->{BT_MIN_EVAL_AMPLITUDE},
   488 		 $dta->{BT_MIN_CORRELATION},$dta->{BT_MIN_EVAL_AMPLITUDE},
   478 		 $dta->{BT_MIN_PERCENT_GOOD},$dta->{BT_MODE},
   489 		 $dta->{BT_MIN_PERCENT_GOOD},$dta->{BT_MODE},
   479 		 $dta->{BT_MAX_ERROR_VELOCITY}) = unpack('vvvCCCCv',$buf);
   490 		 $dta->{BT_MAX_ERROR_VELOCITY}) = unpack('vvvCCCCv',$buf);
   480 		 
   491 		 
   484 		$dta->{BT_MAX_ERROR_VELOCITY} =
   495 		$dta->{BT_MAX_ERROR_VELOCITY} =
   485 			$dta->{BT_MAX_ERROR_VELOCITY} ? $dta->{BT_MAX_ERROR_VELOCITY} / 1000
   496 			$dta->{BT_MAX_ERROR_VELOCITY} ? $dta->{BT_MAX_ERROR_VELOCITY} / 1000
   486 										  : undef;
   497 										  : undef;
   487 	
   498 	
   488 		seek(WBRF,28,1) || die("$WBRcfn: $!");
   499 		seek(WBRF,28,1) || die("$WBRcfn: $!");
   489 		read(WBRF,$buf,6) == 6 || die("$WBRcfn: $!\n");
   500 		read(WBRF,$buf,6) == 6 || die("$WBRcfn: $!");
   490 		($dta->{BT_RL_MIN_SIZE},$dta->{BT_RL_NEAR},$dta->{BT_RL_FAR})
   501 		($dta->{BT_RL_MIN_SIZE},$dta->{BT_RL_NEAR},$dta->{BT_RL_FAR})
   491 			= unpack('vvv',$buf);
   502 			= unpack('vvv',$buf);
   492 	
   503 	
   493 		$dta->{BT_RL_MIN_SIZE} /= 10;
   504 		$dta->{BT_RL_MIN_SIZE} /= 10;
   494 		$dta->{BT_RL_NEAR} /= 10;
   505 		$dta->{BT_RL_NEAR} /= 10;
   495 		$dta->{BT_RL_FAR} /= 10;
   506 		$dta->{BT_RL_FAR} /= 10;
   496 	    
   507 	    
   497 		seek(WBRF,20,1) || die("$WBRcfn: $!");		# skip data
   508 		seek(WBRF,20,1) || die("$WBRcfn: $!");		# skip data
   498 		read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   509 		read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   499 	    $dta->{BT_MAX_TRACKING_DEPTH} = unpack('v',$buf) / 10;
   510 	    $dta->{BT_MAX_TRACKING_DEPTH} = unpack('v',$buf) / 10;
   500     }
   511     }
   501     
   512     
   502     return $dta;
   513     return $dta;
   503 }
   514 }
   504 
   515 
   505 sub WBRens($$$$$)
   516 sub WBRens($$$)
   506 {
   517 {
   507 	my($nbins,$ens_length,$BT_present,$data_format,$E) = @_;
   518 	my($nbins,$fixed_leader_bytes,$E) = @_;
   508 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs);
   519 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs,@WBRofs);
   509 	my($ens,$ensNo,$dayStart);
   520 	my($ens,$ensNo,$dayStart,$ens_length,$BT_present,$hid,$did,$ndt);
   510 
   521 
   511 	for ($ens=$start_ens=0; 1; $ens++,$start_ens+=$ens_length+2) {
   522 	for ($ens=$start_ens=0; 1; $ens++,$start_ens+=$ens_length+2) {
       
   523 #		print(STDERR "ens = $ens\n");
   512 #		print(STDERR "start_ens = $start_ens\n");
   524 #		print(STDERR "start_ens = $start_ens\n");
   513 
   525 
       
   526 		#----------------------------------------
       
   527 		# Get ensemble length and # of data types 
       
   528 		#----------------------------------------
       
   529 
       
   530 		seek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
       
   531 		read(WBRF,$buf,6) == 6 || last;
       
   532 		($hid,$did,$ens_length,$dummy,$ndt) = unpack('CCvCC',$buf);
       
   533 		$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,0));
       
   534 		$did == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Data Source",$did,0));
       
   535 		printf(STDERR "\n$WBRcfn: WARNING: unexpected number of data types (%d)\n",$ndt)
       
   536 				unless ($ndt == 6 || $ndt == 7);
       
   537 		$BT_present = ($ndt == 7);
       
   538 		read(WBRF,$buf,2*$ndt) == 2*$ndt || die("$WBRcfn: $!");
       
   539 		@WBRofs = unpack("v$ndt",$buf);
       
   540 		$fixed_leader_bytes = $WBRofs[1] - $WBRofs[0];
       
   541 #		print(STDERR "@WBRofs\n");
       
   542 	
   514 		#-------------------------------
   543 		#-------------------------------
   515 		# Make Sure Ensemble is Complete
   544 		# Make Sure Ensemble is Complete
   516 		#-------------------------------
   545 		#-------------------------------
   517 
   546 
   518 		# UH BB150 writes incomplete ensembles (i.e. short read
   547 		# UH BB150 writes incomplete ensembles (i.e. short read
   528 		#------------------------------
   557 		#------------------------------
   529 		# Variable Leader
   558 		# Variable Leader
   530 		#------------------------------
   559 		#------------------------------
   531 	
   560 	
   532 		seek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
   561 		seek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
   533 		read(WBRF,$buf,4) == 4 || die("$WBRcfn: $!\n");
   562 		read(WBRF,$buf,4) == 4 || die("$WBRcfn: $!");
   534 		($id,$ensNo) = unpack("vv",$buf);
   563 		($id,$ensNo) = unpack("vv",$buf);
   535 
   564 
   536 		$id == 0x0080 ||
   565 		$id == 0x0080 ||
   537 			die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo+1));
   566 			die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo+1));
   538 
   567 
   539 		if ($data_format eq 'BB150') {						# non Y2K RTC
   568 		if ($fixed_leader_bytes==42 || $fixed_leader_bytes==58) {			# BB150 & Explorer DVL
   540 			read(WBRF,$buf,7) == 7 || die("$WBRcfn: $!\n");
   569 			read(WBRF,$buf,7) == 7 || die("$WBRcfn: $!");
   541 			(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},
   570 			(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},
   542 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
   571 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
   543 			 ${$E}[$ens]->{SECONDS},$B4) = unpack('CCCCCCC',$buf);
   572 			 ${$E}[$ens]->{SECONDS},$B4) = unpack('CCCCCCC',$buf);
   544 			${$E}[$ens]->{SECONDS} += $B4/100;
   573 			${$E}[$ens]->{SECONDS} += $B4/100;
   545 			${$E}[$ens]->{YEAR} += (${$E}[$ens]->{YEAR} > 80) ? 1900 : 2000;
   574 			${$E}[$ens]->{YEAR} += (${$E}[$ens]->{YEAR} > 80) ? 1900 : 2000;
   546 		} else {
   575 		} else {
   547 			seek(WBRF,7,1) || die("$WBRcfn: $!");			# use Y2K RTC
   576 			seek(WBRF,7,1) || die("$WBRcfn: $!");							# use Y2K RTC instead
   548 		}
   577 		}
   549 
   578 
   550 		read(WBRF,$buf,1) == 1 || die("$WBRcfn: $!\n");
   579 		read(WBRF,$buf,1) == 1 || die("$WBRcfn: $!");
   551 		$ensNo += unpack('C',$buf) << 16;
   580 		$ensNo += unpack('C',$buf) << 16;
   552 		${$E}[$ens]->{NUMBER} = $ensNo;
   581 		${$E}[$ens]->{NUMBER} = $ensNo;
   553 		
   582 		
   554 		read(WBRF,$buf,30) == 30 || die("$WBRcfn: $!\n");
   583 		read(WBRF,$buf,30) == 30 || die("$WBRcfn: $!");
   555 		(${$E}[$ens]->{BUILT_IN_TEST_ERROR},${$E}[$ens]->{SPEED_OF_SOUND},
   584 		(${$E}[$ens]->{BUILT_IN_TEST_ERROR},${$E}[$ens]->{SPEED_OF_SOUND},
   556 		 ${$E}[$ens]->{XDUCER_DEPTH},${$E}[$ens]->{HEADING},
   585 		 ${$E}[$ens]->{XDUCER_DEPTH},${$E}[$ens]->{HEADING},
   557 		 ${$E}[$ens]->{PITCH},${$E}[$ens]->{ROLL},
   586 		 ${$E}[$ens]->{PITCH},${$E}[$ens]->{ROLL},
   558 		 ${$E}[$ens]->{SALINITY},${$E}[$ens]->{TEMPERATURE},
   587 		 ${$E}[$ens]->{SALINITY},${$E}[$ens]->{TEMPERATURE},
   559 		 ${$E}[$ens]->{MIN_PRE_PING_WAIT_TIME},$B1,$B2,
   588 		 ${$E}[$ens]->{MIN_PRE_PING_WAIT_TIME},$B1,$B2,
   578 		${$E}[$ens]->{MIN_PRE_PING_WAIT_TIME} *= 60;
   607 		${$E}[$ens]->{MIN_PRE_PING_WAIT_TIME} *= 60;
   579 		${$E}[$ens]->{MIN_PRE_PING_WAIT_TIME} += $B1 + $B2/100;
   608 		${$E}[$ens]->{MIN_PRE_PING_WAIT_TIME} += $B1 + $B2/100;
   580 		${$E}[$ens]->{PITCH_STDDEV} /= 10;
   609 		${$E}[$ens]->{PITCH_STDDEV} /= 10;
   581 		${$E}[$ens]->{ROLL_STDDEV} /= 10;
   610 		${$E}[$ens]->{ROLL_STDDEV} /= 10;
   582 
   611 
   583 		if ($data_format eq 'WH300') {
   612 		if ($fixed_leader_bytes==53 || $fixed_leader_bytes==59) {			# Workhorse instruments
   584 			read(WBRF,$buf,23) == 23 || die("$WBRcfn: $!\n");
   613 			read(WBRF,$buf,23) == 23 || die("$WBRcfn: $!");
   585 			(${$E}[$ens]->{ERROR_STATUS_WORD},
   614 			(${$E}[$ens]->{ERROR_STATUS_WORD},
   586 		 	 $dummy,${$E}[$ens]->{PRESSURE},${$E}[$ens]->{PRESSURE_STDDEV},
   615 		 	 $dummy,${$E}[$ens]->{PRESSURE},${$E}[$ens]->{PRESSURE_STDDEV},
   587 			 $dummy,${$E}[$ens]->{YEAR},$B3,${$E}[$ens]->{MONTH},
   616 			 $dummy,${$E}[$ens]->{YEAR},$B3,${$E}[$ens]->{MONTH},
   588 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
   617 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
   589 			 ${$E}[$ens]->{SECONDS},$B4)
   618 			 ${$E}[$ens]->{SECONDS},$B4)
   591 
   620 
   592 			${$E}[$ens]->{PRESSURE} /= 1000;
   621 			${$E}[$ens]->{PRESSURE} /= 1000;
   593 			${$E}[$ens]->{PRESSURE_STDDEV} /= 1000;
   622 			${$E}[$ens]->{PRESSURE_STDDEV} /= 1000;
   594 			${$E}[$ens]->{YEAR} *= 100; ${$E}[$ens]->{YEAR} += $B3;
   623 			${$E}[$ens]->{YEAR} *= 100; ${$E}[$ens]->{YEAR} += $B3;
   595 			${$E}[$ens]->{SECONDS} += $B4/100;
   624 			${$E}[$ens]->{SECONDS} += $B4/100;
       
   625 		}
       
   626 
       
   627 		if ($fixed_leader_bytes == 58) {									# Explorer DVL
       
   628 			read(WBRF,$buf,14) == 14 || die("$WBRcfn: $!");
       
   629 			(${$E}[$ens]->{ERROR_STATUS_WORD},
       
   630 		 	 $dummy,${$E}[$ens]->{PRESSURE},${$E}[$ens]->{PRESSURE_STDDEV})
       
   631 				= unpack('VvVV',$buf);
       
   632 			${$E}[$ens]->{PRESSURE} /= 1000;
       
   633 			${$E}[$ens]->{PRESSURE_STDDEV} /= 1000;
   596 		}
   634 		}
   597 		
   635 		
   598 		${$E}[$ens]->{DATE}
   636 		${$E}[$ens]->{DATE}
   599 			= sprintf("%02d/%02d/%d",${$E}[$ens]->{MONTH},
   637 			= sprintf("%02d/%02d/%d",${$E}[$ens]->{MONTH},
   600 									 ${$E}[$ens]->{DAY},
   638 									 ${$E}[$ens]->{DAY},
   630         }
   668         }
   631 
   669 
   632 		seek(WBRF,$start_ens+$WBRofs[0]+4,0)		# System Config / Fixed Leader
   670 		seek(WBRF,$start_ens+$WBRofs[0]+4,0)		# System Config / Fixed Leader
   633 			|| die("$WBRcfn: $!");
   671 			|| die("$WBRcfn: $!");
   634 
   672 
   635 		read(WBRF,$buf,5) == 5 || die("$WBRcfn: $!\n");
   673 		read(WBRF,$buf,5) == 5 || die("$WBRcfn: $!");
   636 		($B1,$dummy,$dummy,$dummy,${$E}[$ens]->{N_BEAMS_USED})
   674 		($B1,$dummy,$dummy,$dummy,${$E}[$ens]->{N_BEAMS_USED})
   637 			= unpack('CCCCC',$buf);		
   675 			= unpack('CCCCC',$buf);		
   638 		${$E}[$ens]->{XDUCER_FACING_UP}   = 1 if     ($B1 & 0x80);
   676 		${$E}[$ens]->{XDUCER_FACING_UP}   = 1 if     ($B1 & 0x80);
   639 		${$E}[$ens]->{XDUCER_FACING_DOWN} = 1 unless ($B1 & 0x80);
   677 		${$E}[$ens]->{XDUCER_FACING_DOWN} = 1 unless ($B1 & 0x80);
   640 
   678 
   643 		#--------------------
   681 		#--------------------
   644 
   682 
   645 		my($ndata) = $nbins * 4;
   683 		my($ndata) = $nbins * 4;
   646 
   684 
   647 		seek(WBRF,$start_ens+$WBRofs[2],0) || die("$WBRcfn: $!");
   685 		seek(WBRF,$start_ens+$WBRofs[2],0) || die("$WBRcfn: $!");
   648 		read(WBRF,$buf,2+$ndata*2) == 2+$ndata*2 || die("$WBRcfn: $!\n");
   686 		read(WBRF,$buf,2+$ndata*2) == 2+$ndata*2 || die("$WBRcfn: $!");
   649 		($id,@dta) = unpack("vv$ndata",$buf);
   687 		($id,@dta) = unpack("vv$ndata",$buf);
   650 
   688 
   651 		$id == 0x0100 ||
   689 		$id == 0x0100 ||
   652 			die(sprintf($FmtErr,$WBRcfn,"Velocity Data",$id,$ens));
   690 			die(sprintf($FmtErr,$WBRcfn,"Velocity Data",$id,$ens));
   653 		
   691 		
   662 		#--------------------
   700 		#--------------------
   663 		# Correlation Data
   701 		# Correlation Data
   664 		#--------------------
   702 		#--------------------
   665 
   703 
   666 		seek(WBRF,$start_ens+$WBRofs[3],0) || die("$WBRcfn: $!");
   704 		seek(WBRF,$start_ens+$WBRofs[3],0) || die("$WBRcfn: $!");
   667 		read(WBRF,$buf,2+$ndata) == 2+$ndata || die("$WBRcfn: $!\n");
   705 		read(WBRF,$buf,2+$ndata) == 2+$ndata || die("$WBRcfn: $!");
   668 		($id,@dta) = unpack("vC$ndata",$buf);
   706 		($id,@dta) = unpack("vC$ndata",$buf);
   669 
   707 
   670 		$id == 0x0200 ||
   708 		$id == 0x0200 ||
   671 			die(sprintf($FmtErr,$WBRcfn,"Correlation Data",$id,$ens));
   709 			die(sprintf($FmtErr,$WBRcfn,"Correlation Data",$id,$ens));
   672 		
   710 		
   680 		#--------------------
   718 		#--------------------
   681 		# Echo Intensity Data
   719 		# Echo Intensity Data
   682 		#--------------------
   720 		#--------------------
   683 
   721 
   684 		seek(WBRF,$start_ens+$WBRofs[4],0) || die("$WBRcfn: $!");
   722 		seek(WBRF,$start_ens+$WBRofs[4],0) || die("$WBRcfn: $!");
   685 		read(WBRF,$buf,2+$ndata) == 2+$ndata || die("$WBRcfn: $!\n");
   723 		read(WBRF,$buf,2+$ndata) == 2+$ndata || die("$WBRcfn: $!");
   686 		($id,@dta) = unpack("vC$ndata",$buf);
   724 		($id,@dta) = unpack("vC$ndata",$buf);
   687 
   725 
   688 		$id == 0x0300 ||
   726 		$id == 0x0300 ||
   689 			die(sprintf($FmtErr,$WBRcfn,"Echo Intensity",$id,$ens));
   727 			die(sprintf($FmtErr,$WBRcfn,"Echo Intensity",$id,$ens));
   690 
   728 
   697 		#--------------------
   735 		#--------------------
   698 		# Percent Good Data
   736 		# Percent Good Data
   699 		#--------------------
   737 		#--------------------
   700 
   738 
   701 		seek(WBRF,$start_ens+$WBRofs[5],0) || die("$WBRcfn: $!");
   739 		seek(WBRF,$start_ens+$WBRofs[5],0) || die("$WBRcfn: $!");
   702 		read(WBRF,$buf,2+$ndata) == 2+$ndata || die("$WBRcfn: $!\n");
   740 		read(WBRF,$buf,2+$ndata) == 2+$ndata || die("$WBRcfn: $!");
   703 		($id,@dta) = unpack("vC$ndata",$buf);
   741 		($id,@dta) = unpack("vC$ndata",$buf);
   704 
   742 
   705 		$id == 0x0400 ||
   743 		$id == 0x0400 ||
   706 			die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,$ens));
   744 			die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,$ens));
   707 
   745 
   715 		# Bottom-Track Data
   753 		# Bottom-Track Data
   716 		#--------------------
   754 		#--------------------
   717 
   755 
   718 		if ($BT_present) {
   756 		if ($BT_present) {
   719 			seek(WBRF,$start_ens+$WBRofs[6],0) || die("$WBRcfn: $!");
   757 			seek(WBRF,$start_ens+$WBRofs[6],0) || die("$WBRcfn: $!");
   720 			read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!\n");
   758 			read(WBRF,$buf,2) == 2 || die("$WBRcfn: $!");
   721 			$id = unpack('v',$buf);
   759 			$id = unpack('v',$buf);
   722 	
   760 	
   723 			$id == 0x0600 ||
   761 			$id == 0x0600 ||
   724 				die(sprintf($FmtErr,$WBRcfn,"Bottom Track",$id,$ens));
   762 				die(sprintf($FmtErr,$WBRcfn,"Bottom Track",$id,$ens));
   725 	
   763 	
   726 			seek(WBRF,14,1) || die("$WBRcfn: $!");		# BT config
   764 			seek(WBRF,14,1) || die("$WBRcfn: $!");		# BT config
   727 	
   765 	
   728 			read(WBRF,$buf,28) == 28 || die("$WBRcfn: $!\n");
   766 			read(WBRF,$buf,28) == 28 || die("$WBRcfn: $!");
   729 			@dta = unpack('v4v4C4C4C4',$buf);
   767 			@dta = unpack('v4v4C4C4C4',$buf);
   730 		    
   768 		    
   731 			for ($beam=0; $beam<4; $beam++) {
   769 			for ($beam=0; $beam<4; $beam++) {
   732 				${$E}[$ens]->{BT_RANGE}[$beam] = $dta[$beam] / 100
   770 				${$E}[$ens]->{BT_RANGE}[$beam] = $dta[$beam] / 100
   733 						if ($dta[$beam]);
   771 						if ($dta[$beam]);
   748 				${$E}[$ens]->{BT_PERCENT_GOOD}[$beam] = $dta[16+$beam];
   786 				${$E}[$ens]->{BT_PERCENT_GOOD}[$beam] = $dta[16+$beam];
   749 			}
   787 			}
   750 	
   788 	
   751 			seek(WBRF,6,1) || die("$WBRcfn: $!");		# BT config
   789 			seek(WBRF,6,1) || die("$WBRcfn: $!");		# BT config
   752 	
   790 	
   753 			read(WBRF,$buf,20) == 20 || die("$WBRcfn: $!\n");
   791 			read(WBRF,$buf,20) == 20 || die("$WBRcfn: $!");
   754 			@dta = unpack('v4C4C4C4',$buf);
   792 			@dta = unpack('v4C4C4C4',$buf);
   755 	
   793 	
   756 			for ($beam=0; $beam<4; $beam++) {
   794 			for ($beam=0; $beam<4; $beam++) {
   757 				${$E}[$ens]->{BT_RL_VELOCITY}[$beam] =
   795 				${$E}[$ens]->{BT_RL_VELOCITY}[$beam] =
   758 					unpack('s',pack('S',$dta[$beam])) / 1000
   796 					unpack('s',pack('S',$dta[$beam])) / 1000
   769 				${$E}[$ens]->{BT_RL_PERCENT_GOOD}[$beam] = $dta[12+$beam];
   807 				${$E}[$ens]->{BT_RL_PERCENT_GOOD}[$beam] = $dta[12+$beam];
   770 			}
   808 			}
   771 	
   809 	
   772 			seek(WBRF,2,1) || die("$WBRcfn: $!");		# BT config
   810 			seek(WBRF,2,1) || die("$WBRcfn: $!");		# BT config
   773 	
   811 	
   774 			read(WBRF,$buf,9) == 9 || die("$WBRcfn: $!\n");
   812 			read(WBRF,$buf,9) == 9 || die("$WBRcfn: $!");
   775 			@dta = unpack('C4CC4',$buf);
   813 			@dta = unpack('C4CC4',$buf);
   776 	
   814 	
   777 			for ($beam=0; $beam<4; $beam++) {
   815 			for ($beam=0; $beam<4; $beam++) {
   778 				${$E}[$ens]->{BT_SIGNAL_STRENGTH}[$beam] = $dta[$beam];
   816 				${$E}[$ens]->{BT_SIGNAL_STRENGTH}[$beam] = $dta[$beam];
   779 			}
   817 			}
   789 
   827 
   790 sub readHeader(@)
   828 sub readHeader(@)
   791 {
   829 {
   792 	my($fn,$dta) = @_;
   830 	my($fn,$dta) = @_;
   793 	$WBRcfn = $fn;
   831 	$WBRcfn = $fn;
   794     open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
   832     open(WBRF,$WBRcfn) || die("$WBRcfn: $!");
   795     WBRhdr($dta);    
   833     WBRhdr($dta);    
   796 }
   834 }
   797 
   835 
   798 sub readData(@)
   836 sub readData(@)
   799 {
   837 {
   800 	my($fn,$dta) = @_;
   838 	my($fn,$dta) = @_;
   801 	$WBRcfn = $fn;
   839 	$WBRcfn = $fn;
   802     open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
   840     open(WBRF,$WBRcfn) || die("$WBRcfn: $!");
   803     WBRhdr($dta);
   841     WBRhdr($dta);
   804 	WBRens($dta->{N_BINS},$dta->{ENSEMBLE_BYTES},
   842 	WBRens($dta->{N_BINS},$dta->{FIXED_LEADER_BYTES},
   805 		   $dta->{BT_PRESENT},$dta->{DATA_FORMAT},
       
   806 		   \@{$dta->{ENSEMBLE}});
   843 		   \@{$dta->{ENSEMBLE}});
   807 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
   844 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
   808 		if ($BIT_errors);
   845 		if ($BIT_errors);
   809 }
   846 }
   810 
   847