V1.9
authorA.M. Thurnherr <athurnherr@yahoo.com>
Sun, 12 Mar 2017 12:11:40 -0400
changeset 37 40d85448debf
parent 36 515b06dae59c
child 38 b1b1ef3f4a9a
V1.9
ADCP_tools_lib.pl
HISTORY
RDI_PD0_IO.pl
editPD0
listBins
listEns
listHdr
mkProfile
--- a/ADCP_tools_lib.pl
+++ b/ADCP_tools_lib.pl
@@ -1,15 +1,18 @@
 #======================================================================
 #                    A D C P _ T O O L S _ L I B . P L 
 #                    doc: Tue Jan  5 10:45:47 2016
-#                    dlm: Thu May 26 10:40:42 2016
+#                    dlm: Sun Mar 12 12:09:33 2017
 #                    (c) 2016 A.M. Thurnherr
-#                    uE-Info: 12 25 NIL 0 0 72 0 2 4 NIL ofnI
+#                    uE-Info: 13 51 NIL 0 0 72 0 2 4 NIL ofnI
 #======================================================================
 
 # HISTORY:
 #	Jan  5, 2015: - created
+#	...
+#	Aug 23, 2016: - updated to V1.8
+#	Mar 12, 2017: - updated to V1.9 for LADCP_w 1.3
 
-$ADCP_tools_version = 1.7;
+$ADCP_tools_version = 1.9;
 
 die(sprintf("$0: obsolete ADCP_tools V%.1f; V%.1f required\n",
     $ADCP_tools_version,$ADCP_tools_minVersion))
--- a/HISTORY
+++ b/HISTORY
@@ -1,9 +1,9 @@
 ======================================================================
                     H I S T O R Y 
                     doc: Tue May 15 18:04:39 2012
-                    dlm: Fri Aug  5 10:10:06 2016
+                    dlm: Sun Mar 12 12:09:14 2017
                     (c) 2012 A.M. Thurnherr
-                    uE-Info: 150 32 NIL 0 0 72 3 2 4 NIL ofnI
+                    uE-Info: 171 41 NIL 0 0 72 3 2 4 NIL ofnI
 ======================================================================
 
 --------------------------------------
@@ -148,3 +148,24 @@
 Jul 30, 2016:
 	- minor bug in [RDI_PD0_IO.pl]
 	- improvements to [splitPD0]
+
+Aug 23, 2016:
+	- added &clearEns to [RDI_PD0_IO.pl]
+	- updated to V1.8
+
+Sep 12, 2016:
+	- improvement to [mkProfile]	
+
+Nov  9-23, 2016:
+	- improvements to [RDI_PD0_IO.pl]
+	- BUG in [editPD0] [listEns] [listHdr]
+
+Jan 17, 2017:
+	- improvements to [listBins]
+
+Mar 7, 2017:
+	- namespace clash in [RDI_PD0_IO.pl]
+
+Mar 12, 2017:
+	- updated HISTORY
+	- updated to V1.9 [ADCP_tools_lib.pl]
--- a/RDI_PD0_IO.pl
+++ b/RDI_PD0_IO.pl
@@ -1,12 +1,12 @@
 #======================================================================
-#                    R D I _ B B _ R E A D . P L 
+#                    R D I _ P D 0 _ I O . P L 
 #                    doc: Sat Jan 18 14:54:43 2003
-#                    dlm: Sat Jul 30 18:34:46 2016
+#                    dlm: Tue Mar  7 12:07:29 2017
 #                    (c) 2003 A.M. Thurnherr
-#                    uE-Info: 402 62 NIL 0 0 72 10 2 4 NIL ofnI
+#                    uE-Info: 1250 39 NIL 0 0 72 0 2 4 NIL ofnI
 #======================================================================
 
-# Read RDI BroadBand Binary Data Files (*.[0-9][0-9][0-9])
+# Read RDI PD0 binary data files (*.[0-9][0-9][0-9])
 
 # HISTORY:
 #	Jan 18, 2003: - incepted aboard the Aurora Australis (KAOS)
@@ -78,6 +78,12 @@
 #	Feb 29, 2016: - LEAP DAY: actually got BT data patching to work
 #	Jul 30, 2016: - BUG: incomplete last ensemble with garbage content was returned on reading
 #						 WH300 data
