LADCP_w_CTD
changeset 52 4ccccbf69dfd
parent 51 0f6d9e64cc4f
child 54 828e5466391b
equal deleted inserted replaced
51:0f6d9e64cc4f 52:4ccccbf69dfd
     1 #!/usr/bin/perl
     1 #!/usr/bin/perl
     2 #======================================================================
     2 #======================================================================
     3 #                    L A D C P _ W _ C T D 
     3 #                    L A D C P _ W _ C T D 
     4 #                    doc: Mon Nov  3 17:34:19 2014
     4 #                    doc: Mon Nov  3 17:34:19 2014
     5 #                    dlm: Mon Apr 29 18:00:44 2019
     5 #                    dlm: Wed Aug 28 17:47:08 2019
     6 #                    (c) 2014 A.M. Thurnherr
     6 #                    (c) 2014 A.M. Thurnherr
     7 #                    uE-Info: 111 15 NIL 0 0 72 2 2 4 NIL ofnI
     7 #                    uE-Info: 296 0 NIL 0 0 72 2 2 4 NIL ofnI
     8 #======================================================================
     8 #======================================================================
     9 
     9 
    10 $antsSummary = 'pre-process SBE 9plus CTD data for LADCP_w';
    10 $antsSummary = 'pre-process SBE 9plus CTD data for LADCP_w';
    11 
    11 
    12 # HISTORY:
    12 # HISTORY:
    85 #				  - BUG: initial in-air scans were not handled correctly (nscans not updated)
    85 #				  - BUG: initial in-air scans were not handled correctly (nscans not updated)
    86 #	Mar 25, 2019: - changed error message to allow creating 1Hz from 4Hz file (SBE19)
    86 #	Mar 25, 2019: - changed error message to allow creating 1Hz from 4Hz file (SBE19)
    87 #				  - BUG: ITS was not set. How is this possible?????
    87 #				  - BUG: ITS was not set. How is this possible?????
    88 #	Apr 21, 2019: - modified code to allow production of 24Hz files (previous code required
    88 #	Apr 21, 2019: - modified code to allow production of 24Hz files (previous code required
    89 #					min 2 samples per bin, allowing for max 12Hz sampling rate)
    89 #					min 2 samples per bin, allowing for max 12Hz sampling rate)
       
    90 #	Aug 27, 2019: - began adding correction for dropped CTD scans
       
    91 #	Aug 28, 2019: - made it work
    90 
    92 
    91 # NOTES:
    93 # NOTES:
    92 #	w_CTD is positive during the downcast to make the sign of the apparent
    94 #	w_CTD is positive during the downcast to make the sign of the apparent
    93 #		  water velocity consistent with w_ocean
    95 #		  water velocity consistent with w_ocean
    94 
    96 
   148 if ($rec =~ /^\*/) {												# SBE CNV file
   150 if ($rec =~ /^\*/) {												# SBE CNV file
   149 	$libSBE_quiet = 1;												# suppress diagnostic messages
   151 	$libSBE_quiet = 1;												# suppress diagnostic messages
   150 	($nfields,$nscans,$sampint,$badval,$ftype,$lat,$lon) =			# decode SBE header 
   152 	($nfields,$nscans,$sampint,$badval,$ftype,$lat,$lon) =			# decode SBE header 
   151 		SBE_parseHeader(F,0,0); 									# SBE field names, no time check
   153 		SBE_parseHeader(F,0,0); 									# SBE field names, no time check
   152 	
   154 	
   153 #	_croak("$CNVfile: unexpected sampling interval $sampint s\n")
       
   154 #		unless (abs($sampint-1/24) < 1e-5);
       
   155 	_croak("$CNVfile: insufficient time resolution ($sampint s) for ${opt_s}Hz time series\n")
   155 	_croak("$CNVfile: insufficient time resolution ($sampint s) for ${opt_s}Hz time series\n")
   156 		if (round(1/$sampint/$opt_s) < 1);
   156 		if (round(1/$sampint/$opt_s) < 1);
   157 
   157 
   158 	if (defined($opt_l)) {											# set/override station location with -l
   158 	if (defined($opt_l)) {											# set/override station location with -l
   159 		my($slat,$slon) = split('[,/]',$opt_l);
   159 		my($slat,$slon) = split('[,/]',$opt_l);
   191 		} else {
   191 		} else {
   192 			$condF	= fnr('c0mS/cm');
   192 			$condF	= fnr('c0mS/cm');
   193 			$condHistRes = 2;
   193 			$condHistRes = 2;
   194 		}
   194 		}
   195 	}
   195 	}
       
   196 
       
   197 	$systimeF = &fnrNoErr('timeY');									# apply correction for dropped scans
       
   198 	$xmerrF  = &fnrNoErr('modError');
       
   199 	if (defined($systimeF) && defined($xmerrF)) {
       
   200 		$fill_gaps = 1;
       
   201 	} else {
       
   202 		print(STDERR "\n\n") if ($opt_v > 1);
       
   203 		print(STDERR "WARNING: timeY and/or modError missing from $CNVfile -- cannot correct for CTD modulo errors\n");
       
   204     }	
   196 	
   205 	
   197 	$latF = &fnrNoErr('lat');										# GPS data if available (to make files useful for u/v processing)
   206 	$latF = &fnrNoErr('lat');										# GPS data if available (to make files useful for u/v processing)
   198 	$lonF = &fnrNoErr('lon');
   207 	$lonF = &fnrNoErr('lon');
   199 	
   208 	
   200 	&antsInstallBufFull(0); 										# read entire CNV file
   209 	&antsInstallBufFull(0); 										# read entire CNV file
   228 		$ants_[$nscans][$tempF] = nan unless defined($ants_[$nscans][$tempF]);
   237 		$ants_[$nscans][$tempF] = nan unless defined($ants_[$nscans][$tempF]);
   229 		$ants_[$nscans][$condF] = nan unless defined($ants_[$nscans][$condF]);
   238 		$ants_[$nscans][$condF] = nan unless defined($ants_[$nscans][$condF]);
   230 	}
   239 	}
   231 }
   240 }
   232 
   241 
   233 printf(STDERR "\n\t%d scans (%d seconds)",$nscans,int($nscans*$sampint))
   242 printf(STDERR "\n\t%d scans (%d minutes)",$nscans,round($nscans*$sampint/60))
   234 	if ($opt_v > 1);
   243 	if ($opt_v > 1);
   235 printf(STDERR "\n") if ($opt_v);
   244 printf(STDERR "\n") if ($opt_v);
       
   245 
       
   246 #----------------------------------------------------------------------
       
   247 # Fill gaps in CTD time series due to dropped scans (modulo errors)
       
   248 #----------------------------------------------------------------------
       
   249 
       
   250 if ($fill_gaps) {
       
   251 	print(STDERR "Filling CTD time-series gaps...") if ($opt_v);
       
   252 
       
   253 	my($scans_filled) = 0;
       
   254 	my($scans_replaced) = 0;
       
   255 	my($scans_deleted) = 0;
       
   256 
       
   257 	for (my($scan)=30; $scan<@ants_; $scan++) {										# start a bit more than 1 second into the cast
       
   258 		next if ($ants_[$scan][$systimeF] == $ants_[$scan-1][$systimeF]);			# skip forward to next systime second
       
   259 
       
   260 #		while ($ants_[$scan][$systimeF] > $ants_[$scan-1][$systimeF]+1) {			# gap spans full second
       
   261 #			my(@splicescan);														# scan to splice in 
       
   262 #			$splicescan[$systimeF] = $ants_[$scan-1][$systimeF] + 1;
       
   263 #			$splicescan[$xmerrF] = $ants_[$scan-1][$xmerrF];
       
   264 #			for (my($i)=0; $i<24; $i++) {
       
   265 #				splice(@ants_,$scan,0,\@splicescan);
       
   266 #				$scans_filled++;
       
   267 #			}
       
   268 #		}
       
   269 			
       
   270 		my($second_start) = $scan - 1;												# backtrack to beginning of second
       
   271 		my($scans_this_sec) = 1;
       
   272 		while ($ants_[$second_start-1][$systimeF] == $ants_[$second_start][$systimeF]) {
       
   273 			$second_start--; $scans_this_sec++;
       
   274 		}
       
   275 		die("\nscan = $scan, second_start = $second_start, st = $ants_[$second_start][$systimeF]")
       
   276 			unless ($second_start > 0);
       
   277 
       
   278 		while ($scans_this_sec > 24) {												# CTD clock running fast => remove scans
       
   279 			splice(@ants_,$scan-1,1);
       
   280 			$scans_deleted++; $scans_this_sec--; $scan--;
       
   281 		}
       
   282 
       
   283 		my($gap_len) = 24 - $scans_this_sec;										# gap length in this second only
       
   284 		die("gap_len = $gap_len at scan#$scan") unless ($gap_len >= 0);
       
   285 		
       
   286 		my($ngaps) = 0;																# gaps between end of previous and beginning of next section
       
   287 		for (my($s)=$second_start; $s<=$scan; $s++) {
       
   288 			$ngaps++ if ($ants_[$s][$xmerrF] > $ants_[$s-1][$xmerrF]);
       
   289 		}
       
   290 
       
   291 		my(@splicescan);															# scan to splice in 
       
   292 		$splicescan[$systimeF] = $ants_[$second_start][$systimeF];
       
   293 		$splicescan[$xmerrF] = $ants_[$second_start][$xmerrF];
       
   294 
       
   295 		if ($ngaps==0 && $gap_len>0) {												# clock drift => add scan
       
   296 #			print(STDERR "adding $gap_len scans to make up for clock drift starting at scan $scan-1 (ss = $second_start) (st = $splicescan[$systimeF])\n");
       
   297 			for (my($i)=0; $i<$gap_len; $i++) {										# fill gap
       
   298 				splice(@ants_,$scan-1,0,\@splicescan);
       
   299 				$scans_filled++; $scan++;
       
   300 			}
       
   301         } elsif ($ngaps == 1) {														# single gap => fill it
       
   302 			my($gap_location) = $second_start;										# find it
       
   303 			while ($ants_[$gap_location][$xmerrF] == $ants_[$second_start-1][$xmerrF]) {
       
   304 				$gap_location++;
       
   305 			}
       
   306 			die('INTERNAL ERROR') unless ($gap_location <= $scan);
       
   307 #			print(STDERR "filling $gap_len gap starting at scan $gap_location (ss = $second_start) (st = $splicescan[$systimeF])\n");
       
   308 			for (my($i)=0; $i<$gap_len; $i++) {										# fill gap
       
   309 				splice(@ants_,$gap_location,0,\@splicescan);
       
   310 				$scans_filled++; $scan++;
       
   311 			}
       
   312 		} else {																	# multiple gaps => delete data
       
   313 			my($last_gap);
       
   314 			for (my($s)=$second_start; $s<$scan; $s++) {							# replace ambiguous data scans
       
   315 				next if ($ants_[$s][$xmerrF] == $ants_[$second_start-1][$xmerrF]);
       
   316 				last if ($ants_[$s][$xmerrF] == $ants_[$scan][$xmerrF]);
       
   317 				splice(@ants_,$s,1,\@splicescan);									# replace
       
   318 				$scans_replaced++;
       
   319 				$last_gap = $s;
       
   320             }		
       
   321 			for (my($i)=0; $i<$gap_len; $i++) {										# fill gaps
       
   322 				splice(@ants_,$last_gap+1,0,\@splicescan);
       
   323 				$scans_filled++; $scan++;
       
   324 			}
       
   325 		}
       
   326 
       
   327 		my($scans_this_sec) = 0;
       
   328 		die("$ants_[$second_start][$systimeF]==$ants_[$second_start-1][$systimeF]+1")
       
   329 			unless ($ants_[$second_start][$systimeF]==$ants_[$second_start-1][$systimeF]+1);
       
   330 		for (my($i)=0; $ants_[$second_start+$i][$systimeF]==$ants_[$second_start-1][$systimeF]+1; $i++) {
       
   331 			$scans_this_sec++;
       
   332 		}
       
   333 		die("\n$scans_this_sec scans in second $ants_[$second_start][$systimeF] at scans $second_start .. $scan-1 (gap_len $gap_len, $ngaps gaps)\n")
       
   334 			unless ($scans_this_sec == 24);
       
   335 	} # for ($scan
       
   336 
       
   337 #	my($sts) = 1;
       
   338 #	for (my($scan)=1; $scan<@ants_; $scan++) {
       
   339 #		$sts++,next if ($ants_[$scan][$systimeF] == $ants_[$scan-1][$systimeF]);
       
   340 #		printf(STDERR "sts = $sts beginning at scan %d (st = $ants_[$scan-1][$systimeF])\n",$scan-$sts)
       
   341 #			unless ($sts == 24);
       
   342 #		$sts = 1;
       
   343 #	}
       
   344 		
       
   345 
       
   346 	&antsAddParams('gaps_scans_filled',$scans_filled,
       
   347 				   'scans_removed',$scans_deleted,
       
   348 				   'gap_scans_cleared',$scans_replaced);
       
   349 
       
   350 	if ($opt_v>1 && $scans_filled) {
       
   351 		printf(STDERR "\n\t%d scans removed (clock drift)",$scans_deleted);
       
   352 		printf(STDERR "\n\t%d scans added (gaps & clock drift)",$scans_filled);
       
   353 		printf(STDERR "\n\t%d scans cleared (gap clusters)",$scans_replaced);
       
   354 	}
       
   355 	printf(STDERR "\n") if ($opt_v);
       
   356 } # if ($fill_gaps
   236 
   357 
   237 #----------------------------------------------------------------------
   358 #----------------------------------------------------------------------
   238 # Redirect STDOUT to %.6Hz & create %_w_CTD.ps,%_sspd.ps if STDOUT is a tty
   359 # Redirect STDOUT to %.6Hz & create %_w_CTD.ps,%_sspd.ps if STDOUT is a tty
   239 #----------------------------------------------------------------------
   360 #----------------------------------------------------------------------
   240 
   361 
   307 	#	- histogram shifted by 100dbar to allow for negative values
   428 	#	- histogram shifted by 100dbar to allow for negative values
   308 	#-------------------------------------------------------------------------
   429 	#-------------------------------------------------------------------------
   309 	my($press_rez) = 2;
   430 	my($press_rez) = 2;
   310 	my($outliers) = my($modeSamp) = 0; my($modeBin,$min,$max); local(@hist);
   431 	my($outliers) = my($modeSamp) = 0; my($modeBin,$min,$max); local(@hist);
   311 	for (my($s)=0; $s<$nscans; $s++) {
   432 	for (my($s)=0; $s<$nscans; $s++) {
       
   433 		$ants_[$s][$pressF] = nan unless defined($ants_[$s][$pressF]);
   312 		next unless ($ants_[$s][$pressF]>=-100 && $ants_[$s][$pressF]<=6500);
   434 		next unless ($ants_[$s][$pressF]>=-100 && $ants_[$s][$pressF]<=6500);
   313 		my($b) = ($ants_[$s][$pressF]+100) / $press_rez;
   435 		my($b) = ($ants_[$s][$pressF]+100) / $press_rez;
   314 		$hist[$b]++;
   436 		$hist[$b]++;
   315 		next unless ($hist[$b] > $modeSamp);
   437 		next unless ($hist[$b] > $modeSamp);
   316 		$modeSamp = $hist[$b]; $modeBin = $b;
   438 		$modeSamp = $hist[$b]; $modeBin = $b;
   318 	printf(STDERR "\n\tvalid pressure guess: %d dbar (%d samples)",$press_rez*$modeBin-100,$modeSamp)
   440 	printf(STDERR "\n\tvalid pressure guess: %d dbar (%d samples)",$press_rez*$modeBin-100,$modeSamp)
   319 		if ($opt_v > 1);
   441 		if ($opt_v > 1);
   320 	($min,$max) = validRange($modeBin);
   442 	($min,$max) = validRange($modeBin);
   321 	$min = $press_rez*$min-100; $max = $press_rez*$max-100;
   443 	$min = $press_rez*$min-100; $max = $press_rez*$max-100;
   322 	for (my($s)=0; $s<$nscans; $s++) {
   444 	for (my($s)=0; $s<$nscans; $s++) {
       
   445 		next unless numberp($ants_[$s][$pressF]);
   323 		if ($ants_[$s][$pressF] > $max) { $outliers++; $ants_[$s][$pressF] = nan; }
   446 		if ($ants_[$s][$pressF] > $max) { $outliers++; $ants_[$s][$pressF] = nan; }
   324 		if ($ants_[$s][$pressF] < $min) { $outliers++; $ants_[$s][$pressF] = nan; }
   447 		if ($ants_[$s][$pressF] < $min) { $outliers++; $ants_[$s][$pressF] = nan; }
   325 	}
   448 	}
   326 	&antsAddParams("pressure_outliers",sprintf("%d",$outliers));
   449 	&antsAddParams("pressure_outliers",sprintf("%d",$outliers));
   327 	printf(STDERR "\n\tcontinuous pressure range: %d..%d dbar (%d outliers removed)",$min,$max,$outliers) if ($opt_v > 1);
   450 	printf(STDERR "\n\tcontinuous pressure range: %d..%d dbar (%d outliers removed)",$min,$max,$outliers) if ($opt_v > 1);
   332 	# edit conductivity outliers outside contiguous range
   455 	# edit conductivity outliers outside contiguous range
   333 	#----------------------------------------------------
   456 	#----------------------------------------------------
   334 	$outliers = $modeSamp = 0;
   457 	$outliers = $modeSamp = 0;
   335 	undef(@hist);
   458 	undef(@hist);
   336 	for (my($s)=0; $s<$nscans; $s++) {
   459 	for (my($s)=0; $s<$nscans; $s++) {
       
   460 		$ants_[$s][$condF] = nan unless defined($ants_[$s][$condF]);
   337 		next unless ($ants_[$s][$condF] > 0);
   461 		next unless ($ants_[$s][$condF] > 0);
   338 		my($b) = $ants_[$s][$condF]*$condHistRes;					# 1/10 S/m histogram resolution (1 mS/cm)
   462 		my($b) = $ants_[$s][$condF]*$condHistRes;					# 1/10 S/m histogram resolution (1 mS/cm)
   339 		$hist[$b]++;
   463 		$hist[$b]++;
   340 		next unless ($hist[$b] > $modeSamp);
   464 		next unless ($hist[$b] > $modeSamp);
   341 		$modeSamp = $hist[$b]; $modeBin = $b;
   465 		$modeSamp = $hist[$b]; $modeBin = $b;
   342 	}
   466 	}
   343 	($min,$max) = validRange($modeBin);
   467 	($min,$max) = validRange($modeBin);
   344 	$min /= $condHistRes; $max /= $condHistRes;
   468 	$min /= $condHistRes; $max /= $condHistRes;
   345 	for (my($s)=0; $s<$nscans; $s++) {
   469 	for (my($s)=0; $s<$nscans; $s++) {
       
   470 		next unless numberp($ants_[$s][$condF]);
   346 		if ($ants_[$s][$condF] > $max) { $outliers++; $ants_[$s][$condF] = nan; }
   471 		if ($ants_[$s][$condF] > $max) { $outliers++; $ants_[$s][$condF] = nan; }
   347 		if ($ants_[$s][$condF] < $min) { $outliers++; $ants_[$s][$condF] = nan; }
   472 		if ($ants_[$s][$condF] < $min) { $outliers++; $ants_[$s][$condF] = nan; }
   348 	}
   473 	}
   349 	&antsAddParams("conductivity_outliers",sprintf("%d",$outliers));
   474 	&antsAddParams("conductivity_outliers",sprintf("%d",$outliers));
   350 	printf(STDERR "\n\tcontinuous conductivity range: %.1f..%.1f S/m (%d outliers removed)",$min,$max,$outliers) if ($opt_v > 1);
   475 	printf(STDERR "\n\tcontinuous conductivity range: %.1f..%.1f S/m (%d outliers removed)",$min,$max,$outliers) if ($opt_v > 1);
   360 	#     apparent adverse effects
   485 	#     apparent adverse effects
   361 	#----------------------------------------------------
   486 	#----------------------------------------------------
   362 	$outliers = $modeSamp = 0;
   487 	$outliers = $modeSamp = 0;
   363 	undef(@hist);
   488 	undef(@hist);
   364 	for (my($s)=0; $s<$nscans; $s++) {
   489 	for (my($s)=0; $s<$nscans; $s++) {
       
   490 		$ants_[$s][$tempF] = nan unless defined($ants_[$s][$tempF]);
   365 		next unless ($ants_[$s][$tempF] >= -10);
   491 		next unless ($ants_[$s][$tempF] >= -10);
   366 		my($b) = ($ants_[$s][$tempF] + 10) * 0.5;
   492 		my($b) = ($ants_[$s][$tempF] + 10) * 0.5;
   367 		$hist[$b]++;
   493 		$hist[$b]++;
   368 		next unless ($hist[$b] > $modeSamp);
   494 		next unless ($hist[$b] > $modeSamp);
   369 		$modeSamp = $hist[$b]; $modeBin = $b;
   495 		$modeSamp = $hist[$b]; $modeBin = $b;
   372 #		if ($opt_v > 1);
   498 #		if ($opt_v > 1);
   373 	($min,$max) = validRange($modeBin);
   499 	($min,$max) = validRange($modeBin);
   374 	$min = ($min / 0.5) - 10;
   500 	$min = ($min / 0.5) - 10;
   375 	$max = ($max / 0.5) - 10;
   501 	$max = ($max / 0.5) - 10;
   376 	for (my($s)=0; $s<$nscans; $s++) {
   502 	for (my($s)=0; $s<$nscans; $s++) {
       
   503 		next unless numberp($ants_[$s][$tempF]);
   377 		if ($ants_[$s][$tempF] > $max) { $outliers++; $ants_[$s][$tempF] = nan; }
   504 		if ($ants_[$s][$tempF] > $max) { $outliers++; $ants_[$s][$tempF] = nan; }
   378 		if ($ants_[$s][$tempF] < $min) { $outliers++; $ants_[$s][$tempF] = nan; }
   505 		if ($ants_[$s][$tempF] < $min) { $outliers++; $ants_[$s][$tempF] = nan; }
   379 	}
   506 	}
   380 	&antsAddParams("temperature_outliers",sprintf("%d",$outliers));
   507 	&antsAddParams("temperature_outliers",sprintf("%d",$outliers));
   381 	printf(STDERR "\n\tcontinuous temperature range: %.1f..%.1f degC (%d outliers removed)",$min,$max,$outliers)
   508 	printf(STDERR "\n\tcontinuous temperature range: %.1f..%.1f degC (%d outliers removed)",$min,$max,$outliers)