function Adv = adv2nc(cdfbFile, cdfsFile, ncFile, switches, bursts) % adv2nc - process adv burst data from a netcdf raw file % function Adv = adv2nc(cdfbFile, cdfsFile, ncFile, switches, bursts); % applies rotations, removes wild points, flags bursts that may need hand editing. % you will want to convert from a .adp file to .cdf using adr2cdf.m % cdfbFile = raw ADV data netCDF file of burst data *b.cdf % cdfsFile = raw ADV netCDF file of stats data *s.cdf % ncFile = file name for the netCDF output file, to which b.nc is appended % for the burst data, which is not EPIC compliant, shaped [time, sample, depth]; % and *vp-cal.nc for the statistics, in EPIC compliant format % switches = a structure of settings that will affect how data are processed % if they are omitted, defaults shown below will be used % switches.ecorr_showplots = 0; % Vector length must match the number of bursts this function is % asked to process % switches.override_heading = []; % switches.override_pitch = []; % switches.override_roll = []; % The ADV has a nasty habit of recording the first few pressure samples % incorrectly. You will see this as an initial spike in the burst, consistently % in the same place in all bursts. Use this override to get rid of it, % use this as a mask, where override_pmask is the same length as % nsamples per burst, and set to zero where spiky data are to be omitted % in each burst. This masking method works best for spikes at the % beginning of a pressure burst. Only for external frequency pressure. % switches.override_pmask = []; % switches.override_pmask = [zeros(5,1); ones(4500-5,1)]; % If your instrument did not have a compass installed, and you don't % provide overrides, all values will be assumed to be zero. % switches = []; invokes all defaults % If you set record sensor to No, then no temperature will appear int he % burst file. Pressure will be converted to scientific units using mean % temperature in the statistics file. % bursts = [startburst endburst], as a way into the cdfb{'burst'} list % (if you're working with a subset of data with a burst range of % 2500:4000 only, and you then want to convert only 2500-3500, enter % [2500 3500] for the last argument) ** use burst numbers % Adv = data from the last burst read warning off MATLAB:divideByZero % prerequisite % adr2cdf.m convert raw Sontek binary adp files to raw cdf (engineering units). No processing done % called functions % netCDF toolbox % hydrapress2db.m handler for frequency Druck or Paros sensors % uv2polar.m calculate current speed and direction from east and north % xyz2enu.m rotates pcadp or adv data from XYZ to ENU coordinate system % ecorr.m clean up elevation from range to boundary (vrange) % add_vardesc.m % tools which can see this data % advbrowsegui.m for the burst data % ncbrowser.m for the stats data % TODO handle a situation where the ADVO has a separated compass and % probe... or an ADV Field. There is NO WAY to detect this from the data % file % TODO what if someone deploys one with internal and external pressure?? % TODO do the right thing if ADV temp is not 'measured' or no temp data available % this affects how pressure is processed % TODO test with an internal pressure sensor data set % TODO test with a druck pressure sensor data set % Written by Marinna Martini for the U.S. Geological Survey % Coastal & Marine Program Woods Hole Field Center, Woods Hole, MA % http://woodshole.er.usgs.gov/ Please report bugs to mmartini@usgs.gov % Aug 09 % -- change corr units to 'percent', xducer to transducer % 5 Feb 08 % -- fix the EPIC code for STD press % 5 Nov 07 % -- fix a bug which bombed when no pressure data was present. % -- cleanup some syntax stuff flagged by mlint % 4 Nov 07 % -- fix a bug which allowed a negative heading after declination % correction. % 14 Apr 06 % -- revise to handle serial paroscientific external pressure % -- fix AGC names % 13 Apr 06 % -- fix end burst function when working on subsets % 21 Nove 05 % -- change the input bursts to reference Sontek burst numbers, not file % indeces % 24 oct 05 version 3.2 % -- add amplitude output to nc files % 06 oct 05 version 3.1 % -- remove Qa output, this is now handled by cleanhydra % 20 sep 05 % -- remove and make private subfunctions that are the same for ADV and % -- remove despiking operations, now done in cleanhydra % PCADP % 16 Sep 2005 Version 3.0 % -- remove deglitch, it is now implemented in cleanhydra, which will % apply deglitch to any data (not just velocity, as was done here) % 7 Jul 2005 version 2.6 % fix bug where sed was not getting written when coef were supplied % 25 May 2005 % -- compute mean speed and direction in the vector sense (i.e. convert % mean U and V to polar coordinates rather than record a mean of per sample % speed and direction % -- variable Sed not written if no OBS calibration coefficients provided % 29 Mar 2005 % -- add code for calibrated OBS data % 25 Mar 2005 % -- replace variable attribute serial_number with serial to be consistent % -- fix a unit problem with brange & vrange, yes, again! % with .cdf file % 22 Mar 2005 Version 2.5 % -- Add Rachel's initial_sensor_height attributes % -- check / fix height and depth units, all as m % 21 Mar 2005 % -- delgitched data is now actually saved to the .nc file! % 3 Mar 2005 % -- fix a unit problem with brange & vrange % -- fix start and stop time computation % 1 Dec 2004 % -- store trans voltage to the trans variable, not attenuation % 15 Oct 2004 % -- after a data meeting finalaize variable names (again) % 12-oct-2004 % -- fix EPIC issues: temperature names, history storage % -- sorry! this involved manyv ariable name changes! % 26-sep-2004 % -- use vrange instead of brange to calculate height of measurement % 29-Aug-2004 % -- there are ADVs data sets without burst temperature! Fix this program so that the % logical thing is done... look for Mean Temperature, and assume that there % is always at least that available % 25-Aug-2004 % -- put the correct expression of depth and height in the right places % 20-Aug-2004 % -- add median heading pitch and roll to stats file output % -- keep ecorr for range to bed, in some cases thismeans ecorr would be % applied twice, but it does not seem to do any harm and may catch more % spikes % -- use gmax, gmin, gmean, gmedian and gstd throughout % 6-Jul-2004 % -- add despike for pressure, heading, tilt and roll % -- add ecorr for range to bed % -- output range to bed in stats file % 23-Jun-2004 % -- fix add_minmaxvalues to work on variables with more than two % dimensions % 14-Jun-2004 Version 0.0 % first version for Chris and Racheal to play with mversion = 'SVN $Revision$'; %ATOD = 5/(2^16); % counts to volts conversion for 16 bit storage of A/D Adv = []; % ----- resolve inputs if ~exist('cdfbFile', 'var'), [fname, pathname] = uigetfile('*b.cdf', 'Pick a netcdf file containing ADV burst data'); if isequal(fname,0) || isequal(pathname,0) disp('User pressed cancel') return end cdfbFile = fullfile(pathname, fname); disp(['User selected ', cdfbFile]) end if ~exist('cdfsFile','var'), [fname, pathname] = uigetfile('*s.cdf', 'Pick a netcdf file containing ADV stats data'); if isequal(fname,0) || isequal(pathname,0) disp('User pressed cancel') return end cdfsFile = fullfile(pathname, fname); disp(['User selected ', cdfsFile]) end if ~exist('ncFile','var') || isempty(ncFile), prompt = {'Enter an output file name to which b-cal.nc and s-cal.nc will be appended:'}; def = {'outputname'}; title = 'Output file name'; lineNo = 1; result = inputdlg(prompt,title,lineNo,def); ncFile = result{1}; end % make sure this is a burst ADV data file cdfb = netcdf(cdfbFile,'nowrite'); if ~strcmp(cdfb.DESCRIPT(:),'Sontek ADV raw data burst file'), disp('File is not a Sontek ADV raw data netCDF file containing all sample data (*b.cdf)') close (cdfb) return end % make sure this is a burst ADV data file cdfs = netcdf(cdfsFile,'nowrite'); if ~strcmp(cdfs.DESCRIPT(:),'Sontek ADV raw data statistics file'), disp('File is not a Sontek ADV raw data netCDF file containing stats data (*s.cdf)') close (cdfs) return end if ~exist('switches','var'), switches = []; end if ~isfield(switches,'ecorr_showplots'), switches.ecorr_showplots = 0; end % check the compass overrides if isfield(switches,'override_heading') && ~isempty(switches.override_heading), if length(switches.override_heading) == 1, switches.override_heading = ones(size(bursts(1):bursts(2))).*switches.override_heading; elseif length(switches.override_heading) ~= bursts(2)-bursts(1)+1, disp('Heading override vector length does not match number of bursts to process') disp('Heading override will be ignored') switches.override_heading = []; end elseif strcmp(cdfb.CompassInstalled(:), 'No'), disp('Compass was not installed and no heading provided. Heading set to 0') switches.override_heading = 0; end if isfield(switches,'override_pitch') && ~isempty(switches.override_pitch), if length(switches.override_pitch) == 1, switches.override_pitch = ones(size(bursts(1):bursts(2))).*switches.override_pitch; elseif length(switches.override_pitch) ~= bursts(2)-bursts(1)+1, disp('Pitch override vector length does not match number of bursts to process') disp('Pitch override will be ignored') switches.override_pitch = []; end elseif strcmp(cdfb.CompassInstalled(:), 'No'), disp('Compass was not installed and no pitch provided. Pitch set to 0') switches.override_pitch = 0; end if isfield(switches,'override_roll') && ~isempty(switches.override_roll), if length(switches.override_roll) == 1, switches.override_roll = ones(size(bursts(1):bursts(2))).*switches.override_roll; elseif length(switches.override_roll) ~= bursts(2)-bursts(1)+1, disp('Roll override vector length does not match number of bursts to process') disp('Roll override will be ignored') switches.override_roll = []; end elseif strcmp(cdfb.CompassInstalled(:), 'No'), disp('Compass was not installed and no roll provided. Roll set to 0') switches.override_roll = 0; end % check the pressure mask if isfield(switches,'override_pmask') && ~isempty(switches.override_pmask), if length(switches.override_pmask) ~= cdfb.ADVDeploymentSetupSamplesPerBurst(1); disp('Pressure mask vector length does not match number of samples in a burst') disp('All pressure samples will be processed') switches.override_pmask = ones(1,cdfb.ADVDeploymentSetupSamplesPerBurst(1)); end else disp('No pressure mask provided, all pressure samples will be processed') switches.override_pmask = ones(1,cdfb.ADVDeploymentSetupSamplesPerBurst(1)); end % ---------- start initializations before main loop % the user wants a limited number of bursts if ~exist('bursts','var') || isempty(bursts), bursts = [1 Inf]; end if find(bursts < 1), % no negative indeces disp('Cannot use negative indeces') close (cdfb) return end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% commented stuff below fixes the behavior of explicit ranges [3703:3750] %% but breaks the [1 inf] case, having it fail prematurely etm04/26/06 % if isinf(bursts(2)) || (bursts(2) > cdfb{'burst'}(end)), % bursts(2) = cdfb{'burst'}(end); % end % translate the burst numbers given by the user into burst indeces into the % netCDF file idx = find(bursts(1) <= cdfb{'burst'}(:),1,'first'); if isempty(idx), bursts(1) = 1; else bursts(1) = idx; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% commented stuff below fixes the behavior of explicit ranges [3703:3750] %% but breaks the [1 inf] case, having it fail prematurely etm04/26/06 %if isinf(bursts(2)) || (bursts(2) > cdfb{'burst'}(end)), % bursts(2) = cdfb{'burst'}(end); %else % idx = find(bursts(2) == cdfb{'burst'}(:),1,'first'); % if isempty(idx), bursts(2) = cdfb{'burst'}(end); % else bursts(2) = idx; % end %end if isinf(bursts(2)) bursts(2) = length(recdim(cdfb)); else idx = find(bursts(2) <= cdfb{'burst'}(:),1,'first'); if isempty(idx), bursts(2) = length(recdim(cdfb)); else bursts(2) = idx; end end disp(sprintf('Processing bursts %d to %d in file %s',... cdfb{'burst'}(bursts(1)),cdfb{'burst'}(bursts(2)),cdfbFile)) tic % define the .nc file ncs = definencsfile(cdfb, ncFile, cdfs); % define the file which will contain burst stats ncs{'lon'}(:) = cdfb.longitude(:); ncs{'lat'}(:) = cdfb.latitude(:); ncb = definencbfile(cdfb, ncFile, bursts); % define the file which will contain the bursts ncb{'lon'}(:) = cdfb.longitude(:); ncb{'lat'}(:) = cdfb.latitude(:); % read in metadata... though I don't know if we'll do anything with this, % just to be able to use tools from advlib metadata.cpusoftwarevernum = cdfb.cpuSoftWareVerNum(:); metadata.dspsoftwarevernum = cdfb.dspSoftWareVerNum(:); metadata.sensororientation = cdfb.SensorOrientation(:); metadata.compassinstalled = cdfb.CompassInstalled(:); metadata.recorderinstalled = cdfb.RecorderInstalled(:); metadata.tempinstalled = cdfb.TempInstalled(:); metadata.pressinstalled = cdfb.PressInstalled(:); metadata.pressscale = cdfb.PressScale(:); metadata.pressoffset = cdfb.PressOffset(:); metadata.compassoffset = cdfb.CompassOffset(:); metadata.pressfreqoffset = cdfb.PressFreqOffset(:); metadata.extsensorinstalled = cdfb.ExtSensorInstalled(:); metadata.extpressinstalled = cdfb.ExtPressInstalled(:); metadata.pressscale_2 = cdfb.PressScale_2(:); metadata.ctdinstalled = cdfb.CTDInstalled(:); metadata.serialnum = cdfb.ADVProbeSerial(:); metadata.probetype = cdfb.ADVProbeConfig(:); % as per JPX lookup table metadata.xfercoeff = cdfb.ADVProbeXformMat(:); metadata.xmtrecdist = cdfb.ADVProbeXmtRecDist(:); metadata.calcw = cdfb.ADVProbeCalCW(:); metadata.configtype = cdfb.ADVDeploymentSetupType(:); metadata.configver = cdfb.ADVDeploymentSetupVer(:); %metadata.nbytes = cdfb.253\ % skip this - no function in advlib does anything with it metadata.temp = cdfb.ADVDeploymentSetupTemp(:); metadata.sal = cdfb.ADVDeploymentSetupSal(:); metadata.cw = cdfb.ADVDeploymentSetupCW(:); metadata.tempmode = cdfb.ADVDeploymentSetupTempMode(:); metadata.velrangeind = cdfb.ADVDeploymentSetupVelRangeIndex(:); metadata.syncmode = cdfb.ADVDeploymentSetupSyncMode(:); metadata.coordsystem = cdfb.ADVDeploymentSetupCoordSystem(:); metadata.samprate = cdfb.ADVDeploymentSetupSampleRate(1); metadata.burstinterval = cdfb.ADVDeploymentSetupBurstInterval(1); metadata.sampleperburst = cdfb.ADVDeploymentSetupSamplesPerBurst(1); %metadata.rectype = cdfb. % skip this - no function in advlib does anything with it metadata.outmode = cdfb.ADVDeploymentSetupOutMode(:); metadata.outformat = cdfb.ADVDeploymentSetupOutFormat(:); metadata.recorderenabled = cdfb.ADVDeploymentSetupRecorderEnabled(:); metadata.recordermode = cdfb.ADVDeploymentSetupRecorderMode(:); metadata.deploymentmode = cdfb.ADVDeploymentSetupDeploymentMode(:); metadata.deploymentname = cdfb.ADVDeploymentSetupDeploymentName(:); metadata.begindeploymentdatetime = cdfb.ADVDeploymentSetupBeginDeployment(:); metadata.comment = cdfb.ADVDeploymentSetupADRComments(:); %metadata.autosleep = cdfb.1 % skip this - no function in advlib does anything with it % things I added metadata.water_depth = cdfb.WATER_DEPTH(:); metadata.magnetic_variation_at_site = cdfb.magnetic_variation(:); metadata.magnetic_variation_applied = cdfb.magnetic_variation(:); % build the static part of the pressure data structure needed for hydrapress2db if ~isempty(cdfb{'extpressfreq'}) || ~isempty(cdfb{'extpress'}) || ~isempty(cdfb{'pressure'}), P.ExtPressInstalled = metadata.extpressinstalled; % 'No' | 'Paros' | 'Druck' | 'ParosFreq' P.PressFreqOffset = metadata.pressfreqoffset; P.PressOffset = metadata.pressoffset; P.PressScale = metadata.pressscale; P.PressScale_2 = metadata.pressscale_2; P.INST_TYPE = cdfb.INST_TYPE(:); % frequency sensor case if strcmp(P.ExtPressInstalled,'Druck') || strcmp(P.ExtPressInstalled,'ParosFreq'), P.presscals = get_calvalues(cdfb{'extpressfreq'}); if isempty(P.presscals), disp('Expected to find frequency pressure calibrations and did not.') disp('Pressure will not be processed') end else P.presscals = []; end P.omit = find(~switches.override_pmask); if ~isempty(P.omit), P.nanmask = ones(1,length(P.omit)).*NaN; end else P = []; end if isempty(cdfb{'temperature'}) && isempty(cdfs{'MeanTemperature'}(:)); disp('Unable to find temperature data in the statistics or burst files'); disp('Pressure will not be converted to scientific units by this program.'); P = []; end % build the static part of the optical data if ~isempty(cdfb{'extsensor1'}) extmeta{1} = getmetafromatts(cdfb{'extsensor1'}); end if ~isempty(cdfb{'extsensor2'}) extmeta{2} = getmetafromatts(cdfb{'extsensor2'}); end % deal with the CTD now, load it at once to avoid extra read in for loop if ~isempty(cdfs{'CTD_temp'}), Adv.ctd(:,1) = cdfs{'CTD_temp'}(bursts(1):bursts(2)); Adv.ctd(:,2) = cdfs{'CTD_cond'}(bursts(1):bursts(2)); Adv.ctd(:,3) = cdfs{'CTD_press'}(bursts(1):bursts(2)); Adv.ctd(:,4) = cdfs{'CTD_sal'}(bursts(1):bursts(2)); end % fix the elevations, using all data present in the stats file no matter % how many bursts are being worked on in the burst file Adv.vrange = cdfs{'vrange'}(bursts(1):bursts(2))./1000; % in mm convert to m Adv.vrange = ecorr(Adv.vrange,switches.ecorr_showplots); Adv.brange = cdfs{'brange'}(bursts(1):bursts(2))./1000; % in mm convert to m Adv.brange = ecorr(Adv.brange,switches.ecorr_showplots); ncs.ADVCalcSampleVolumeOffset = gmedian(Adv.brange-Adv.vrange); ncb.ADVCalcSampleVolumeOffset = gmedian(Adv.brange-Adv.vrange); zburst = zeros([metadata.sampleperburst, 1]); % holder of a vector of zeros for speed % ------------------- main loop % load and process one burst at a time. % follow the structure originally defined in adr2mat with some extra stuff ncburstidx = 1; % write index for output files for cdfburstidx = bursts(1):bursts(2), % things added by Marinna Adv.burstnum = cdfb{'burst'}(:); % things from the burst file Adv.burstrange = [Adv.burstnum(1) Adv.burstnum(end)]; Adv.jt{1} = (cdfb{'time'}(cdfburstidx,:)+cdfb{'time2'}(cdfburstidx,:)./(24*3600*1000))'; if ~isempty(cdfb{'heading'}), Adv.heading{1} = (cdfb{'heading'}(cdfburstidx,:))'; % mean heading (degrees magnetic) Adv.pitch{1} = (cdfb{'pitch'}(cdfburstidx,:))'; % rotation about Y axis, deg Adv.roll{1} = (cdfb{'roll'}(cdfburstidx,:))'; % rotation about X axis, deg end if ~isempty(cdfb{'temperature'}), Adv.temperature{1} = (cdfb{'temperature'}(cdfburstidx,:))'; % ADV probe temp elseif ~isempty(cdfs{'MeanTemperature'}), Adv.temperature{1} = (cdfs{'MeanTemperature'}(cdfburstidx)); % ADV probe temp end if ~isempty(P), if ~isempty(cdfb{'pressure'}), Adv.pressure{1} = (cdfb{'pressure'}(cdfburstidx,:))'; end % ADV probe pressure if ~isempty(cdfb{'extpressfreq'}), Adv.extpressfreq{1} = (cdfb{'extpressfreq'}(cdfburstidx,:))'; end % pressure frequency, Hz if ~isempty(cdfb{'extpress'}), Adv.extpress{1} = (cdfb{'extpress'}(cdfburstidx,:))'; end % pressure count if ~isempty(cdfs{'StdPressure'}), Adv.stdpress = cdfs{'StdPressure'}(cdfburstidx); end % std of press end if ~isempty(cdfb{'extsensor1'}), Adv.extsensor{1}(:,1) = (cdfb{'extsensor1'}(cdfburstidx,:))'; else Adv.extsensor{1}(:,1) = zburst; end % analog input ch 1, a/d counts to volts if ~isempty(cdfb{'extsensor2'}), Adv.extsensor{1}(:,2) = (cdfb{'extsensor2'}(cdfburstidx,:))'; else Adv.extsensor{1}(:,2) = zburst; end % analog input ch 2, a/d counts if ~isempty(cdfb{'amp'}), if length(size(cdfb{'amp'})) == 3, Adv.amp{1} = (cdfb{'amp'}(cdfburstidx,:,:)); % amplitude for all beams else % fake three axes using the avg data Adv.amp{1} = (cdfb{'amp'}(cdfburstidx,:))'; % mean amplitude Adv.amp{1}(:,2) = Adv.amp(:,1); Adv.amp{1}(:,3) = Adv.amp(:,1); end end if ~isempty(cdfb{'cor'}), % assume always size 3 Adv.corr{1} = (cdfb{'cor'}(cdfburstidx,:,:)); end % correlation for all beams % this is the problem read in matlab 2009a with mexcdf v206 names = {'x','y','z'}; for i = 1:3, % beams 1 - 3 eval(sprintf('Adv.V%s{1} = (cdfb{''Vel%s''}(cdfburstidx,:))'';',names{i},names{i})); % [burst, sample] in cm/s end Adv.samprate = cdfb{'burst'}.samplerate(1); % in Hz Adv.sampleperburst = cdfb{'burst'}.sampleperburst(1); % in Hz Adv.soundspd = cdfs{'soundspd'}(cdfburstidx); names = {'Amp','Corr','Heading','Pitch','Roll','Temperature','Pressure','Velx','Vely','Velz'}; if ~isempty(cdfs{'MeanPressure'}), names{7} = 'Pressure'; else names{7} = []; end Adv.gmean(cdfburstidx,:) = zeros(1,length(names)); Adv.gstd(cdfburstidx,:) = zeros(1,length(names)); for i = 1:length(names), if ~isempty(cdfs{['Mean' names{i}]}), Adv.gmean(cdfburstidx,i) = cdfs{['Mean' names{i}]}(cdfburstidx); Adv.gstd(cdfburstidx,i) = cdfs{['Std' names{i}]}(cdfburstidx); end end % things omitted % status - no function in advlib does did anything with it % soundspeed - all data were zero, data was in soundspd % do pressure of there is pressure AND temperature if ~isempty(P), % build the rest of the pressure data structure needed if isfield(Adv,'temperature'), P.temp = Adv.temperature{1}; end % temperature, deg C if isfield(Adv,'pressure'), P.pressure = Adv.pressure{1}; end % pressure, counts, least significant bits if isfield(Adv,'stdpress'), P.stdpress = Adv.stdpress; end % std of pressure, for strain guage or smart paros or ADV paros if isfield(Adv,'extpress'), P.extpress = Adv.extpress{1}; end if isfield(Adv,'extpressfreq'), P.extpressfreq = Adv.extpressfreq{1}; % get rid of the spikes, if there are any if isfield(P,'nanmask'), P.extpressfreq(P.omit) = P.nanmask; end end % fields will come back empty if cal could not be done P = hydrapress2db(P); % this will sort out freq vs serial vs strain guage pressure if isempty(P); disp('mfilename: abort, failure at hydrapress2db call'); break; end Adv.pressdb{1}= P.pressdb; end % apply overrides if user wants it or if the compass is missing if isfield(switches,'override_heading') && ~isempty(switches.override_heading), if length(switches.override_heading) > 1, Adv.heading{1} = switches.override_heading(ncburstidx); else Adv.heading{1} = switches.override_heading; end end if(Adv.heading{1}>=360.), Adv.heading{1}=Adv.heading{1}-360.; end if isfield(switches,'override_pitch') && ~isempty(switches.override_pitch), if length(switches.override_pitch) > 1, Adv.pitch{1} = switches.override_pitch(ncburstidx); else Adv.pitch{1} = switches.override_pitch; end end if isfield(switches,'override_roll') && ~isempty(switches.override_roll), if length(switches.override_roll) > 1, Adv.pitch{1} = switches.override_roll(ncburstidx); else Adv.roll{1} = switches.override_roll; end end % correct for declination Adv.heading{1} = Adv.heading{1}+metadata.magnetic_variation_applied; idx = find(Adv.heading{1}>=360.); if ~isempty(idx), Adv.heading{1}(idx)=Adv.heading{1}(idx)-360.; end idx = find(Adv.heading{1}<0); if ~isempty(idx), Adv.heading{1}(idx)=Adv.heading{1}(idx)+360.; end % convert to earth coordinates % xyz2enu wants velocities in the shape [nbursts nsamples] If it's not this shapes % when passed, xyz2enu corrects it- so don't be shocked if adv.vu is a different % shape returned than it passes in! (etm- 2/12/10) % xyz2eun applies median heading, pitch and roll to convert to e, n, up % [Adv.Vu{1},Adv.Vv{1},Adv.Vw{1}]=xyz2enu(Adv.Vx{1},Adv.Vy{1},Adv.Vz{1},... % heading(ncburstidx),pitch(ncburstidx),roll(ncburstidx),metadata.sampleperburst); [Adv.Vu{1},Adv.Vv{1},Adv.Vw{1}]=xyz2enu(Adv.Vx{1},Adv.Vy{1},Adv.Vz{1},... Adv.heading{1},Adv.pitch{1},Adv.roll{1},metadata.sampleperburst); [Adv.Dir{1},Adv.Spd{1}] = uv2polar(Adv.Vu{1},Adv.Vv{1}); % make calculations for optic sensors, if attached if isfield(switches,'remove_minVolt') extmeta{1}.remove_minVolt=switches.remove_minVolt; end for i=1:2, varname = sprintf('extsensor%d',i); cdfobj = cdfb{varname}; if ~isempty(cdfobj) optic{i} = dohydraoptic(extmeta{i},Adv.extsensor{1}(:,i)); if isempty(optic{i}), optic{i} = zburst; end end end % write the burst data as shape [time, sample] ncb{'burst'}(ncburstidx) = cdfb{'burst'}(cdfburstidx); ncb{'time'}(ncburstidx,:) = cdfb{'time'}(cdfburstidx,:); ncb{'time2'}(ncburstidx,:) = cdfb{'time2'}(cdfburstidx,:); ncb{'u_1205'}(ncburstidx,:) = Adv.Vu{1}; ncb{'v_1206'}(ncburstidx,:) = Adv.Vv{1}; ncb{'w_1204'}(ncburstidx,:) = Adv.Vw{1}; if ~isempty(cdfb{'amp'}), for i=1:3, ncvarname = sprintf('AGC%1d_122%1d',i,i); ncb{ncvarname}(ncburstidx,:) = Adv.amp{1}(:,i); end end if ~isempty(cdfb{'cor'}), epnum=1284; for i=1:3, epnum=epnum+1; ncvarname = sprintf('cor%1d_%3d',i,epnum); ncb{ncvarname}(ncburstidx,:) = Adv.corr{1}(:,i); end end if isfield(P,'pressdb') && ~isempty(P.pressdb), ncb{'P_4022'}(ncburstidx,:) = Adv.pressdb{1}.*100; % convert db to mbar end for i=1:2, cdfobj = cdfb{sprintf('extsensor%d',i)}; %disp(size(optic{i})); if ~isempty(cdfobj) if ~isempty(findstr('TRANS',cdfobj.serial(:))), ncvarname = sprintf('tran%d_4010',i); % first save the trans voltage ncb{ncvarname}(ncburstidx,:) = Adv.extsensor{1}(:,i); ncs{ncvarname}(ncburstidx,:) = gmean(Adv.extsensor{1}(:,i)); ncvarname = sprintf('ATTN%d_55',i); % now get set to write attenuation elseif ~isempty(findstr('OBS',cdfobj.serial(:))), ncvarname = sprintf('NEP%d_56',i); ncb{ncvarname}(ncburstidx,:) = Adv.extsensor{1}(:,i); ncs{ncvarname}(ncburstidx,:) = gmean(Adv.extsensor{1}(:,i)); ncvarname = sprintf('Sed%d_981',i); % now get set to write concentration else ncvarname = sprintf('extsensor%d',i); end if ~isempty(ncb{ncvarname}), ncb{ncvarname}(ncburstidx,:) = optic{i}; ncs{ncvarname}(ncburstidx) = gmean(optic{i}); end end end if ~isempty(cdfb{'temperature'}), ncb{'Tx_1211'}(ncburstidx,:) = Adv.temperature{1}; end % write the stats data % find the middle of the burst and use it as The Time Stamp for stats % note that this technique does not lose precision, MATLAB has enough tjmid = Adv.jt{1}(1)+(Adv.jt{1}(end)-Adv.jt{1}(1))/2; ncs{'burst'}(ncburstidx) = cdfb{'burst'}(cdfburstidx); ncs{'time'}(ncburstidx) = floor(tjmid); ncs{'time2'}(ncburstidx) = (tjmid - floor(tjmid)).*(3600*24*1000); uMean = gmean(Adv.Vu{1}(:,1)); ncs{'u_1205'}(ncburstidx,1,1,1) = uMean; ncs{'USTD_4097'}(ncburstidx,1,1,1) = gstd(Adv.Vu{1}(:,1)); ncs{'u_1205min'}(ncburstidx,1,1,1) = gmin(Adv.Vu{1}(:,1)); ncs{'u_1205max'}(ncburstidx,1,1,1) = gmax(Adv.Vu{1}(:,1)); vMean = gmean(Adv.Vv{1}); ncs{'v_1206'}(ncburstidx,1,1,1) = vMean; ncs{'VSTD_4098'}(ncburstidx,1,1,1) = gstd(Adv.Vv{1}(:,1)); ncs{'v_1206min'}(ncburstidx,1,1,1) = gmin(Adv.Vv{1}(:,1)); ncs{'v_1206max'}(ncburstidx,1,1,1) = gmax(Adv.Vv{1}(:,1)); ncs{'w_1204'}(ncburstidx,1,1,1) = gmean(Adv.Vw{1}(:,1)); ncs{'WSTD_4099'}(ncburstidx,1,1,1) = gstd(Adv.Vw{1}(:,1)); ncs{'w_1204min'}(ncburstidx,1,1,1) = gmin(Adv.Vw{1}(:,1)); ncs{'w_1204max'}(ncburstidx,1,1,1) = gmax(Adv.Vw{1}(:,1)); [dMean,sMean] = uv2polar(uMean,vMean); ncs{'CS_300'}(ncburstidx,1,1,1) = sMean; ncs{'CD_310'}(ncburstidx,1,1,1) = dMean; if ~isempty(cdfb{'amp'}), for i=1:3, ncvarname = sprintf('AGC%1d_122%1d',i,i); ncs{ncvarname}(ncburstidx,1,1,1) = gmean(Adv.amp{1}(:,i)); end end if ~isempty(cdfb{'cor'}), epnum=1284; for i=1:3, epnum=epnum+1; ncvarname = sprintf('cor%1d_%3d',i,epnum); ncs{ncvarname}(ncburstidx,1,1,1) = gmean(Adv.corr{1}(:,i)); end end ncs{'Tx_1211'}(ncburstidx,1,1,1) = gmean(Adv.temperature{1}); if ~isempty(cdfb{'heading'}), ncs{'Hdg_1215'}(ncburstidx,1,1,1) = gmedian(Adv.heading{1}); ncs{'HSD_1218'}(ncburstidx,1,1,1) = gstd(Adv.heading{1}); end if ~isempty(cdfb{'pitch'}), ncs{'Ptch_1216'}(ncburstidx,1,1,1) = gmedian(Adv.pitch{1}); ncs{'PSD_1219'}(ncburstidx,1,1,1) = gstd(Adv.pitch{1}); end if ~isempty(cdfb{'roll'}), ncs{'Roll_1217'}(ncburstidx,1,1,1) = gmedian(Adv.roll{1}); ncs{'RSD_1220'}(ncburstidx,1,1,1) = gstd(Adv.roll{1}); end if isfield(P,'pressdb') && ~isempty(P.pressdb), ncs{'P_4023'}(ncburstidx,1,1,1) = gmean(Adv.pressdb{1}).*100; % convert db to mbar ncs{'SDP_850'}(ncburstidx,1,1,1) = gstd(Adv.pressdb{1}).*100; % should be in mbar end % ext sensor stats are written with the burst for speed in a for loop %disp(sprintf('Finished burst %d, %4.2f mintues elapsed',cdfb{'burst'}(cdfburstidx),toc./60)) if cdfburstidx<50 && ~rem(cdfburstidx,10), disp(sprintf('Finished burst #%d %4.2f min elapsed',cdfb{'burst'}(cdfburstidx),toc./60)), end if cdfburstidx>50 && ~rem(cdfburstidx,100), disp(sprintf('Finished burst #%d %4.2f min elapsed',cdfb{'burst'}(cdfburstidx),toc./60)), end ncburstidx = ncburstidx +1; end % end of for burstidx = 1:Adv.nbursts % ------------------- wrap up % put in the bin locations using the cleaned up time series of height off bottom % depth, m = depth at station - range to measurement volume ncs{'depth'}(:) = cdfs.WATER_DEPTH(:)-gmean(Adv.vrange); ncb{'depth'}(:) = cdfb.WATER_DEPTH(:)-gmean(Adv.vrange); ncs{'vrange'}(:,1,1,1) = Adv.vrange; ncs{'brange'}(:,1,1,1) = Adv.brange; % write the CTD all at once if ~isempty(cdfs{'CTD_temp'}), ncs{'CTDTMP_4211'}(:,1,1,1) = Adv.ctd(:,1); ncs{'CTDCON_4218'}(:,1,1,1) = Adv.ctd(:,2); if ~isempty(ncs{'CTDPRS_4203'}), ncs{'CTDPRS_4203'}(:,1,1,1) = Adv.ctd(:,3)-10.1325; end ncs{'CTDSAL_4214'}(:,1,1,1) = Adv.ctd(:,4); end % add to the history mv=mversion; mf=mfilename; ncb.history = sprintf('converted by %s; %s: %s', mf, mv, cdfb.history(:)); ncs.history = sprintf('converted by %s; %s: %s', mf, mv, cdfs.history(:)); % a couple more EPIC attributes tjul = ncs{'time'}(1)+ncs{'time2'}(1)./(3600*1000*24); ncs.start_time = ncchar(datestr(datenum(gregorian(tjul)))); tjul = ncs{'time'}(end)+ncs{'time2'}(end)./(3600*1000*24); ncs.stop_time = ncchar(datestr(datenum(gregorian(tjul)))); tjul = ncb{'time'}(1,1)+ncb{'time2'}(1,1)./(3600*1000*24); ncb.start_time = ncchar(datestr(datenum(gregorian(tjul)))); tjul = ncb{'time'}(end,end)+ncb{'time2'}(end,end)./(3600*1000*24); ncb.stop_time = ncchar(datestr(datenum(gregorian(tjul)))); % june-2010, do add_minmax2any as a separate step after creating the .nc file % so as not to risk crashing. add_minmax values takes up too much memory % and will crash on files larger than about 1GB (~1500 bursts) % nc=add_minmaxvalues(ncs); % ncb=add_minmaxvalues(ncb); % remove extraneous globals remove_ADVglobal_atts(ncs) remove_ADVglobal_atts(ncb) close(ncs) close(ncb) close(cdfb) close(cdfs) disp(sprintf('Finished writing data, %4.2f mintues elapsed',toc./60)) return % ------------------- sub-functions function nc = definencbfile(cdf,ncFile,bursts) % cdf must be the *burst* file disp(sprintf('Now writing the new netCDF burst file ... this may take a while')) % the new burst file [pathstr,filename,fileext,versn] = fileparts(name(cdf)); ncFile = fullfile(pathstr,[ncFile 'b-cal.nc' versn]); nc = netcdf(ncFile, 'clobber'); % copy all the global attributes for now. ncobjs = att(cdf); for n = 1:length(ncobjs), copy(ncobjs{n},nc); end nc.DESCRIPT = ncchar('Sontek ADV calibrated data burst file'); nc.CREATION_DATE = datestr(now); % new dimensions, going from profile number to time nc('time') = 0; % the record dimension, set fixed as we are in define mode nc('depth') = 1; nc('lon') = 1; nc('lat') = 1; nc('sample') = cdf.ADVDeploymentSetupSamplesPerBurst(1); % copy some of the variables, do the 1D ones first % basic indeces nc{'burst'} = ncfloat('time'); nc{'burst'}.units = ncchar('count'); nc{'burst'}.name = ncchar('burst'); nc{'burst'}.generic_name = ncchar('record'); nc{'burst'}.epic_code = nclong(1207); nc{'burst'}.long_name = ncchar('Burst Number'); nc{'time'} = nclong('time','sample'); nc{'time'}.FORTRAN_format = ncchar('F10.2'); nc{'time'}.units = ncchar('True Julian Day'); nc{'time'}.type = ncchar('EVEN'); nc{'time'}.name = ncchar('time'); nc{'time'}.generic_name = ncchar('time'); nc{'time'}.epic_code = nclong(624); nc{'time'}.comment = ncchar('UT Julian days that begin at midnight; 1968-05-23 = 2440000'); %old: nc{'time'}.comment= ncchar('UT Julian days using USGS convention where 1968-05-23 00:00:00 UT = 2440000'); nc{'time2'} = nclong('time','sample'); nc{'time2'}.FORTRAN_format = ncchar('F10.2'); nc{'time2'}.type = ncchar('EVEN'); nc{'time2'}.name = ncchar('time2'); nc{'time2'}.generic_name = ncchar('time'); nc{'time2'}.epic_code = nclong(624); nc{'time2'}.units = ncchar('msec since 0:00 GMT'); nc{'depth'} = ncfloat('depth'); nc{'depth'}.FORTRAN_format = ncchar('F10.2'); nc{'depth'}.units = ncchar('m'); nc{'depth'}.type = ncchar('EVEN'); nc{'depth'}.name = ncchar('depth'); nc{'depth'}.long_name = ncchar('DEPTH (m)'); nc{'depth'}.generic_name = ncchar('depth'); nc{'depth'}.epic_code = nclong(3); nc{'depth'}.sample_volume_offset = ncdouble(cdf.ADVSampleVolumeOffset(:)); % 18 cm? nc{'depth'}.transducer_offset_from_bottom = ncdouble(cdf.ADVProbeHeight(:)); nc{'lon'} = ncfloat('lon'); nc{'lon'}.FORTRAN_format = ncchar('f10.4'); nc{'lon'}.units = ncchar('degree_east'); nc{'lon'}.type = ncchar('EVEN'); nc{'lon'}.epic_code = nclong(502); nc{'lon'}.name = ncchar('LON'); nc{'lon'}.long_name = ncchar('LONGITUDE'); nc{'lon'}.generic_name = ncchar('lon'); nc{'lat'} = ncfloat('lat'); nc{'lat'}.FORTRAN_format = ncchar('F10.2'); nc{'lat'}.units = ncchar('degree_north'); nc{'lat'}.type = ncchar('EVEN'); nc{'lat'}.epic_code = nclong(500); nc{'lat'}.name = ncchar('LAT'); nc{'lat'}.long_name = ncchar('LATITUDE'); nc{'lat'}.generic_name = ncchar('lat'); % write the new 3D rotated velocities names = {'u','v','w'}; long_names = {'Eastward','Northward','Vertical'}; epcodes = [1205 1206 1204]; if strcmp(cdf.ADVProbeConfig(:),'Ocean Probe'), velrangesADVhoriz = [30 40 80 300 500]; velrangesADVvert = [8 10 20 75 120]; else % assume it's an ADVF 16 & 10 MHz velrangesADVhoriz = [30 60 120 300 360]; velrangesADVvert = [8 15 30 75 90]; end vidx = nc.ADVDeploymentSetupVelRangeIndex(:)+1; for i = 1:length(names), ncname = sprintf('%s_%d',names{i},epcodes(i)); nc{ncname} = ncfloat('time', 'sample'); ncobj = nc{ncname}; ncobj.name = ncchar(names{i}); ncobj.long_name = ncchar([long_names{i} ' Velocity']); ncobj.generic_name = ncchar(names{i}); ncobj.FORTRAN_format = ncchar(' '); ncobj.units = ncchar('cm/s'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdf.ADVProbeHeight(:)); ncobj.serial = cdf.ADVProbeSerial(:); if i < 3, ncobj.valid_range = ncfloat([(-1)*velrangesADVhoriz(vidx) velrangesADVhoriz(vidx)]); else ncobj.valid_range = ncfloat([(-1)*velrangesADVvert(vidx) velrangesADVvert(vidx)]); end end % include the signal amplitude of each axis % 1221:AGC1:Echo Intensity (AGC) Beam 1:AGC:counts: :ADCP Beam 1 AGC % 1222:AGC2:Echo Intensity (AGC) Beam 2:AGC:counts: :ADCP Beam 2 AGC % 1223:AGC3:Echo Intensity (AGC) Beam 3:AGC:counts: :ADCP Beam 3 AGC % Signal strength, recorded for each ADV receiver, is a measure of the intensity of the reflected % acoustic signal. This is recorded as raw signal amplitude in internal logarithmic units of counts; % one count is equal to 0.43 dB. With the ADV software, signal strength can be accessed either as % signal amplitude in counts or as signal-to-noise ratio (SNR) in dB. SNR is derived from signal % amplitude by subtracting the ambient noise level and converting to units of dB. epcodes = [1221 1222 1223]; for i = 1:length(epcodes), ncname = sprintf('AGC%1d_%d',i,epcodes(i)); nc{ncname} = ncfloat('time', 'sample'); ncobj = nc{ncname}; ncobj.name = ncchar(sprintf('AGC%1d',i)); ncobj.long_name = ncchar(sprintf('Echo Intensity (AGC) Beam %1d',i)); ncobj.generic_name = ncchar('AGC'); ncobj.FORTRAN_format = ncchar(' '); ncobj.units = ncchar('counts'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdf.ADVProbeHeight(:)); ncobj.serial = cdf.ADVProbeSerial(:); ncobj.valid_range = [0 1000]; % max range not known ncobj.NOTE = ncchar('dB = counts x 0.43'); end % include the signal correlation of each axis % 1285:cor1:Beam 1 correlation: :counts:i10:AV correlation of beam 1 % 1286:cor2:Beam 2 correlation: :counts:i10:ADV correlation of beam 2 % 1287:cor3:Beam 3 correlation: :counts:i10:ADV correlation of beam 3 epcodes = [1285 1286 1287]; for i = 1:length(epcodes), ncname = sprintf('cor%1d_%d',i,epcodes(i)); nc{ncname} = ncfloat('time', 'sample'); ncobj = nc{ncname}; ncobj.name = ncchar(sprintf('cor%1d',i)); ncobj.long_name = ncchar(sprintf(' Beam %1d Correlation',i)); ncobj.generic_name = ncchar('Cor'); ncobj.FORTRAN_format = ncchar(' '); ncobj.units = ncchar('percent'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdf.ADVProbeHeight(:)); ncobj.serial = cdf.ADVProbeSerial(:); ncobj.valid_range = [0 100]; % max range not known end % conceivably, the user might not have selected temperature as recorded % data, so mustmake sure it's really in there if ~isempty(cdf{'temperature'}), % this is the temperature off the ADV Probe % 1211:Tx :ADCP Transducer Temp. :temp:deg. C:F10.2:ADCP Transducer Temp nc{'Tx_1211'} = ncfloat('time','sample'); ncobj = nc{'Tx_1211'}; ncobj.name = ncchar('Tx'); ncobj.long_name = ncchar('Transducer Temp. '); ncobj.generic_name = ncchar('temp'); ncobj.epic_code = nclong(1211); ncobj.units = ncchar('Deg. C'); ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdf.ADVProbeHeight(:)); ncobj.serial = ncchar(cdf.ADVProbeSerial(:)); ncobj.valid_range = ncfloat([-5 40]); end % detect if pressure was recorded in the raw file at all % we will have problems if an internal and external pressure sensor was used % modelled loosely after this key %4022:P :INTERVAL PRESSURE :pres:mbar:f10.3:interval pressure measurement, Seadata tripod if ~isempty(cdf{'extpressfreq'}) || ~isempty(cdf{'extpress'}) || ~isempty(cdf{'pressure'}), nc{'P_4022'} = ncfloat('time', 'sample'); ncobj = nc{'P_4022'}; ncobj.name = ncchar('P'); ncobj.long_name = ncchar('PRESSURE '); ncobj.generic_name = ncchar('pres'); ncobj.FORTRAN_format = ncchar('f10.3'); ncobj.epic_code = nclong(4022); ncobj.units = ncchar('mbar'); if strcmp(cdf.ExtPressInstalled(:),'No'), ncobj.sensor_type = ncchar('Sontek ADV Internal'); else ncobj.sensor_type = ncchar(cdf.ExtPressInstalled(:)); end if strcmp(cdf.ExtPressInstalled(:),'Druck') || strcmp(cdf.ExtPressInstalled(:),'ParosFreq'), ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf{'extpressfreq'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdf{'extpressfreq'}.height(:)); ncobj.serial = cdf{'extpressfreq'}.serial(:); elseif strcmp(cdf.ExtPressInstalled(:),'Paros'), % serial pressure in real units ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf{'extpress'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdf{'extpress'}.height(:)); ncobj.serial = cdf{'extpress'}.serial(:); elseif strcmp(cdf.PressInstalled,'Yes'), % internal pressure ncobj.sensor_depth = ncdouble(cdf.WATER_DEPTH(:)-cdf.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdf.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); end ncobj.valid_range = ncfloat([0 100000]); end for i=1:2, cdfobj = cdf{sprintf('extsensor%d',i)}; if ~isempty(cdfobj), if findstr('TRANS',cdfobj.serial(:)), % a transmissometer ncvarname = sprintf('ATTN%d_55',i); % 55:ATTN:ATTENUATION :attn:m-1:f7.5:added for r2d2 ctd data nc{ncvarname} = ncfloat('time','sample'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('attn%d',i)); ncobj.long_name = ncchar(sprintf('ATTENUATION %d',i)); ncobj.generic_name = ncchar('attn'); ncobj.epic_code = nclong(55); ncobj.units = ncchar('m-1'); ncobj.sensor_type = ncchar('transmissometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 10000]); ncobj.varcomment.ATTN_55 = 'ATTN = -(1/focal_length) .* log(tran_4010./(.95*Vair))'; % 4010:tran:TRANSMISSION (VOLTS) :trans:volts:f10.3:basic measurement, transmissometer ncvarname = sprintf('tran%d_4010',i); nc{ncvarname} = ncfloat('time','sample'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('tran%d',i)); ncobj.long_name = ncchar(sprintf('TRANSMISSION (VOLTS) #%d',i)); ncobj.generic_name = ncchar('trans'); ncobj.epic_code = nclong(4010); ncobj.units = ncchar('volts'); ncobj.FORTRAN_format = ncchar('F10.3'); ncobj.sensor_type = ncchar('transmissometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5]); elseif findstr('OBS',cdfobj.serial(:)), % an OBS % 56:NEP:BACKSCATTER INTENSITY :nephelometer:v:f10.6: added for Dave Pashinski ncvarname = sprintf('NEP%d_56',i); nc{ncvarname} = ncfloat('time','sample'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('nep%d',i)); ncobj.long_name = ncchar(sprintf('BACKSCATTER INTENSITY %d',i)); ncobj.generic_name = ncchar('nephelometer'); ncobj.epic_code = nclong(56); ncobj.units = ncchar('v'); ncobj.sensor_type = ncchar('nephelometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5]); % now, if there's cal info to compute concentration, then set up a variable if (ncals > 0) && ~isempty(cdfobj.cal_coef(:)), % 981:Sed:Sediment concentration :sed:g/l:f10.2:sediment concentration ncvarname = sprintf('Sed%d_981',i); nc{ncvarname} = ncfloat('time','sample'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('sed%d',i)); ncobj.long_name = ncchar(sprintf('Sediment concentration %d',i)); ncobj.generic_name = ncchar('sed'); ncobj.epic_code = nclong(981); ncobj.units = ncchar('g/l'); ncobj.sensor_type = ncchar('nephelometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 10000]); else disp(sprintf('No coefficients for ext sensor %d, Sed variable not written',i)) end else ncvarname = sprintf('extsensor%d',i); nc{ncvarname} = ncfloat('time','sample'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('ext%d',i)); ncobj.long_name = ncchar(sprintf('external sensor %d',i)); ncobj.generic_name = ncchar(' '); ncobj.epic_code = nclong(0); ncobj.units = ncchar('v'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5]); end end end add_fillvalues(nc,1e35) add_vardesc(nc) return function nc = definencsfile(cdfb, ncFile, cdfs) % cdfb must be the *burst* file disp(sprintf('Defining the new netCDF statistics file ... %d mintues elapsed so far',toc./60)) % the new average file [pathstr,filename,fileext,versn] = fileparts(name(cdfb)); ncFile = fullfile(pathstr,[ncFile 's-cal.nc' versn]); nc = netcdf(ncFile, 'clobber'); % copy all the global attributes for now. Adjust this later by doing it by % name as the variables are done below ncobjs = att(cdfb); for n = 1:length(ncobjs), copy(ncobjs{n},nc); end nc.DESCRIPT = ncchar('Sontek ADV calibrated data statistics file'); nc.CREATION_DATE = datestr(now); nc('time') = 0; % the record dimension, set fixed as we are in define mode nc('depth') = 1; nc('lon') = 1; nc('lat') = 1; % define first, then load, as we must do statistics, etc. % basic indeces nc{'burst'} = ncfloat('time'); nc{'burst'}.units = ncchar('count'); nc{'burst'}.name = ncchar('burst'); nc{'burst'}.generic_name = ncchar('record'); nc{'burst'}.epic_code = nclong(1207); nc{'burst'}.long_name = ncchar('Burst Number'); nc{'time'} = nclong('time'); nc{'time'}.FORTRAN_format = ncchar('F10.2'); nc{'time'}.units = ncchar('True Julian Day'); nc{'time'}.type = ncchar('EVEN'); nc{'time'}.name = ncchar('time'); nc{'time'}.generic_name = ncchar('time'); nc{'time'}.epic_code = nclong(624); nc{'time'}.comment = ncchar('UT Julian days that begin at midnight; 1968-05-23 = 2440000'); %old: nc{'time'}.comment = ncchar('UT Julian days using USGS convention where 1968-05-23 00:00:00 UT = 2440000'); nc{'time'}.comment1 = ncchar('time, taken from burst data, fixed at center of burst'); nc{'time2'} = nclong('time'); nc{'time2'}.FORTRAN_format = ncchar('F10.2'); nc{'time2'}.generic_name = ncchar('time'); nc{'time2'}.name = ncchar('time2'); nc{'time2'}.epic_code = nclong(624); nc{'time2'}.units = ncchar('msec since 0:00 GMT'); nc{'time2'}.type = ncchar('EVEN'); nc{'depth'} = ncfloat('depth'); nc{'depth'}.FORTRAN_format = ncchar('F10.2'); nc{'depth'}.units = ncchar('m'); nc{'depth'}.name = ncchar('depth'); nc{'depth'}.type = ncchar('EVEN'); nc{'depth'}.generic_name = ncchar('depth'); nc{'depth'}.epic_code = nclong(3); nc{'depth'}.long_name = ncchar('DEPTH (m)'); nc{'depth'} = ncfloat('depth'); nc{'depth'}.sample_volume_offset = ncdouble(cdfb.ADVSampleVolumeOffset(:)); % 18 cm? nc{'depth'}.transducer_offset_from_bottom = ncdouble(cdfb.ADVProbeHeight(:)); nc{'lon'} = ncfloat('lon'); nc{'lon'}.FORTRAN_format = ncchar('f10.4'); nc{'lon'}.units = ncchar('degree_east'); nc{'lon'}.type = ncchar('EVEN'); nc{'lon'}.epic_code = nclong(502); nc{'lon'}.name = ncchar('LON'); nc{'lon'}.long_name = ncchar('LONGITUDE'); nc{'lon'}.generic_name = ncchar('lon'); nc{'lat'} = ncfloat('lat'); nc{'lat'}.FORTRAN_format = ncchar('F10.2'); nc{'lat'}.units = ncchar('degree_north'); nc{'lat'}.type = ncchar('EVEN'); nc{'lat'}.epic_code = nclong(500); nc{'lat'}.name = ncchar('LAT'); nc{'lat'}.long_name = ncchar('LATITUDE'); nc{'lat'}.generic_name = ncchar('lat'); % Velocities - mean for burst names = {'u','v','w'}; long_names = {'Eastward','Northward','Vertical'}; epcodes = [1205 1206 1204]; % write the new 2D rotated velocities for i = 1:length(names), ncname = sprintf('%s_%d',names{i},epcodes(i)); nc{ncname} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{ncname}; ncobj.epic_code = nclong(epcodes(i)); ncobj.name = ncchar(names{i}); ncobj.long_name = ncchar(['Mean ' long_names{i} ' Velocity']); ncobj.generic_name = ncchar(names{i}); ncobj.units = ncchar('cm/s'); ncobj.FORTRAN_format = ncchar(' '); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.sample_volume_offset = ncdouble(cdfb.ADVSampleVolumeOffset(:)); % 18 cm? ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-1000 1000]); end % Std. Dev. of Velocities % 4097:USTD:STD Eastward velocity :ustd:cm s-1:f8.2:standard deviation % of east component % 4098:VSTD:STD Northward velocity :vstd:cm s-1:f8.2:standard deviation % of north component % 4099:WSTD:STD Vertical velocity :wstd:cm s-1:f8.2:standard deviation % names = {'USTD','VSTD','WSTD'}; long_names = {'east','north','upward'}; epcodes = [4097 4098 4099]; % just used to name the variable namestub = {'ustd','vstd','wstd'}; % write the new 2D rotated velocities for i = 1:length(names), ncname = sprintf('%s_%d',names{i},epcodes(i)); nc{ncname} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{ncname}; ncobj.epic_code = nclong(epcodes(i)); ncobj.name = ncchar(names{i}); ncobj.long_name = ncchar(['Std. Dev. of ' long_names{i} ' component']); ncobj.generic_name = ncchar(namestub{i}); ncobj.units = ncchar('cm s-1'); ncobj.FORTRAN_format = ncchar('f8.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-1000 1000]); end % Min of Velocities names = {'u','v','w'}; long_names = {'Eastward','Northward','Vertical'}; epcodes = [1205 1206 1204]; namestub = 'min'; % write the new 2D rotated velocities for i = 1:length(names), ncname = sprintf('%s_%d%s',names{i},epcodes(i),namestub); nc{ncname} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{ncname}; % No epic code defined for this yet %ncobj.epic_code = nclong(epcodes(i)); ncobj.name = ncchar(sprintf('%s_%s',names{i},namestub)); ncobj.long_name = ncchar(['Minimum ' long_names{i} ' Velocity']); ncobj.generic_name = ncchar(sprintf('%s_%s',names{i},namestub)); ncobj.units = ncchar('cm/s'); ncobj.FORTRAN_format = ncchar(' '); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-1000 1000]); end % Max of Velocities names = {'u','v','w'}; long_names = {'Eastward','Northward','Vertical'}; epcodes = [1205 1206 1204]; % just used to name the variable namestub = 'max'; % write the new 2D rotated velocities for i = 1:length(names), ncname = sprintf('%s_%d%s',names{i},epcodes(i),namestub); nc{ncname} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{ncname}; % No epic code defined for this yet %ncobj.epic_code = nclong(epcodes(i)); ncobj.name = ncchar(sprintf('%s_%s',names{i},namestub)); ncobj.long_name = ncchar(['Maximum ' long_names{i} ' Velocity']); ncobj.generic_name = ncchar(sprintf('%s_%s',names{i},namestub)); ncobj.units = ncchar('cm/s'); ncobj.FORTRAN_format = ncchar(' '); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-1000 1000]); end %300:CS :CURRENT SPEED (CM/S) :vspd:cm s-1:f8.2:oceanographic (going to) nc{'CS_300'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'CS_300'}; ncobj.epic_code = nclong(300); ncobj.name = ncchar('CS'); ncobj.long_name = ncchar('CURRENT SPEED (CM/S) '); ncobj.generic_name = ncchar('vspd'); ncobj.units = ncchar('cm s-1'); ncobj.FORTRAN_format = ncchar('f8.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 1000]); %310:CD :CURRENT DIRECTION (T) :vdir:degrees:f8.2:oceanographic (going to), using true north nc{'CD_310'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'CD_310'}; ncobj.epic_code = nclong(310); ncobj.name = ncchar('CD'); ncobj.long_name = ncchar('CURRENT DIRECTION (T) '); ncobj.generic_name = ncchar('vdir'); ncobj.units = ncchar('degrees'); ncobj.FORTRAN_format = ncchar('f8.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); % rmh ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 360]); % include the signal amplitude of each axis % 1221:AGC1:Echo Intensity (AGC) Beam 1:AGC:counts: :ADCP Beam 1 AGC % 1222:AGC2:Echo Intensity (AGC) Beam 2:AGC:counts: :ADCP Beam 2 AGC % 1223:AGC3:Echo Intensity (AGC) Beam 3:AGC:counts: :ADCP Beam 3 AGC % Signal strength, recorded for each ADV receiver, is a measure of the intensity of the reflected % acoustic signal. This is recorded as raw signal amplitude in internal logarithmic units of counts; % one count is equal to 0.43 dB. With the ADV software, signal strength can be accessed either as % signal amplitude in counts or as signal-to-noise ratio (SNR) in dB. SNR is derived from signal % amplitude by subtracting the ambient noise level and converting to units of dB. epcodes = [1221 1222 1223]; for i = 1:length(epcodes), ncname = sprintf('AGC%1d_%d',i,epcodes(i)); nc{ncname} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{ncname}; ncobj.name = ncchar(sprintf('AGC%1d',i)); ncobj.long_name = ncchar(sprintf('Mean Echo Intensity (AGC) Beam %1d',i)); ncobj.generic_name = ncchar('AGC'); ncobj.FORTRAN_format = ncchar(' '); ncobj.units = ncchar('counts'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = cdfb.ADVProbeSerial(:); ncobj.valid_range = [0 32767.*0.43]; % max range not known ncobj.NOTE = ncchar('dB = counts x 0.43'); end % include the signal correlation of each axis % 1285:cor1:Beam 1 correlation: :counts:i10:ADCP correlation of beam 1 % 1286:cor2:Beam 2 correlation: :counts:i10:ADCP correlation of beam 2 % 1287:cor3:Beam 3 correlation: :counts:i10:ADCP correlation of beam 3 epcodes = [1285 1286 1287]; for i = 1:length(epcodes), ncname = sprintf('cor%1d_%d',i,epcodes(i)); nc{ncname} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{ncname}; ncobj.name = ncchar(sprintf('cor%1d',i)); ncobj.long_name = ncchar(sprintf(' Beam %1d Correlation',i)); ncobj.generic_name = ncchar('Cor'); ncobj.FORTRAN_format = ncchar(' '); ncobj.units = ncchar('percent'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = cdfb.ADVProbeSerial(:); ncobj.valid_range = [0 100]; % max range not known end % 4023:P :AVERAGE BURST PRESSURE :pres:mbar:f10.3:average of burst pressures % detect if pressure was recorded in the raw file at all % we willhave problems if an internal and external pressure sensor was used if ~isempty(cdfb{'extpressfreq'}) || ~isempty(cdfb{'extpress'}) || ~isempty(cdfb{'pressure'}), nc{'P_4023'} = ncfloat('time', 'lat', 'lon'); ncobj = nc{'P_4023'}; ncobj.epic_code = nclong(4023); ncobj.name = ncchar('P'); ncobj.long_name = ncchar('AVERAGE BURST PRESSURE '); ncobj.generic_name = ncchar('pres'); ncobj.units = ncchar('mbar'); ncobj.FORTRAN_format = ncchar('f10.3'); if strcmp(cdfb.ExtPressInstalled(:),'No'), ncobj.sensor_type = ncchar('Sontek ADV Internal'); else ncobj.sensor_type = ncchar(cdfb.ExtPressInstalled(:)); end if strcmp(cdfb.ExtPressInstalled(:),'Druck') || strcmp(cdfb.ExtPressInstalled(:), 'ParosFreq'), ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb{'extpressfreq'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfb{'extpressfreq'}.height(:)); ncobj.serial = cdfb{'extpressfreq'}.serial(:); elseif strcmp(cdfb.ExtPressInstalled(:),'Paros'), % serial pressure in real units ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb{'extpress'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfb{'extpress'}.height(:)); ncobj.serial = cdfb{'extpress'}.serial(:); elseif strcmp(cdfb.PressInstalled,'Yes'), % internal pressure ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); end ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 100000]); % 850:SDP:STAND. DEV. (PRESS) :pres:mbar:f10.5:std. deviation of burst pressures nc{'SDP_850'} = ncfloat('time','lat', 'lon'); ncobj = nc{'SDP_850'}; ncobj.epic_code = nclong(850); ncobj.name = ncchar('SDP'); ncobj.long_name = ncchar('STAND. DEV. (PRESS) '); ncobj.generic_name = ncchar('pres'); ncobj.units = ncchar('mbar'); if strcmp(cdfb.ExtPressInstalled(:),'No'), ncobj.sensor_type = ncchar('Sontek ADV Internal'); else ncobj.sensor_type = ncchar(cdfb.ExtPressInstalled(:)); end if strcmp(cdfb.ExtPressInstalled(:),'Druck') || strcmp(cdfb.ExtPressInstalled(:), 'ParosFreq'), ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb{'extpressfreq'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfb{'extpressfreq'}.height(:)); ncobj.serial = cdfb{'extpressfreq'}.serial(:); elseif strcmp(cdfb.PressInstalled, 'Yes'), % internal pressure ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); end ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5000]); % going to transfer calibration values, use the right sensor name... if ~isempty(cdfb{'extpressfreq'}), cdfobj = cdfb{'extpressfreq'}; elseif ~isempty(cdfb{'extpress'}), cdfobj = cdfb{'extpress'}; elseif ~isempty(cdfb{'pressure'}), cdfobj = cdfb{'pressure'}; else cdfobj = []; end if ~isempty(cdfobj), transfer_calvalues(cdfobj,ncobj); end end % for right now, assume there is always at least the MeanTemperature in the % stats file. If not, this will be empty. % 1211:Tx :ADCP Transducer Temp. :temp:deg. C:F10.2:ADCP Transducer Temp nc{'Tx_1211'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'Tx_1211'}; ncobj.name = ncchar('Tx'); ncobj.long_name = ncchar('Transducer Temp.'); ncobj.generic_name = ncchar('temp'); ncobj.units = ncchar('degrees C'); ncobj.epic_code = nclong(1211); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-5 40]); for i=1:2, cdfobj = cdfb{sprintf('extsensor%d',i)}; if ~isempty(cdfobj), if findstr('TRANS',cdfobj.serial(:)), % a transmissometer ncvarname = sprintf('ATTN%d_55',i); % 55:ATTN:ATTENUATION :attn:m-1:f7.5:added for r2d2 ctd data nc{ncvarname} = ncfloat('time','lat', 'lon'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('attn%d',i)); ncobj.long_name = ncchar(sprintf('ATTENUATION %d',i)); ncobj.units = ncchar('m-1'); ncobj.generic_name = ncchar('transmissometer'); ncobj.epic_code = nclong(55); ncobj.sensor_type = ncchar('transmissometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 10000]); ncobj.varcomment.ATTN_55 = 'ATTN = -(1/focal_length) .* log(tran_4010./(.95*Vair))'; % 4010:tran:TRANSMISSION (VOLTS) :trans:volts:f10.3:basic measurement, transmissometer ncvarname = sprintf('tran%d_4010',i); nc{ncvarname} = ncfloat('time','lat', 'lon'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('tran%d',i)); ncobj.long_name = ncchar(sprintf('TRANSMISSION (VOLTS) #%d',i)); ncobj.generic_name = ncchar('transmissometer'); ncobj.epic_code = nclong(4010); ncobj.units = ncchar('volts'); ncobj.FORTRAN_format = ncchar('F10.3'); ncobj.sensor_type = ncchar('transmissometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5]); elseif findstr('OBS',cdfobj.serial(:)), % an OBS ncvarname = sprintf('NEP%d_56',i); % 56:NEP:BACKSCATTER INTENSITY :nephelometer:v:f10.6: added for Dave Pashinski nc{ncvarname} = ncfloat('time','lat', 'lon'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('nep%d',i)); ncobj.long_name = ncchar(sprintf('BACKSCATTER INTENSITY %d',i)); ncobj.units = ncchar('v'); ncobj.generic_name = ncchar('nephelometer'); ncobj.epic_code = nclong(56); ncobj.sensor_type = ncchar('nephelometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5]); % now, if there's cal info to compute concentration, then set up a variable if (ncals > 0) && ~isempty(cdfobj.cal_coef(:)), % 981:Sed:Sediment concentration :sed:g/l:f10.2:sediment concentration ncvarname = sprintf('Sed%d_981',i); nc{ncvarname} = ncfloat('time','lat','lon'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('sed%d',i)); ncobj.long_name = ncchar(sprintf('Sediment concentration %d',i)); ncobj.generic_name = ncchar('sed'); ncobj.epic_code = nclong(981); ncobj.units = ncchar('g/l'); ncobj.sensor_type = ncchar('nephelometer'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 10000]); else disp(sprintf('No coefficients for ext sensor %d, Sed variable not written',i)) end else ncvarname = sprintf('extsensor%d',i); nc{ncvarname} = ncfloat('time','lat', 'lon'); ncobj = nc{ncvarname}; ncals = transfer_calvalues(cdfobj,ncobj); ncobj.name = ncchar(sprintf('ext%d',i)); ncobj.long_name = ncchar(sprintf('external sensor %d',i)); ncobj.generic_name = ncchar(' '); ncobj.epic_code = nclong(0); ncobj.units = ncchar('v'); ncobj.sensor_depth = ncdouble(nc.WATER_DEPTH(:)-cdfobj.height(:)); ncobj.initial_sensor_height = ncdouble(cdfobj.height(:)); ncobj.serial = ncchar(cdfobj.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 5]); end end end % % 0:vrange:Volume Range to Boundary:vrange:cm::distance from velocity measurement volume to boundary nc{'vrange'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'vrange'}; ncobj.name = ncchar('vrange'); ncobj.long_name = ncchar('Volume Range to Boundary'); ncobj.generic_name = ncchar('vrange'); ncobj.epic_code = nclong(0); ncobj.units = ncchar('m'); ncobj.FORTRAN_format = ncchar(' '); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 200]); ncobj.NOTE = ncchar('edited by ecorr'); % % 0:brange:Sensor Range to Boundary:brange:cm::distance from sensor to boundary nc{'brange'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'brange'}; ncobj.name = ncchar('brange'); ncobj.long_name = ncchar('Sensor Range to Boundary'); ncobj.generic_name = ncchar('brange'); ncobj.epic_code = nclong(0); ncobj.units = ncchar('m'); ncobj.FORTRAN_format = ncchar(' '); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 200]); ncobj.NOTE = ncchar('edited by ecorr'); if strcmp(cdfb.CompassInstalled(:), 'Yes'), % 1215:Hdg :INST Heading :hdg:degrees:F10.2:ADCP heading nc{'Hdg_1215'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'Hdg_1215'}; ncobj.name = ncchar('Hdg'); ncobj.long_name = ncchar('INST Heading '); ncobj.generic_name = ncchar('hdg'); ncobj.epic_code = nclong(1215); ncobj.units = ncchar('degrees'); ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 360]); ncobj.NOTE = ncchar('Median of burst heading data'); % 1216:Ptch:INST Pitch :ptch:degrees:F10.2:ADCP pitch nc{'Ptch_1216'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'Ptch_1216'}; ncobj.name = ncchar('Ptch'); ncobj.long_name = ncchar('INST Pitch '); ncobj.generic_name = ncchar('ptch'); ncobj.epic_code = nclong(1216); ncobj.units = ncchar('degrees'); ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-180 180]); ncobj.NOTE = ncchar('Median of burst pitch data'); % 1217:Roll:INST Roll :roll:degrees:F10.2:ADCP roll nc{'Roll_1217'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'Roll_1217'}; ncobj.name = ncchar('Roll'); ncobj.long_name = ncchar('INST Roll '); ncobj.generic_name = ncchar('roll'); ncobj.epic_code = nclong(1217); ncobj.units = ncchar('degrees'); ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-180 180]); ncobj.NOTE = ncchar('Median of burst roll data'); % 1218:HSD :Heading Std. Dev. :hdg:degrees: :ADCP Hdg. Std. Dev. nc{'HSD_1218'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'HSD_1218'}; ncobj.name = ncchar('HSD'); ncobj.long_name = ncchar('Heading Std. Dev. '); ncobj.generic_name = ncchar('hdg'); ncobj.epic_code = nclong(1218); ncobj.units = ncchar('degrees'); %ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 360]); ncobj.NOTE = ncchar('Calculated from burst heading data'); % 1219:PSD :Pitch Std. Dev. :ptch:degrees: :ADCP Pitch Std. Dev. nc{'PSD_1219'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'PSD_1219'}; ncobj.name = ncchar('PSD'); ncobj.long_name = ncchar('Pitch Std. Dev. '); ncobj.generic_name = ncchar('ptch'); ncobj.epic_code = nclong(1219); ncobj.units = ncchar('degrees'); %ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-180 180]); ncobj.NOTE = ncchar('Calculated from burst pitch data'); % 1220:RSD :Roll Std. Dev. :roll:degrees: :ADCP Roll Std. Dev. nc{'RSD_1220'} = ncfloat('time','depth', 'lat', 'lon'); ncobj = nc{'RSD_1220'}; ncobj.name = ncchar('RSD'); ncobj.long_name = ncchar('Roll Std. Dev. '); ncobj.generic_name = ncchar('roll'); ncobj.epic_code = nclong(1220); ncobj.units = ncchar('degrees'); %ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.sensor_type = ncchar('Sontek ADV'); ncobj.sensor_depth = ncdouble(cdfb.WATER_DEPTH(:)-cdfb.ADVProbeHeight(:)); ncobj.initial_sensor_height = ncdouble(cdfb.ADVProbeHeight(:)); ncobj.serial = ncchar(cdfb.ADVProbeSerial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-180 180]); ncobj.NOTE = ncchar('Calculated from burst roll data'); end % data from the external CTD if ~isempty(cdfs{'CTD_temp'}), %4211:CTDTMP :Temperature, ITS-90 :temp:degC:f9.4: nc{'CTDTMP_4211'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'CTDTMP_4211'}; ncobj.name = ncchar('CTDTMP'); ncobj.long_name = ncchar('TEMPERATURE (C)'); ncobj.generic_name = ncchar('temp'); ncobj.FORTRAN_format = ncchar('f10.2'); ncobj.units = ncchar('C'); ncobj.epic_code = nclong(4211); ncobj.sensor_type = ncchar('Seabird SBE37'); ncobj.sensor_depth = ncdouble(cdfs.WATER_DEPTH(:)-cdfs{'CTD_temp'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfs{'CTD_temp'}.height(:)); ncobj.serial = ncchar(cdfs{'CTD_temp'}.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([-5 40]); ncobj.comment = ncchar('ITS-1990 standard'); %4218:CTDCON:CTD Conductivity :con:S/m:f10.3: nc{'CTDCON_4218'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'CTDCON_4218'}; ncobj.name = ncchar('CTDCON'); ncobj.long_name = ncchar('CTD Conductivity '); ncobj.generic_name = ncchar('con'); ncobj.FORTRAN_format = ncchar('f10.3'); ncobj.units = ncchar('S/m'); ncobj.epic_code = nclong(4218); ncobj.sensor_type = ncchar('Seabird SBE37'); ncobj.sensor_depth = ncdouble(cdfs.WATER_DEPTH(:)-cdfs{'CTD_cond'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfs{'CTD_cond'}.height(:)); ncobj.serial = ncchar(cdfs{'CTD_cond'}.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0.0 10.0]); % check to see if there's any pressure in the pressure if any(cdfs{'CTD_press'}(:)), %4203:CTDPRS:Pressure :depth:dbar:f9.1: nc{'CTDPRS_4203'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'CTDPRS_4203'}; ncobj.epic_code = nclong(4203); ncobj.name = ncchar('CTDPRS'); ncobj.long_name = ncchar('Pressure '); ncobj.generic_name = ncchar('depth'); ncobj.units = ncchar('dbar'); ncobj.FORTRAN_format = ncchar('f9.1'); ncobj.sensor_type = ncchar('Seabird SBE37'); ncobj.sensor_depth = ncdouble(cdfs.WATER_DEPTH(:)-cdfs{'CTD_press'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfs{'CTD_press'}.height(:)); ncobj.serial = ncchar(cdfs{'CTD_press'}.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0 3000]); ncobj.transducer_offset_from_bottom = ncdouble(cdfs{'CTD_press'}.height(:)); end % 4214:CTDSAL :CTD Salinity, PSS-78 :sal:PSU:f9.4: nc{'CTDSAL_4214'} = ncfloat('time', 'depth', 'lat', 'lon'); ncobj = nc{'CTDSAL_4214'}; ncobj.name = ncchar('CTDSAL'); ncobj.long_name = ncchar('CTD Salinity, PSS-78'); ncobj.generic_name = ncchar('sal'); ncobj.FORTRAN_format = ncchar('f9.4'); ncobj.units = ncchar('PSU'); ncobj.epic_code = nclong(4214); ncobj.sensor_type = ncchar('Seabird SBE37'); ncobj.sensor_depth = ncdouble(cdfs.WATER_DEPTH(:)-cdfs{'CTD_sal'}.height(:)); ncobj.initial_sensor_height = ncdouble(cdfs{'CTD_sal'}.height(:)); ncobj.serial = ncchar(cdfs{'CTD_sal'}.serial(:)); ncobj.minimum = ncfloat(0); ncobj.maximum = ncfloat(0); ncobj.valid_range = ncfloat([0.0 40.0]); ncobj.comment = ncchar('Practical Salinity Units'); end add_fillvalues(nc,1e35); add_vardesc(nc); return function remove_ADVglobal_atts(cdf) % remove global attibutes that are not really needed in the BBV delete(cdf.ADVProbeNomPeakPos); delete(cdf.ADVProbeNsamp); delete(cdf.ADVProbePulseLag); delete(cdf.ADVProbeNxmt); delete(cdf.ADVProbeLagDelay); delete(cdf.ADVProbeBeamDelay); delete(cdf.ADVProbePingDelay); delete(cdf.ADVProbeXformMat); delete(cdf.ADVProbeCalCW); delete(cdf.ADVDeploymentSetupTemp); delete(cdf.ADVDeploymentSetupSal); delete(cdf.ADVDeploymentSetupCW); delete(cdf.ADVDeploymentSetupTempMode); delete(cdf.ADVDeploymentSetupVelRangeIndex); delete(cdf.ADVDeploymentSetupCoordSystem); delete(cdf.ADVDeploymentSetupOutMode); delete(cdf.ADVDeploymentSetupOutFormat); delete(cdf.ADVDeploymentSetupRecorderEnabled); delete(cdf.ADVDeploymentSetupRecorderMode); delete(cdf.ADVDeploymentSetupDeploymentMode); return