+#	Aug  5, 2016: - cosmetics
+#	Aug 23, 2016: - added &clearEns()
+#	Nov  9, 2016: - made WBRhdr() return undef on "empty" files
+#	Nov 18, 2016: - BUG: ensNo was not reported correctly in format errors
+#	Nov 23, 2016: - no longer set pitch/roll/heading to undef in clearEns()
+#	Mar  7, 2016: - renamed round() to stop clashing with ANTSLIB
 
 # FIRMWARE VERSIONS:
 #	It appears that different firmware versions generate different file
@@ -375,7 +381,7 @@
 	my($fn,$dta) = @_;
 	$WBRcfn = $fn;
     open(WBRF,$WBRcfn) || die("$WBRcfn: $!");
-    WBRhdr($dta);    
+    WBRhdr($dta) || die("$WBRcfn: Insufficient data\n");
 }
 
 sub WBRhdr($)
@@ -389,14 +395,14 @@
 	#--------------------
 
 	skip_initial_trash();
-	sysread(WBRF,$buf,6) == 6 || die("$WBRcfn: $!");
+	sysread(WBRF,$buf,6) == 6 || return undef;
 	($hid,$did,$dta->{ENSEMBLE_BYTES},$dummy,$dta->{NUMBER_OF_DATA_TYPES})
 		= unpack('CCvCC',$buf);
 	$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (hid)",$hid,0));
 	$did == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (did)",$did,0));
 
-	$start_ens = sysseek(WBRF,$dta->{ENSEMBLE_BYTES}-6+2,1) || die("$WBRcfn: $!");
-	sysread(WBRF,$buf,6) == 6 || die("$WBRcfn: $!");
+	$start_ens = sysseek(WBRF,$dta->{ENSEMBLE_BYTES}-6+2,1) || return undef;
+	sysread(WBRF,$buf,6) == 6 || return undef;
 	($hid,$did,$dta->{ENSEMBLE_BYTES},$dummy,$dta->{NUMBER_OF_DATA_TYPES})
 		= unpack('CCvCC',$buf);
 	$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header (hid2)",$hid,0));
@@ -643,8 +649,8 @@
 {
 	my($fn,$dta) = @_;
 	$WBRcfn = $fn;
-    open(WBRF,$WBRcfn) || die("$WBRcfn: $!");
-    WBRhdr($dta);
+    open(WBRF,$WBRcfn) || die("$WBRcfn: $!\n");
+    WBRhdr($dta) || die("$WBRcfn: Insufficient Data\n");
 	WBRens($dta->{N_BINS},$dta->{FIXED_LEADER_BYTES},
 		   \@{$dta->{ENSEMBLE}});
 	print(STDERR "$WBRcfn: $BIT_errors built-in-test errors\n")
@@ -670,7 +676,7 @@
 		sysseek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
 		sysread(WBRF,$buf,6) == 6 || last;
 		($hid,$did,$ens_length,$dummy,$ndt) = unpack('CCvCC',$buf);
-		$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,0));
+		$hid == 0x7f || die(sprintf($FmtErr,$WBRcfn,"Header",$hid,defined($ensNo)?$ensNo+1:0));
 		${$E}[$ens]->{DATA_SOURCE_ID} = $did;
 		if ($did == 0x7f) {
 			${$E}[$ens]->{PRODUCER} = 'TRDI ADCP';
@@ -698,26 +704,31 @@
 		# final ensemble.
 
 		sysseek(WBRF,$start_ens,0) || die("$WBRcfn: $!");
-		unless ((sysread(WBRF,$buf,$ens_length) == $ens_length) &&
+		unless ((sysread(WBRF,$buf,$ens_length) == $ens_length) &&				# incomplete ensemble
 				(sysread(WBRF,$cs,2) == 2)) {
 			pop(@{$E});
 			last;
 		}
 
-		pop(@{$E}),last unless (unpack('%16C*',$buf) == unpack('v',$cs));
+		unless (unpack('%16C*',$buf) == unpack('v',$cs)) {						# bad checksum
+			pop(@{$E});
+			last;
+#			next;																# using this might make the code work
+		}																		# for files with isolated bad ensembles
 
 		#------------------------------
 		# Variable Leader
 		#------------------------------
 	
+		my($lastEns) = $ensNo;
 		sysseek(WBRF,$start_ens+$WBRofs[1],0) || die("$WBRcfn: $!");
 		sysread(WBRF,$buf,4) == 4 || die("$WBRcfn: $!");
-		($id,$ensNo) = unpack("vv",$buf);
+		($id,$ensNo) = unpack("vv",$buf);										# only lower two bytes!!!
 
 		$id == 0x0080 ||
-			die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo+1));
+			die(sprintf($FmtErr,$WBRcfn,"Variable Leader",$id,$ensNo + ($lastEns - ($lastEns & 0xFFFF))));
 
