function [badBursts, badCells] = mask_aqdpCurr_cdf(cdfFileRoot); % mask_aqdpCurr_cdf.m A function to QA/QC Aquadopp velocity data in a netCDF % (.cdf) file. % % usage: mask_aqdpCurr_cdf(cdfFileRoot); % % where: cdfFileRoot - the name of the .cdf file which contains % Aquadopp velocity data as output by % AquaPro, surrounded by single quotes with % no file extension % badBursts - a list of bad bursts, which are collected % during instrument deployment and recovery % badCells - a list of bad cells, which are located above % MSL and contaminated by sidelobe reflection % errors % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Use of this program is described in: % % Sullivan, C.M., Warner, J.C., Martini, M.A., Voulgaris, G., % Work, P.A., Haas, K.A., and Hanes, D.H. (2006) % South Carolina Coastal Erosion Study Data Report for Observations % October 2003 - April 2004., USGS Open-File Report 2005-1429. % % Program written in Matlab v7.1.0 SP3 % Program ran on PC with Windows XP Professional OS. % % "Although this program has been used by the USGS, no warranty, % expressed or implied, is made by the USGS or the United States % Government as to the accuracy and functioning of the program % and related program material nor shall the fact of distribution % constitute any such warranty, and no responsibility is assumed % by the USGS in connection therewith." %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright 2004 % USGS Woods Hole Field Center % Written by Charlene Sullivan % csullivan@usgs.gov % % Dependencies: % smart_interp.m % gmin.m % gmax.m % C. Sullivan 06/17/05, version 1.1 % If the Aquadopp's wave interval is longer than the current profile % interval, the waves take precedence!! I believe the AquaPro software % interpolates where current profiles are skipped, but something weird is % happening to the temperature. I now NaN the profiles that are skipped % (interpolated) b/c of waves, and do my own interpolation. % C. Sullivan 06/16/05, version 1.0 % This mfile applies QA/QC measures including removal of bad data collected % during deployment and recovery of the instrument, and removal of cells % (bins) contaminated by sidelobe reflection errors. version = '1.1'; % Copy cdfFileRoot.cdf to cdfFileRoot-mask.cdf. In the 'mask' version % all bad data will be masked (set to FILL). Masked data should NOT be % written to the BBV .nc file. The mask file is an intermediate file, % between the .cdf file created from AquaPro output, and the .nc file % created for distribution. copyfile([cdfFileRoot,'.cdf'],[cdfFileRoot,'-mask.cdf']) cdf = netcdf([cdfFileRoot,'-mask.cdf'],'w'); % Get the dimensions theBurstDim = cdf('burst'); nBursts = max(size(theBurstDim)); % Data is interpolated internally by AquaPro for those bursts % that occur during a wave burst. Ask the user if they'd like to loose % AquaPro's interpolation, and do their own linear interpolation FOR % TEMPERATURE DATA ONLY (velocity and pressure data has been okay). temperature = cdf{'temperature'}(:); burst = cdf{'burst'}(:); time = cdf{'time'}(:) + (cdf{'time2'}(:)/3600/1000/24); prof_int = cdf.AQDP_Profile_interval(:); nums = ~isletter(prof_int); prof_int = str2num(prof_int(nums)); wave_int = cdf.AQDP_Wave__Interval(:); nums = ~isletter(wave_int); wave_int = str2num(wave_int(nums)); if wave_int > prof_int figure plot(burst, temperature) hold on xlabel('Burst number') ylabel('Temperature (\circC)') disp(' ') disp('Please look at temperature data and look for evidence of bad') disp('interpolations by AquaPro') answer = input('Would you like to re-interpolate temperature data? y/n: ','s'); if strcmp(answer,'Y') || strcmp(answer,'y') first_bad_interp = input('Enter the burst # with the 1st bad interplolation: '); bad_interps = [first_bad_interp:wave_int/prof_int:nBursts]'; temperature(bad_interps) = nan; disp('Re-interpolating bad temperature data ...') temperature = smart_interp(time, temperature, time, 1); plot(burst, temperature, 'r') legend('AquaPro-interpolated data','self-interpolated data') bads = find(isnan(temperature)); temperature(bads) = 1e35; %set nans to fill value cdf{'temperature'}(:) = temperature; cdf{'temperature'}.NOTE = ncchar('Temperature data re-interpolated at those bursts that occured during wave bursts'); hist = cdf.history(:); hist_new = ['Temperature data re-interpolated by mask_aqdpCurr_cdf.m V ',... version,'; ',hist]; cdf.history = ncchar(hist_new); end end % Identify bad data collected during instrument deployment and % recovery by looking at a plot of pressure. Ask the user to enter the % first and last good bursts of data. Data up to the first good burst and % data after the last good burst will be masked. figure plot(cdf{'burst'}(:), cdf{'pressure'}(:)) hold on ylabel('Pressure (m)') xlabel('Burst number') disp(' ') disp('Please view the figure (feel free to zoom!) and look for bad') disp('bursts collected during instrument deployment and recovery') answer = input(['Would you like to mask any bad bursts? y/n : '],'s'); if ~strcmp(answer,'Y') & ~strcmp(answer,'y') & ... ~strcmp(answer,'N') & ~strcmp(answer,'n') disp(['Valid answers are either yes ("y") or no ("n")']) answer = input(['Would you like to mask any bad bursts? y/n : '],'s'); end if strcmp(answer,'N') || strcmp(answer,'n') first_good = 1; last_good = nBursts; bad_bursts = []; disp(' ') disp('Not masking any bursts') disp(['All bursts (1:',num2str(nBursts),') remain un-masked in ',... cdfFileRoot,'-mask.cdf']) elseif strcmp(answer,'Y') || strcmp(answer,'y') first_good = input('Please view the figure and enter the first GOOD burst: '); if isempty(first_good) disp('Valid answers are numeric only') first_good = input('Please view the figure and enter the first GOOD burst: '); end last_good = input('Please view the figure and enter the last GOOD burst: '); if isempty(last_good) disp('Valid answers are numeric only') last_good = input('Please view the figure and enter the last GOOD burst: '); end if isequal(first_good,1) disp(' ') disp(['Bursts ',num2str(last_good),':',num2str(nBursts),... ' will be masked in ',cdfFileRoot,'-mask.cdf']) badBursts = [last_good:nBursts]'; elseif isequal(last_good,nBursts) disp(['Bursts 1:',num2str(first_good),' will be masked in ',... cdfFileRoot,'-mask.cdf']) badBursts = [1:first_good]'; else disp(['Bursts 1:',num2str(first_good),' and bursts ',num2str(last_good), ... ':',num2str(nBursts),' will be masked in ',cdfFileRoot,'-mask.cdf']) badBursts = [1:first_good last_good:nBursts]'; end end plot(cdf{'burst'}(1:first_good), cdf{'pressure'}(1:first_good),'r') plot(cdf{'burst'}(last_good:nBursts), cdf{'pressure'}(last_good:nBursts),'r') legend('good data','masked data') % Calculate MSL and 1/2 the tidal range press = cdf{'pressure'}(:); press(badBursts) = nan; MSL = gmean(press); tide = gstd(press); disp(' ') disp(['The calculated MSL is ',num2str(MSL), ' meters']) disp(['The calculated tidal range is ',num2str(tide),' meters']) % Data within approx. 6% of the surface is contaminated by sidelobe % reflection errors. We will mask the data in these cells. % Reference: http://www.nortekusa.com/principles/Doppler.html cellHeight = cdf{'height'}(:); %cell height above bottom, m badCells = find( cellHeight > (0.94*(MSL+(tide)))); disp(' ') if isempty(badCells) disp('No cells found within the specified depth range') else disp(['Cells [',num2str(badCells'),'] are above MSL']) disp('Data in these cells will be masked') end % Mask data, recalculate min/max values, and % set NaN's to fill disp(' ') disp('Masking data ...') theVars = var(cdf); if ~isempty(badBursts) || ~isempty(badCells) for i = 1:length(theVars), if ~strcmp(ncnames(theVars{i}),'time') & ... ~strcmp(ncnames(theVars{i}),'time2') & ... ~strcmp(ncnames(theVars{i}),'burst') & ... ~strcmp(ncnames(theVars{i}),'lat') & ... ~strcmp(ncnames(theVars{i}),'lon') & ... ~strcmp(ncnames(theVars{i}),'height') theFillVal = theVars{i}.FillValue_(:); data = theVars{i}(:); [nR, nC] = size(data); fills = find(data >= theFillVal); if ~isempty(fills) data(fills) = nan; end if nC > 1 if ~isempty(badBursts) data(badBursts,:) = nan; end if ~isempty(badCells) data(:,badCells) = nan; end else if ~isempty(badBursts) data(badBursts) = nan; end end theVars{i}.minimum = gmin(data(:)); theVars{i}.maximum = gmax(data(:)); bads = find(isnan(data)); data(bads) = theFillVal; theVars{i}(:) = data; end end else disp(' ') disp(['Not masking any data in the file ',cdfFileRoot,'-mask.cdf']) end % Set attribute WATER_DEPTH to MSL cdf.WATER_DEPTH = ncfloat(MSL); cdf.WATER_DEPTH_NOTE = ncchar('Water depth is MSL'); % Update the file history if ~isempty(badBursts) && ~isempty(badCells) hist = cdf.history(:); hist_new = ['Bad data collected during instrument deployment and recovery, ',... 'and data collected above MSL masked by mask_aqdpCurr_cdf.m V ',... version,'; ',hist]; cdf.history = ncchar(hist_new); elseif ~isempty(badBursts) hist = cdf.history(:); hist_new = ['Bad data collected during instrument deployment and recovery ',... 'masked by mask_aqdpCurr_cdf.m V ',version,'; ',hist]; cdf.history = ncchar(hist_new); elseif ~isempty(badCells) hist = cdf.history(:); hist_new = ['Data collected above MSL ',... 'masked by mask_aqdpCurr_cdf.m V ',version,'; ',hist]; cdf.history = ncchar(hist_new); end % Update the file creation date cdf.CREATION_DATE = ncchar(datestr(now,0)); close(cdf); disp(' ') disp('Finished masking data') disp(' ') disp('NOTE: Masking data for sidelobes does not necessarily remove all') disp(' contaminated cells. The user should load the file ') disp([' ',cdfFileRoot,'-mask.cdf and manually look for and remove if']) disp(' necessary additional contaminated cells')