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

root/gliderproc/trunk/gliderCTD_Generate_L1_Data.m

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

Remove versioning from naming.

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