NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/gliderproc/trunk/gliderCTD_Generate_L1_Data.m

Revision 498 (checked in by cbc, 11 years ago)

Initial Calloway revisions to Seim revisions.

Line 
1 %
2 %  gliderCTD_Generate_L1_Data_v1.m
3 %
4 %  Purpose: Apply thermal lag correction to glider CTD salinity and density
5 %           and generate Level 1 data mat files.
6 %
7 %  NOTE: At top of file, set glider index, deployment number and desired correction parameters
8 %
9 %  Requires:
10 %  correctThermalLag.m - Code from authors that implements Garau et al. 2011, Thermal
11 %  lag correction on Slocum CTD glider data, JAOT, 28,
12 %  1065-1071,doi:10.1175/JTECH-D-10-05030.1
13 %
14 %  MATLAB folder - contains util, seawater,
15 %
16 %
17 %  Author:  William Stark
18 %           Marine Sciences Department
19 %           UNC-Chapel Hill
20 %
21 %  Created: 21 May 2012
22 %   reviewed, modified by HES December 2012, March 2013, called v1
23 %
24 %//////////////////////////////////////////////////////////////////////////
25 % TO DO: (done, 3/12/2013)
26 % compare sci_m_present_time and sci_ctdxx_timestamp - looks like ptime-ctd_time is
27 % 0.6 +-0.6 second, never goes negative but can be very big positive
28 % number.  Appears to happen at surfacing, and that the CTD throws several
29 % measurements with the same time stamp, maybe a flushed buffer?  Have
30 % badflagged the values, identified as times when the two timestamps are very
31 % different.
32 %
33 % check final density calculation - done correctly
34 %
35 % resolve best determination of lat and lon - HES: looked at this some;
36 % lat/lon are most common measure but appear to reflect dead reckoning.
37 % waypoints are just that, intended targets, so chose gps in the end for
38 % determining position of each CTD point.  Commented out code to process
39 % other position measures for now.
40 %
41 % removed chlorophyll from processing, do separately
42
43
44 clear all;
45
46
47 %!!!!!!  SET PRIOR TO RUNNING CODE  !!!!!!!!!!!!!!!!!!!!!!!!
48 %!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
49 %
50 % SET THE GLIDER INDEX (Pelagia = 1, Ramses = 2) ...
51 gliderIndex = 2;
52
53 % SET THE DEPLOYMENT NUMBER (1, 2 or 3) ...
54 deploymentNumber = 3;
55
56 % SET CORRECTION PARAMETERS STRUCTURE...
57 correctionParams = [0.1587 0.0214 6.5316 1.5969];
58
59 %
60 %!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
61 %!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
62
63
64
65 % add paths for required files...
66 addpath('MATLAB/util/');
67 %addpath('MATLAB/matutil/');
68 addpath('MATLAB/seawater/');
69 %addpath('MATLAB/plots/');
70 %addpath('MATLAB/strfun/');
71 %addpath('MATLAB/opnml/');
72 %addpath('MATLAB/opnml/FEM/');
73
74
75
76 % glider name string...
77 if (gliderIndex==1)
78     strGliderName = 'Pelagia';
79 else
80     strGliderName = 'Ramses';
81 end
82
83 % populate arrays for the deployment start and end dates...
84 % ex. strStart(2, 3) is start date for Ramses, Deployment 3
85 strStart = {'26-Jan-2012', '16-Feb-2012', '16-Mar-2012'; '26-Jan-2012', '16-Feb-2012', '16-Mar-2012'};
86 strEnd   = {'14-Feb-2012', '08-Mar-2012', '04-Apr-2012'; '14-Feb-2012', '12-Mar-2012', '03-Apr-2012'};
87
88 % deployment number string...
89 strDeploymentNumber = num2str(deploymentNumber);
90
91 % deployment start date string...
92 strStartDate = strStart(gliderIndex, deploymentNumber);
93
94 % deployment end date string...
95 strEndDate = strEnd(gliderIndex, deploymentNumber);
96
97 % define the path to the glider ascii files...
98 %datadir = strcat('/Users/haloboy/Documents/MASC/MATLAB/CTD_data_correction/GLIDER_CTD_DATA_LEVEL0/',...
99 datadir = strcat('GLIDER_DATA_LEVEL0/', strGliderName, '_Deployment', strDeploymentNumber, '/');
100
101 % define default bounds for use in plots...
102 switch gliderIndex
103     case 1  % Pelagia
104         switch deploymentNumber
105             case 1  % Deployment 1
106                 tempBounds =  [17.0 24.0];
107                 salinBounds = [36.0 36.4];
108                 densBounds =  [1025.0 1026.6];
109                 chlorBounds = [0.0 4.0];
110                
111             case 2  % Deployment 2
112                 tempBounds =  [17.0 24.0];
113                 salinBounds = [36.0 36.5];
114                 densBounds =  [1024.5 1026.8];
115                 chlorBounds = [0.0 4.0];
116                
117             case 3  % Deployment 3
118                 tempBounds =  [17.0 24.0];
119                 salinBounds = [35.9 36.7];
120                 densBounds =  [1024.4 1026.4];
121                 chlorBounds = [0.0 4.0];
122         end
123     case 2  % Ramses
124         switch deploymentNumber
125             case 1  % Deployment 1
126                 tempBounds =  [8.0 23.0];
127                 salinBounds = [35.0 36.4];
128                 densBounds =  [1024.5 1027.5];
129                 chlorBounds = [0.0 4.0];
130            
131             case 2  % Deployment 2
132                 tempBounds =  [9.0 25.0];
133                 salinBounds = [35.2 36.6];
134                 densBounds =  [1024.0 1027.5];
135                 chlorBounds = [0.0 4.0];
136            
137             case 3  % Deployment 3
138                 tempBounds =  [10.0 24.5];
139                 salinBounds = [35.3 36.7];
140                 densBounds =  [1024.4 1027.4];
141                 chlorBounds = [0.0 4.0];
142         end
143 end
144
145
146 %##########################################################################################
147
148
149
150
151
152 %*** READ IN EBD DATA *****************************************************
153 % declare variables for storing data...
154 temp=[];
155 cond=[];
156 pres=[];
157 ctd_time=[];
158 %chlor=[];
159 ptime=[];
160 % mtime=[]; scioxy=[]; scibb=[]; scicdom=[]; scichlor=[]; scibbam=[];
161
162 % try to load all *.ebdasc files at once...
163 [files, Dstruct] = wilddir(datadir, '.ebdasc');
164 nfile = size(files, 1);
165
166 for i=1:nfile-1
167     % protect against empty ebd file
168     if(Dstruct(i).bytes>0)
169         data = read_gliderasc2([datadir, files(i,:)]);
170         %data = read_gliderasc3([datadir, files(i,:)]);
171
172         % if the number of values (in data.data) is less than the number of
173         % vars (in data.vars), this means that the data were not completely read
174         % in.  To correct this, pad data.data with NaNs until its length
175         % equals that of data.vars...
176         if (length(data.data) < length(data.vars))
177             data.data = padarray(data.data, [0 length(data.vars)-length(data.data)], NaN, 'post');
178         end
179
180         % populate variables with data...
181         if(~isempty(data.data))
182              temp = [temp;data.data(:,strmatch('sci_water_temp', data.vars, 'exact'))];             % temperature
183              cond = [cond;data.data(:,strmatch('sci_water_cond', data.vars, 'exact'))];             % conductivity
184              pres = [pres;data.data(:,strmatch('sci_water_pressure', data.vars, 'exact'))];         % pressure (measure of depth) science bay
185              ctd_time = [ctd_time;data.data(:,strmatch('sci_ctd41cp_timestamp', data.vars, 'exact'))];         % ctd timestamp
186         %    switch gliderIndex
187          %       case 1  % this is Pelagia...
188          %           chlor = [chlor;data.data(:,strmatch('sci_bbfl2s_chlor_scaled', data.vars, 'exact'))];  % chlorophyll
189          %       case 2  % this is Ramses...
190          %           chlor = [chlor;data.data(:,strmatch('sci_flbbcd_chlor_units', data.vars, 'exact'))];  % chlorophyll
191          %   end
192             ptime = [ptime;data.data(:,strmatch('sci_m_present_time', data.vars, 'exact'))];       % present time
193         end
194
195         data = [];
196     end 
197 end
198 %**************************************************************************
199
200 %*** READ IN DBD DATA *****************************************************
201 % declare variables for storing data...
202 ptime_dbd=[];
203 horizontalVelocity=[];
204 depth = [];
205 pitch=[];
206 avgDepthRate = [];
207 angleOfAttack = [];
208 %lat = [];
209 %lon = [];
210 gpsLat = [];
211 gpsLon = [];
212 %wptLat = [];
213 %wptLon = [];
214
215 % try to load all *.dbdasc files at once...
216 [files, Dstruct] = wilddir(datadir, '.dbdasc');
217 nfile = size(files, 1);
218
219 clear data;
220
221 for i=1:nfile
222     % protect against empty dbd file
223     if(Dstruct(i).bytes>0)
224         data = read_gliderasc2([datadir, files(i,:)]);
225         %data = read_gliderasc3([datadir, files(i,:)]);
226
227         % if the number of values (in data.data) is less than the number of
228         % vars (in data.vars), this means that the data were not completely read
229         % in.  To correct this, pad data.data with NaNs until its length
230         % equals that of data.vars...
231         if (length(data.data) < length(data.vars))
232             data.data = padarray(data.data, [0 length(data.vars)-length(data.data)], NaN, 'post');
233         end
234
235         % populate variables with data...
236         if(~isempty(data.data))
237             ptime_dbd = [ptime_dbd; data.data(:,strmatch('m_present_time', data.vars, 'exact'))];               % present time
238             horizontalVelocity = [horizontalVelocity; data.data(:,strmatch('m_speed', data.vars, 'exact'))];    % horizontal glider velocity     
239             depth = [depth; data.data(:,strmatch('m_depth', data.vars, 'exact'))];                              % depth     
240             pitch = [pitch; data.data(:,strmatch('m_pitch', data.vars, 'exact'))];                              % pitch (radians)
241             avgDepthRate = [avgDepthRate; data.data(:,strmatch('m_avg_depth_rate', data.vars, 'exact'))];       % avg depth rate
242             angleOfAttack = [angleOfAttack; data.data(:,strmatch('u_angle_of_attack', data.vars, 'exact'))];    % angle of attack (radians)
243           %  wptLat = [wptLat; data.data(:,strmatch('c_wpt_lat', data.vars, 'exact'))];                          % Waypoint latitude
244           %  wptLon = [wptLon; data.data(:,strmatch('c_wpt_lon', data.vars, 'exact'))];                          % Waypoint longitude
245             gpsLat = [gpsLat; data.data(:,strmatch('m_gps_lat', data.vars, 'exact'))];                          % GPS latitude
246             gpsLon = [gpsLon; data.data(:,strmatch('m_gps_lon', data.vars, 'exact'))];                          % GPS longitude
247           %  lat = [lat; data.data(:,strmatch('m_lat', data.vars, 'exact'))];                                    % latitude
248           %  lon = [lon; data.data(:,strmatch('m_lon', data.vars, 'exact'))];                                    % longitude
249         end
250
251         data = [];
252     end
253 end
254 %**************************************************************************
255
256
257 % first, apply the sort() function to make sure that values in the time vectors
258 % (ptime and ptime_dbd) increase monotonically...
259 [Y,I] = sort(ptime);
260 ptime = Y;
261 temp = temp(I);
262 cond = cond(I);
263 pres = pres(I);
264 ctd_time=ctd_time(I);
265 %chlor = chlor(I);
266
267 [Y,I] = sort(ptime_dbd);
268 ptime_dbd = Y;
269 horizontalVelocity = horizontalVelocity(I);
270 depth = depth(I);
271 pitch = pitch(I);
272 avgDepthRate = avgDepthRate(I);
273 angleOfAttack = angleOfAttack(I);
274 %wptLat = wptLat(I);
275 %wptLon = wptLon(I);
276 gpsLat = gpsLat(I);
277 gpsLon = gpsLon(I);
278 %lat = lat(I);
279 %lon = lon(I);
280
281
282 % remove ctd measurements when ptime and ctd_time are a lot different
283 iweird=find(ptime-ctd_time > 10);
284 ptime(iweird)=NaN; temp(iweird)=NaN; pres(iweird)=NaN; cond(iweird)=NaN;
285 ctd_time(iweird)=NaN;
286
287 % remove nans from EBD data...
288 % HES - need full triplet from CTD
289 i = find(~isnan(temp) & ~isnan(pres) & ~isnan(cond));
290 ptime = ptime(i); temp = temp(i); cond = cond(i); pres = pres(i);
291 ctd_time = ctd_time(i);
292
293 % remove conductivity values less than 1, must be at surface or bad
294 i = find(cond>=1);
295 ptime = ptime(i);  temp = temp(i);  pres = pres(i);  cond = cond(i);
296 ctd_time = ctd_time(i);
297
298 % remove pressure values less than 0
299 i = find(pres>=0);
300 ptime = ptime(i);  temp = temp(i);  pres = pres(i);  cond = cond(i);
301 ctd_time = ctd_time(i);
302
303 % convert pitch and angle of attack from radians to degrees...
304 pitch = pitch*180/pi;
305 angleOfAttack = angleOfAttack*180/pi;
306
307 % compute actual glide angle = pitch + angle of attack...
308 glideAngle = pitch + angleOfAttack;
309
310 % make copy of dbd time stamp vector for use in salinity/density correction...
311 ptime1_dbd = ptime_dbd;
312
313 % remove nans from DBD data...HES - re-wrote this to interpolate each
314 % variable to ebd timebase using all existing values.  Includes threshold
315 % on horizontal velocity of greater than zero (really important, fair number
316 % of values <0, not sure where these happen but think at surface) and >0.6
317 % m/s (less certain of this, could be looked at further)
318 i = find(~isnan(horizontalVelocity)&(horizontalVelocity>0 & horizontalVelocity<0.6));
319 horizontalVelocity = interp1(ptime1_dbd(i), horizontalVelocity(i), ctd_time);
320
321 i = find(~isnan(depth));
322 depth = interp1(ptime1_dbd(i), depth(i), ctd_time);
323
324 i = find(~isnan(pitch));
325 pitch = interp1(ptime1_dbd(i), pitch(i), ctd_time);
326
327 i = find(~isnan(avgDepthRate));
328 avgDepthRate = interp1(ptime1_dbd(i), avgDepthRate(i), ctd_time);
329
330 i = find(~isnan(glideAngle));
331 glideAngle = interp1(ptime1_dbd(i), glideAngle(i), ctd_time);
332
333 % make sure there are no NaNs in the final set of data...HES: important for
334 % horizontalVelocity - a start-up problem - just zaps opening points??
335 i = find(~isnan(horizontalVelocity));
336 horizontalVelocity = horizontalVelocity(i); depth = depth(i); pitch = pitch(i);
337 avgDepthRate = avgDepthRate(i); glideAngle = glideAngle(i);
338 ptime = ptime(i); temp = temp(i); cond = cond(i); pres = pres(i);
339 ctd_time = ctd_time(i);
340
341 % scale up the pressure...
342 pres = pres*10;
343
344 % calculate salinity (without correction)...
345 salin = sw_salt(10*cond/sw_c3515, temp, pres);
346
347 % calculate density (without correction)...
348 dens = sw_pden(salin, temp, pres, 0);
349
350 % calculate glider velocity using horizontal velocity (m_speed) and average depth rate (m_avg_depth_rate)...
351 gliderVelocity = sqrt(horizontalVelocity.^2 + avgDepthRate.^2);
352
353 % pass the correction parameters into the correctThermalLags function, which
354 % returns the corrected profile structure (with corrected temp and cond added)...
355 %
356 % profileStructure:  ptime: Present time instant at which this row was collected
357 %                    depth: Depth (pressure in decibars) measured by the CTD
358 %                    temp: Temperature measured by the CTD
359 %                    cond: Conductivity measured by the CTD
360 %                    pitch: Pitch angle of the glider (optional)
361 %
362 %
363
364 % CORRECTION SCHEME:  Pass in a glider velocity vector, so that correctThermalLag() will return
365 % profile data that has been corrected using flow speed equal to this glider velocity.
366 % Correction parameters are calculated using passed-in glider velocity.
367 profileStructure = struct('ptime', ctd_time, 'depth', pres, 'temp', temp, 'cond', cond, 'pitch', glideAngle);
368 [correctedProfileData, varargout] = correctThermalLag(profileStructure, correctionParams, gliderVelocity);
369
370 % get the corrected temperature...
371 tempCorrected = correctedProfileData.tempInCell;
372
373 % calculate the corrected salinity using the corrected temperature...
374 salinCorrected = sw_salt(10*cond/sw_c3515, tempCorrected, pres);
375
376 % calculate density...should this use temp corrected?? HES - no, now have
377 % best estimate of salinity to combine with originally measured temp
378 densCorrected = sw_pden(salinCorrected, temp, pres, 0);
379
380 % convert ptime into datenum style...for plotting I think, commenting out
381 ptime_datenum = ptime/3600/24+datenum(1970, 1, 1, 0, 0, 0);
382 %ptime_datenum_dbd = ptime_dbd/3600/24+datenum(1970, 1, 1, 0, 0, 0);
383 %ptimehrly = fix(ptime_datenum*24)/24;
384 %ptimedaily = fix(ptime_datenum);
385 %ptimedaily2 = unique(ptimedaily);
386 %ptimedaily2 = ptimedaily2(1:2:end);
387
388
389 % make copies of dbd time stamp vector for use in lat/lon interpolation...
390 ptime_dbd_gps = ptime_dbd;
391 %ptime_dbd_wpt = ptime_dbd;
392
393 % convert lats and lons to digital degrees...
394 gpsLat = ddmm2decdeg(gpsLat);
395 gpsLon = ddmm2decdeg(gpsLon);
396 %wptLat = ddmm2decdeg(wptLat);
397 %wptLon = ddmm2decdeg(wptLon);
398 %lat = ddmm2decdeg(lat);
399 %lon = ddmm2decdeg(lon);
400
401 % eliminate outliers in gpsLat, gpsLon...
402 i = find(abs(gpsLat) <= 90.0);
403 gpsLat = gpsLat(i);  gpsLon = gpsLon(i);  ptime_dbd_gps = ptime_dbd_gps(i);
404 i = find(abs(gpsLon) <= 180.0);
405 gpsLat = gpsLat(i);  gpsLon = gpsLon(i);  ptime_dbd_gps = ptime_dbd_gps(i);
406
407 % eliminate outliers in wptLat, wptLon...
408 %i = find(abs(wptLat) <= 90.0);
409 %wptLat = wptLat(i);  wptLon = wptLon(i);  ptime_dbd_wpt = ptime_dbd_wpt(i);
410 %i = find(abs(wptLon) <= 180.0);
411 %wptLat = wptLat(i);  wptLon = wptLon(i);  ptime_dbd_wpt = ptime_dbd_wpt(i);
412
413 % eliminate entries where wptLat==0
414 %i = find(wptLat~=0);
415 %wptLat = wptLat(i);  wptLon = wptLon(i);  ptime_dbd_wpt = ptime_dbd_wpt(i);
416
417 % eliminate nans before interpolating...
418 i = find(~isnan(gpsLat));
419 gpsLat = gpsLat(i);  gpsLon = gpsLon(i);  ptime_dbd_gps = ptime_dbd_gps(i);
420 %i = find(~isnan(wptLat));
421 %wptLat = wptLat(i);  wptLon = wptLon(i);  ptime_dbd_wpt = ptime_dbd_wpt(i);
422
423 % interpolate DBD lat/lon data to align with EBD data...
424 gpsLat = interp1(ptime_dbd_gps, gpsLat, ptime);
425 gpsLon = interp1(ptime_dbd_gps, gpsLon, ptime);
426 %wptLat = interp1(ptime_dbd_wpt, wptLat, ptime);
427 %wptLon = interp1(ptime_dbd_wpt, wptLon, ptime);
428
429 % use sw_dpth() to calculate depth from pres...
430 depth = sw_dpth(pres, gpsLat);
431
432
433 % create configuration struct...
434 units = struct(... %'chlor', 'micrograms liter-1',...
435                'dens', 'kg m-3',...
436                'densCorrected', 'kg m-3',...
437                'depth', 'm',...
438                'gpsLat', 'decimal degrees',...
439                'gpsLon', 'decimal degrees',...
440                'pres', 'decibars',...
441                'ptime', 'seconds since 0000-01-01T00:00',...
442                'ptime_datenum', 'seconds since 0000-01-01T00:00',...
443                'salin', 'psu',...
444                'salinCorrected', 'psu',...
445                'temp', 'deg C');
446
447 variable_description = struct(... %'chlor', 'chlorophyll measured by glider',...
448                               ... %'chlorBounds', 'default chlorophyll bounds for plots',...
449                               'dens', 'density measured by glider',...
450                               'densBounds', 'default density bounds for plots',...
451                               'densCorrected', 'density corrected for thermal lag',...
452                               'depth', 'depth calculated as function of pressure and position latitude',...
453                               'gpsLat', 'position latitude measured by glider GPS',...
454                               'gpsLon', 'position longitude measured by glider GPS',...
455                               'pres', 'pressure measured by glider',...
456                               'ptime', 'time vector reported by glider',...
457                               'ptime_datenum', 'Serial Date Number string',...
458                               'salin', 'salinity measured by glider',...
459                               'salinBounds', 'default salinity bounds for plots',...
460                               'salinCorrected', 'salinity corrected for thermal lag',...
461                               'temp', 'temperature measured by glider',...
462                               'tempBounds', 'default temperature bounds for plots');
463
464 correction_parameters = struct('alpha_offset', correctionParams(1),...
465                                'alpha_slope', correctionParams(2),...
466                                'tau_offset', correctionParams(3),...
467                                'tau_slope', correctionParams(4));
468            
469 config = struct('glider_name', strGliderName,...
470                 'deployment_number', strDeploymentNumber,...
471                 'start_date', strStartDate,...
472                 'end_date', strEndDate,...
473                 'thermal_lag_correction_parameters', correction_parameters,...
474                 'var_descriptions', variable_description,...
475                 'var_units', units);
476
477 % set Level 1 data mat file name...
478 strMatFileName = strcat(strGliderName, '_Deployment', strDeploymentNumber, '_CTD_L1.mat');
479
480
481
482 % save glider/deployment data to mat file...
483 save(strMatFileName,...
484      'config',...
485      ...% 'chlor',...
486      ...% 'chlorBounds',...
487      'dens',...
488      'densBounds',...
489      'densCorrected',...
490      'depth',...
491      'gpsLat',...
492      'gpsLon',...
493      'pres',...
494      'ptime',...
495      'ptime_datenum',...
496      'salin',...
497      'salinBounds',...
498      'salinCorrected',...
499      'temp',...
500      'tempBounds');
501
502
503
Note: See TracBrowser for help on using the browser.