-		if ($fixed_leader_bytes==42 || $fixed_leader_bytes==58) {			# BB150 & Explorer DVL
+		if ($fixed_leader_bytes==42 || $fixed_leader_bytes==58) {				# BB150 & Explorer DVL
 			sysread(WBRF,$buf,7) == 7 || die("$WBRcfn: $!");
 			(${$E}[$ens]->{YEAR},${$E}[$ens]->{MONTH},
 			 ${$E}[$ens]->{DAY},${$E}[$ens]->{HOUR},${$E}[$ens]->{MINUTE},
@@ -854,7 +865,7 @@
 		($id,@dta) = unpack("vv$ndata",$buf);
 
 		$id == 0x0100 ||
-			die(sprintf($FmtErr,$WBRcfn,"Velocity Data",$id,$ens));
+			die(sprintf($FmtErr,$WBRcfn,"Velocity Data",$id,$ensNo));
 		
 		for ($i=0,$bin=0; $bin<$nbins; $bin++) {
 			for ($beam=0; $beam<4; $beam++,$i++) {
@@ -873,7 +884,7 @@
 		($id,@dta) = unpack("vC$ndata",$buf);
 
 		$id == 0x0200 ||
-			die(sprintf($FmtErr,$WBRcfn,"Correlation Data",$id,$ens));
+			die(sprintf($FmtErr,$WBRcfn,"Correlation Data",$id,$ensNo));
 		
 		for ($i=0,$bin=0; $bin<$nbins; $bin++) {
 			for ($beam=0; $beam<4; $beam++,$i++) {
@@ -891,7 +902,7 @@
 		($id,@dta) = unpack("vC$ndata",$buf);
 
 		$id == 0x0300 ||
-			die(sprintf($FmtErr,$WBRcfn,"Echo Intensity",$id,$ens));
+			die(sprintf($FmtErr,$WBRcfn,"Echo Intensity",$id,$ensNo));
 
 		for ($i=0,$bin=0; $bin<$nbins; $bin++) {
 			for ($beam=0; $beam<4; $beam++,$i++) {
@@ -908,7 +919,7 @@
 		($id,@dta) = unpack("vC$ndata",$buf);
 
 		$id == 0x0400 ||
-			die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,$ens));
+			die(sprintf($FmtErr,$WBRcfn,"Percent-Good Data",$id,$ensNo));
 
 		for ($i=0,$bin=0; $bin<$nbins; $bin++) {
 #			printf(STDERR "%-GOOD($bin): ");
@@ -1028,7 +1039,7 @@
     WBPens($dta->{N_BINS},$dta->{FIXED_LEADER_BYTES},$dta);
 }
 
-sub round(@)
+sub _round(@)
 {
 	return $_[0] >= 0 ? int($_[0] + 0.5)
 					  : int($_[0] - 0.5);
@@ -1039,7 +1050,7 @@
 {
 	my($nbins,$fixed_leader_bytes,$dta) = @_;
 	my($start_ens,$B1,$B2,$B3,$B4,$I,$id,$bin,$beam,$buf,$dummy,@dta,$i,$cs,@WBPofs);
-	my($ens,$ensNo,$dayStart,$ens_length,$hid,$ndt);
+	my($ens,$dayStart,$ens_length,$hid,$ndt);
 
 	for ($ens=$start_ens=0; $ens<=$#{$dta->{ENSEMBLE}}; $ens++,$start_ens+=$ens_length+2) {
 
@@ -1100,24 +1111,24 @@
 
 		sysseek(WBPF,$start_ens+$WBPofs[1]+14,0) || die("$WBPcfn: $!");		# jump to SPEED_OF_SOUND
 		
-		$dta->{ENSEMBLE}[$ens]->{XDUCER_DEPTH} = round($dta->{ENSEMBLE}[$ens]->{XDUCER_DEPTH}*10);
+		$dta->{ENSEMBLE}[$ens]->{XDUCER_DEPTH} = _round($dta->{ENSEMBLE}[$ens]->{XDUCER_DEPTH}*10);
 
 		#---------------------------------
 		# NB: IMP allows for missing value
 		#---------------------------------
 
 		$dta->{ENSEMBLE}[$ens]->{HEADING} = defined($dta->{ENSEMBLE}[$ens]->{HEADING})
-							   ? round($dta->{ENSEMBLE}[$ens]->{HEADING}*100)
+							   ? _round($dta->{ENSEMBLE}[$ens]->{HEADING}*100)
 							   : 0xF000;
 		$dta->{ENSEMBLE}[$ens]->{PITCH} = defined($dta->{ENSEMBLE}[$ens]->{PITCH})
-							 ? unpack('S',pack('s',round($dta->{ENSEMBLE}[$ens]->{PITCH}*100)))
+							 ? unpack('S',pack('s',_round($dta->{ENSEMBLE}[$ens]->{PITCH}*100)))
 							 : 0x8000;
 		$dta->{ENSEMBLE}[$ens]->{ROLL} = defined($dta->{ENSEMBLE}[$ens]->{ROLL})
-						    ? unpack('S',pack('s',round($dta->{ENSEMBLE}[$ens]->{ROLL}*100)))
+						    ? unpack('S',pack('s',_round($dta->{ENSEMBLE}[$ens]->{ROLL}*100)))
 						    : 0x8000;
 
 		$dta->{ENSEMBLE}[$ens]->{TEMPERATURE} =
-			unpack('S',pack('s',round($dta->{ENSEMBLE}[$ens]->{TEMPERATURE}*100)));
+			unpack('S',pack('s',_round($dta->{ENSEMBLE}[$ens]->{TEMPERATURE}*100)));
 
 		$buf = pack('vvvvvvv',
 			 $dta->{ENSEMBLE}[$ens]->{SPEED_OF_SOUND},
@@ -1137,7 +1148,7 @@
 		for ($bin=0; $bin<$nbins; $bin++) {
 			for ($beam=0; $beam<4; $beam++) {
 				$dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin][$beam] = defined($dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin][$beam])
-							   						 ? round($dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin][$beam]*1000)
+							   						 ? _round($dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin][$beam]*1000)
 							   						 : 0x8000;
 				$buf = pack('v',unpack('S',pack('s',$dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin][$beam])));
 				my($nw) = syswrite(WBPF,$buf,2);
@@ -1202,7 +1213,7 @@
 		unless ($nxt == $ndt) {													# BT found
 			sysseek(WBPF,14,1) || die("$WBPcfn: $!");							# skip BT config
 			for ($beam=0; $beam<4; $beam++) {									# BT range low bytes (2 per beam)
-				$buf = pack('v',round($dta->{ENSEMBLE}[$ens]->{BT_RANGE}[$beam] * 100) & 0xFFFF);
+				$buf = pack('v',_round($dta->{ENSEMBLE}[$ens]->{BT_RANGE}[$beam] * 100) & 0xFFFF);
 				my($nw) = syswrite(WBPF,$buf,2);
 				$nw == 2 || die("$WBPcfn: $nw bytes written ($!)");
 			}
@@ -1210,7 +1221,7 @@
 			for ($beam=0; $beam<4; $beam++) {									# BT velocities
 				$buf = pack('v',unpack('S',pack('s',
 						defined($dta->{ENSEMBLE}[$ens]->{BT_VELOCITY}[$beam])
-							? round($dta->{ENSEMBLE}[$ens]->{BT_VELOCITY}[$beam]*1000)
+							? _round($dta->{ENSEMBLE}[$ens]->{BT_VELOCITY}[$beam]*1000)
 							: 0x8000)));
 				my($nw) = syswrite(WBPF,$buf,2);
 				$nw == 2 || die("$WBPcfn: $nw bytes written ($!)");
@@ -1236,7 +1247,7 @@
 
 			sysseek(WBPF,33,1) || die("$WBPcfn: $!");							# BT range high bytes (1 per beam)
 			for ($beam=0; $beam<4; $beam++) {
-				$buf = pack('C',(round($dta->{ENSEMBLE}[$ens]->{BT_RANGE}[$beam]*100) & 0xFF0000) >> 16);
+				$buf = pack('C',(_round($dta->{ENSEMBLE}[$ens]->{BT_RANGE}[$beam]*100) & 0xFF0000) >> 16);
 				my($nw) = syswrite(WBPF,$buf,1);
 				$nw == 1 || die("$WBPcfn: $nw bytes written ($!)");
 			}
@@ -1257,4 +1268,40 @@
 	} # ens loop
 }
 
+#----------------------------------------------------------------------
+# &clearEns(^data,ens-index)
+#	- undefine all velocities in ensemble, including BT
+#		- this is required for the LDEO_IX software,
+#		  which does not recognize missing attitude values
+#	- set percent good to zero
+#		- this is done for consistency
+#	- DO NOT undef heading, pitch and roll
+#		- the LDEO software does not recognize missing attitude vals
+#		  and, therefore, misinterprets those
+#		- while this should not matter because all the velocities are
+#		  also deleted, it was found that setting only the heading to
+#		  undef'd and leaving pitch and roll unchanged causes
+#		  significant errors in GPS velocity referencing! This
+#		  must be a bug
+#		- also, if attitudes are undef'd the LDEO software
+#		  cannto determine the instrument offset from pitch/roll
+#		  and the pitch/roll DL vs UL plots are bogus
+#----------------------------------------------------------------------
+
+sub clearEns($$)
+{
+	my($dta,$ens) = @_;
+	croak("clearEns: ens-index $ens out of range\n")
+		unless ($ens>=0 && $ens<=$#{$dta->{ENSEMBLE}});
+	for (my($bin)=0; $bin<$dta->{N_BINS}; $bin++) {
+		undef(@{$dta->{ENSEMBLE}[$ens]->{VELOCITY}[$bin]});
+		@{$dta->{ENSEMBLE}[$ens]->{PERCENT_GOOD}[$bin]} = (0,0,0,0);
+	}
+	undef(@{$dta->{ENSEMBLE}[$ens]->{BT_VELOCITY}});
+	@{$dta->{ENSEMBLE}[$ens]->{BT_PERCENT_GOOD}} = (0,0,0,0);
+#	undef($dta->{ENSEMBLE}[$ens]->{HEADING});
+#	undef($dta->{ENSEMBLE}[$ens]->{PITCH});
+#	undef($dta->{ENSEMBLE}[$ens]->{ROLL});
+}
+
 1;      # return true for all the world to see
--- a/editPD0
+++ b/editPD0
@@ -2,9 +2,9 @@
 #======================================================================
 #                    E D I T P D 0 
 #                    doc: Mon Nov 25 20:24:31 2013
-#                    dlm: Tue Jul 12 18:56:55 2016
+#                    dlm: Tue Nov 15 11:05:46 2016
 #                    (c) 2013 A.M. Thurnherr
-#                    uE-Info: 118 0 NIL 0 0 72 2 2 4 NIL ofnI
+#                    uE-Info: 73 76 NIL 0 0 72 2 2 4 NIL ofnI
 #======================================================================
 
 # edit RDI PD0 file, e.g. to replace pitch/roll/heading with external values
@@ -70,6 +70,7 @@
 #				  - added %-good to beam2earth and earth2beam
 #				  - made single-ping ensemble requirement for most routines
 #	Jul 12, 2016: - updated ensure_{DL,UL} routines
+#	Nov 15, 2016: - BUG: ensure_{DL,UL} routines did not negate heading data
 
 use Getopt::Std;
 
@@ -89,7 +90,8 @@
 
 print(STDERR "Reading $ARGV[0]...");                # read data
 readData($ARGV[0],\%dta);
-print(STDERR "done\n");
+printf(STDERR "done (%d complete ensembles)\n",
+	scalar(@{$dta{ENSEMBLE}}));
 
 #----------------------------------------------------------------------
 
@@ -111,16 +113,26 @@
 #
 #	These routines are intended to correct ADCP data for
 #	erroneous orientation switch readings, primarily because
-#	of a stuck switch. While not fully debugged, negating
-#	the roll value greatly improves the vertical velocity
-#	solutions of 2007 CLIVAR I08S profile #1. (#2-#7 could
-#	also be used for testing)
-#
+#	of a stuck switch.
+#		Roll: Based on text from the coord trans manual,
+#			  it seems likely that the roll data need to
+#			  be negated. In case of 2007(?) CLIVAR I08S
+#			  profile #1 w gets much better with negated
+#			  roll. Also, in 2016 CLIVAR P18 profile 003
+#			  the instrument-offset calculation from
+#			  compass and pitch/roll only agree with the
+#			  roll negated.
+#		Hdg: Based on the time-series of headings recorded
+#		     during P18 profile 003 the heading needs
+#			 to be negated. Doing so yields a good profile.
 
 sub ensure_DL()
 {
 	if ($dta{ENSEMBLE}[$e]->{XDUCER_FACING_UP}) {
 		$dta{ENSEMBLE}[$e]->{ROLL} *= -1;
+		$dta{ENSEMBLE}[$e]->{HEADING} *= -1; 
+		$dta{ENSEMBLE}[$e]->{HEADING} += 360
+			if ($dta{ENSEMBLE}[$e]->{HEADING} < 0);
 		$dta{ENSEMBLE}[$e]->{XDUCER_FACING_DOWN} = 1;
 		$dta{ENSEMBLE}[$e]->{XDUCER_FACING_UP} = undef;
 	}
@@ -131,6 +143,9 @@
 {
 	if ($dta{ENSEMBLE}[$e]->{XDUCER_FACING_DOWN}) {
 		$dta{ENSEMBLE}[$e]->{ROLL} *= -1;
+		$dta{ENSEMBLE}[$e]->{HEADING} *= -1; 
+		$dta{ENSEMBLE}[$e]->{HEADING} += 360
+			if ($dta{ENSEMBLE}[$e]->{HEADING} < 0);
 		$dta{ENSEMBLE}[$e]->{XDUCER_FACING_UP} = 1;
 		$dta{ENSEMBLE}[$e]->{XDUCER_FACING_DOWN} = undef;
 	}
--- a/listBins
+++ b/listBins
@@ -2,9 +2,9 @@
 #======================================================================
 #                    L I S T B I N S 
 #                    doc: Fri Aug 25 15:57:05 2006
-#                    dlm: Thu Jun  9 19:09:47 2016
+#                    dlm: Fri Jan 27 17:41:10 2017
 #                    (c) 2006 A.M. Thurnherr
-#                    uE-Info: 332 0 NIL 0 0 72 10 2 4 NIL ofnI
+#                    uE-Info: 68 81 NIL 0 0 72 2 2 4 NIL ofnI
 #======================================================================
 
 # Split data file into per-bin time series.
@@ -60,6 +60,12 @@
 #   Mar 17, 2016: - adapted to new Getopt library
 #	Jun  9, 2016: - minor improvements
 #				  - BUG: velBeamToEarth() has new interface
+#	Jan 27, 2017: - BUG: earth-coordinate label was wrong
+#				  - changed semantics to include all %-good fields of earth-coordinate data
+#					and also report total pcg (3+4 beam) in min_pcg
+#				  - BUG: 3-beam %ages were incorrect: 1) they were based on goodvels instead of the
+#						 entire ensemble range; 2) pings-per-ensemble were not considered
+#				  - BUG: output layout was all messed up for non-valid velocities
 
 # General Notes:
 #	- everything (e.g. beams) is numbered from 1
@@ -87,6 +93,12 @@
 #	- non-monotonic min_pcg is particularly obvious with the DYNAMUCK BM_ADCP
 #	  data, where one of the beams performed much worse than the others
 
+# %-good of earth-coordinate data:
+#	pcg1	3-beam (correlation threshold)
+#	pcg2	bad (errvel threshold)
+#	pcg3	bad (>1 bad beam)
+#	pcg4	4-beam
+
 use Getopt::Std;
 
 $ADCP_tools_minVersion = 1.4;
@@ -151,13 +163,10 @@
 	foreach my $k (keys(%P)) {
 		print(P "$k\{$P{$k}\} ");
 	}
-	my($pct3b);
-#	if ($beamCoords) {
-		$pct3b = ($good_vels[$b] > 0) ? 100*$three_beam[$b]/$good_vels[$b] : nan;
-		printf(STDERR "%02d:%.0f%%/%.0f%% ",$b+1,100*$good_vels[$b]/($le-$fe+1),$pct3b);
-#	} else {
-#		printf(STDERR "%02d:%.0f%% ",$b+1,100*$good_vels[$b]/($le-$fe+1));
-#	}
+	my($pct3b) = 100*$three_beam[$b]/(($le-$fe+1) * $dta{PINGS_PER_ENSEMBLE});
+	die("assertion failed: gv[$b] = $good_vels[$b]; 3b = $three_beam[$b]; ens=$fe..$le; ")
+		if ($pct3b > 100);
+	printf(STDERR "%02d:%.0f%%/%.0f%% ",$b+1,100*$good_vels[$b]/($le-$fe+1),$pct3b);
 
 	printf(P "pct_3_beam{%.0f} ",$pct3b);
 	printf(P "pct_good_vels{%.0f} ",100*$good_vels[$b]/($le-$fe+1));
@@ -223,17 +232,25 @@
 		} else {
 			print(P "nan nan nan nan ");
 			print(P "nan nan nan nan ");
+			print(P "nan nan nan nan ");
 		}
 		print(P "@{$dta{ENSEMBLE}[$e]->{CORRELATION}[$b]} ");
 		print(P "@{$dta{ENSEMBLE}[$e]->{ECHO_AMPLITUDE}[$b]} ");
+		if ($dta{ENSEMBLE}[$e]->{GOOD_VEL}[$b]) {
+			print(P "@{$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b]} ");
+		} else {
+			print(P "nan nan nan nan ");
+		}
 		if ($beamCoords) {
-			print(P "@{$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b]} ");
 			printf(P "%d ",$dta{ENSEMBLE}[$e]->{THREE_BEAM}[$b]);
 			printf(P "%s ",min(@{$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b]}));
 		} else {
-			print(P "nan nan nan nan ");
-			print(P "$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b][0] ");
-			print(P "nan ");
+			if ($dta{ENSEMBLE}[$e]->{GOOD_VEL}[$b]) {
+				print(P "$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b][0] ");
+			} else {
+				print(P "nan ");
+			}
+			printf(P "%g ",$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b][0]+$dta{ENSEMBLE}[$e]->{PERCENT_GOOD}[$b][3]);
 		}
 		printf(P "%g ",$dz[$b]*$ssCorr) if ($variable_ssCorr);
 		print(P "\n");
@@ -377,10 +394,8 @@
 											   $RDI_Coords::threeBeam_3,
 											   $RDI_Coords::threeBeam_4);
 	
-	print(STDERR "Good/3-beam: ");
-} else {
-	print(STDERR "Good       : ");
 }
+print(STDERR "Good/3-Beam: ");
 for ($b=$firstBin; $b<=$lastBin; $b++) {				# generate output
 	dumpBin($b,$fe,$le);
 }
--- a/listEns
+++ b/listEns
@@ -2,9 +2,9 @@
 #======================================================================
 #                    L I S T E N S 
 #                    doc: Sat Jan 18 18:41:49 2003
-#                    dlm: Tue Apr 19 21:11:12 2016
+#                    dlm: Wed Nov  9 12:27:32 2016
 #                    (c) 2003 A.M. Thurnherr
-#                    uE-Info: 181 53 NIL 0 0 72 2 2 4 NIL ofnI
+#                    uE-Info: 115 0 NIL 0 0 72 2 2 4 NIL ofnI
 #======================================================================
 
 # Print useful info from the ensemble list or dump ensembles to
@@ -47,6 +47,7 @@
 #	Sep  8, 2014: - added -B)T  
 #   Mar 17, 2016: - adapted to new Getopt library
 #	Apr 19, 2016: - added %date, %time to -E output
+#	Nov  9, 2016: - BUG: no error on missing files
 
 # Notes:
 #	- -E outputs data in earth coordinates, unless -b is set also
@@ -106,13 +107,16 @@
 # MAIN
 #----------------------------------------------------------------------
 
-while (-f $ARGV[0]) {
+while ($ARGV[0] ne '') {
+	die("$ARGV[0]: No such file or directory\n")
+		unless (-f $ARGV[0]);
+
+	readData(@ARGV,\%dta);
 	if ($opt_A && !$opt_E) {
 		print("#ANTS#PARAMS# RDI_file{$ARGV[0]}\n");
 	} elsif (!$opt_Q) {
 		print(STDERR "$ARGV[0]: ");
 	}
-	readData(@ARGV,\%dta);
 	printf(STDERR "%d complete ensembles...\n",scalar(@{$dta{ENSEMBLE}}))
 		unless ($opt_Q);
 	$dta{HEADING_BIAS} = -$opt_M;						# magnetic declination
--- a/listHdr
+++ b/listHdr
@@ -2,9 +2,9 @@
 #======================================================================
 #                    L I S T H D R 
 #                    doc: Sat Jan 18 18:41:49 2003
-#                    dlm: Sat Jan  9 17:16:01 2016
+#                    dlm: Wed Nov  9 12:25:36 2016
 #                    (c) 2003 A.M. Thurnherr
-#                    uE-Info: 41 32 NIL 0 0 72 10 2 4 NIL ofnI
+#                    uE-Info: 65 0 NIL 0 0 72 10 2 4 NIL ofnI
 #======================================================================
 
 # Print useful info from the RDI BB header
@@ -39,6 +39,7 @@
 #				  - updated getopts to current perl version
 #				  - updated IO library name
 #				  - adapted to [ADCP_tools_lib.pl]
+#	Nov  9, 2016: - BUG: no error on missing files
 
 use Getopt::Std;
 
@@ -55,9 +56,12 @@
 	print("#ANTS#FIELDS# {id} {serial_number} {transducer_frequency} {number_of_bins} {blanking_distance} {bin_length} {pulse_length}\n");
 }
 
-while (-f $ARGV[0]) {
+while ($ARGV[0] ne '') {
+	die("$ARGV[0]: No such file or directory\n")
+		unless (-f $ARGV[0]);
+	
+	readHeader($ARGV[0],\%hdr);
 	print("$ARGV[0]:\n") unless ($opt_s);
-	readHeader($ARGV[0],\%hdr);
 
 	if ($opt_s) {														# summary ANTS output
 		my($id) = $ARGV[0];
--- a/mkProfile
+++ b/mkProfile
@@ -2,9 +2,9 @@
 #======================================================================
 #                    M K P R O F I L E 
 #                    doc: Sun Jan 19 18:55:26 2003
-#                    dlm: Thu Mar 17 07:41:03 2016
+#                    dlm: Mon Sep 12 14:02:24 2016
 #                    (c) 2003 A.M. Thurnherr
-#                    uE-Info: 93 35 NIL 0 0 72 0 2 4 NIL ofnI
+#                    uE-Info: 94 26 NIL 0 0 72 0 2 4 NIL ofnI
 #======================================================================
 
 # Make an LADCP Profile by Integrating W (similar to Firing's scan*).
@@ -91,6 +91,7 @@
 #	Mar 22, 2015: - made it work for moored time series as well
 #	Mar 17, 2015: - adapted to new Getopt library
 #				  - removed warning
+#	Sep 12, 2016: - added %PD0_file
 
 # NOTES:
 #	- the battery values are based on transmission voltages (different
@@ -720,6 +721,7 @@
 				 "end_time{$dta{ENSEMBLE}[$lastgood]->{TIME}} " .
 	  "bottom_xmit_voltage{$dta{ENSEMBLE}[$atbottom]->{ADC_XMIT_VOLTAGE}} " .
 	  "bottom_xmit_current{$dta{ENSEMBLE}[$atbottom]->{ADC_XMIT_CURRENT}} " .
+				 "PD0_file{$ARGV[0]} " .
 		 "pinging_duration{%.1f} " .
 			"cast_duration{%.1f} " .
 		   "0.8_valid_bins{%.1f} " .