diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1d5b89902f6af96f3744b484e22fc774567be283
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/FR29/*
+*.000
+/FR29_bis/*
+/FR28/*
diff --git a/data_example/FR24_000.000 b/data_example/FR24_000.000
deleted file mode 100644
index 3cc71039d9c94722fe2e6c6cf731ef5b69a2b298..0000000000000000000000000000000000000000
Binary files a/data_example/FR24_000.000 and /dev/null differ
diff --git a/merge_PIRATA_ADCP_10W.m b/merge_PIRATA_ADCP_10W.m
new file mode 100644
index 0000000000000000000000000000000000000000..61394a3de52a6bf360609d1c9358a0414b324a54
--- /dev/null
+++ b/merge_PIRATA_ADCP_10W.m
@@ -0,0 +1,289 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% merge_PIRATA_ADCP_10W.m
+% ------------------------------------
+% Merge ADCP datasets from 2001 to 2017
+% -------------------------------
+% Author : Jérémie HABASQUE - IRD
+% -------------------------------
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+clear all;close all;
+
+% path
+addpath('.\moored_adcp_proc');
+
+fpath = 'C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\merge_data\';
+mooring.name='10W0N';
+
+% niveaux d'affichages des champs u et v
+niv_u = (-1.5:0.1:1.5);
+niv_v = (-0.5:0.1:0.5);
+
+% subsampling on a regular 24-hour grid
+% 2001,2003 and 2004 datasets are processed with a daily resolution
+step_subsampling = 1; 
+
+% Read data
+
+% 2001 (on le lit pas car periode de 2 mois..)
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2001-2002\UV_day_10w_int.mat');
+d0=datenum(2001,12,11,0,0,0);
+d1=datenum(2002,02,23,0,0,0); 
+adcp_2001_time=d0:step_subsampling:d1;
+adcp_2001_time = adcp_2001_time';
+adcp_2001_u = uvmoy(:,1:75)/100;
+adcp_2001_v = uvmoy(:,76:150)/100;
+adcp_2001_bin_length = 4;
+adcp_2001_z = 0:adcp_2001_bin_length:104;%Pas certain...
+adcp_2001_z = adcp_2001_z';%Pas certain...  
+[YY,MM,DD,hh,mm,ss] = datevec(adcp_2001_time);
+adcp_2001_time=julian(YY,MM,DD,hh,mm,ss);
+ 
+% 2003 
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2003-2004\UV_moy_10w.mat');
+d0=datenum(2003,05,07,0,0,0);
+d1=datenum(2004,02,03,0,0,0); 
+adcp_2003_time=d0:step_subsampling:d1;
+adcp_2003_time = adcp_2003_time';
+adcp_2003_u = uvmoy(1:39,:)/100;
+adcp_2003_v = uvmoy(40:78,:)/100;
+adcp_2003_bin_length = 8;
+adcp_2003_z = 26:adcp_2003_bin_length:332;%Pas certain...
+adcp_2003_z = adcp_2003_z';%Pas certain...  
+[YY,MM,DD,hh,mm,ss] = datevec(adcp_2003_time);
+adcp_2003_time=julian(YY,MM,DD,hh,mm,ss);
+
+% 2004
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2004-2005\UV_day_10w_int10.mat');
+d0=datenum(2004,02,06,0,0,0);
+d1=datenum(2005,06,17,0,0,0); 
+adcp_2004_time=d0:step_subsampling:d1;
+adcp_2004_time = adcp_2004_time';
+adcp_2004_u=uvmoy(:,1:498)/100;
+adcp_2004_v=uvmoy(:,499:996)/100;
+adcp_2004_bin_length = 10;
+adcp_2004_z = 0:adcp_2004_bin_length:301;%Pas certain...
+adcp_2004_z = adcp_2004_z';%Pas certain...
+[YY,MM,DD,hh,mm,ss] = datevec(adcp_2004_time);
+adcp_2004_time=julian(YY,MM,DD,hh,mm,ss);
+
+%2006-2008
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2006-2008\PIRATA_FR18_10W_daily.mat');
+d0=datenum(2006,06,26,00,00,0);
+d1=datenum(2008,09,27,00,00,0); 
+adcp_2006_time=d0:step_subsampling:d1;
+adcp_2006_time = adcp_2006_time';
+[YY,MM,DD,hh,mm,ss] = datevec(adcp_2006_time);
+adcp_2006_time=julian(YY,MM,DD,hh,mm,ss);
+adcp_2006_u = u'/100;
+adcp_2006_v = v'/100;
+adcp_2006_z = Z'; 
+
+% 2011
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2010-2011\FR22-10W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2011_time = data.inttim;
+adcp_2011_u = data.u_final; 
+adcp_2011_v = data.v_final; 
+adcp_2011_z = data.z_final';
+adcp_2011_bin_length = raw.config.cell;
+
+% 2014
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2012-2013\FR24-10W_15258_instr_01_int_filt_sub.mat');
+adcp_2014_time = data.inttim;
+adcp_2014_u = data.uintfilt;
+adcp_2014_v = data.vintfilt;
+adcp_2014_z = data.Z';
+adcp_2014_bin_length = raw.config.cell;
+
+% 2015
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2014-2015\FR25-10W_15258_instr_01_int_filt_sub.mat');
+adcp_2015_time = data.inttim;
+adcp_2015_u = data.uintfilt;
+adcp_2015_v = data.vintfilt;
+adcp_2015_z = data.Z';
+adcp_2015_bin_length = raw.config.cell;
+
+% 2017
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\10W-0N\2015-2017\10W0N_15258_instr_01_int_filt_sub.mat');
+adcp_2017_time = data.inttim;
+adcp_2017_u = data.uintfilt;
+adcp_2017_v = data.vintfilt;
+adcp_2017_z = data.Z';
+adcp_2017_bin_length = raw.config.cell;
+
+%% Interpolate data on a regular vertical grid
+
+%Z = fliplr(min_depth:max_bin_length:max_depth);
+Z = fliplr(0:5:350); % on se base sur la maille des donnees TACE etendue a 350m
+Zmax = max(Z);
+
+%interpolation for each timestep for 2001 data
+u_interp_2001 = NaN(length(Z),length(adcp_2001_time));
+v_interp_2001 = NaN(length(Z),length(adcp_2001_time));
+for i=1:length(adcp_2001_time)    
+     ind_ok = find(~isnan(adcp_2001_u(:,i)));
+     u_interp_2001(:,i) = interp1(adcp_2001_z(ind_ok),adcp_2001_u(ind_ok,i),Z);
+     v_interp_2001(:,i) = interp1(adcp_2001_z(ind_ok),adcp_2001_v(ind_ok,i),Z);    
+end
+%interpolation for each timestep for 2003 data
+u_interp_2003 = NaN(length(Z),length(adcp_2003_time));
+v_interp_2003 = NaN(length(Z),length(adcp_2003_time));
+for i=1:length(adcp_2003_time)    
+     ind_ok = find(~isnan(adcp_2003_u(:,i)));
+     u_interp_2003(:,i) = interp1(adcp_2003_z(ind_ok),adcp_2003_u(ind_ok,i),Z);
+     v_interp_2003(:,i) = interp1(adcp_2003_z(ind_ok),adcp_2003_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2004 data
+u_interp_2004 = NaN(length(Z),length(adcp_2004_time));
+v_interp_2004 = NaN(length(Z),length(adcp_2004_time));
+for i=1:length(adcp_2004_time)    
+     ind_ok = find(~isnan(adcp_2004_u(:,i)));
+     u_interp_2004(:,i) = interp1(adcp_2004_z(ind_ok),adcp_2004_u(ind_ok,i),Z);
+     v_interp_2004(:,i) = interp1(adcp_2004_z(ind_ok),adcp_2004_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2006 data
+u_interp_2006 = NaN(length(Z),length(adcp_2006_time));
+v_interp_2006 = NaN(length(Z),length(adcp_2006_time));
+for i=1:length(adcp_2006_time)    
+     ind_ok = find(~isnan(adcp_2006_u(:,i)));
+     u_interp_2006(:,i) = interp1(adcp_2006_z(ind_ok),adcp_2006_u(ind_ok,i),Z);
+     v_interp_2006(:,i) = interp1(adcp_2006_z(ind_ok),adcp_2006_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2011 data
+u_interp_2011 = NaN(length(Z),length(adcp_2011_time));
+v_interp_2011 = NaN(length(Z),length(adcp_2011_time));
+for i=1:length(adcp_2011_time)    
+     ind_ok = find(~isnan(adcp_2011_u(:,i)));
+     u_interp_2011(:,i) = interp1(adcp_2011_z(ind_ok),adcp_2011_u(ind_ok,i),Z);
+     v_interp_2011(:,i) = interp1(adcp_2011_z(ind_ok),adcp_2011_v(ind_ok,i),Z);  
+end
+
+%interpolation for each timestep for 2014 data
+u_interp_2014 = NaN(length(Z),length(adcp_2014_time));
+v_interp_2014 = NaN(length(Z),length(adcp_2014_time));
+for i=1:length(adcp_2014_time)
+    ind_ok = find(~isnan(adcp_2014_u(:,i)));
+    u_interp_2014(:,i) = interp1(adcp_2014_z(ind_ok),adcp_2014_u(ind_ok,i),Z);
+    v_interp_2014(:,i) = interp1(adcp_2014_z(ind_ok),adcp_2014_v(ind_ok,i),Z);
+end
+
+%interpolation for each timestep for 2015 data
+u_interp_2015 = NaN(length(Z),length(adcp_2015_time));
+v_interp_2015 = NaN(length(Z),length(adcp_2015_time));
+for i=1:length(adcp_2015_time)
+ ind_ok = find(~isnan(adcp_2015_u(:,i)));
+    u_interp_2015(:,i) = interp1(adcp_2015_z(ind_ok),adcp_2015_u(ind_ok,i),Z);
+    v_interp_2015(:,i) = interp1(adcp_2015_z(ind_ok),adcp_2015_v(ind_ok,i),Z);
+end
+ 
+%interpolation for each timestep for 2017 data
+u_interp_2017 = NaN(length(Z),length(adcp_2017_time));
+v_interp_2017 = NaN(length(Z),length(adcp_2017_time));
+for i=1:length(adcp_2017_time)
+ ind_ok = find(~isnan(adcp_2017_u(:,i)));
+    u_interp_2017(:,i) = interp1(adcp_2017_z(ind_ok),adcp_2017_u(ind_ok,i),Z);
+    v_interp_2017(:,i) = interp1(adcp_2017_z(ind_ok),adcp_2017_v(ind_ok,i),Z);
+end
+
+%% combine all data
+all_time = [adcp_2001_time' adcp_2003_time' adcp_2004_time' adcp_2006_time' adcp_2011_time adcp_2014_time adcp_2015_time adcp_2017_time];
+%check sampling interval
+% figure;
+% hist(diff(all_time),100); xlim([0, 2]);
+
+all_u_interp = [u_interp_2001 u_interp_2003 u_interp_2004 u_interp_2006 u_interp_2011 u_interp_2014 u_interp_2015 u_interp_2017];
+all_v_interp = [v_interp_2001 v_interp_2003 v_interp_2004 v_interp_2006 v_interp_2011 v_interp_2014 v_interp_2015 v_interp_2017];
+
+% create a continuous series of daily data, ranging from min(d) to max(d)
+ADCP_final.time = ceil(min(all_time)):step_subsampling:floor(max(all_time));
+ADCP_final.depth = Z;
+ADCP_final.u = NaN(length(ADCP_final.depth),length(ADCP_final.time));
+ADCP_final.v = NaN(length(ADCP_final.depth),length(ADCP_final.time));
+
+for i_time = 1:length(ADCP_final.time)
+    for j_time = 1:length(all_time)
+        if ADCP_final.time(i_time) == all_time(j_time)
+            ADCP_final.u(:,i_time)=all_u_interp(:,j_time);
+            ADCP_final.v(:,i_time)=all_v_interp(:,j_time);
+        end
+    end
+end
+
+% save global data
+save([fpath, 'ADCP_10W0N_2001_2017_int_filt_sub.mat'],'ADCP_final');
+
+%% FIGURES
+
+hf=figure('position', [0, 0, 1400, 1000]);
+%u
+subplot(2,1,1);
+[C,h] = contourf(ADCP_final.time,Z,ADCP_final.u,niv_u);
+set(h,'LineColor','none');
+caxis(niv_u([1 end]));
+h=colorbar;
+ylabel(h,'U [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylim([0 350]);
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({['10°W 0°N - ZONAL VELOCITY']});
+
+%v
+subplot(2,1,2);
+[C,h] = contourf(ADCP_final.time,Z,ADCP_final.v,niv_v);
+set(h,'LineColor','none');
+caxis(niv_v([1 end]));
+h=colorbar;
+ylabel(h,'V [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylim([0 350]);
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({['10°W 0°N - MERIDIONAL VELOCITY']});
+
+graph_name = [fpath, 'ADCP_10W0N_2001_2017_U_V_daily'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+
+% Histogramme des valeurs U et V
+
+hf=figure('position', [0, 0, 1400, 1000]);
+subplot(1,2,1); hist(ADCP_final.u(:),100); xlabel('U [m s^-^1]');
+subplot(1,2,2); hist(ADCP_final.v(:),100); xlabel('V [m s^-^1]');
+
+graph_name = [fpath, 'ADCP_U_V_10W_daily_histo'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+
+
+%%  Write netcdf file                           
+ncid=netcdf.create([fpath,'ADCP_10W0N_2001_2017_1d.nc'],'NC_WRITE');
+ 
+%create dimension
+dimidt = netcdf.defDim(ncid,'time',length(ADCP_final.time));
+dimidz = netcdf.defDim(ncid,'depth',length(ADCP_final.depth));
+%Define IDs for the dimension variables (pressure,time,latitude,...)
+time_ID=netcdf.defVar(ncid,'time','double',[dimidt]);
+depth_ID=netcdf.defVar(ncid,'depth','double',[dimidz]);
+%Define the main variable ()
+u_ID = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
+v_ID = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
+%We are done defining the NetCdf
+netcdf.endDef(ncid);
+%Then store the dimension variables in
+netcdf.putVar(ncid,time_ID,ADCP_final.time);
+netcdf.putVar(ncid,depth_ID,ADCP_final.depth);  
+%Then store my main variable
+netcdf.putVar(ncid,u_ID,ADCP_final.u);
+netcdf.putVar(ncid,v_ID,ADCP_final.v);
+%We're done, close the netcdf
+netcdf.close(ncid);
diff --git a/merge_PIRATA_ADCP_10W_enmer.m b/merge_PIRATA_ADCP_10W_enmer.m
new file mode 100644
index 0000000000000000000000000000000000000000..5b1bc27700858b06653fa1b733c911dc06007443
--- /dev/null
+++ b/merge_PIRATA_ADCP_10W_enmer.m
@@ -0,0 +1,193 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% merge_PIRATA_ADCP_10W.m
+% ------------------------------------
+% Merge ADCP datasets from 2001 to 2019
+% -------------------------------
+% Author : Jérémie HABASQUE - IRD
+%          Pierre Rousselot - IRD
+% -------------------------------
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+clear all;close all;
+
+% path
+addpath('.\moored_adcp_proc');
+
+fpath        = 'C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\FR29\merge_data\';
+mooring.name = '0N10W';
+
+% niveaux d'affichages des champs u et v
+niv_u = (-1.5:0.1:1.5);
+niv_v = (-0.5:0.1:0.5);
+
+% subsampling on a regular 24-hour grid
+% 2001,2003 and 2004 datasets are processed with a daily resolution
+step_subsampling = 1; 
+
+% Read data
+
+% 2001-2017
+load('C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\FR29\ADCP_10W0N_2001_2017_int_filt_sub.mat');
+u     = ADCP_final.u;
+v     = ADCP_final.v;
+depth = ADCP_final.depth;
+time  = ADCP_final.time;
+clear ADCP_final
+
+% 2019
+load('C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\FR29\0N10W_15258_instr_01_int_filt_sub.mat');
+adcp_2019_time       = data.inttim;
+adcp_2019_u          = data.uintfilt;
+adcp_2019_v          = data.vintfilt;
+adcp_2019_z          = data.Z';
+
+%% Interpolate data on a regular vertical grid
+
+%Z = fliplr(min_depth:max_bin_length:max_depth);
+Z    = fliplr(0:5:350); % on se base sur la maille des donnees TACE etendue a 350m
+Zmax = max(Z);
+
+%interpolation for each timestep for 2019 data
+u_interp_2019 = NaN(length(Z),length(adcp_2019_time));
+v_interp_2019 = NaN(length(Z),length(adcp_2019_time));
+for i=1:length(adcp_2019_time)
+    ind_ok             = find(~isnan(adcp_2019_u(:,i)));
+    u_interp_2019(:,i) = interp1(adcp_2019_z(ind_ok),adcp_2019_u(ind_ok,i),Z);
+    v_interp_2019(:,i) = interp1(adcp_2019_z(ind_ok),adcp_2019_v(ind_ok,i),Z);
+end
+
+%% combine all data
+all_time = [time adcp_2019_time];
+%check sampling interval
+% figure;
+% hist(diff(all_time),100); xlim([0, 2]);
+
+all_u_interp = [u u_interp_2019];
+all_v_interp = [v v_interp_2019];
+
+% create a continuous series of daily data, ranging from min(d) to max(d)
+ADCP_final.time  = ceil(min(all_time)):step_subsampling:floor(max(all_time));
+ADCP_final.depth = Z;
+ADCP_final.u     = NaN(length(ADCP_final.depth),length(ADCP_final.time));
+ADCP_final.v     = NaN(length(ADCP_final.depth),length(ADCP_final.time));
+
+for i_time = 1:length(ADCP_final.time)
+    for j_time = 1:length(all_time)
+        if ADCP_final.time(i_time) == all_time(j_time)
+            ADCP_final.u(:,i_time) = all_u_interp(:,j_time);
+            ADCP_final.v(:,i_time) = all_v_interp(:,j_time);
+        end
+    end
+end
+
+% save global data
+save([fpath, 'ADCP_10W0N_2001_2019_int_filt_sub.mat'],'ADCP_final');
+
+%% FIGURES
+
+hf=figure('position', [0, 0, 1400, 1000]);
+%u
+subplot(2,1,1);
+[C,h] = contourf(ADCP_final.time,Z,ADCP_final.u,niv_u);
+set(h,'LineColor','none');
+caxis(niv_u([1 end]));
+h=colorbar;
+ylabel(h,'U [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylim([0 350]);
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({['10°W 0°N - ZONAL VELOCITY']});
+
+%v
+subplot(2,1,2);
+[C,h] = contourf(ADCP_final.time,Z,ADCP_final.v,niv_v);
+set(h,'LineColor','none');
+caxis(niv_v([1 end]));
+h=colorbar;
+ylabel(h,'V [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylim([0 350]);
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({['10°W 0°N - MERIDIONAL VELOCITY']});
+
+graph_name = [fpath, 'ADCP_10W0N_2001_2019_U_V_daily'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+
+% % FIGURE 2
+% cUaxis = [-0.8 0.8];   % For U colorbar
+% cVaxis = [-0.4 0.4];   % For V colorbar
+% parU = jet(64);
+% parV = jet(32); 
+% figure
+% subplot(2,1,1)
+% pcolor(ADCP_final.time,Z,ADCP_final.u); shading interp
+% hold on
+% contour(ADCP_final.time,Z,ADCP_final.u,[0,0],'Linewidth',2,'Color','k','ShowText','on','LabelSpacing',144*3);
+% contour(ADCP_final.time,Z,ADCP_final.u,[0.1:0.1:1],'Color','k','ShowText','on','LabelSpacing',144*3);
+% contour(ADCP_final.time,Z,ADCP_final.u,[-1:0.1:-0.1],'--k','ShowText','on','LabelSpacing',144*3);
+% hold off
+% ylabel('Profondeur [m]')
+% title({['10°W 0°N - ZONAL VELOCITY']});
+% caxis([cUaxis])
+% c = colorbar;
+% ylabel(c, 'U [m/s]')
+% axis([min(ADCP_final.u) max(ADCP_final.u) 0 350])
+% colormap(gca, parU)
+% set(gca,'ydir', 'reverse');
+% 
+% subplot(2,1,2);
+% pcolor(ADCP_final.time,Z,ADCP_final.v); shading interp
+% hold on
+% contour(ADCP_final.time,Z,ADCP_final.v,[0,0],'Linewidth',2,'Color','k','ShowText','on','LabelSpacing',144*3);
+% contour(ADCP_final.time,Z,ADCP_final.v,[0.1:0.1:1],'Color','k','ShowText','on','LabelSpacing',144*3);
+% contour(ADCP_final.time,Z,ADCP_final.v,[-1:0.1:-0.1],'--k','ShowText','on','LabelSpacing',144*3);
+% hold off
+% gregtick
+% ylabel('Profondeur [m]')
+% axis([min(ADCP_final.v) max(ADCP_final.v) 0 350])
+% caxis([cVaxis])
+% colormap(gca, parV)
+% set(gca,'ydir', 'reverse');
+
+% Histogramme des valeurs U et V
+
+hf=figure('position', [0, 0, 1400, 1000]);
+subplot(1,2,1); hist(ADCP_final.u(:),100); xlabel('U [m s^-^1]');
+subplot(1,2,2); hist(ADCP_final.v(:),100); xlabel('V [m s^-^1]');
+
+graph_name = [fpath, 'ADCP_U_V_10W_daily_histo'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+
+
+%%  Write netcdf file                           
+ncid=netcdf.create([fpath,'ADCP_10W0N_2001_2019_1d.nc'],'NC_WRITE');
+ 
+%create dimension
+dimidt = netcdf.defDim(ncid,'time',length(ADCP_final.time));
+dimidz = netcdf.defDim(ncid,'depth',length(ADCP_final.depth));
+%Define IDs for the dimension variables (pressure,time,latitude,...)
+time_ID=netcdf.defVar(ncid,'time','double',[dimidt]);
+depth_ID=netcdf.defVar(ncid,'depth','double',[dimidz]);
+%Define the main variable ()
+u_ID = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
+v_ID = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
+%We are done defining the NetCdf
+netcdf.endDef(ncid);
+%Then store the dimension variables in
+netcdf.putVar(ncid,time_ID,ADCP_final.time);
+netcdf.putVar(ncid,depth_ID,ADCP_final.depth);  
+%Then store my main variable
+netcdf.putVar(ncid,u_ID,ADCP_final.u);
+netcdf.putVar(ncid,v_ID,ADCP_final.v);
+%We're done, close the netcdf
+netcdf.close(ncid);
diff --git a/merge_PIRATA_ADCP_23W.m b/merge_PIRATA_ADCP_23W.m
new file mode 100644
index 0000000000000000000000000000000000000000..16bf6b9b206dca9903d9763f047fce7065aac28f
--- /dev/null
+++ b/merge_PIRATA_ADCP_23W.m
@@ -0,0 +1,278 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% merge_PIRATA_ADCP_23W.m
+% ------------------------------------
+% Merge ADCP datasets from 2001 to 2016
+% -------------------------------
+% Author : Jérémie HABASQUE - IRD
+% -------------------------------
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+clear all;close all;
+% 
+% r = [0 0 1];       %# start
+% w = [.9 .9 .9];    %# middle
+% b = [1 0 0];       %# end
+% 
+% %# colormap of size 64-by-3, ranging from red -> white -> blue
+% c1 = zeros(32,3); c2 = zeros(32,3);
+% for i=1:3
+%     c1(:,i) = linspace(r(i), w(i), 32);
+%     c2(:,i) = linspace(w(i), b(i), 32);
+% end
+% c = [c1(1:end-1,:);c2];
+
+fpath = 'C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\merge_data\';
+mooring.name='23W0N';
+
+step_subsampling = 0.5; %subsampling on a regular 12-hour grid  
+
+%% Read data
+
+%2001-2002
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2001-2002 - kpo_0611\23W0N_kpo_0611_instr_01_int_filt_sub.mat');
+adcp_2002_time = data.inttim;
+adcp_2002_u = data.uintfilt;
+adcp_2002_v = data.vintfilt;
+adcp_2002_z = data.Z; 
+
+%2004-2005
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2004-2005 - kpo_0612\23W0N_kpo_0612_instr_01_int_filt_sub.mat');
+adcp_2004_time = data.inttim;
+adcp_2004_u = data.uintfilt;
+adcp_2004_v = data.vintfilt;
+adcp_2004_z = data.Z; 
+
+%2006-2008
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2006-2008 - kpo_1001\23W0N_kpo_1001_instr_01_int_filt_sub.mat');
+adcp_2006_time = data.inttim;
+adcp_2006_u = data.uintfilt;
+adcp_2006_v = data.vintfilt;
+adcp_2006_z = data.Z; 
+
+%2008-2009
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2008-2009 - kpo_1023\23W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2008_time = data.inttim;
+adcp_2008_u = data.u_final;
+adcp_2008_v = data.v_final;
+adcp_2008_z = data.z_final; 
+
+% 2010-2011
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2009-2011 - kpo_1044\23W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2010_time = data.inttim;
+adcp_2010_u = data.u_final;
+adcp_2010_v = data.v_final;
+adcp_2010_z = data.z_final; 
+
+% 2011-2012
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2011-2012 - kpo_1063\23W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2011_time = data.inttim;
+adcp_2011_u = data.u_final;
+adcp_2011_v = data.v_final;
+adcp_2011_z = data.z_final; 
+
+% 2012-2014
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2012-2014 - kpo_1089\23W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2012_time = data.inttim;
+adcp_2012_u = data.u_final;
+adcp_2012_v = data.v_final;
+adcp_2012_z = data.z_final; 
+
+% 2014-2015
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2014-2015 - kpo_1125\23W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2014_time = data.inttim;
+adcp_2014_u = data.u_final;
+adcp_2014_v = data.v_final;
+adcp_2014_z = data.z_final; 
+
+% 2015-2016
+load('C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2015-2016 - kpo_1140\23W0N_UP_DOWN_int_filt_sub.mat');
+adcp_2015_time = data.inttim;
+adcp_2015_u = data.u_final;
+adcp_2015_v = data.v_final;
+adcp_2015_z = data.z_final; 
+
+%% Interpolate data on a regular vertical grid
+  
+%Z = fliplr(min_depth:max_bin_length:max_depth);
+Z = fliplr(0:5:350); % on se base sur la maille des donnees TACE etendue a 350m
+Zmax = max(Z);
+
+%interpolation for each timestep for 2002 data
+u_interp_2002 = NaN(length(Z),length(adcp_2002_time));
+v_interp_2002 = NaN(length(Z),length(adcp_2002_time));
+for i=1:length(adcp_2002_time)    
+     ind_ok = find(~isnan(adcp_2002_u(:,i)));
+     u_interp_2002(:,i) = interp1(adcp_2002_z(ind_ok),adcp_2002_u(ind_ok,i),Z);
+     v_interp_2002(:,i) = interp1(adcp_2002_z(ind_ok),adcp_2002_v(ind_ok,i),Z);    
+end 
+
+%interpolation for each timestep for 2004 data
+u_interp_2004 = NaN(length(Z),length(adcp_2004_time));
+v_interp_2004 = NaN(length(Z),length(adcp_2004_time));
+for i=1:length(adcp_2004_time)    
+     ind_ok = find(~isnan(adcp_2004_u(:,i)));
+     u_interp_2004(:,i) = interp1(adcp_2004_z(ind_ok),adcp_2004_u(ind_ok,i),Z);
+     v_interp_2004(:,i) = interp1(adcp_2004_z(ind_ok),adcp_2004_v(ind_ok,i),Z);    
+end 
+
+%interpolation for each timestep for 2006 data
+u_interp_2006 = NaN(length(Z),length(adcp_2006_time));
+v_interp_2006 = NaN(length(Z),length(adcp_2006_time));
+for i=1:length(adcp_2006_time)    
+     ind_ok = find(~isnan(adcp_2006_u(:,i)));
+     u_interp_2006(:,i) = interp1(adcp_2006_z(ind_ok),adcp_2006_u(ind_ok,i),Z);
+     v_interp_2006(:,i) = interp1(adcp_2006_z(ind_ok),adcp_2006_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2008 data
+u_interp_2008 = NaN(length(Z),length(adcp_2008_time));
+v_interp_2008 = NaN(length(Z),length(adcp_2008_time));
+for i=1:length(adcp_2008_time)    
+     ind_ok = find(~isnan(adcp_2008_u(:,i)));
+     u_interp_2008(:,i) = interp1(adcp_2008_z(ind_ok),adcp_2008_u(ind_ok,i),Z);
+     v_interp_2008(:,i) = interp1(adcp_2008_z(ind_ok),adcp_2008_v(ind_ok,i),Z);    
+end
+ 
+%interpolation for each timestep for 2010 data
+u_interp_2010 = NaN(length(Z),length(adcp_2010_time));
+v_interp_2010 = NaN(length(Z),length(adcp_2010_time));
+for i=1:length(adcp_2010_time)    
+     ind_ok = find(~isnan(adcp_2010_u(:,i)));
+     u_interp_2010(:,i) = interp1(adcp_2010_z(ind_ok),adcp_2010_u(ind_ok,i),Z);
+     v_interp_2010(:,i) = interp1(adcp_2010_z(ind_ok),adcp_2010_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2011 data
+u_interp_2011 = NaN(length(Z),length(adcp_2011_time));
+v_interp_2011 = NaN(length(Z),length(adcp_2011_time));
+for i=1:length(adcp_2011_time)    
+     ind_ok = find(~isnan(adcp_2011_u(:,i)));
+     u_interp_2011(:,i) = interp1(adcp_2011_z(ind_ok),adcp_2011_u(ind_ok,i),Z);
+     v_interp_2011(:,i) = interp1(adcp_2011_z(ind_ok),adcp_2011_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2012 data
+u_interp_2012 = NaN(length(Z),length(adcp_2012_time));
+v_interp_2012 = NaN(length(Z),length(adcp_2012_time));
+for i=1:length(adcp_2012_time)    
+     ind_ok = find(~isnan(adcp_2012_u(:,i)));
+     u_interp_2012(:,i) = interp1(adcp_2012_z(ind_ok),adcp_2012_u(ind_ok,i),Z);
+     v_interp_2012(:,i) = interp1(adcp_2012_z(ind_ok),adcp_2012_v(ind_ok,i),Z);    
+end
+
+%interpolation for each timestep for 2014 data
+u_interp_2014 = NaN(length(Z),length(adcp_2014_time));
+v_interp_2014 = NaN(length(Z),length(adcp_2014_time));
+for i=1:length(adcp_2014_time)
+    ind_ok = find(~isnan(adcp_2014_u(:,i)));
+    u_interp_2014(:,i) = interp1(adcp_2014_z(ind_ok),adcp_2014_u(ind_ok,i),Z);
+    v_interp_2014(:,i) = interp1(adcp_2014_z(ind_ok),adcp_2014_v(ind_ok,i),Z);
+end
+
+%interpolation for each timestep for 2015 data
+u_interp_2015 = NaN(length(Z),length(adcp_2015_time));
+v_interp_2015 = NaN(length(Z),length(adcp_2015_time));
+for i=1:length(adcp_2015_time)
+    ind_ok = find(~isnan(adcp_2015_u(:,i)));
+    u_interp_2015(:,i) = interp1(adcp_2015_z(ind_ok),adcp_2015_u(ind_ok,i),Z);
+    v_interp_2015(:,i) = interp1(adcp_2015_z(ind_ok),adcp_2015_v(ind_ok,i),Z);
+end
+  
+%% combine all data
+all_time = [adcp_2002_time adcp_2004_time adcp_2006_time adcp_2008_time adcp_2010_time adcp_2011_time adcp_2012_time adcp_2014_time adcp_2015_time];
+all_u_interp = [u_interp_2002 u_interp_2004 u_interp_2006 u_interp_2008 u_interp_2010 u_interp_2011 u_interp_2012 u_interp_2014 u_interp_2015];
+all_v_interp = [v_interp_2002 v_interp_2004 v_interp_2006 v_interp_2008 v_interp_2010 v_interp_2011 v_interp_2012 v_interp_2014 v_interp_2015];
+
+% create a continuous series of daily data, ranging from min(d) to max(d)
+ADCP_final.time = ceil(min(all_time)):step_subsampling:floor(max(all_time));
+ADCP_final.depth = Z;
+ADCP_final.u = NaN(length(ADCP_final.depth),length(ADCP_final.time));
+ADCP_final.v = NaN(length(ADCP_final.depth),length(ADCP_final.time));
+
+for i_time = 1:length(ADCP_final.time)
+    for j_time = 1:length(all_time)
+        if ADCP_final.time(i_time) == all_time(j_time)
+            ADCP_final.u(:,i_time)=all_u_interp(:,j_time);
+            ADCP_final.v(:,i_time)=all_v_interp(:,j_time);
+        end
+    end
+end
+
+% save global data
+save([fpath, 'ADCP_',mooring.name '_2001_2016_int_filt_sub.mat'],'ADCP_final');
+
+%% FIGURES
+niv_u = (-1.5:0.1:1.5);
+niv_v = (-0.5:0.1:0.5);
+
+hf=figure('position', [0, 0, 1400, 1000]);
+%u
+subplot(2,1,1);
+[C,h] = contourf(ADCP_final.time,Z,ADCP_final.u,niv_u);
+set(h,'LineColor','none');
+caxis(niv_u([1 end]));
+h=colorbar;
+%colormap(c);
+ylabel(h,'U [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylim([0 350]);
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({['23°W - ZONAL VELOCITY']});
+
+%v
+subplot(2,1,2);
+[C,h] = contourf(ADCP_final.time,Z,ADCP_final.v,niv_v);
+set(h,'LineColor','none');
+caxis(niv_v([1 end]));
+h=colorbar;
+ylabel(h,'V [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylim([0 350]);
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({['23°W - MERIDIONAL VELOCITY']});
+
+graph_name = [fpath, 'ADCP_23W0N_2001_2016_U_V_daily'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+
+% Histogramme des valeurs U et V
+
+hf=figure('position', [0, 0, 1400, 1000]);
+subplot(1,2,1); hist(ADCP_final.u(:),100); xlabel('U [m s^-^1]');
+subplot(1,2,2); hist(ADCP_final.v(:),100); xlabel('V [m s^-^1]');
+
+graph_name = [fpath, 'ADCP_23W0N_2001_2016_U_V_histo'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+
+%%  Write netcdf file                           
+ncid=netcdf.create([fpath,'ADCP_23W0N_2001_2016_1d.nc'],'NC_WRITE');
+ 
+%create dimension
+dimidt = netcdf.defDim(ncid,'time',length(ADCP_final.time));
+dimidz = netcdf.defDim(ncid,'depth',length(ADCP_final.depth));
+%Define IDs for the dimension variables (pressure,time,latitude,...)
+time_ID=netcdf.defVar(ncid,'time','double',[dimidt]);
+depth_ID=netcdf.defVar(ncid,'depth','double',[dimidz]);
+%Define the main variable ()
+u_ID = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
+v_ID = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
+%We are done defining the NetCdf
+netcdf.endDef(ncid);
+%Then store the dimension variables in
+netcdf.putVar(ncid,time_ID,ADCP_final.time);
+netcdf.putVar(ncid,depth_ID,ADCP_final.depth);  
+%Then store my main variable
+netcdf.putVar(ncid,u_ID,ADCP_final.u);
+netcdf.putVar(ncid,v_ID,ADCP_final.v);
+%We're done, close the netcdf
+netcdf.close(ncid);
+
diff --git a/moored_adcp_proc/PROTOCOLE_ADCP_MOUILLAGE.docx b/moored_adcp_proc/PROTOCOLE_ADCP_MOUILLAGE.docx
new file mode 100644
index 0000000000000000000000000000000000000000..441184e59aa4861650a924d1dc677a579a543805
Binary files /dev/null and b/moored_adcp_proc/PROTOCOLE_ADCP_MOUILLAGE.docx differ
diff --git a/moored_adcp_proc/adcp_check_surface.m b/moored_adcp_proc/adcp_check_surface.m
index 3f193220b36e9fd034551798e83a3d1d994042ca..2dec06fce0cd664a1449f2bb4f0e9cbffff450f4 100644
--- a/moored_adcp_proc/adcp_check_surface.m
+++ b/moored_adcp_proc/adcp_check_surface.m
@@ -10,7 +10,7 @@ set(gca,'YTick',bins);
 ylabel('Bins');
 hold on;
 gregtick;
-title('Meridional velocity before correction');
+title('Zonal velocity before correction');
 
 subplot 323;
 imagesc(time,bin(bins),u1(bins,:),[-1 1]);
@@ -18,7 +18,7 @@ set(gca,'YTick',bins);
 hold on;
 ylabel('Bins');
 gregtick;
-title('Meridional velocity after correction');
+title('Zonal velocity after correction');
 
 subplot 322;
 imagesc(time,bin(bins),v(bins,:),[-1 1]);
@@ -26,7 +26,7 @@ set(gca,'YTick',bins);
 hold on;
 ylabel('Bins');
 gregtick;
-title('Zonal velocity before correction');
+title('Meridional velocity before correction');
 
 subplot 324;
 imagesc(time,bin(bins),v1(bins,:),[-1 1]);
@@ -34,10 +34,10 @@ set(gca,'YTick',bins);
 hold on;
 ylabel('Bins');
 gregtick;
-title('Zonal velocity after correction');
+title('Meridional velocity after correction');
 
 subplot 325;
-for i=1:length(bins);
+for i=1:length(bins)
     plot(time,hammfilter_nodec(z(bins(i),:),73),'color','k');
     hold on;
 end
@@ -47,7 +47,7 @@ ylabel('Bins');
 title('Bin depth after correction');
 
 subplot 326;
-for i=1:length(bins);
+for i=1:length(bins)
     plot(time,hammfilter_nodec(z(bins(i),:),73),'color','k');
     hold on;
 end
diff --git a/moored_adcp_proc/adcp_filt_sub.m b/moored_adcp_proc/adcp_filt_sub.m
index 310e7c7c5f716f7b279e0213fa5773a0ac8c64bd..9eb57d4dc4e546cdcb6fd342b7011b21117f0a34 100644
--- a/moored_adcp_proc/adcp_filt_sub.m
+++ b/moored_adcp_proc/adcp_filt_sub.m
@@ -1,77 +1,104 @@
-function [uintfilt,vintfilt,inttim]=adcp_filt_sub(data,ui,vi,intdepvec,nhours)
+function [uintfilt,vintfilt,uifilt,vifilt,inttim,utid_baro,vtid_baro]=adcp_filt_sub(data,ui,vi,intdepvec,nhours)
 
-sf=1/((data.time(2)-data.time(1))*24);
+sf = 1/((data.time(2)-data.time(1))*24);
 
 for iidep = 1:length(intdepvec)
     
-  adcpui = ui(iidep,:);
-  adcpvi = vi(iidep,:);
-  valid = find(~isnan(adcpui));
+  adcpui  = ui(iidep,:);
+  adcpvi  = vi(iidep,:);
+  valid   = find(~isnan(adcpui));
   invalid = find(isnan(adcpui));
  
-  if length(valid)>1 && length(valid(1):valid(end))>240; % length(invalid)>900 & 
+  if length(valid)>1 && length(valid(1):valid(end))>240 % length(invalid)>900 & 
 
       % Horizontal interpolation to fill gaps
-      timval = data.time(valid);
-      adcpui = interp1(timval,adcpui(valid),data.time);
-      adcpvi = interp1(timval,adcpvi(valid),data.time);
+      timval                           = data.time(valid);
+      adcpui                           = interp1(timval,adcpui(valid),data.time);
+      adcpvi                           = interp1(timval,adcpvi(valid),data.time);
       sum(isnan(adcpui));
       sum(isnan(adcpvi));
       % filtering
-      adcpui(valid(1):valid(end)) = mfilter(adcpui(valid(1):valid(end)),sf,1/nhours,0,2*nhours,1);
-      adcpvi(valid(1):valid(end)) = mfilter(adcpvi(valid(1):valid(end)),sf,1/nhours,0,2*nhours,1);
+      adcpui(valid(1):valid(end))      = mfilter(adcpui(valid(1):valid(end)),sf,1/nhours,0,2*nhours,1);
+      adcpvi(valid(1):valid(end))      = mfilter(adcpvi(valid(1):valid(end)),sf,1/nhours,0,2*nhours,1);
+      %[adcpui(valid(1):valid(end)) ] = fft_filter(adcpui(valid(1):valid(end)),2,[24 Inf]);
+      %[adcpvi(valid(1):valid(end))]  = fft_filter(adcpvi(valid(1):valid(end)),2,[24 Inf]);
       sum(isnan(adcpui));
       sum(isnan(adcpvi));
 %       adcpui(invalid) = NaN;
 %       adcpvi(invalid) = NaN;
       sum(isnan(adcpui));
-      uifilt(iidep,1:length(adcpui)) = adcpui;
-      vifilt(iidep,1:length(adcpvi)) = adcpvi;
+      uifilt(iidep,1:length(adcpui))   = adcpui;
+      vifilt(iidep,1:length(adcpvi))   = adcpvi;
       % subsampling
-      inttim = [ceil(data.time(1)):0.25:floor(data.time(end))];
+      inttim                           = [ceil(data.time(1)):0.25:floor(data.time(end))];
       uintfilt(iidep,1:length(inttim)) = interp1(data.time,transpose(uifilt(iidep,:)),inttim);
       vintfilt(iidep,1:length(inttim)) = interp1(data.time,transpose(vifilt(iidep,:)),inttim);
       inttim = inttim;  
    
   elseif length(valid)<=1 || length(valid(1):valid(end))<=240
       % subsampling
-      uifilt(iidep,1:length(adcpui)) = nan;
-      vifilt(iidep,1:length(adcpvi)) = nan;
-      inttim = [ceil(data.time(1)):0.25:floor(data.time(end))];
-      uintfilt(iidep,1:length(inttim)) =nan;
-      vintfilt(iidep,1:length(inttim)) =nan;
+      uifilt(iidep,1:length(adcpui))   = NaN;
+      vifilt(iidep,1:length(adcpvi))   = NaN;
+      inttim                           = [ceil(data.time(1)):0.25:floor(data.time(end))];
+      uintfilt(iidep,1:length(inttim)) = NaN;
+      vintfilt(iidep,1:length(inttim)) = NaN;
       
   else
       % Horizontal interpolation to fill gaps
-      timval = data.time(valid);
-      adcpui = interp1(timval,adcpui(valid),data.time);
-      adcpvi = interp1(timval,adcpvi(valid),data.time); 
-      % filtering
-      adcpui = mfilter(adcpui,sf,1/nhours,0,2*nhours,1);
-      adcpvi = mfilter(adcpvi,sf,1/nhours,0,2*nhours,1);
+      timval                           = data.time(valid);
+      adcpui                           = interp1(timval,adcpui(valid),data.time);
+      adcpvi                           = interp1(timval,adcpvi(valid),data.time); 
+      %adcpui                           = mfilter(adcpui,sf,1/nhours,0,2*nhours,1);
+      %adcpvi                           = mfilter(adcpvi,sf,1/nhours,0,2*nhours,1);
 %       adcpui(invalid) = NaN;
 %       adcpvi(invalid) = NaN;
-      uifilt(iidep,1:length(adcpui)) = adcpui;
-      vifilt(iidep,1:length(adcpvi)) = adcpvi;
+      uifilt(iidep,1:length(adcpui))   = adcpui;
+      vifilt(iidep,1:length(adcpvi))   = adcpvi;
       %subsampling
-      inttim = [ceil(data.time(1)):0.25:floor(data.time(end))];
+      inttim                           = [ceil(data.time(1)):0.25:floor(data.time(end))];
       uintfilt(iidep,1:length(inttim)) = interp1(timval,transpose(uifilt(iidep,valid)),inttim);
       vintfilt(iidep,1:length(inttim)) = interp1(timval,transpose(vifilt(iidep,valid)),inttim);
-      inttim = inttim;
+      inttim                           = inttim;
       
   end
       
 end
 
+% [uintfilt] = fft_filter(uintfilt,2,[40 Inf]);
+% [vintfilt] = fft_filter(vintfilt,2,[40 Inf]);
+% uintfilt   = real(uintfilt);
+% vintfilt   = real(vintfilt);
+
+%% Calc tide
+addpath(genpath('C:\Users\proussel\Documents\outils\TOOLS\TMD'))
+addpath(genpath('C:\Users\proussel\Documents\outils\TOOLS\TIDES\tpxo9_atlas'))
+addpath(genpath('C:\Users\proussel\Documents\outils\TOOLS\TIDES\tpxo8_atlas'))
+% u component
+utid      = tmd_tide_pred('C:\Users\proussel\Documents\outils\TOOLS\TIDES\tpxo8_atlas_compact\Model_tpxo8.v1',datenum(julian(inttim')),data.lat,data.lon,'u')/100;
+%utid      = tmd_tide_pred('C:\Users\proussel\Documents\outils\TOOLS\TIDES\tpxo9.1\Model_tpxo9.v1',datenum(julian(inttim')),data.lat,data.lon,'u')/100;
+%utid      = tmd_tide_pred('C:\Users\proussel\Documents\outils\TOOLS\TMD\DATA\Model_tpxo7.2',datenum(julian(inttim')),data.lat,data.lon,'u')/100;
+utid_baro = utid'.*ones(length(uintfilt(:,1)),1);
+% v component
+vtid      = tmd_tide_pred('C:\Users\proussel\Documents\outils\TOOLS\TIDES\tpxo8_atlas_compact\Model_tpxo8.v1',datenum(julian(inttim')),data.lat,data.lon,'v')/100;
+%vtid      = tmd_tide_pred('C:\Users\proussel\Documents\outils\TOOLS\TIDES\tpxo9.1\Model_tpxo9.v1',datenum(julian(inttim')),data.lat,data.lon,'v')/100;
+%vtid      = tmd_tide_pred('C:\Users\proussel\Documents\outils\TOOLS\TMD\DATA\Model_tpxo7.2',datenum(julian(inttim')),data.lat,data.lon,'v')/100;
+vtid_baro = vtid'.*ones(length(vintfilt(:,1)),1);
+utid_baro=0;
+vtid_baro=0;
+
+%% Correct tide
+% uintfilt = uintfilt-utid_baro;
+% vintfilt = vintfilt-vtid_baro;
+
 hf=figure('position', [0, 0, 1400, 1000]);
 subplot 121;
-imagesc(data.time,intdepvec,ui,[-1 1]);
+imagesc(data.time,intdepvec,ui(intdepvec,:),[-1 1]);
 %set(gca,'YLim',[min(intdepvec)-10 max(intdepvec)+10]);
 gregtick;
 ylabel('Bins');
 title('U field - data raw');
 subplot 122;
-imagesc(data.time,intdepvec,uintfilt,[-1 1]);
+imagesc(inttim,intdepvec,uintfilt,[-1 1]);
 %set(gca,'YLim',[min(intdepvec)-10 max(intdepvec)+10]);
 gregtick;
 ylabel('Bins');
@@ -79,13 +106,13 @@ title('U field - data interpolated, filtered and subsampled');
 
 hf=figure('position', [0, 0, 1400, 1000]);
 subplot 121;
-imagesc(data.time,intdepvec,vi,[-1 1]);
+imagesc(data.time,intdepvec,vi(intdepvec,:),[-1 1]);
 %set(gca,'YLim',[min(intdepvec)-10 max(intdepvec)+10])
 gregtick;
 ylabel('Bins');
 title('V field - data raw');
 subplot 122;
-imagesc(data.time,intdepvec,vintfilt,[-1 1]);
+imagesc(inttim,intdepvec,vintfilt,[-1 1]);
 %set(gca,'YLim',[min(intdepvec)-10 max(intdepvec)+10]);
 gregtick;
 ylabel('Bins');
diff --git a/moored_adcp_proc/adcp_surface_fit.m b/moored_adcp_proc/adcp_surface_fit.m
index 560fe2c3bf130a38b99291d6303882b3748ea5c1..b4b5d092f3def4646837f8a3c90e5d5ec037f0c7 100644
--- a/moored_adcp_proc/adcp_surface_fit.m
+++ b/moored_adcp_proc/adcp_surface_fit.m
@@ -1,32 +1,34 @@
 function [zbins,zadcp1,offset,x_null]=adcp_surface_fit(zadcp,ea,surface_bins,blen,blnk,nbin);
     
     % Bin depth matrix
-    dpt1 = repmat(zadcp,nbin,1);
+    dpt1   = repmat(zadcp,nbin,1);
     binmat = repmat((1:nbin)',1,length(dpt1));   
-    z = dpt1-(binmat-0.5)*blen-blnk; 
+    z      = dpt1-(binmat-0.5)*blen-blnk; 
     
     % Loop over time, determine bin of maximum ea in surface bin range and 
     % do quadratic fit over 2 neighbouring and center bins   
-    easurf=ea(surface_bins,:);
+    easurf = ea(surface_bins,:);
     for ii=1:length(ea)
-        [eamax,maxind]=max(easurf(:,ii));
+        [eamax,maxind] = max(easurf(:,ii));
         
-        if length(eamax)>1; maxind=maxind(1); end
+        if length(eamax)>1 
+            maxind = maxind(1); 
+        end
         
-        if maxind>1 & eamax>80
-            if surface_bins(maxind)==nbin; 
+        if maxind>1 && eamax>80
+            if surface_bins(maxind)==nbin 
                 xtofit(1:2) = easurf(maxind-1:maxind,ii);
-                xtofit(3) = 0;
+                xtofit(3)   = 0;
             else           
-                xtofit = easurf(maxind-1:maxind+1,ii);
+                xtofit      = easurf(maxind-1:maxind+1,ii);
             end
 
             for jj=1:3
-                A(jj,:)=[(surface_bins(maxind)+jj-2)^2, surface_bins(maxind)+jj-2, 1];
+                A(jj,:) = [(surface_bins(maxind)+jj-2)^2, surface_bins(maxind)+jj-2, 1];
             end 
-            coef(:,ii)= A\xtofit;
+            coef(:,ii) = A\xtofit;
         else
-            coef(:,ii)=NaN;
+            coef(:,ii) = NaN;
         end
     end
     
@@ -34,18 +36,26 @@ function [zbins,zadcp1,offset,x_null]=adcp_surface_fit(zadcp,ea,surface_bins,ble
     x_null = -coef(2,:)./2./coef(1,:);
 
     %offset=-round(nmedian((x_null-0.5)*blen+blnk)-nmedian(zadcp));
-    offset=-round(((x_null)*blen+blnk)-(zadcp)); %% P. Rousselot - offset over time
+    offset = round(((x_null-0.5)*blen+blnk)-(zadcp)); %% P. Rousselot - offset over time
     disp('-------------------------------');
     disp(['Depth offset is ' num2str(nanmean(offset)) ' m']);
     disp('-------------------------------');
-    
+    %offset = nanmedian(offset);
+    [offset_clean,~] = clean_median(offset,20,2.8,[0.5 5],2,NaN);
+    lin_offset       = linspace(1,length(offset),length(offset));
+    offset           = interp1(lin_offset(~isnan(offset_clean)),...
+        offset_clean(~isnan(offset_clean)),lin_offset,'linear','extrap');
     
     % Plot histogram of differences
-    dz=((x_null)*blen+blnk)-zadcp;
-    count=[-100:1:100];
-    ncount=hist(-dz,count);
+    dz     = ((x_null-0.5)*blen+blnk)-zadcp;
+    count  = [-20:1:20];
+    ncount = hist(-dz,count);
+    ncount = ncount/length(dz)*100;
     figure(1);
-    bar(count,ncount);
+    bar(count,ncount);  
+    axis([-21 21 0 50])
+    xlabel('Depth difference [m]')
+    ylabel('Occurrence percentage [%]')
     grid on
     title('Histogram of differences between original and reconstructed depth record');
     
@@ -53,7 +63,7 @@ function [zbins,zadcp1,offset,x_null]=adcp_surface_fit(zadcp,ea,surface_bins,ble
     plot(-zadcp,'b');
     grid on
     hold on;
-    plot(-(x_null)*blen+blnk,'r');
+    plot(-((x_null-0.5)*blen+blnk),'r');
     legend('Original','Reconstructed from surface reflection');
     
 %     if abs(offset)>15
@@ -67,17 +77,18 @@ function [zbins,zadcp1,offset,x_null]=adcp_surface_fit(zadcp,ea,surface_bins,ble
 %        end
 %     end
     
-    disp(['Offset of ' num2str(offset) ' m is applied']);
-    offset = 19;
+    %disp(['Offset of ' num2str(offset) ' m is applied']);
     % Apply offset to get correct bin depth and instrument depth:
-    zbins=z-offset;
-    zadcp1=zadcp-offset;
+    zbins  = z+offset;
+    zadcp1 = zadcp+offset;
     
     figure(2);
     plot(-zadcp1,'y');
     text(300, max(zadcp),['Offset applied: ' num2str(offset) ' m']);
     %,'fonts',12,'fontw','bold','backgroundc','w');
     legend('Original','Reconstructed from surface reflection','Offset applied');
+    xlabel('Time Index')
+    ylabel('Depth [m]')
     
     %print -dpng surface_fit;
     
diff --git a/moored_adcp_proc/adcp_surface_fit_bis.m b/moored_adcp_proc/adcp_surface_fit_bis.m
new file mode 100644
index 0000000000000000000000000000000000000000..ff978b4ad20228d3608f823a670fac66367ec70a
--- /dev/null
+++ b/moored_adcp_proc/adcp_surface_fit_bis.m
@@ -0,0 +1,91 @@
+function [zbins,zadcp1,offset,x_null]=adcp_surface_fit(zadcp,ea,surface_bins,blen,blnk,nbin);
+    
+    % Bin depth matrix
+    dpt1   = repmat(zadcp,nbin,1);
+    binmat = repmat((1:nbin)',1,length(dpt1));   
+    z      = dpt1-(binmat-0.5)*blen-blnk; 
+    
+    % Loop over time, determine bin of maximum ea in surface bin range and 
+    % do quadratic fit over 2 neighbouring and center bins   
+    easurf = ea(surface_bins,:);
+    for ii=1:length(ea)
+        [eamax,maxind] = max(easurf(:,ii));
+        
+        if length(eamax)>1 
+            maxind = maxind(1); 
+        end
+        
+        if maxind>1 && eamax>80
+            if surface_bins(maxind)==nbin 
+                xtofit(1:2) = easurf(maxind-1:maxind,ii);
+                xtofit(3)   = 0;
+            else           
+                xtofit      = easurf(maxind-1:maxind+1,ii);
+            end
+
+            for jj=1:3
+                A(jj,:) = [(surface_bins(maxind)+jj-2)^2, surface_bins(maxind)+jj-2, 1];
+            end 
+            coef(:,ii) = A\xtofit;
+        else
+            coef(:,ii) = NaN;
+        end
+    end
+    
+    % Find maximum of quadratic fit ax^2+bx+c: 2ax+b=0
+    x_null = -coef(2,:)./2./coef(1,:);
+
+    %offset=-round(nmedian((x_null-0.5)*blen+blnk)-nmedian(zadcp));
+    offset = -round(((x_null)*blen+blnk)-(zadcp)); %% P. Rousselot - offset over time
+    disp('-------------------------------');
+    disp(['Depth offset is ' num2str(nanmean(offset)) ' m']);
+    disp('-------------------------------');
+    offset = nanmean(offset);
+    
+    % Plot histogram of differences
+    dz     = ((x_null)*blen+blnk)-zadcp;
+    count  = [-100:1:100];
+    ncount = hist(-dz,count);
+    figure(1);
+    bar(count,ncount);
+    grid on
+    title('Histogram of differences between original and reconstructed depth record');
+    
+    figure(2);
+    plot(-zadcp,'b');
+    grid on
+    hold on;
+    plot(-(x_null)*blen+blnk,'r');
+    legend('Original','Reconstructed from surface reflection');
+    
+%     if abs(offset)>15
+%        reply = input('Do you want to overwrite offset? 1/0 [0]:');
+%        if isempty(reply)
+%           reply = 0;
+%        end
+%        
+%        if reply==1
+%            offset=input('Enter new offset:');
+%        end
+%     end
+    
+    %disp(['Offset of ' num2str(offset) ' m is applied']);
+    % Apply offset to get correct bin depth and instrument depth:
+    zbins  = z-offset;
+    zadcp1 = zadcp-offset;
+    
+    figure(2);
+    plot(-zadcp1,'y');
+    text(300, max(zadcp),['Offset applied: ' num2str(offset) ' m']);
+    %,'fonts',12,'fontw','bold','backgroundc','w');
+    legend('Original','Reconstructed from surface reflection','Offset applied');
+    
+    %print -dpng surface_fit;
+    
+    figure(3);
+    pcolor([1:length(x_null)],-zbins,ea); shading flat;
+    title('Amplitude'); colorbar; ylabel('Depth [m]');xlabel('Time index');
+    
+    %print -dpng surface_ea;
+
+end
\ No newline at end of file
diff --git a/moored_adcp_proc/adcp_surface_fit_orig.m b/moored_adcp_proc/adcp_surface_fit_orig.m
new file mode 100644
index 0000000000000000000000000000000000000000..560fe2c3bf130a38b99291d6303882b3748ea5c1
--- /dev/null
+++ b/moored_adcp_proc/adcp_surface_fit_orig.m
@@ -0,0 +1,90 @@
+function [zbins,zadcp1,offset,x_null]=adcp_surface_fit(zadcp,ea,surface_bins,blen,blnk,nbin);
+    
+    % Bin depth matrix
+    dpt1 = repmat(zadcp,nbin,1);
+    binmat = repmat((1:nbin)',1,length(dpt1));   
+    z = dpt1-(binmat-0.5)*blen-blnk; 
+    
+    % Loop over time, determine bin of maximum ea in surface bin range and 
+    % do quadratic fit over 2 neighbouring and center bins   
+    easurf=ea(surface_bins,:);
+    for ii=1:length(ea)
+        [eamax,maxind]=max(easurf(:,ii));
+        
+        if length(eamax)>1; maxind=maxind(1); end
+        
+        if maxind>1 & eamax>80
+            if surface_bins(maxind)==nbin; 
+                xtofit(1:2) = easurf(maxind-1:maxind,ii);
+                xtofit(3) = 0;
+            else           
+                xtofit = easurf(maxind-1:maxind+1,ii);
+            end
+
+            for jj=1:3
+                A(jj,:)=[(surface_bins(maxind)+jj-2)^2, surface_bins(maxind)+jj-2, 1];
+            end 
+            coef(:,ii)= A\xtofit;
+        else
+            coef(:,ii)=NaN;
+        end
+    end
+    
+    % Find maximum of quadratic fit ax^2+bx+c: 2ax+b=0
+    x_null = -coef(2,:)./2./coef(1,:);
+
+    %offset=-round(nmedian((x_null-0.5)*blen+blnk)-nmedian(zadcp));
+    offset=-round(((x_null)*blen+blnk)-(zadcp)); %% P. Rousselot - offset over time
+    disp('-------------------------------');
+    disp(['Depth offset is ' num2str(nanmean(offset)) ' m']);
+    disp('-------------------------------');
+    
+    
+    % Plot histogram of differences
+    dz=((x_null)*blen+blnk)-zadcp;
+    count=[-100:1:100];
+    ncount=hist(-dz,count);
+    figure(1);
+    bar(count,ncount);
+    grid on
+    title('Histogram of differences between original and reconstructed depth record');
+    
+    figure(2);
+    plot(-zadcp,'b');
+    grid on
+    hold on;
+    plot(-(x_null)*blen+blnk,'r');
+    legend('Original','Reconstructed from surface reflection');
+    
+%     if abs(offset)>15
+%        reply = input('Do you want to overwrite offset? 1/0 [0]:');
+%        if isempty(reply)
+%           reply = 0;
+%        end
+%        
+%        if reply==1
+%            offset=input('Enter new offset:');
+%        end
+%     end
+    
+    disp(['Offset of ' num2str(offset) ' m is applied']);
+    offset = 19;
+    % Apply offset to get correct bin depth and instrument depth:
+    zbins=z-offset;
+    zadcp1=zadcp-offset;
+    
+    figure(2);
+    plot(-zadcp1,'y');
+    text(300, max(zadcp),['Offset applied: ' num2str(offset) ' m']);
+    %,'fonts',12,'fontw','bold','backgroundc','w');
+    legend('Original','Reconstructed from surface reflection','Offset applied');
+    
+    %print -dpng surface_fit;
+    
+    figure(3);
+    pcolor([1:length(x_null)],-zbins,ea); shading flat;
+    title('Amplitude'); colorbar; ylabel('Depth [m]');xlabel('Time index');
+    
+    %print -dpng surface_ea;
+
+end
\ No newline at end of file
diff --git a/moored_adcp_proc/hammfilter_nodec.m b/moored_adcp_proc/hammfilter_nodec.m
deleted file mode 100644
index e910275fe8a68405b9684ab595a4241a019c6182..0000000000000000000000000000000000000000
--- a/moored_adcp_proc/hammfilter_nodec.m
+++ /dev/null
@@ -1,29 +0,0 @@
-function uf = hammfilter_nodec(u,aver)
-
-% run a box filter of length aver (points) over time series u
-% replaces lowpass filter if toolbox license is missing
-% no decimation (subsampling)
-%
-% -------- R. Zantopp  24 Nov 2006 
-
-nn=length(u);
-na=floor(aver/2);
-% [w]=hamming(aver);
-w=hamm(aver);       % weights
-uu=reshape(u,1,length(u));
-
-for i=1:nn-aver
-    u1=uu(i:i+aver-1);
-    au=u1'.*w;
-    nau=sum(isnan(au));
-    if nau<=floor(aver/2) 
-      au_good=find(~isnan(au));
-      au2=meanmiss(au)/meanmiss(w(au_good));
-      u2(i+na)=au2;
-    else
-      u2(i+na)=nan;
-    end;
-end;
-u2(1:na)=nan;
-u2(nn-na:nn)=nan;
-uf=u2;
diff --git a/qualify_data.m b/qualify_data.m
new file mode 100644
index 0000000000000000000000000000000000000000..3c177fda89286bdc80b98e275978d125abff55c7
--- /dev/null
+++ b/qualify_data.m
@@ -0,0 +1,324 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Comparison ADCP Data Mooring with SADCP/LADCP/DVL
+% Autor: Pierre Rousselot
+% Date:  03/2019
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+clear all; close all; 
+addpath(genpath('C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\moored_adcp_proc'))
+addpath(genpath('C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\tools'))
+
+%% Load Mooring data
+lat_moor  = 0.0025;
+lon_moor  = -9.8858;
+Moorfile  = 'C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\FR29\0N10W_15258_instr_01.mat';
+Moorfile2 = 'C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\FR29\0N10W_15258_instr_01_int_filt.mat';
+%Moorfile  = 'C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\FR29\0N10W_15258_instr_01_int_filt_sub.mat';
+
+%FR29
+OS38file  = 'Z:\PIRATA\PIRATA-FR29\data-final\SADCP\OS38\data\LTA\PIRATA-FR29-OS38_LTA_osite_fhv1_final.nc';
+OS150file = 'Z:\PIRATA\PIRATA-FR29\data-final\SADCP\OS150\data\LTA\PIRATA-FR29-OS150_LTA_osite_fhv1_final.nc';
+DVLfile   = 'Z:\PIRATA\PIRATA-FR29\data-final\SADCP\DVL600\data\LTA\PIRATA-FR29-DVL600_LTA_osite_fhv1_final.nc';
+LADCPfil1 = 'C:\Users\proussel\Documents\outils\ADCP\ladcp\PIRATA-FR29\profiles\FR29_003.mat';
+LADCPfil2 = 'C:\Users\proussel\Documents\outils\ADCP\ladcp\PIRATA-FR29\profiles\FR29_045.mat';
+
+%FR27
+OS38file  = 'I:\pirata\pirata-data\adcp\sadcp_cascade\PIRATAFR27\38kHz\fr27_fhv1_38kHz.nc';
+OS150file = 'I:\pirata\pirata-data\adcp\sadcp_cascade\PIRATAFR27\150kHz\fr27_fhv1_150kHz.nc';
+LADCPfil1 = 'Z:\PIRATA\PIRATA-FR27\data-processing\LADCP\v10.16.2\PIRATA-FR27\profiles\fr27026.mat';
+LADCPfil2 = 'Z:\PIRATA\PIRATA-FR27\data-processing\LADCP\v10.16.2\PIRATA-FR27\profiles\fr27048.mat';
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Load dataset
+%% Load mooring Data
+load(Moorfile)
+if contains(Moorfile, 'int_filt_sub')
+    u_mooring     = data.uintfilt;
+    v_mooring     = data.vintfilt;
+    t_mooring     = data.inttim;
+    z_mooring     = data.Z;
+elseif contains(Moorfile, 'int_filt')
+    u_mooring     = uifilt;
+    v_mooring     = vifilt;
+    t_mooring     = data.time;
+    z_mooring     = data.Z;
+else
+    u_mooring     = data.u;
+    v_mooring     = data.v;  
+    t_mooring     = data.time;
+    z_mooring     = data.z_bins;
+end
+
+load(Moorfile2)
+u_mooring2     = uifilt;
+v_mooring2     = vifilt;
+t_mooring2     = data.time;
+z_mooring2     = data.Z;
+    
+%% Load DVL Data
+dvl.u    = ncread(DVLfile, 'UVEL_ADCP');
+dvl.v    = ncread(DVLfile, 'VVEL_ADCP');
+dvl.t    = ncread(DVLfile, 'JULD');
+dvl.d    = ncread(DVLfile, 'DEPH');
+dvl.lat  = ncread(DVLfile, 'LATITUDE'); 
+dvl.lon  = ncread(DVLfile, 'LONGITUDE');
+
+%% Load OS150 Data
+os.u     = ncread(OS150file, 'UVEL_ADCP');
+os.v     = ncread(OS150file, 'VVEL_ADCP');
+os.t     = ncread(OS150file, 'JULD');
+os.d     = ncread(OS150file, 'DEPH');
+os.lat   = ncread(OS150file, 'LATITUDE'); 
+os.lon   = ncread(OS150file, 'LONGITUDE');
+
+%% Load OS38 Data
+os38.u   = ncread(OS38file, 'UVEL_ADCP');
+os38.v   = ncread(OS38file, 'VVEL_ADCP');
+os38.t   = ncread(OS38file, 'JULD');
+os38.d   = ncread(OS38file, 'DEPH');
+os38.lat = ncread(OS38file, 'LATITUDE'); 
+os38.lon = ncread(OS38file, 'LONGITUDE');
+
+%% Load LADCP Data
+load(LADCPfil1)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Research Date - reference LADCP
+h_mooring     = datenum(julian(t_mooring(:)));
+
+h_ladcp       = datenum(dr.date);
+[~,plt_m]     = min(abs((h_mooring)-h_ladcp));
+
+h_os          = os.t + datenum('1950-01-01');
+[~,plt_os]    = min(abs((h_os)-h_ladcp));
+
+h_os38        = os38.t + datenum('1950-01-01');
+[~,plt_os38]  = min(abs((h_os38)-h_ladcp));
+
+h_dvl         = dvl.t + datenum('1950-01-01');
+[~,plt_dvl]   = min(abs((h_dvl)-h_ladcp));
+
+if contains(Moorfile, 'int_filt')
+    z_mooring_ext = z_mooring;
+else
+    z_mooring_ext = z_mooring(:, plt_m);
+end
+z_mooring_ext2 = z_mooring2;
+
+%% Calc distance
+d_station     = dist(lat_moor,lon_moor,dr.lat,dr.lon);
+d_station     = d_station * 0.539957;
+
+%% Plot Before
+% plt_dvl = plt_dvl + 1;
+% plt_os  = plt_os-1;
+
+figure
+subplot(1,2,1)
+plot(dr.u, -dr.z, '--g')
+hold on; 
+plot(os.u(:,plt_os), os.d,'r')
+plot(os38.u(:,plt_os38), os38.d,'c')
+if contains(LADCPfil1, 'FR29')
+    plot(dvl.u(:,plt_dvl), dvl.d,'k')
+end
+plot(u_mooring(:,plt_m), -z_mooring_ext,'LineWidth',2,'Color','b')
+plot(u_mooring2(:,plt_m), -z_mooring_ext2,'--','LineWidth',1.5,'Color','b')
+
+hold off
+xlabel('U [m.s^{-1}]')
+ylabel('Depth [m]')
+grid on
+ylim([-300 0])
+date_dvl   = (h_dvl(plt_dvl)-h_mooring(plt_m))*(24*60);
+date_os    = (h_os(plt_os)-h_mooring(plt_m))*(24*60);
+date_os38  = (h_os38(plt_os38)-h_mooring(plt_m))*(24*60);
+date_ladcp = (h_ladcp-h_mooring(plt_m))*(24*60);
+if date_os<0
+    if contains(LADCPfil1, 'FR29')
+        legend(['LADCP / ' num2str(round(date_ladcp)) 'min'], ['OS150 / ' num2str(round(date_os)) 'min'], ['OS38 / ' num2str(round(date_os38)) 'min'], ['DVL / ' num2str(round(date_dvl)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    else
+        legend(['LADCP / ' num2str(round(date_ladcp)) 'min'], ['OS150 / ' num2str(round(date_os)) 'min'], ['OS38 / ' num2str(round(date_os38)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    end    
+else
+    if contains(LADCPfil1, 'FR29')
+        legend(['LADCP / +' num2str(round(date_ladcp)) 'min'], ['OS150 / +' num2str(round(date_os)) 'min'], ['OS38 / +' num2str(round(date_os38)) 'min'], ['DVL / +' num2str(round(date_dvl)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    else
+        legend(['LADCP / +' num2str(round(date_ladcp)) 'min'], ['OS150 / +' num2str(round(date_os)) 'min'], ['OS38 / +' num2str(round(date_os38)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    end
+end
+title(['ADCP Mooring measurement vs other current measurement sources [distance = ' num2str(round(d_station*10)/10) 'NM]'])
+
+subplot(1,2,2)
+plot(dr.v, -dr.z, '--g')
+hold on; 
+plot(os.v(:,plt_os), os.d,'r')
+plot(os38.v(:,plt_os38), os38.d,'c')
+if contains(LADCPfil1, 'FR29')
+    plot(dvl.v(:,plt_dvl), dvl.d,'k')
+end
+plot(v_mooring(:,plt_m), -z_mooring_ext,'LineWidth',2,'Color','b')
+plot(v_mooring2(:,plt_m), -z_mooring_ext2,'--','LineWidth',1.5,'Color','b')
+hold off
+xlabel('V [m.s^{-1}]')
+ylabel('Depth [m]')
+grid on
+ylim([-300 0])
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Load LADCP Data
+load(LADCPfil2)
+
+%% Research Date
+h_ladcp     = datenum(dr.date);
+[~,plt_m]   = min(abs((h_mooring)-h_ladcp));
+
+[~,plt_os]  = min(abs((h_os)-h_ladcp));
+
+[~,plt_os38]  = min(abs((h_os38)-h_ladcp));
+
+[~,plt_dvl] = min(abs((h_dvl)-h_ladcp));
+
+if contains(Moorfile, 'int_filt')
+    z_mooring_ext = z_mooring;
+else
+    z_mooring_ext = z_mooring(:, plt_m);
+end
+
+%% Calc distance
+d_station     = dist(lat_moor,lon_moor,dr.lat,dr.lon);
+d_station     = d_station * 0.539957;
+
+%% Plot After
+figure
+subplot(1,2,1)
+plot(dr.u, -dr.z, '--g')
+hold on; 
+plot(os.u(:,plt_os), os.d,'r')
+plot(os38.u(:,plt_os38), os38.d,'c')
+if contains(LADCPfil1, 'FR29')
+    plot(dvl.u(:,plt_dvl), dvl.d,'k')
+end
+plot(u_mooring(:,plt_m), -z_mooring_ext,'LineWidth',2,'Color','b')   %data.Z(:,plt_m)
+plot(u_mooring2(:,plt_m), -z_mooring_ext2,'--','LineWidth',1.5,'Color','b')
+hold off
+xlabel('U [m.s^{-1}]')
+ylabel('Depth [m]')
+grid on
+ylim([-300 0])
+if contains(LADCPfil1, 'FR29')
+    legend(['LADCP / ' datestr(h_ladcp)], ['OS150 / ' datestr(h_os(plt_os))], ['OS38 / ' datestr(h_os38(plt_os38))], ['DVL / ' datestr(h_dvl(plt_dvl))], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+else
+    legend(['LADCP / ' datestr(h_ladcp)], ['OS150 / ' datestr(h_os(plt_os))], ['OS38 / ' datestr(h_os38(plt_os38))], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')    
+end 
+date_dvl   = (h_dvl(plt_dvl)-h_mooring(plt_m))*(24*60);
+date_os    = (h_os(plt_os)-h_mooring(plt_m))*(24*60);
+date_os38  = (h_os38(plt_os38)-h_mooring(plt_m))*(24*60);
+date_ladcp = (h_ladcp-h_mooring(plt_m))*(24*60);
+if date_os<0
+    if contains(LADCPfil1, 'FR29')
+        legend(['LADCP / ' num2str(round(date_ladcp)) 'min'], ['OS150 / ' num2str(round(date_os)) 'min'], ['OS38 / ' num2str(round(date_os38)) 'min'], ['DVL / ' num2str(round(date_dvl)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    else
+        legend(['LADCP / ' num2str(round(date_ladcp)) 'min'], ['OS150 / ' num2str(round(date_os)) 'min'], ['OS38 / ' num2str(round(date_os38)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    end    
+else
+    if contains(LADCPfil1, 'FR29')
+        legend(['LADCP / +' num2str(round(date_ladcp)) 'min'], ['OS150 / +' num2str(round(date_os)) 'min'], ['OS38 / +' num2str(round(date_os38)) 'min'], ['DVL / +' num2str(round(date_dvl)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    else
+        legend(['LADCP / +' num2str(round(date_ladcp)) 'min'], ['OS150 / +' num2str(round(date_os)) 'min'], ['OS38 / +' num2str(round(date_os38)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    end
+end
+title(['ADCP Mooring measurement vs other current measurement sources [distance = ' num2str(round(d_station*10)/10) 'NM]'])
+
+subplot(1,2,2)
+plot(dr.v, -dr.z, '--g')
+hold on; 
+plot(os.v(:,plt_os), os.d,'r')
+plot(os38.v(:,plt_os38), os38.d,'c')
+if contains(LADCPfil1, 'FR29')
+    plot(dvl.v(:,plt_dvl), dvl.d,'k')
+end
+plot(v_mooring(:,plt_m), -z_mooring_ext,'LineWidth',2,'Color','b')
+plot(v_mooring2(:,plt_m), -z_mooring_ext2,'--','LineWidth',1.5,'Color','b')
+hold off
+xlabel('V [m.s^{-1}]')
+ylabel('Depth [m]')
+grid on
+ylim([-300 0])
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Research nearest position
+d_os38          = dist(lat_moor,lon_moor,os38.lat,os38.lon);
+[d_mo,plt_os38] = min(d_os38);
+d_mo            = d_mo * 0.539957;
+
+d_os150         = dist(lat_moor,lon_moor,os.lat,os.lon);
+[~,plt_os]      = min(d_os150);
+
+d_dvl           = dist(lat_moor,lon_moor,dvl.lat,dvl.lon);
+[~,plt_dvl]     = min(d_dvl);
+
+[~,plt_m]       = min(abs(h_os38(plt_os38)-h_mooring));
+%plt_m = plt_m-3;
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% En dur
+% plt_os38 = plt_os38 -20;
+% plt_os = plt_os -20;
+% plt_dvl = plt_dvl -20;
+% d_mo          = dist(lat_moor,lon_moor,os38.lat(plt_os38+1),os38.lon(plt_os38+1));
+% d_mo            = d_mo * 0.539957;
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+if contains(Moorfile, 'int_filt')
+    z_mooring_ext = z_mooring;
+else
+    z_mooring_ext = z_mooring(:, plt_m);
+end
+
+%% Plot 
+figure
+subplot(1,2,1)
+hold on; 
+plot(os.u(:,plt_os), os.d,'r')
+plot(os38.u(:,plt_os38), os38.d,'c')
+if contains(LADCPfil1, 'FR29')
+    plot(dvl.u(:,plt_dvl), dvl.d,'k')
+end
+plot(u_mooring(:,plt_m), -z_mooring_ext,'LineWidth',2,'Color','b')
+plot(u_mooring2(:,plt_m), -z_mooring_ext2,'--','LineWidth',1.5,'Color','b')
+hold off
+xlabel('U [m.s^{-1}]')
+ylabel('Depth [m]')
+grid on
+ylim([-300 0])
+date_dvl  = (h_dvl(plt_dvl)-h_mooring(plt_m))*(24*60);
+date_os   = (h_os(plt_os)-h_mooring(plt_m))*(24*60);
+date_os38 = (h_os38(plt_os38)-h_mooring(plt_m))*(24*60);
+if date_os<0
+    if contains(LADCPfil1, 'FR29')
+        legend(['OS150 / ' num2str(round(date_os)) 'min'], ['OS38 / ' num2str(round(date_os38)) 'min'], ['DVL / ' num2str(round(date_dvl)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    else
+        legend(['OS150 / ' num2str(round(date_os)) 'min'], ['OS38 / ' num2str(round(date_os38)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    end    
+else
+    if contains(LADCPfil1, 'FR29')
+        legend(['OS150 / +' num2str(round(date_os)) 'min'], ['OS38 / +' num2str(round(date_os38)) 'min'], ['DVL / +' num2str(round(date_dvl)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    else
+        legend(['OS150 / +' num2str(round(date_os)) 'min'], ['OS38 / +' num2str(round(date_os38)) 'min'], ['ADCP Mooring / ' datestr(h_mooring(plt_m))], 'ADCP Mooring filtered', 'location', 'southeast')
+    end
+end
+title(['ADCP Mooring measurement vs other current measurement sources [distance = ' num2str(round(d_mo*10)/10) 'NM]'])
+
+subplot(1,2,2)
+hold on; 
+plot(os.v(:,plt_os), os.d,'r')
+plot(os38.v(:,plt_os38), os38.d,'c')
+if contains(LADCPfil1, 'FR29')
+    plot(dvl.v(:,plt_dvl), dvl.d,'k')
+end
+plot(v_mooring(:,plt_m), -z_mooring_ext,'LineWidth',2,'Color','b')
+plot(v_mooring2(:,plt_m), -z_mooring_ext2,'--','LineWidth',1.5,'Color','b')
+hold off
+xlabel('V [m.s^{-1}]')
+ylabel('Depth [m]')
+grid on
+ylim([-300 0])
diff --git a/template_get_adcp_data.m b/template_get_adcp_data.m
index d68e9466925c6e828540fd855d09297401dca0b4..cd96120c53af50726d788df50f48d6f41feaba1d 100644
--- a/template_get_adcp_data.m
+++ b/template_get_adcp_data.m
@@ -2,12 +2,15 @@
 % template_get_adcp_data.m
 % -------------------------------
 % Author : Jérémie HABASQUE - IRD
+% Modification: Pierre ROUSSELOT - IRD
 % -------------------------------
 % INPUTS:
 % - binary raw file with .000 extension
 % OUTPUTS:
 % - U and V fields interpolated on a regulard grid, filtered and subsampled
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+addpath('C:\Users\proussel\Documents\outils\TOOLS\climato\nansuite')
+addpath(genpath('C:\Users\proussel\Documents\outils\ADCP\ADCP_mooring_data_processing\tools'))
 
 % First part --------------------------------------------------------------------------------------------------------------------
 close all
@@ -21,37 +24,37 @@ addpath('.\backscatter'); % (Optionnel) / ou par exemple ('C:\Users\IRD_US_IMAGO
 
 % Location rawfile
 fpath = '';
-rawfile='.\FR26_000.000'; % binary file with .000 extension
+rawfile='.\FR27_000.000'; % binary file with .000 extension
  
 % Directory for outputs
-fpath_output = '.\FR28\';
+fpath_output = '.\FR29_bis\';
 
 % Cruise/mooring info
-cruise.name = 'PIRATA-FR28';
-mooring.name='0N0E'; % 0N10W par exemple
-mooring.lat=00+00/60; %latitude en degrés décimaux
-mooring.lon=00+00/60; %longitude en degrés décimaux
-clock_drift = 253/3600; % convert into hrs
+cruise.name  = 'PIRATA-FR29';
+mooring.name = '0N10W'; % 0N10W par exemple
+mooring.lat  = 00+00.15/60; %latitude en degrés décimaux
+mooring.lon  = -09-53.15/60; %longitude en degrés décimaux
+clock_drift  = 208/3600; % convert into hrs
 
 % ADCP info
-adcp.sn=22545;
-adcp.type='150 khz Quartermaster'; % Type : ‘Quartermaster’, ‘longranger’
-adcp.direction='up';        % upward-looking 'up', downward-looking 'dn'
-adcp.instr_depth=300;       % nominal instrument depth
-instr=1;                    % this is just for name convention and sorting of all mooring instruments
+adcp.sn          = 15258;
+adcp.type        = '150 khz Quartermaster'; % Type : ‘Quartermaster’, ‘longranger’
+adcp.direction   = 'up';        % upward-looking 'up', downward-looking 'dn'
+adcp.instr_depth = 300;       % nominal instrument depth
+instr            = 1;                    % this is just for name convention and sorting of all mooring instruments
 
 % If ADCP was not set up to correct for magnetic deviation internally
 % ("EA0" code in configuration file), use http://www.ngdc.noaa.gov/geomag-web/#declination
 % Magnetic deviation: Mean of deviations at time of deployment and time of recovery 
 
 % Magnetic deviation values
-magnetic_deviation_ini = -5.27;
-magnetic_deviation_end = -4.99;
-rot=(magnetic_deviation_ini+magnetic_deviation_end)/2;  
+magnetic_deviation_ini = -9;
+magnetic_deviation_end = -8.77;
+rot                    = (magnetic_deviation_ini+magnetic_deviation_end)/2;  
 
 % Read rawfile
 fprintf('Read %s\n', rawfile);
-raw=read_os3(rawfile,'all');
+raw            = read_os3(rawfile,'all');
 
 % Correct clock drift
 time0          = julian(raw.juliandate);
@@ -59,16 +62,15 @@ clockd         = linspace(0, clock_drift, length(time0));
 raw.juliandate = raw.juliandate - clockd / 24;                 % P. Rousselot - change clock drift /24
 
 % Magnetic devitation over time %P . Rousselot
-mag_dev = linspace(magnetic_deviation_ini, magnetic_deviation_end, length(time0)); 
-
+mag_dev         = linspace(magnetic_deviation_ini, magnetic_deviation_end, length(time0)); 
 
 figure;plot(raw.pressure);
 detrend_sdata = detrend(raw.pressure);
-trend = raw.pressure - detrend_sdata;
+trend         = raw.pressure - detrend_sdata;
 hold on
 plot(trend, 'r--')
 hold off
-title('Pressure sensor');ylabel('Depth [m]');xlabel('Time index');grid on; 
+title('Pressure sensor');ylabel('Pressure [dbar]');xlabel('Time index');grid on; 
 saveas(gcf,[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Pressure_sensor'],'fig')
 
 figure;plot(raw.temperature);
@@ -76,11 +78,11 @@ title('Temperature sensor');ylabel('Temperature [
 % Second part --------------------------------------------------------------------------------------------------------------------
 
 % Determine first and last indiced when instrument was at depth (you can do this by plotting 'raw.pressure' for example           
-first = 11; 
-last = 17161; 
+first = 10; 
+last  = 17487; 
 
 % amplitude of the bins / Correction ADCP's depth
-ea = squeeze(mean(raw.amp(:,:,first:last),2));   
+ea    = squeeze(mean(raw.amp(:,:,first:last),2));   
 figure; colormap jet; pcolor(ea); shading flat; title('Amplitude of the bins'); colorbar;
 ylabel('Bins');xlabel('Time index'); 
 saveas(gcf,[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Amplitude_bins'],'fig')
@@ -88,55 +90,70 @@ saveas(gcf,[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(ins
 % Third part --------------------------------------------------------------------------------------------------------------------
 
 % If upward looking: range of surface bins used for instrument depth correction below!
-sbins= 29:34;%30:35; % here a range of bins is given which cover the surface reflection
+sbins      = 32:39;%30:35; % here a range of bins is given which cover the surface reflection
 
 % Exclude data with percent good below prct_good
-prct_good = 20;
+prct_good  = 20;
 
 %% Read data
-freq = raw.config.sysconfig.frequency;
-
-u2 = squeeze(raw.vel(:,1,first:last));
-v2 = squeeze(raw.vel(:,2,first:last));
-w = squeeze(raw.vel(:,3,first:last));
-vel_err = squeeze(raw.vel(:,4,first:last));  % the difference in vertical velocity between the two pairs of transducers 
-time = raw.juliandate(first:last);
-ang = [raw.pitch(first:last) raw.roll(first:last) raw.heading(first:last)]; 
+freq       = raw.config.sysconfig.frequency;
+
+u2         = squeeze(raw.vel(:,1,first:last));
+v2         = squeeze(raw.vel(:,2,first:last));
+w          = squeeze(raw.vel(:,3,first:last));
+vel_err    = squeeze(raw.vel(:,4,first:last));  % the difference in vertical velocity between the two pairs of transducers 
+time       = raw.juliandate(first:last);
+ang        = [raw.pitch(first:last) raw.roll(first:last) raw.heading(first:last)]; 
 soundspeed = raw.soundspeed(first:last);
-T = raw.temperature(first:last);
-press = raw.pressure(first:last);
-mag_dev = mag_dev(first:last);
+T          = raw.temperature(first:last);
+press      = raw.pressure(first:last);
+mag_dev    = mag_dev(first:last);
 
-nbin = raw.config.ncells;  % number of bins
-bin  = 1:nbin;
-blen = raw.config.cell;    % bin length
-blnk = raw.config.blank;   % blank distance after transmit
+nbin       = raw.config.ncells;  % number of bins
+bin        = 1:nbin;
+blen       = raw.config.cell;    % bin length
+blnk       = raw.config.blank;   % blank distance after transmit
 
-dt=(time(2)-time(1))*24;   % Sampling interval in hours
+dt         = (time(2)-time(1))*24;   % Sampling interval in hours
 
 for ii = 1 : length(mag_dev)
-    [u(:,ii),v(:,ii)]=uvrot(u2(:,ii), v2(:,ii), -mag_dev(ii));   % Correction of magnetic deviation (over time-P. Rousselot)
+    [u(:,ii),v(:,ii)] = uvrot(u2(:,ii), v2(:,ii), -mag_dev(ii));   % Correction of magnetic deviation (over time-P. Rousselot)
 end
 
-pg = squeeze(raw.pg(:,4,first:last));  % percent good
-
-igap=find(pg<prct_good);           % Exclude data with percent good below prct_good
-u(igap)=nan;
-v(igap)=nan;
-w(igap)=nan;
-vel_err(igap)=nan;
+pg            = squeeze(raw.pg(:,4,first:last));  % percent good
+
+igap          = find(pg<prct_good);           % Exclude data with percent good below prct_good
+u(igap)       = NaN;
+v(igap)       = NaN;
+w(igap)       = NaN;
+vel_err(igap) = NaN;
+
+
+%% Control attitude
+figure
+subplot(3,1,1)
+plot(ang(:,1))
+ylabel('Pitch [°]')
+subplot(3,1,2)
+plot(ang(:,2))
+ylabel('Roll [°]')
+subplot(3,1,3)
+plot(ang(:,3))
+ylabel('Heading [°]')
+axis([0 18000 0 360])
+xlabel('Time Index')
 
 %% Calculate depth of each bin:
-dpt = sw_dpth(press,mooring.lat)';  % convert pressure to depth, press needs to have dimension (n x 1)
-dpt1 = repmat(dpt,nbin,1);
+dpt    = sw_dpth(press,mooring.lat)';  % convert pressure to depth, press needs to have dimension (n x 1)
+dpt1   = repmat(dpt,nbin,1);
 binmat = repmat((1:nbin)',1,length(dpt1));
 
 % If ADCP is upward-looking a depth correction can be inferred from the
 % surface reflection, which is done in adcp_surface_fit
 if strcmp(adcp.direction,'up')  
-    [z,dpt1,offset,xnull]=adcp_surface_fit(-dpt,ea,sbins,blen,blnk,nbin);
+    [z,dpt1,offset,xnull] = adcp_surface_fit(dpt,ea,sbins,blen,blnk,nbin);
 elseif strcmp(adcp.direction,'dn')
-    z = dpt1+(binmat-0.5)*blen+blnk;
+    z                     = dpt1+(binmat-0.5)*blen+blnk;
 else
     error('Bin depth calculation: unknown direction!');
 end
@@ -149,30 +166,29 @@ saveas(figure(3),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2s
 u1=u; v1=v; w1=w; vel_err1=vel_err; ea1=ea;
 
 if strcmp(adcp.direction,'up')
-    for i=1:length(time)
-        sz_dpt(i)=adcp_shadowzone(dpt(i),raw.config.sysconfig.angle); % depending on the instrument depth and the beam angle the shadow zone, i.e. the depth below the surface which is contaminated by the surface reflection is determined
-
-        iz(i)=find(z(:,i)<-sz_dpt(i),1,'first')-1;
-        sbin(i)=bin(iz(i));
+    for i = 1:length(time)
+        sz_dpt(i) = adcp_shadowzone(dpt(i),raw.config.sysconfig.angle); % depending on the instrument depth and the beam angle the shadow zone, i.e. the depth below the surface which is contaminated by the surface reflection is determined
+        iz(i)     = find(z(:,i)>sz_dpt(i),1,'last');
+        sbin(i)   = bin(iz(i));
         
         %sbin(i)=30; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
         % here a manual criterion should be hard-coded if
         % adcp_check_surface (below) shows bad velocities close to the
         % surface
 
-        fz(i)=z(iz(i),i);    
+        fz(i)      = z(iz(i),i);    
     end
 
-    for i=1:length(time) 
-        u1(sbin(i)+1:end,i)=nan;
-        v1(sbin(i)+1:end,i)=nan;
-        w1(sbin(i)+1:end,i)=nan;
-        vel_err1(sbin(i)+1:end,i)=nan;
-        ea1(sbin(i)+1:end,i)=nan;
+    for i = 1:length(time) 
+        u1(sbin(i)+1:end,i)       = NaN;
+        v1(sbin(i)+1:end,i)       = NaN;
+        w1(sbin(i)+1:end,i)       = NaN;
+        vel_err1(sbin(i)+1:end,i) = NaN;
+        ea1(sbin(i)+1:end,i)      = NaN;
     end
 
     if 1
-        bins=nmedian(sbin)-4:nmedian(sbin)+2;
+        bins = nmedian(sbin)-4:nmedian(sbin)+2;
         adcp_check_surface(bins,u,u1,v,v1,time,bin,z); 
         % here the closest bins below the surface are plotted that are supposed to have good velocities, if there are still bad velocities a manual criterion needs to be found
     end
@@ -182,30 +198,33 @@ saveas(figure(4),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2s
 %% SAVE DATA
 % More meta information
 %adcp.comment='';
-adcp.config=raw.config;
-adcp.z_offset=offset;
-adcp.ang=ang;
-adcp.mag_dev=rot;
+adcp.config   = raw.config;
+adcp.z_offset = offset;
+adcp.ang      = ang;
+adcp.mag_dev  = rot;
 
 % Data structure
-data.u=u1;
-data.v=v1;
-data.w=w1;
-data.e=vel_err1;
-data.ea=ea1;
-data.pg=pg;
-data.time=time;
-data.z_bins=z;
-data.depth=dpt;
-data.temp=T;
-data.sspd = soundspeed;
+data.u      = u1;
+data.v      = v1;
+data.w      = w1;
+data.e      = vel_err1;
+data.ea     = ea1;
+data.pg     = pg;
+data.time   = time;
+data.z_bins = z;
+data.depth  = dpt;
+data.temp   = T;
+data.sspd   = soundspeed;
+data.lat    = mooring.lat;
+data.lon    = mooring.lon;
 
 % Save
-save([fpath_output, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '.mat'],'adcp','mooring','data','raw');
+save([fpath_output, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '.mat'],'adcp','mooring','data','raw','-v7.3');
 
 %% Interpolate data on a regular vertical grid
-Z = fliplr(blen/2:blen:max(z(:))+blen);
-Zmax = max(Z);
+Z      = fliplr(blen/2:blen:max(z(:))+blen);
+Zmax   = max(Z);
+data.Z = Z;
 
 u_interp = NaN(length(time),length(Z));
 v_interp = NaN(length(time),length(Z));
@@ -213,23 +232,25 @@ for i=1:length(time)
     % indice correspondant sur la grille finale Z
     ind = round((Zmax-z(1,i))/blen)+1;
     % filling the grid
-    npts = min([length(Z)-ind+1 length(bin)]);
+    npts = min([length(Z)+ind+1 length(bin)]);
     u_interp(i,ind:ind+npts-1) = u1(1:npts,i);
     v_interp(i,ind:ind+npts-1) = v1(1:npts,i);
 end
   
 %% Horizontal interpolation, filtering and subsampling
-[uintfilt,vintfilt,inttim] = adcp_filt_sub(data,u_interp',v_interp',1:length(Z),40);
+[uintfilt,vintfilt,uifilt,vifilt,inttim,utid_baro,vtid_baro] = adcp_filt_sub(data,u_interp',v_interp',1:length(Z),40);
 saveas(figure(5),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','data_raw_filt_subsampled_1'],'fig')
 saveas(figure(6),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','data_raw_filt_subsampled_2'],'fig')
 
+save([fpath_output, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '_int_filt.mat'],'uifilt','vifilt','data');
+
 % Save interpolated data
-bin_start = 1; % bin indice where good interpolated data for the whole dataset start
-bin_end   = length(Z);
-data.uintfilt=uintfilt(bin_start:bin_end,:);
-data.vintfilt=vintfilt(bin_start:bin_end,:);
-data.Z = Z(bin_start:bin_end);
-data.inttim = inttim;
+bin_start     = 4; % bin indice where good interpolated data for the whole dataset start
+bin_end       = length(Z);
+data.uintfilt = uintfilt(bin_start:bin_end,:);
+data.vintfilt = vintfilt(bin_start:bin_end,:);
+data.Z        = Z(bin_start:bin_end);
+data.inttim   = inttim;
 save([fpath_output, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '_int_filt_sub.mat'],'adcp','mooring','data','raw');
 
 %% Figure
@@ -257,7 +278,7 @@ subplot(2,1,2);
 [C,h] = contourf(inttim,Z(bin_start:bin_end),vintfilt(bin_start:bin_end,:),niv_v); 
 set(h,'LineColor','none');
 caxis(niv_v([1 end]));
-h=colorbar;
+h     = colorbar;
 ylabel(h,'V [m s^-^1]');
 set(gca,'ydir', 'reverse');
 ylabel('Depth (m)');
@@ -268,33 +289,67 @@ title({[mooring.name, ' - MERIDIONAL VELOCITY - RDI ',num2str(freq),' kHz']});
 
 graph_name = [fpath_output, mooring.name '_U_V_int_filt_sub'];
 set(hf,'Units','Inches');
-pos = get(hf,'Position');
+pos        = get(hf,'Position');
 set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)]);
 print(hf,graph_name,'-dpdf','-r300');
- 
+
+% %% Plot tide
+% hf=figure('position', [0, 0, 1400, 1000]);
+% niv_tide = (-5:0.2:5);
+% utid_baro = utid_baro * 100;
+% vtid_baro = vtid_baro * 100;
+% %u
+% subplot(2,1,1);
+% colormap jet
+% [C,h] = contourf(inttim,Z(bin_start:bin_end),utid_baro(bin_start:bin_end,:),niv_tide); 
+% set(h,'LineColor','none');
+% caxis(niv_tide([1 end]));
+% h=colorbar;
+% ylabel(h,'U [cm s^-^1]');
+% set(gca,'ydir', 'reverse');
+% ylabel('Depth (m)');
+% ylim([0,adcp.instr_depth]);
+% %change figure label in HH:MM
+% gregtick;
+% title({[mooring.name, ' - ZONAL TIDE VELOCITY - RDI ',num2str(freq),' kHz']});
+% 
+% %v
+% subplot(2,1,2);
+% [C,h] = contourf(inttim,Z(bin_start:bin_end),vtid_baro(bin_start:bin_end,:),niv_tide); 
+% set(h,'LineColor','none');
+% caxis(niv_tide([1 end]));
+% h     = colorbar;
+% ylabel(h,'V [cm s^-^1]');
+% set(gca,'ydir', 'reverse');
+% ylabel('Depth (m)');
+% ylim([0,adcp.instr_depth]);
+% %change figure label in HH:MM
+% gregtick;
+% title({[mooring.name, ' - MERIDIONAL TIDE VELOCITY - RDI ',num2str(freq),' kHz']});
+
 %%  Write netcdf file           
 [yr_start , ~, ~] = gregorian(inttim(1));
-[yr_end,  ~, ~] = gregorian(inttim(length(inttim)));
+[yr_end,  ~, ~]   = gregorian(inttim(length(inttim)));
 
-ncid=netcdf.create([fpath_output,'ADCP_',mooring.name,'_',num2str(yr_start),'_',num2str(yr_end),'_1d.nc'],'NC_WRITE');
+ncid     = netcdf.create([fpath_output,'ADCP_',mooring.name,'_',num2str(yr_start),'_',num2str(yr_end),'_1d.nc'],'NC_WRITE');
  
 %create dimension
-dimidt = netcdf.defDim(ncid,'time',length(inttim));
-dimidz = netcdf.defDim(ncid,'depth',length(Z));
+dimidt   = netcdf.defDim(ncid,'time',length(inttim));
+dimidz   = netcdf.defDim(ncid,'depth',length(Z));
 %Define IDs for the dimension variables (pressure,time,latitude,...)
-time_ID=netcdf.defVar(ncid,'time','double',dimidt);
-depth_ID=netcdf.defVar(ncid,'depth','double',dimidz);
+time_ID  = netcdf.defVar(ncid,'time','double',dimidt);
+depth_ID = netcdf.defVar(ncid,'depth','double',dimidz);
 %Define the main variable ()
-u_ID = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
-v_ID = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
+u_ID     = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
+v_ID     = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
 %We are done defining the NetCdf
 netcdf.endDef(ncid);
 %Then store the dimension variables in
 netcdf.putVar(ncid,time_ID,inttim);
 netcdf.putVar(ncid,depth_ID,Z);  
 %Then store my main variable
-netcdf.putVar(ncid,u_ID,uintfilt);
-netcdf.putVar(ncid,v_ID,vintfilt);
+netcdf.putVar(ncid,u_ID,uintfilt');
+netcdf.putVar(ncid,v_ID,vintfilt');
 %We're done, close the netcdf
 netcdf.close(ncid);
 
diff --git a/template_get_adcp_data_up_and_down.m b/template_get_adcp_data_up_and_down.m
new file mode 100644
index 0000000000000000000000000000000000000000..60204467f6952117b74570fe19e2eed98d8945c2
--- /dev/null
+++ b/template_get_adcp_data_up_and_down.m
@@ -0,0 +1,463 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% template_get_adcp_data_up_and_down.m
+% -------------------------------
+% Author : Jérémie HABASQUE - IRD
+% -------------------------------
+% INPUTS:
+% - binary raw file with .000 extension
+% OUTPUTS:
+% - U and V fields interpolated on a regulard grid, filtered and subsampled
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% First part --------------------------------------------------------------------------------------------------------------------
+close all
+clear all
+ 
+%% META information:
+
+% Path
+addpath('C:\Workspace_Matlab\git\ADCP_mooring_data_processing\moored_adcp_proc');
+addpath('.\backscatter'); % Optionnel
+
+% Location rawfile
+fpath = '';
+rawfile='C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2014-2015 - kpo_1125\QM14911\14911000.000'; % binary file with .000 extension
+ 
+% Directory for outputs
+fpath_output = 'C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2014-2015 - kpo_1125\';
+
+% Cruise/mooring info
+cruise.name = '';
+mooring.name='23W0N';
+mooring.lat=00+00/60; %latitude en degrés décimaux
+mooring.lon=-23+00/60; %longitude en degrés décimaux
+
+% ADCP info
+adcp.sn=14911;
+adcp.type='150 khz Quartermaster'; % Type : ‘Quartermaster’, ‘longranger’
+adcp.direction='up';        % upward-looking 'up', downward-looking 'dn'
+adcp.instr_depth=214;       % nominal instrument depth
+instr=1;                    % this is just for name convention and sorting of all mooring instruments
+
+% If ADCP was not set up to correct for magnetic deviation internally
+% ("EA0" code in configuration file), use http://www.ngdc.noaa.gov/geomag-web/#declination
+% Magnetic deviation: Mean of deviations at time of deployment and time of recovery 
+
+% Magnetic deviation values
+magnetic_deviation_ini = 15.27;
+magnetic_deviation_end = 15.11;
+rot=-(magnetic_deviation_ini+magnetic_deviation_end)/2;  
+
+% Read rawfile
+fprintf('Read %s\n', rawfile);
+raw=read_os3(rawfile,'all');
+figure;plot(raw.pressure);set(gca,'ydir','reverse');
+title('pressure sensor');ylabel('Depth(m)');xlabel('Time'); 
+saveas(gcf,[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Pressure_sensor'],'fig')
+
+% Second part --------------------------------------------------------------------------------------------------------------------
+
+% Determine first and last indiced when instrument was at depth (you can do this by plotting 'raw.pressure' for example           
+first = 44; 
+last = 12181; 
+
+% amplitude of the bins / Correction ADCP's depth
+ea = squeeze(mean(raw.amp(:,:,first:last),2));   
+figure; imagesc(ea);title('Amplitude of the bins'); colorbar;
+ylabel('Bins');xlabel('Time'); 
+saveas(gcf,[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Amplitude_bins'],'fig')
+
+% Third part --------------------------------------------------------------------------------------------------------------------
+
+% If upward looking: range of surface bins used for instrument depth correction below!
+sbins= 25:31; % here a range of bins is given which cover the surface reflection
+
+% Exclude data with percent good below prct_good
+prct_good = 20;
+
+%% Read data
+freq = raw.config.sysconfig.frequency;
+
+u2 = squeeze(raw.vel(:,1,first:last));
+v2 = squeeze(raw.vel(:,2,first:last));
+w = squeeze(raw.vel(:,3,first:last));
+vel_err = squeeze(raw.vel(:,4,first:last));  % the difference in vertical velocity between the two pairs of transducers 
+time = raw.juliandate(first:last);
+ang = [raw.pitch(first:last) raw.roll(first:last) raw.heading(first:last)]; 
+soundspeed = raw.soundspeed(first:last);
+T = raw.temperature(first:last);
+press = raw.pressure(first:last);
+
+nbin = raw.config.ncells;  % number of bins
+bin  = 1:nbin;
+blen = raw.config.cell;    % bin length
+blnk = raw.config.blank;   % blank distance after transmit
+
+dt=(time(2)-time(1))*24;   % Sampling interval in hours
+
+[u,v]=uvrot(u2,v2,-rot);   % Correction of magnetic deviation
+
+pg = squeeze(raw.pg(:,4,first:last));  % percent good
+
+igap=find(pg<prct_good);           % Exclude data with percent good below prct_good
+u(igap)=nan;
+v(igap)=nan;
+w(igap)=nan;
+vel_err(igap)=nan;
+
+%% Calculate depth of each bin:
+dpt = sw_dpth(press,mooring.lat)';  % convert pressure to depth, press needs to have dimension (n x 1)
+dpt1 = repmat(dpt,nbin,1);
+binmat = repmat((1:nbin)',1,length(dpt1));
+
+% If ADCP is upward-looking a depth correction can be inferred from the
+% surface reflection, which is done in adcp_surface_fit
+if strcmp(adcp.direction,'up')  
+    [z,dpt1,offset,xnull]=adcp_surface_fit(dpt,ea,sbins,blen,blnk,nbin);
+elseif strcmp(adcp.direction,'dn')
+    z = dpt1+(binmat-0.5)*blen+blnk;
+else
+    error('Bin depth calculation: unknown direction!');
+end
+
+saveas(figure(1),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Hist_diff_orig-depth_recon-depth'],'fig')
+saveas(figure(2),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Offset_depth'],'fig')
+saveas(figure(3),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Amplitude_bins_2'],'fig')
+
+%% Remove bad data if ADCP is looking upward
+u1=u; v1=v; w1=w; vel_err1=vel_err; ea1=ea;
+
+if strcmp(adcp.direction,'up')
+    for i=1:length(time)
+        sz_dpt(i)=adcp_shadowzone(dpt(i),raw.config.sysconfig.angle); % depending on the instrument depth and the beam angle the shadow zone, i.e. the depth below the surface which is contaminated by the surface reflection is determined
+
+        iz(i)=find(z(:,i)>sz_dpt(i),1,'last');
+        sbin(i)=bin(iz(i));
+        
+        % here a manual criterion should be hard-coded if
+        % adcp_check_surface (below) shows bad velocities close to the
+        % surface
+
+        fz(i)=z(iz(i),i);    
+    end
+
+    for i=1:length(time) 
+        u1(sbin(i)+1:end,i)=nan;
+        v1(sbin(i)+1:end,i)=nan;
+        w1(sbin(i)+1:end,i)=nan;
+        vel_err1(sbin(i)+1:end,i)=nan;
+        ea1(sbin(i)+1:end,i)=nan;
+    end
+
+    if 1
+        bins=nmedian(sbin)-4:nmedian(sbin)+2;
+        adcp_check_surface(bins,u,u1,v,v1,time,bin,z); 
+        % here the closest bins below the surface are plotted that are supposed to have good velocities, if there are still bad velocities a manual criterion needs to be found
+    end
+end
+saveas(figure(4),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','Meridional_zonal_velocity'],'fig')
+
+%% SAVE DATA
+% More meta information
+%adcp.comment='';
+adcp.config=raw.config;
+adcp.z_offset=offset;
+adcp.ang=ang;
+adcp.mag_dev=rot;
+
+% Data structure
+data.u=u1;
+data.v=v1;
+data.w=w1;
+data.e=vel_err1;
+data.ea=ea1;
+data.pg=pg;
+data.time=time;
+data.z_bins=z;
+data.depth=dpt;
+data.temp=T;
+data.sspd = soundspeed;
+
+% Save
+save([fpath_output, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '.mat'],'adcp','mooring','data','raw');
+
+%% Interpolate data on a regular vertical grid
+Z = fliplr(blen/2:blen:max(z(:))+blen);
+Zmax = max(Z);
+ind_up = [] ; npts_up = [];
+
+u_interp = NaN(length(time),length(Z));
+v_interp = NaN(length(time),length(Z));
+for i=1:length(time)
+    % indice correspondant sur la grille finale Z
+    ind = round((Zmax-z(1,i))/blen)+1;
+    % filling the grid
+    npts = min([length(Z)-ind+1 length(bin)]);
+    npts_up = [npts_up npts];
+    u_interp(i,ind:ind+npts-1) = u1(1:npts,i);
+    v_interp(i,ind:ind+npts-1) = v1(1:npts,i);
+end
+  
+%% Horizontal interpolation, filtering and subsampling
+[uintfilt,vintfilt,inttim] = adcp_filt_sub(data,u_interp',v_interp',1:length(Z),40);
+saveas(figure(5),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','data_raw_filt_subsampled_1'],'fig')
+saveas(figure(6),[fpath_output,mooring.name,'_',num2str(adcp.sn),'_instr_',num2str(instr),'_','data_raw_filt_subsampled_2'],'fig')
+
+% Save interpolated data
+bin_start = 3; % bin indice where good interpolated data for the whole dataset start
+data.uintfilt=uintfilt(bin_start:length(Z),:);
+data.vintfilt=vintfilt(bin_start:length(Z),:);
+data.Z = Z(bin_start:length(Z));
+data.inttim = inttim;
+data.npts_up= npts_up;
+save([fpath_output, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '_int_filt_sub.mat'],'adcp','mooring','data','raw');
+
+%% Figure
+niv_u = (-1.5:0.1:1.5);
+niv_v = (-0.5:0.1:0.5);
+
+hf=figure('position', [0, 0, 1400, 1000]);
+%u
+subplot(2,1,1);
+[C,h] = contourf(inttim,Z(bin_start:length(Z)),uintfilt(bin_start:length(Z),:),niv_u); 
+set(h,'LineColor','none');
+caxis(niv_u([1 end]));
+h=colorbar;
+ylabel(h,'U [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylabel('Depth (m)');
+ylim([0,adcp.instr_depth]);
+%change figure label in HH:MM
+gregtick;
+title({[mooring.name, ' - MERIDIONAL VELOCITY - RDI ',num2str(freq),' kHz']});
+
+%v
+subplot(2,1,2);
+[C,h] = contourf(inttim,Z(bin_start:length(Z)),vintfilt(bin_start:length(Z),:),niv_v); 
+set(h,'LineColor','none');
+caxis(niv_v([1 end]));
+h=colorbar;
+ylabel(h,'V [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylabel('Depth (m)');
+ylim([0,adcp.instr_depth]);
+%change figure label in HH:MM
+gregtick;
+title({[mooring.name, ' - ZONAL VELOCITY - RDI ',num2str(freq),' kHz']});
+
+graph_name = [fpath_output, mooring.name '_U_V_int_filt_sub'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)]);
+print(hf,graph_name,'-dpdf','-r300');
+ 
+%%  Write netcdf file           
+[yr_start , ~, ~] = gregorian(inttim(1));
+[yr_end,  ~, ~] = gregorian(inttim(length(inttim)));
+
+ncid=netcdf.create([fpath_output,'ADCP_',mooring.name,'_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '_',num2str(yr_start),'_',num2str(yr_end),'_1d.nc'],'NC_WRITE');
+ 
+%create dimension
+dimidt = netcdf.defDim(ncid,'time',length(inttim));
+dimidz = netcdf.defDim(ncid,'depth',length(Z));
+%Define IDs for the dimension variables (pressure,time,latitude,...)
+time_ID=netcdf.defVar(ncid,'time','double',dimidt);
+depth_ID=netcdf.defVar(ncid,'depth','double',dimidz);
+%Define the main variable ()
+u_ID = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
+v_ID = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
+%We are done defining the NetCdf
+netcdf.endDef(ncid);
+%Then store the dimension variables in
+netcdf.putVar(ncid,time_ID,inttim);
+netcdf.putVar(ncid,depth_ID,Z);  
+%Then store my main variable
+netcdf.putVar(ncid,u_ID,uintfilt);
+netcdf.putVar(ncid,v_ID,vintfilt);
+%We're done, close the netcdf
+netcdf.close(ncid);
+
+%% Downward looking ADCP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+clearvars -except mooring raw rot prct_good fpath fpath_output npts_up
+
+rawfile='C:\Users\jhabasqu\Desktop\PIRATA\ADCP_mooring_data\23W-0N\2014-2015 - kpo_1125\LR2395\02395000.000';
+
+adcp.sn='kpo_1125';
+adcp.type='75 khz LongRanger';
+adcp.direction='dn';        % upward-looking 'up', downward-looking 'dn'
+adcp.instr_depth=230;        % nominal instrument depth
+
+instr=2;                    % this is just for name convention and sorting of all mooring instruments
+ 
+first = 13;                % determine first and last indiced when instrument was at depth (you can do this by plotting 'raw.pressure' for example           
+last = 6081;  
+
+% Exclude data with percent good below prct_good
+prct_good = 20;
+
+% Read rawfile
+fprintf('Read %s\n', rawfile);
+raw=read_os3(rawfile,'all');
+%plot(raw.pressure);set(gca,'ydir','reverse');
+
+freq_down = raw.config.sysconfig.frequency;
+ 
+%% Read data
+ea = squeeze(mean(raw.amp(:,:,first:last),2));  % amplitude of the bins 
+figure; imagesc(ea);title('Amplitude of the bins'); colorbar
+
+u2 = squeeze(raw.vel(:,1,first:last));
+v2 = squeeze(raw.vel(:,2,first:last));
+w = squeeze(raw.vel(:,3,first:last));
+vel_err = squeeze(raw.vel(:,4,first:last));  % the difference in vertical velocity between the two pairs of transducers 
+time = raw.juliandate(first:last);
+ang = [raw.pitch(first:last) raw.roll(first:last) raw.heading(first:last)]; 
+soundspeed = raw.soundspeed(first:last);
+T = raw.temperature(first:last);
+press = raw.pressure(first:last);
+
+nbin = raw.config.ncells;  % number of bins
+bin  = 1:nbin;
+blen = raw.config.cell;    % bin length
+blnk = raw.config.blank;   % blank distance after transmit
+
+dt=(time(2)-time(1))*24;   % Sampling interval in hours
+
+% Magnetic deviation values
+[u,v]=uvrot(u2,v2,-rot);   % Correction of magnetic deviation
+
+pg = squeeze(raw.pg(:,4,first:last));  % percent good
+
+igap=find(pg<prct_good);           % Exclude data with percent good below 20%
+u(igap)=nan;
+v(igap)=nan;
+w(igap)=nan;
+vel_err(igap)=nan;
+
+%% Calculate depth of each bin:
+dpt = sw_dpth(press,mooring.lat)';  % convert pressure to depth, press needs to have dimension (n x 1)
+dpt1 = repmat(dpt,nbin,1);
+binmat = repmat((1:nbin)',1,length(dpt1));
+
+% If ADCP is upward-looking a depth correction can be inferred from the
+% surface reflection, which is done in adcp_surface_fit
+if strcmp(adcp.direction,'up')  
+    [z,dpt1,offset,xnull]=adcp_surface_fit(dpt,ea,sbins,blen,blnk,nbin);
+elseif strcmp(adcp.direction,'dn')
+    z = dpt1+(binmat-0.5)*blen+blnk;
+else
+    error('Bin depth calculation: unknown direction!')
+end
+
+%% SAVE DATA
+% More meta information
+%adcp.comment='';
+adcp.config=raw.config;
+%adcp.z_offset=offset;
+adcp.ang=ang;
+adcp.mag_dev=rot;
+
+% Data structure
+data.u=u;
+data.v=v;
+data.w=w;
+data.e=vel_err;
+data.ea=ea;
+data.pg=pg;
+data.time=time;
+data.z_bins=z;
+data.depth=dpt;
+data.temp=T;
+data.sspd = soundspeed;
+
+% Save
+save([fpath,mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '.mat'],'adcp','mooring','data','raw');
+ 
+%% Interpolate data on a regular vertical grid
+Z = floor(min(z(:))):blen:max(z(:))+blen;
+Zmin = min(Z) ;
+
+u_interp = NaN(length(time),length(Z));
+v_interp = NaN(length(time),length(Z));
+for i=1:length(time)
+    % indice correspondant sur la grille finale Z
+    ind = round(abs(Zmin-z(1,i))/blen)+1;   
+    % filling the grid
+    npts = min([length(Z)-ind+1 nbin]);
+    u_interp(i,ind:ind+npts-1) = u(1:npts,i);
+    v_interp(i,ind:ind+npts-1) = v(1:npts,i);
+end
+ 
+%% Horizontal interpolation, filtering and subsampling
+[uintfilt,vintfilt,inttim] = adcp_filt_sub(data,u_interp',v_interp',1:length(Z),40);
+
+% Save interpolated data
+data.uintfilt=uintfilt(1:40,:);
+data.vintfilt=vintfilt(1:40,:);
+data.Z = Z(1:40);
+data.inttim = inttim;
+save([fpath, mooring.name '_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '_int_filt_sub.mat'],'adcp','mooring','data','raw');
+
+%% Figure
+niv_u = (-1.5:0.1:1.5);
+niv_v = (-1:0.1:1);
+
+hf=figure('position', [0, 0, 1400, 1000]);
+%u
+subplot(2,1,1);
+[C,h] = contourf(inttim,Z(1:40),uintfilt(1:40,:),niv_u); 
+set(h,'LineColor','none');
+caxis(niv_u([1 end]));
+h=colorbar;
+ylabel(h,'U [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick
+title({[mooring.name, ' - MERIDIONAL VELOCITY - RDI ',num2str(freq_down),' kHz']});
+
+%v
+subplot(2,1,2);
+[C,h] = contourf(inttim,Z(1:40),vintfilt(1:40,:),niv_v); 
+set(h,'LineColor','none');
+caxis(niv_v([1 end]));
+h=colorbar;
+ylabel(h,'V [m s^-^1]');
+set(gca,'ydir', 'reverse');
+ylabel('Depth (m)');
+%change figure label in HH:MM
+gregtick;
+title({[mooring.name, ' - ZONAL VELOCITY - RDI ',num2str(freq_down),' kHz']});
+
+graph_name = [fpath, mooring.name '_U_V_int_filt_sub_down'];
+set(hf,'Units','Inches');
+pos = get(hf,'Position');
+set(hf,'PaperPositionMode','Auto','PaperUnits','Inches','PaperSize',[pos(3), pos(4)])
+print(hf,graph_name,'-dpdf','-r300');
+ 
+
+%%  Write netcdf file           
+[yr_start , ~, ~] = gregorian(inttim(1));
+[yr_end,  ~, ~] = gregorian(inttim(length(inttim)));
+
+ncid=netcdf.create([fpath_output,'ADCP_',mooring.name,'_' num2str(adcp.sn) '_instr_' sprintf('%02d',instr) '_',num2str(yr_start),'_',num2str(yr_end),'_1d.nc'],'NC_WRITE');
+ 
+%create dimension
+dimidt = netcdf.defDim(ncid,'time',length(inttim));
+dimidz = netcdf.defDim(ncid,'depth',length(Z));
+%Define IDs for the dimension variables (pressure,time,latitude,...)
+time_ID=netcdf.defVar(ncid,'time','double',dimidt);
+depth_ID=netcdf.defVar(ncid,'depth','double',dimidz);
+%Define the main variable ()
+u_ID = netcdf.defVar(ncid,'u','double',[dimidt dimidz]);
+v_ID = netcdf.defVar(ncid,'v','double',[dimidt dimidz]);
+%We are done defining the NetCdf
+netcdf.endDef(ncid);
+%Then store the dimension variables in
+netcdf.putVar(ncid,time_ID,inttim);
+netcdf.putVar(ncid,depth_ID,Z);  
+%Then store my main variable
+netcdf.putVar(ncid,u_ID,uintfilt);
+netcdf.putVar(ncid,v_ID,vintfilt);
+%We're done, close the netcdf
+netcdf.close(ncid);
\ No newline at end of file
diff --git a/data_example/up_and_down/FR22-10W0N_508_instr_01_int_filt_sub.mat b/tools/data_example/up_and_down/FR22-10W0N_508_instr_01_int_filt_sub.mat
similarity index 100%
rename from data_example/up_and_down/FR22-10W0N_508_instr_01_int_filt_sub.mat
rename to tools/data_example/up_and_down/FR22-10W0N_508_instr_01_int_filt_sub.mat
diff --git a/data_example/up_and_down/FR22-10W0N_509_instr_02_int_filt_sub_down.mat b/tools/data_example/up_and_down/FR22-10W0N_509_instr_02_int_filt_sub_down.mat
similarity index 100%
rename from data_example/up_and_down/FR22-10W0N_509_instr_02_int_filt_sub_down.mat
rename to tools/data_example/up_and_down/FR22-10W0N_509_instr_02_int_filt_sub_down.mat
diff --git a/data_example/up_and_down/FR22-10W0N_UP_DOWN_int_filt_sub.mat b/tools/data_example/up_and_down/FR22-10W0N_UP_DOWN_int_filt_sub.mat
similarity index 100%
rename from data_example/up_and_down/FR22-10W0N_UP_DOWN_int_filt_sub.mat
rename to tools/data_example/up_and_down/FR22-10W0N_UP_DOWN_int_filt_sub.mat
diff --git a/data_example/up_and_down/FR22-10W0N_U_V_UP_DOWN_int_filt_sub.pdf b/tools/data_example/up_and_down/FR22-10W0N_U_V_UP_DOWN_int_filt_sub.pdf
similarity index 100%
rename from data_example/up_and_down/FR22-10W0N_U_V_UP_DOWN_int_filt_sub.pdf
rename to tools/data_example/up_and_down/FR22-10W0N_U_V_UP_DOWN_int_filt_sub.pdf
diff --git a/tools/distance/dist.m b/tools/distance/dist.m
new file mode 100644
index 0000000000000000000000000000000000000000..ca504d89a1e97846880350d7edeb3dc91cd76ae3
--- /dev/null
+++ b/tools/distance/dist.m
@@ -0,0 +1,40 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Script:	dist.m                         Date: October 12th 1989
+% Author:       A. Macdonald
+% Purpose:      To determine the distance between two points
+%               specified as latitude and longitude
+%
+% Inputs:  lata   - latitude of first point in degrees
+%          longa  - longitude of the first point in degrees  
+%          latb   - latitude of second point in degrees
+%          longb  - longitude of the second point in degrees  
+%
+% Outputs: d      - distance between the two points in km
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+function d = dist(lata,longa,latb,longb)
+
+   dlong=abs(longa-longb);
+
+   [m,n]=size(dlong);
+   for i=1:m
+      if (dlong(i) > 180.) 
+          dlong(i)=360.-dlong(i);
+      end
+   end
+
+   dlat=lata-latb;
+   
+   rlata=abs(1.7453293e-2*lata);
+   rlatb=abs(1.7453293e-2*latb);
+   d=111.194929*sqrt(dlat.^2+(cos((rlatb+rlata)/2.).*dlong).^2);
+
+
+   for i=1:m
+     if((dlat(i) == 0) & (dlong(i) == 0))
+        d(i)=0.0;
+      end
+   end
diff --git a/tools/distance/dist_ang.m b/tools/distance/dist_ang.m
new file mode 100644
index 0000000000000000000000000000000000000000..1f7d0cbb76e7258becf4cbf6180ef036a3b35557
--- /dev/null
+++ b/tools/distance/dist_ang.m
@@ -0,0 +1,35 @@
+function lambda = dist_ang(lata,longa,latb,longb)
+%key: distance between two points, in degrees
+%synopsis :  lamdba = distance(lata,longa,latb,longb)
+%
+%description : 
+%
+% Purpose:      To determine the distance between two points
+%               specified as latitude and longitude
+%
+% Inputs:  lata   - latitude of first point in degrees
+%          longa  - longitude of the first point in degrees  
+%          latb   - latitude of second point in degrees
+%          longb  - longitude of the second point in degrees  
+%
+% Outputs: lamdba - distance between the two points in m
+%
+%uses :
+%side effects :
+%
+%author : A.Ganachaud, Apr 95
+%
+%see also :
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+   if any(abs(lata)>90 | abs(latb)>90)
+      error('distance routine: lat > 90')
+   end
+
+   dlon=sublong(longb,longa);
+
+   d2r=2*pi/360;
+   lambda=(1/d2r)*acos( sin(d2r*lata).*sin(d2r*latb)+ ...
+	cos(d2r*lata).*cos(d2r*latb).*cos(d2r*dlon) );
+
diff --git a/tools/distance/distance.m b/tools/distance/distance.m
new file mode 100644
index 0000000000000000000000000000000000000000..bf9af338d3d1bac659cfaeee6670f0df0aa36281
--- /dev/null
+++ b/tools/distance/distance.m
@@ -0,0 +1,43 @@
+function d = distance(lata,longa,latb,longb)
+%key: distance between two points, in METERS
+%synopsis : d = distance(lata,longa,latb,longb)
+%
+%description : 
+%
+% Purpose:      To determine the distance between two points
+%               specified as latitude and longitude
+%
+% Inputs:  lata   - latitude of first point in degrees
+%          longa  - longitude of the first point in degrees  
+%          latb   - latitude of second point in degrees
+%          longb  - longitude of the second point in degrees  
+%
+% Outputs: d      - distance between the two points in m
+%
+%uses : the latitudes must not be too different
+%
+%side effects :
+%
+%author : A. Macdonald -  A.Ganachaud, Apr 95
+%
+%see also :
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+   if any(abs(lata)>90 | abs(latb)>90)
+      error('distance routine: lat > 90')
+   end
+   dlong=abs(longa-longb);
+
+   dbad=find(dlong >= 180.); 
+   dlong(dbad)=360-dlong(dbad);
+
+   dlat=lata-latb;
+   
+   rlata=abs(1.7453293e-2*lata);
+   rlatb=abs(1.7453293e-2*latb);
+   d=111194.929*sqrt(dlat.^2+(cos((rlatb+rlata)/2.).*dlong).^2);
+
+
+   dzer=find((dlat == 0) & (dlong == 0));
+   d(dzer)=zeros(size(dzer));
\ No newline at end of file
diff --git a/tools/distance/sublong.m b/tools/distance/sublong.m
new file mode 100644
index 0000000000000000000000000000000000000000..ab2ea359530a02090fb35e782c9dacfa86f77a7c
--- /dev/null
+++ b/tools/distance/sublong.m
@@ -0,0 +1,26 @@
+function diflon = sublong( lon2, lon1 )
+%key: difference between the two longitudes, according to 360deg repeat
+%synopsis : diflon = sublong( lon2, lon1 )
+%
+%description : 
+%
+% diflon=lon2-lon1 ( modulo 360 )
+%
+%
+%uses :
+%
+%side effects :
+%
+%author : A.Ganachaud (ganacho@gulf.mit.edu) , May 95
+%
+%see also : scan_longitude.m
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+diflon = lon2-lon1;
+
+ii = find( diflon > 180 );
+diflon(ii) = diflon(ii)-360;
+
+jj = find( diflon < -180 );
+diflon(jj) = diflon(jj)+360;
diff --git a/tools/filter/clean_median.m b/tools/filter/clean_median.m
new file mode 100644
index 0000000000000000000000000000000000000000..188800c869621798e1946cebaf11a540be7b12b6
--- /dev/null
+++ b/tools/filter/clean_median.m
@@ -0,0 +1,201 @@
+function [par,info_rejet_med] = clean_median(param,window_lgth,nb_std,delta_lim,iter,fillval)
+% par = clean_median(param,window_length,nb_std,delta_lim,iter,fillval);
+% param = vector of input values
+% window_lgth: number of points in the windows used to define a mean and an std_dev
+% nb_std: number of std dev beyond which data are rejected
+% delta_lim = [delta_min delta_max] : interval of acceptable std_dev in window_lgth 
+%             (NaN possible if any value is accepted)
+% iter = number of iterations of the cleaning
+% fillval = value that will replace the value of the rejected data
+%
+% remarks: 
+%  - nb_std can be a scalar or a vector with the size of 1 x iter
+%  - delta_min is important in case the signal is constant over window_lgth (when std_dev=0)
+%  - delta_max id also useful when the signal is very bad
+%
+% examples:
+%    pressure = clean_median(pressure,20,[3 2.8],[1.5 10],2,-9.990e-29);
+%    temp1 = clean_median(temp1,12,3,[0.05 0.4],2,-9.990e-29);
+%    cond1 = clean_median(cond1,10,2.8,[0.01 0.4],3,-9.990e-29);
+%    oxy1 = clean_median(oxy1,10,2.8,[0.01 0.4],3,-9.990e-29);
+%
+% EX: P = [1:6 40 8:20]
+%  clean_median(P,5,3,[1.5 10],2,-100) : On travaille sur les donnees par paquet de 5 (=longueur de fenetre)
+%  ie les donnees [1:5], puis [6 40 8 9 10], puis [11:15] puis [16:20].
+% Pour chaque groupe de donnnees, on calcule la mediane et l'ecart-type sur
+% ce paquet de donnees auquel on ajoute npts donnees de part et autre; npts
+% etant la longueur de la demie-fenetre, soit ici npts = round(5/2) = 3.
+% Ainsi, dans ce cas, la mediane et l'ecart-type sont calcules sur 11 pts
+% au total (5(longueur de fenetre) + 2*npts = 5 + 2*3 = 11).
+% Chacun des 5 points de la fenetre est ensuite compare avec cette mediane et cet ecart-type
+% et est garde ou non.
+%
+% ??/??/2010 : P. Lherminier : Creation
+%       2017 : P. Rousselot  : Modification valeurs absolues -> par(abs(par)<abs(med-dmax) | abs(par)>abs(med+dmax))
+
+if length(nb_std) == 1
+    nb_std = repmat(nb_std,[iter,1]);
+end
+perm_param = 0;
+
+size_param = size(param);
+if size_param(1) == 1
+    param = param(:);
+    perm_param = 1;
+end
+deja_fillval = find(param==fillval); % On repere les donnees deja a badval.
+param(param==fillval) = NaN;
+nparam = length(param);
+nwin = floor(nparam/window_lgth)+1;
+nlast = nwin*window_lgth-nparam;
+
+par=[param;medianoutnan(param(end-nlast+1:end))*ones(nlast,1)];
+par = reshape(par,[window_lgth nwin]);
+% npts data are added before and after the window to calculate the std_dev (so that 
+% the extremes points of the segments are not eliminated in strong gradients)
+npts = round(window_lgth/2);
+
+for iiter = 1:iter
+    parstd = [[nan(npts,1) par(end-npts+1:end,1:end-1)];par;[par(1:npts,2:end) nan(npts,1)]];
+    [stdm,med] = stdmedian(parstd,0,1);
+    med = med(npts+1:end-npts,:);
+    dmax = repmat(min(max(nb_std(iiter)*stdm,delta_lim(1)),delta_lim(2)),[window_lgth,1]);
+    par(abs(med-par)>=dmax) = NaN;            %%%%%%%%%%%%%%%%%%%%%%%
+end
+par = par(:); med = med(:); dmax = dmax(:);
+par = par(1:nparam); med = med(1:nparam); dmax = dmax(1:nparam);
+
+% Calcule le % de rejet apres le test d'ecart a le mediane.
+nouveau_fillval = find(isnan(par));
+info_rejet_med=(length(nouveau_fillval)-length(deja_fillval))*100/nparam;
+
+par(isnan(par)) = fillval;
+
+
+if perm_param == 1
+    par = par';
+end
+
+%clf
+%plot(param,'b.-');
+%hold on;grid on
+%plot(par,'r.');
+%plot([med-dmax med+dmax],'c-');
+
+function [y,med] = stdmedian(x,flag,dim)
+%STD    Standard deviation from the median and not the mean.
+%   For vectors, STD(X) returns a pseudo standard deviation. For matrices,
+%   STD(X) is a row vector containing the standard deviation of each
+%   column.  For N-D arrays, STD(X) is the standard deviation of the
+%   elements along the first non-singleton dimension of X.
+%
+%   STD(X) normalizes by (N-1) where N is the sequence length.  This
+%   makes STD(X).^2 the best unbiased estimate of the variance if X
+%   is a sample from a normal distribution.
+%
+%   STD(X,1) normalizes by N and produces the second moment of the
+%   sample about its mean.  STD(X,0) is the same as STD(X).
+%
+%   STD(X,FLAG,DIM) takes the standard deviation along the dimension
+%   DIM of X.  When FLAG=0 STD normalizes by (N-1), otherwise STD
+%   normalizes by N.
+%
+%   [Y,M] = STD(X) returns the pseudo std Y and the median M
+%
+%   Example: If X = [4 -2 1
+%                    9  5 7]
+%     then std(X,0,1) is [ 3.5355 4.9497 4.2426] and std(X,0,2) is [3.0
+%                                                                   2.0]
+%   See also COV, MEAN, MEDIAN, CORRCOEF.
+
+%   J.N. Little 4-21-85
+%   Revised 5-9-88 JNL, 3-11-94 BAJ, 5-26-95 dlc, 5-29-96 CMT.
+%   Copyright (c) 1984-98 by The MathWorks, Inc.
+%   $Revision: 5.17 $  $Date: 1997/11/21 23:24:08 $
+
+if nargin<2, flag = 0; end
+if nargin<3,
+  dim = find(size(x)~=1, 1 );
+  if isempty(dim), dim = 1; end
+end
+
+% Avoid divide by zero.
+if size(x,dim)==1, y = zeros(size(x)); return, end
+
+tile = ones(1,max(ndims(x),dim));
+tile(dim) = size(x,dim);
+
+%xc = x - repmat(sum(x,dim)/size(x,dim),tile);  % Remove mean
+med = repmat(medianoutnan(x,dim),tile);
+xc = x - med;  % Remove median
+xcnan=(isnan(xc));
+coef=sum(~xcnan,dim);
+coef(coef==0)=NaN;
+xc(xcnan)=0;
+if flag,
+  y = sqrt(sum(conj(xc).*xc,dim)./coef);
+else
+  coef(coef==1)=2; %then y=0
+  y = sqrt(sum(conj(xc).*xc,dim)./(coef-1));
+end
+
+function y = medianoutnan(x,dim)
+%MEDIAN Median value.
+%   For vectors, MEDIANOUTNAN(X) is the median value of the finite
+%   elements in X. For matrices, MEDIANOUTNAN(X) is a row vector
+%   containing the median value of each column.  For N-D arrays,
+%   MEDIANOUTNAN(X) is the median value of the elements along the
+%   first non-singleton dimension of X.
+%
+%   MEDIANOUTNAN(X,DIM) takes the median along the dimension DIM of X.
+%
+%   Example: If X = [0 1 2   NaN
+%                    3 4 NaN NaN]
+%
+%   then medianoutnan(X,1) is [1.5 2.5 2 NaN]
+%   and medianoutnan(X,2) is [1
+%                             3.5]
+%
+%   See also MEANOUTNAN, STDOUTNAN, MIN, MAX, COV.
+
+%   P. Lherminier, 21/03/2002. From modifications of median.m
+
+if nargin==1,
+  dim = min(find(size(x)~=1));
+  if isempty(dim), dim = 1; end
+end
+if isempty(x), y = []; return, end
+
+siz = [size(x) ones(1,dim-ndims(x))];
+n = size(x,dim);
+
+% Permute and reshape so that DIM becomes the row dimension of a 2-D array
+perm = [dim:max(length(size(x)),dim) 1:dim-1];
+x = reshape(permute(x,perm),n,prod(siz)/n);
+
+% Sort along first dimension
+x = sort(x,1);
+sizx=size(x);
+
+[nnan,ncol]=find(diff(isnan(x)));
+nn=nan*ones(prod(siz)/n,1);
+y=nn';
+nn(ncol)=nnan;
+nn(~isnan(x(end,:)))=n;
+
+ieven=(rem(nn,2)==0);           % Even number of elements along DIM
+iodd=find(~ieven & ~isnan(nn)); % Odd number of elements along DIM
+ieven=find(ieven==1);
+x=x(:);
+if ~isempty(iodd),
+    y(iodd)=x(sub2ind(sizx,(nn(iodd)+1)/2,iodd));
+end;
+if ~isempty(ieven),
+    y(ieven)=(x(sub2ind(sizx,nn(ieven)/2,ieven))+ x(sub2ind(sizx,nn(ieven)/2+1,ieven)))/2;
+end;
+
+% Permute and reshape back
+siz(dim) = 1;
+y = ipermute(reshape(y,siz(perm)),perm);
+
+
diff --git a/tools/filter/diurne.m b/tools/filter/diurne.m
new file mode 100644
index 0000000000000000000000000000000000000000..2a4beda03225682c64218bf1c30baad3ad89e14e
--- /dev/null
+++ b/tools/filter/diurne.m
@@ -0,0 +1,176 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Biais capteurs - zoom cycle diurne                                      %
+% CNRS / Météo-France / Coriolis  --- AtlantOS                            %
+% Date: 01/08/2016  ---  Auteur: Pierre Rousselot                         %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%addpath('
+date = datestr(positions.timef(:,1));
+
+% Profil fin de nuit
+time =datestr(positions.timefinterp(:,1),'HH:MM');
+j=1;
+jj=1;
+for ii = 2:length(time(:,1))
+    %end_night(ii)=strcmp(time(ii,:),'03:00');
+    if strcmp(time(ii,:),'03:00')
+        end_night(j)=ii;
+        j=j+1;
+        if strcmp(time(ii+3,:),'06:00')
+            end_night_bis(jj)=ii+3;
+            jj=jj+1;
+        else
+            if strcmp(time(ii+2,:),'05:00')
+                end_night_bis(jj)=ii+2;
+                jj=jj+1;              
+            else
+                if strcmp(time(ii+1,:),'04:00')
+                    end_night_bis(jj)=ii+1;
+                    jj=jj+1;    
+                else
+                    if strcmp(time(ii,:),'03:00')
+                        end_night_bis(jj)=ii;
+                        jj=jj+1; 
+                    end
+                end
+            end
+        end
+    end
+
+end
+
+for ii= 1:331
+    mean_temp(ii,:) = mean(tempf(end_night(ii):end_night_bis(ii),:),1);
+    mean_depth(ii,:) = mean(depth(end_night(ii):end_night_bis(ii),:),1);
+    mean_time(ii,:) = mean(positions.timefinterp(end_night(ii):end_night_bis(ii),:),1);
+end
+
+% %Conv filtering
+% for ii=1:length(tempf(1,:))
+%     ttt(:,ii)=conv(tempf(end_night,ii),ones(1,3)/3,'valid');
+% end
+% 
+% %Median filtering
+% %tt=medfilt1(tempf(end_night,:));
+% hh=hann(24);
+% for ii=1:length(tempf(1,:))
+%     tt(:,ii)=conv(tempf(end_night,ii),hh,'valid');
+% end
+% 
+% 
+% %Filter
+% %tttt=filter(ones(1,3)/3,1,(tempf(end_night,:)));
+% 
+% %Zero-Phase filtering
+% ttttt=filtfilt(ones(1,3)/3,1,(tempf(end_night,:)));
+% 
+% %Savitky-Golay fitering
+% tttttt=sgolayfilt(tempf(:,:),3,7);
+% 
+% %LPO-ecart median
+% % for ii=1:length(tempf(1,:))
+% %     [par(:,ii),info_rejet_med]=clean_median(tempf(:,ii),6,2.8,[0.05 1],1,NaN);
+% % end
+% 
+% figure(1);
+% title('End-Night - Different filter')
+% subplot(5,1,1)
+% plot(tempf(end_night,:))
+% subplot(5,1,2)
+% plot(tt)
+% subplot(5,1,3)
+% plot(ttt)
+% subplot(5,1,4)
+% plot(ttttt)
+% subplot(5,1,5)
+% plot(tttttt)
+
+figure
+contourf(mean_time, -mean_depth, mean_temp);
+
+
+
+figure(2);
+datestr(positions.timefinterp(end_night,1));
+contourf(positions.timefinterp(end_night,:), -depth_ma(end_night,:), yyyy(:,end_night)')
+datetick('x','mm/yy','keepticks')
+cc = colorbar;
+xlabel('Time')
+ylabel('Depth [m]')
+ylabel(cc,'Temperature [{\circ}C]');
+caxis([min(min(tempf_interp)) max(max(tempf_interp))])
+grid on
+title('End-Night Mean (3h-6h) Time Series')
+
+% TT = 18;
+% %Zoom cycle diurne - biais capteur (fin de nuit)
+% figure(3);
+% plot(positions.timef(TT:TT+700,:),tempf(TT:TT+700,:))
+% datetick('x','HH dd/mm','keepticks')
+% grid on
+% j1m = mean(tempf(46:49,:));
+% j2m = mean(tempf(69:72,:));
+% diff_mean = j1m-j2m
+% title('Zoom 2 days')
+
+
+%Biais intercapteur
+% figure(4);
+% 
+% if options.marisonde
+%     %plot(positions.timef(500+24*3:500+24*6,:),tempf(500+24*3:500+24*6,:))
+%     plot(positions.timef(TT+24*3:TT+24*6,:),tempf(TT+24*3:TT+24*6,:))
+% else
+%     plot(positions.timef(5100+24*3:5100+24*6,:),tempf(5100+24*3:5100+24*6,:))
+% end
+% datetick('x','HH dd/mm','keepticks')
+% grid on
+% title('Zoom Homogeneous Period')
+
+
+% figure(5);
+% if options.marisonde
+%     %plot(tempf(500,:))
+%     plot(tempf(TT,:))
+%     hold on
+%     %plot(mean(tempf(500,:))*ones(1,17),'r--')
+%     plot(mean(tempf(TT,:))*ones(1,17),'r--')
+%     %max_ecart_intersensor = range(tempf(500,:))  
+%     max_ecart_intersensor = range(tempf(TT,:)) 
+% else
+%     plot(tempf(5178+24,:))
+%     hold on
+%     plot(mean(tempf(5178+24,:))*ones(1,17),'r--')
+%     max_ecart_intersensor = range(tempf(5178,:))
+% end
+% hold off
+% grid on
+% view([90 90])
+% title('Inter-sensor Bias')
+% 
+% %[maxbias_intersensor,ind_maxbias_intersensor]=max(std(tempf(4934:5280,:),[],2))
+% [maxbias_intersensor,ind_maxbias_intersensor]=max(std(tempf(TT:100,:),[],2))
+% 
+% % mm = mean(tempf(5100+24*3:5100+24*6,:));
+% % stdmm = std(mm);
+% mm = mean(tempf(TT+24*3:TT+24*6,:));
+% stdmm = std(mm);
+% 
+% %Derive dans le temps??
+% j=1;
+% %for ii=4950:7400 %1:length(time(:,1))
+% for ii=TT:TT+1100 %1:length(time(:,1))
+%     %end_night(ii)=strcmp(time(ii,:),'03:00');
+%     if strcmp(time(ii,:),'03:00')
+%         end_night2(j)=ii;
+%         j=j+1;
+%     end
+% end
+% mmean = nanmean(tempf(end_night2,:),2);
+% for ii = 1:length(tempf(1,:))
+% diff(:,ii) = tempf(end_night2,ii)-mmean;
+% end
+% mmmmm = nanmean(diff);
+% for ii=1:length(end_night2(1,:))
+% te(ii,:)=tempf(end_night2(1,ii),:)-mmmmm;
+% end
\ No newline at end of file
diff --git a/tools/filter/fft_filter.m b/tools/filter/fft_filter.m
new file mode 100644
index 0000000000000000000000000000000000000000..12006812cdfad644a9f8036345e67a38a4b5cd61
--- /dev/null
+++ b/tools/filter/fft_filter.m
@@ -0,0 +1,80 @@
+function [reconstructed] = fft_filter(data,dimension,bandlimits)
+%The filter is designed to filter gridded data in an array of up to 5 dimensions (DATA)
+%with the filtering done only in the dimension specified by DIMENSION.
+%The signal in data is first decomposed in the frequency domain (amplitude vs frequency).
+%Then the amplitudes associated with frequencies outside of the BANDLIMITS are set to zero.
+%Then an inverse Fourier transform is performed resulting in the reconstructed signal.
+ 
+% DATA : array to filter of up to 5 dimensions, the dimension to filter must be evenly spaced and be a multiple of 2.
+% if the dimension is spaced unevenly, interpolation to regular interval is advised.
+% DIMENSION : 1 integer indication the dimension to filter
+% BANDLIMITS : 2 numbers indicating the boundary of the selected frequency band. The bandlimit is actually
+% expressed as the period. Bandlimits have the same unit as the spacing between measurements
+% ex: if filtering with measurements every day, bandlimit of [5 10] means only oscillation with periods
+% of 5 to 10 days are kept in the signal. To construct a low-pass or high-pass, on can use [5 Inf] or [0 5]
+% RECONSTRUCTED : array of same size as data containing the filtered signal.
+ 
+% ATTENTION : Fourier transform are accurate for periodic signals. If the filtered values are not periodic,
+% it is advised to make them periodic. Ex signal= 1 2 5 coult be modified to 1 2 5 5 2 1 for periodicity.
+% Only the first half of reconstructed should be used in this case.
+ 
+%Bring dimension to filter to dimension 1
+si_data=size(data);
+order=1:length(si_data);
+otherdim=order(ismember(order,dimension)==0);
+neworder=[dimension,otherdim];
+data=permute(data,[dimension,otherdim]);
+ 
+%Fourier transform and associated frequencies
+amplitudes = fft(data,[],1);
+n1=size(data);
+n=size(amplitudes,1); %replaced dimension by 1
+nyquist=1/2;
+nyquist_location=n/2+1;
+ 
+%Generate a list of the frequencies
+if rem(size(data,1),2)==0;
+frequencies=[0,(1:n/2-1)/n,n/2/n,(n/2-1:-1:1)/n];
+end
+if rem(size(data,1),2)==1;
+frequencies=[0,(1:(n-1)/2)/n,((n-1)/2:-1:1)/n];
+end
+ 
+%Computes periods and decide of the ones to keep
+periods=1./frequencies;
+periodstokeep=(periods>=bandlimits(1) & periods<=bandlimits(2));
+periods(periodstokeep);
+filteredamps=amplitudes*0;
+ 
+%Only keep desired periods (amplitudes)
+if ndims(data)==1
+filteredamps(periodstokeep)=amplitudes(periodstokeep);
+end
+if ndims(data)==2
+filteredamps(periodstokeep,:)=amplitudes(periodstokeep,:);
+end
+if ndims(data)==3
+filteredamps(periodstokeep,:,:)=amplitudes(periodstokeep,:,:);
+end
+if ndims(data)==4
+filteredamps(periodstokeep,:,:,:)=amplitudes(periodstokeep,:,:,:);
+end
+if ndims(data)==5
+filteredamps(periodstokeep,:,:,:,:)=amplitudes(periodstokeep,:,:,:,:);
+end
+ 
+%Reconstruct the signal
+reconstructed=ifft(filteredamps,[],1);
+reconstructedr=real(reconstructed);
+reconstructedi=imag(reconstructed);
+ 
+%Return matrix in original order
+ordereturn=zeros([1,length(si_data)]);
+ordereturn(dimension)=1;
+iszero=find(ordereturn==0);
+for i=1:length(iszero)
+ordereturn(iszero(i))=i+1;
+end
+reconstructed=permute(reconstructed,ordereturn);
+ 
+end
\ No newline at end of file
diff --git a/tools/filter/filterfft.m b/tools/filter/filterfft.m
new file mode 100644
index 0000000000000000000000000000000000000000..4eb546bd9d5493802ecad3c61cf00be23a0f5ca5
--- /dev/null
+++ b/tools/filter/filterfft.m
@@ -0,0 +1,71 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Plot filtering data                                                     %
+% CNRS / Météo-France / Coriolis  --- AtlantOS                            %
+% Date: 14/09/2016  ---  Auteur: Pierre Rousselot                         %
+% fft_filter: Patrick Martineau                                           %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% tempf(end+1:length(tempf)*2,:)=tempf;
+% positions.timef(end+1:length(positions.timef)*2,:)=positions.timef;
+% depth(end+1:length(depth)*2,:)=depth;
+% positions.timef = linspace(1,15940,15940);
+% for i = 2:17
+%     positions.timef(i,:)=positions.timef(1,:);
+% end
+
+[reconstructed2]=fft_filter(tempf,1,[0 24]);
+
+figure
+plot(positions.timef,reconstructed)
+datetick('x','mm/yy','keepticks')
+xlabel('Time')
+ylabel('Temperature [degC]')
+%axis([min(positions.timef(:,1)) max(positions.timef(:,1)) 11 22])
+grid on
+title('Filtering process diff (<1week) - (<1day)')
+
+% figure;
+% contourf(positions.timef', -depth, reconstructed)
+
+figure
+subplot(4,1,1)
+contourf(vec_mean,-depth_mean,temp_mean)
+axis([7.34313e5 7.34318e5 -83 0])
+datetick('x','dd/mm','keeplimits')
+xlabel('Time')
+ylabel('Depth [m]')
+cc=colorbar;
+ylabel(cc,'Temperature [{\circ}C]')
+title('Daily mean')
+
+subplot(4,1,2)
+contourf(positions.timef,-depth,tempf)
+axis([7.34313e5 7.34318e5 -83 0])
+datetick('x','dd/mm','keeplimits')
+xlabel('Time')
+ylabel('Depth [m]')
+cc=colorbar;
+ylabel(cc,'Temperature [{\circ}C]')
+title('Raw Data')
+
+subplot(4,1,3)
+contourf(positions.timef,-depth,reconstructed)
+axis([7.34313e5 7.34318e5 -83 0])
+datetick('x','dd/mm','keeplimits')
+xlabel('Time')
+ylabel('Depth [m]')
+cc=colorbar;
+ylabel(cc,'Temperature [{\circ}C]')
+title('Filtered Data (>24h)')
+
+subplot(4,1,4)
+contourf(positions.timef,-depth,reconstructed2)
+axis([7.34313e5 7.34318e5 -83 0])
+xlabel('Time')
+ylabel('Depth [m]')
+cc=colorbar;
+ylabel(cc,'Temperature Anomaly [{\circ}C]')
+title('Diurnal Temperature Anomaly (<24h)')
+
+
+
diff --git a/moored_adcp_proc/filtfilt.m b/tools/filter/filtfilt.m
similarity index 100%
rename from moored_adcp_proc/filtfilt.m
rename to tools/filter/filtfilt.m
diff --git a/moored_adcp_proc/hamm.m b/tools/filter/hamm.m
similarity index 100%
rename from moored_adcp_proc/hamm.m
rename to tools/filter/hamm.m
diff --git a/tools/filter/hammfilter_nodec.m b/tools/filter/hammfilter_nodec.m
new file mode 100644
index 0000000000000000000000000000000000000000..d3d616dd96dbd1cd8257394780580736b2e0cc68
--- /dev/null
+++ b/tools/filter/hammfilter_nodec.m
@@ -0,0 +1,29 @@
+function uf = hammfilter_nodec(u,aver)
+
+% run a box filter of length aver (points) over time series u
+% replaces lowpass filter if toolbox license is missing
+% no decimation (subsampling)
+%
+% -------- R. Zantopp  24 Nov 2006 
+
+nn = length(u);
+na = floor(aver/2);
+% [w]=hamming(aver);
+w  = hamm(aver);       % weights
+uu = reshape(u,1,length(u));
+
+for i = 1:nn-aver
+    u1  = uu(i:i+aver-1);
+    au  = u1'.*w;
+    nau = sum(isnan(au));
+    if nau<=floor(aver/2) 
+      au_good  = find(~isnan(au));
+      au2      = meanmiss(au)/meanmiss(w(au_good));
+      u2(i+na) = au2;
+    else
+      u2(i+na) = NaN;
+    end;
+end;
+u2(1:na)     = NaN;
+u2(nn-na:nn) = NaN;
+uf           = u2;
diff --git a/moored_adcp_proc/hamming.m b/tools/filter/hamming.m
similarity index 100%
rename from moored_adcp_proc/hamming.m
rename to tools/filter/hamming.m
diff --git a/tools/filter/lisse.m b/tools/filter/lisse.m
new file mode 100644
index 0000000000000000000000000000000000000000..fadfd79c9a231461c17b370eed97cd2fec7a44c2
--- /dev/null
+++ b/tools/filter/lisse.m
@@ -0,0 +1,131 @@
+function [yl,yy] = lisse(y0,tab,typ,dk,fillval)
+%
+% Yl = lisse(Y,TAB) is a 1-D running window filter. Y = Y(i) is the signal to be filtered, 
+% TAB is a vector containing the non-zero weights defining the shape of the window [i-n i+n],  
+% where n*2+1 = length(TAB), n being the half-width of the window, and length(TAB) must be odd.  
+% TAB allows to define manually the window size and shape. If the weights provided in TAB do not sum to 1, 
+% they are normalized so that the energy of the signal is conserved. 
+% 
+% Yl = lisse(Y,TAB,typ) is equivalent to Yl = lisse(Y,TAB) if typ = 'mean'. 
+% Using typ = 'min'('max') allows to select the minimum (maximum) value over the running window 
+% rather than apply the weights.  
+%
+% For the n first and last points, the signal is only partially defined over the window. 
+% Yl = lisse(Y,TAB,typ,dk,fillval) permits to provide a fill-value to pad the signal 
+% over the window for these first and last points. 
+% If fillval is the string 'extrapol', Y(1) or Y(end) are used for padding. 
+% If fillval is a scalar, it is used for padding anywhere. 
+% If fillval is a pair of scalars, the first value is used for padding at the beginning, 
+% the second one at the end.
+%
+% If dk differs from 0, the signal is further shifted forward or backward and padded 
+% with the corresponding fill-value, so that 
+%    Yl(k) = Yl(k+dk)
+% 
+y = y0(:)'; 
+if sum(tab)~=1, tab = tab/sum(tab); end
+if nargin<5, 
+  fillval_beg = NaN; 
+  fillval_end = NaN; 
+else
+  if isstr(fillval),
+    if strcmp(fillval,'extrapol'), 
+      fillval_beg = y(1); fillval_end = y(end); 
+    end
+  else
+    switch length(fillval)
+     case 1, fillval_beg = fillval; fillval_end = fillval; 
+     case 2, fillval_beg = fillval(1); fillval_end = fillval(2); 
+    end
+  end
+end
+if nargin<4, dk = 0; end
+if nargin<3 | isempty(typ), typ = 'mean'; end
+%if strcmp(typ,'max') | strcmp(typ,'min'), tab = ones(size(tab)); end
+%
+in = floor((length(tab)+1)/2); halfwidth = in-1; 
+
+if size(y0,1)==1, % y0 is a vector
+  %
+  yy = zeros(length(tab),length(y));
+  %
+  switch typ
+   case 'mean',
+    yy(in,:) = tab(in)*y; 
+    for k = 1:in-1
+      decneg = in-k; 
+      yy(k,:) = tab(k)*[fillval_beg*ones(1,decneg) y(1:end-decneg)]; 
+    end
+    for k = in+1:length(tab)
+      decpos = k-in; 
+      yy(k,:) = tab(k)*[y(1+decpos:end) fillval_end*ones(1,decpos)]; 
+    end
+    yl = nansum(yy); 
+    %
+   case 'mean2',
+    yy(in,:) = tab(in)*y; 
+    for k = 1:in-1
+      decneg = in-k; 
+      yy(k,:) = tab(k)*[fillval_beg*ones(1,decneg) y(1:end-decneg)].^2; 
+    end
+    for k = in+1:length(tab)
+      decpos = k-in; 
+      yy(k,:) = tab(k)*[y(1+decpos:end) fillval_end*ones(1,decpos)].^2; 
+    end
+    yl = sqrt(nansum(yy)); 
+    %
+   case 'median',
+    yy(in,:) = y; 
+    for k = 1:in-1
+      decneg = in-k; 
+      yy(k,:) = [fillval_beg*ones(1,decneg) y(1:end-decneg)]; 
+    end
+    for k = in+1:length(tab)
+      decpos = k-in; 
+      yy(k,:) = [y(1+decpos:end) fillval_end*ones(1,decpos)]; 
+    end
+    yl = nanmedian(yy); 
+    %
+   case 'max',
+    yy(in,:) = y; 
+    for k = 1:in-1
+      decneg = in-k; 
+      yy(k,:) = [fillval_beg*ones(1,decneg) y(1:end-decneg)]; 
+    end
+    for k = in+1:length(tab)
+      decpos = k-in; 
+      yy(k,:) = [y(1+decpos:end) fillval_end*ones(1,decpos)]; 
+    end
+    yl = nanmax(yy); 
+    %
+   case 'min',
+    yy(in,:) = y;
+    for k = 1:in-1
+      decneg = in-k;
+      yy(k,:) = [fillval_beg*ones(1,decneg) y(1:end-decneg)];
+    end
+    for k = in+1:length(tab)
+      decpos = k-in;
+      yy(k,:) = [y(1+decpos:end) fillval_end*ones(1,decpos)];
+    end
+    yl = nanmin(yy);
+    %
+  end
+else          % y0 is a matrice
+  'coucou'
+end
+%
+y0 = yl; 
+if dk == 0, yl = y0; 
+elseif dk < 0, 
+%keyboard
+%pause
+  yl = [fillval_beg*ones(1,-dk) y0(1:end+dk)]; 
+elseif dk > 0, 
+  yl = [y0(1+dk:end) fillval_end*ones(1,dk)]; 
+end
+
+%keyboard
+%pause
+
+return
diff --git a/tools/filter/medfilt1_perso.m b/tools/filter/medfilt1_perso.m
new file mode 100644
index 0000000000000000000000000000000000000000..19397ff21b2aac66f110cb9947e6bfb375a1d394
--- /dev/null
+++ b/tools/filter/medfilt1_perso.m
@@ -0,0 +1,22 @@
+function y = medfilt1_perso(x,w)
+%MEDFILT1_PERSO 
+%Gere la non presence de signal processing toolbox
+%
+%odd w
+if(mod(w,1)==1)
+    w=w+1;
+end
+%
+n=length(x);
+for i=1:n
+    if(i<=w/2)
+       y(i)=median(x(1:i+w/2)); 
+    elseif(i>=n-w/2)
+       y(i)=median(x(i-w/2:end)); 
+    else
+       y(i)=median(x(i-w/2:i+w/2)); 
+    end
+end
+
+end
+
diff --git a/tools/filter/median_filter.m b/tools/filter/median_filter.m
new file mode 100644
index 0000000000000000000000000000000000000000..51dcea1a82a18925c92843b5031eca8786ea7083
--- /dev/null
+++ b/tools/filter/median_filter.m
@@ -0,0 +1,106 @@
+function y=median_filter(x,n,blksz,DIM)
+%MEDFILT1  One dimensional median filter.
+%   Y = MEDFILT1(X,N) returns the output of the order N, one dimensional
+%   median filtering of X.  Y is the same size as X; for the edge points,
+%   zeros are assumed to the left and right of X.  If X is a matrix,
+%   then MEDFILT1 operates along the columns of X.
+%
+%   If you do not specify N, MEDFILT1 uses a default of N = 3.
+%   For N odd, Y(k) is the median of X( k-(N-1)/2 : k+(N-1)/2 ).
+%   For N even, Y(k) is the median of X( k-N/2 : k+N/2-1 ).
+%
+%   Y = MEDFILT1(X,N,BLKSZ) uses a for-loop to compute BLKSZ ("block size") 
+%   output samples at a time.  Use this option with BLKSZ << LENGTH(X) if 
+%   you are low on memory (MEDFILT1 uses a working matrix of size
+%   N x BLKSZ).  By default, BLKSZ == LENGTH(X); this is the fastest
+%   execution if you have the memory for it.
+%
+%   For matrices and N-D arrays, Y = MEDFILT1(X,N,[],DIM) or 
+%   Y = MEDFILT1(X,N,BLKSZ,DIM) operates along the dimension DIM.
+%
+%   See also MEDIAN, FILTER, SGOLAYFILT, and MEDFILT2 in the Image
+%   Processing Toolbox.
+
+%   Author(s): L. Shure and T. Krauss, 8-3-93
+%   Copyright 1988-2004 The MathWorks, Inc.
+%   $Revision: 1.8.4.2 $  $Date: 2004/12/26 22:16:21 $
+
+% Validate number of input arguments
+narginchk(1,4)
+if nargin < 2, n = []; end
+if nargin < 3, blksz = []; end
+if nargin < 4, DIM = []; end
+
+% Check if the input arguments are valid
+if isempty(n)
+  n = 3;
+end
+
+if ~isempty(DIM) && DIM > ndims(x)
+	error('Dimension specified exceeds the dimensions of X.')
+end
+
+% Reshape x into the right dimension.
+if isempty(DIM)
+	% Work along the first non-singleton dimension
+	[x, nshifts] = shiftdim(x);
+else
+	% Put DIM in the first (row) dimension (this matches the order 
+	% that the built-in filter function uses)
+	perm = [DIM,1:DIM-1,DIM+1:ndims(x)];
+	x = permute(x,perm);
+end
+
+% Verify that the block size is valid.
+siz = size(x);
+if isempty(blksz),
+	blksz = siz(1); % siz(1) is the number of rows of x (default)
+else
+	blksz = blksz(:);
+end
+
+% Initialize y with the correct dimension
+y = zeros(siz); 
+
+% Call medfilt1D (vector)
+for i = 1:prod(siz(2:end)),
+	y(:,i) = medfilt1D(x(:,i),n,blksz);
+end
+
+% Convert y to the original shape of x
+if isempty(DIM)
+	y = shiftdim(y, -nshifts);
+else
+	y = ipermute(y,perm);
+end
+
+
+%-------------------------------------------------------------------
+%                       Local Function
+%-------------------------------------------------------------------
+function y = medfilt1D(x,n,blksz)
+%MEDFILT1D  One dimensional median filter.
+%
+% Inputs:
+%   x     - vector
+%   n     - order of the filter
+%   blksz - block size
+
+nx = length(x);
+if rem(n,2)~=1    % n even
+    m = n/2;
+else
+    m = (n-1)/2;
+end
+X = [zeros(m,1); x; zeros(m,1)];
+y = zeros(nx,1);
+
+% Work in chunks to save memory
+indr = (0:n-1)';
+indc = 1:nx;
+for i=1:blksz:nx
+    ind = indc(ones(1,n),i:min(i+blksz-1,nx)) + ...
+          indr(:,ones(1,min(i+blksz-1,nx)-i+1));
+    xx = reshape(X(ind),n,min(i+blksz-1,nx)-i+1);
+    y(i:min(i+blksz-1,nx)) = median(xx,1);
+end
\ No newline at end of file
diff --git a/moored_adcp_proc/mfilter.m b/tools/filter/mfilter.m
similarity index 100%
rename from moored_adcp_proc/mfilter.m
rename to tools/filter/mfilter.m
diff --git a/tools/filter/naninterp.m b/tools/filter/naninterp.m
new file mode 100644
index 0000000000000000000000000000000000000000..492192d11eca07b06492c0f26631af1c2be6ffed
--- /dev/null
+++ b/tools/filter/naninterp.m
@@ -0,0 +1,5 @@
+function X = naninterp(X)
+% Interpolate over NaNs
+% See INTERP1 for more info
+X(isnan(X)) = interp1(find(~isnan(X)), X(~isnan(X)), find(isnan(X)),'cubic');
+return
\ No newline at end of file
diff --git a/tools/filter/nanmedfilt2.m b/tools/filter/nanmedfilt2.m
new file mode 100644
index 0000000000000000000000000000000000000000..e7c426e80af78865b3288e3c45bdec002c37238d
--- /dev/null
+++ b/tools/filter/nanmedfilt2.m
@@ -0,0 +1,44 @@
+function [M] = nanmedfilt2(A, sz)
+%This MATLAB function performs median filtering of the matrix A in two dimensions
+%while *ignoring* NaNs (based on discussions here
+%http://www.mathworks.com/matlabcentral/newsreader/view_thread/251787)
+
+%A is a 2d matrix, sz is tile size, M is filtered A.
+
+if nargin<2
+    sz = 5;
+end
+if length(sz)==1
+    sz = [sz sz];
+end
+
+if any(mod(sz,2)==0)
+    error('kernel size SZ must be odd)')
+end
+margin=(sz-1)/2;
+AA = nan(size(A)+2*margin);
+AA(1+margin(1):end-margin(1),1+margin(2):end-margin(2))=A;
+[iB,jB]=ndgrid(1:sz(1),1:sz(2));
+is=sub2ind(size(AA),iB,jB);
+[iA, jA]=ndgrid(1:size(A,1),1:size(A,2));
+iA=sub2ind(size(AA),iA,jA);
+idx=bsxfun(@plus,iA(:).',is(:)-1);
+
+B = sort(AA(idx),1);
+j=any(isnan(B),1);
+last = zeros(1,size(B,2))+size(B,1);
+[~, last(j)]=max(isnan(B(:,j)),[],1);
+last(j)=last(j)-1;
+
+M = nan(1,size(B,2));
+valid = find(last>0);
+mid = (1 + last)/2;
+i1 = floor(mid(valid));
+i2 = ceil(mid(valid));
+i1 = sub2ind(size(B),i1,valid);
+i2 = sub2ind(size(B),i2,valid);
+M(valid) = 0.5*(B(i1) + B(i2));
+M = reshape(M,size(A));
+
+end % medianna
+
diff --git a/tools/filter/ndnanfilter.m b/tools/filter/ndnanfilter.m
new file mode 100644
index 0000000000000000000000000000000000000000..cf47c67ae3e67c0d7113f974d2697143117f0bf0
--- /dev/null
+++ b/tools/filter/ndnanfilter.m
@@ -0,0 +1,484 @@
+function [Y,W] = ndnanfilter(X,HWIN,F,DIM,WINOPT,PADOPT,WNAN)
+% NDNANFILTER   N-dimensional zero-phase digital filter, ignoring NaNs.
+%
+%   Syntax:
+%         Y = ndnanfilter(X,HWIN,F);
+%         Y = ndnanfilter(X,HWIN,F,DIM);
+%         Y = ndnanfilter(X,HWIN,F,DIM,WINOPT);
+%         Y = ndnanfilter(X,HWIN,F,DIM,WINOPT,PADOPT);
+%         Y = ndnanfilter(X,HWIN,F,DIM,WINOPT,PADOPT,WNAN);
+%     [Y,W] = ndnanfilter(...);
+%
+%   Input:
+%     X      - Data to be filtered with/without NaNs.
+%     HWIN   - Window function handle (or name) or numeric multidimensional
+%              window to be used (without NaNs). See WINDOW for details.   
+%              Default:   @rectwin  or 'rectwin' (moving average).
+%     F      - A vector specifying the semi-width of the window for each
+%              dimension. The final window's width will be 2*F+1. 
+%              Default: 3 (i.e. a 1-dimensional window of width 6).
+%     DIM    - If F is a single scalar, the window will be applied through
+%              this dimension; otherwise, this will be ignored. 
+%              Default: columns (or the first non-singleton dimension).
+%     WINOPT - Cell array specifying optional arguments for the window
+%              function HWIN (in addition to the width). 
+%              Default: {} (window's defaults).
+%     PADOPT - Cell array specifying the optional arguments for the
+%              PADARRAY MATLAB's function (in addition to the array X and
+%              the padsize: 2*F+1). If the function is not found, data is
+%              padded with zeros or the specified value: try {mean(X(:))}
+%              for example.
+%              Default: {'replicate'} (repeats border elements of X).
+%              Default: {0} (pads with zeros if PADARRAY not found).
+%     WNAN   - Integer indicating NaNs treatment and program behaviour!:
+%              0: Filters data and interpolates NaNs         (default). 
+%              1: Filters data but do not interpolates NaNs 
+%              2: "Do not filters data" but interpolates NaNs!
+%              See the NOTEs below
+%
+%   Output:
+%     Y      - Filtered X data (same size as X!).
+%     W      - N-dimensional window with central symmetry generated by a
+%              special subfunction called NDWIND. See the description below
+%              for details.
+%
+%   Description:
+%     This function applies a N-dimensional convolution of X with W, using
+%     the MATLAB's IMFILTER or CONVN function. One important aspect of the
+%     function is the generation of the N-dimensional window (W) from the
+%     specified function and width, which cannot be done with MATLAB's
+%     functions. Besides, unlike MATLAB's FILTER, FILTER2 and IMFILTER,
+%     NaNs elements are taken into account (ignored).
+%
+%     The N-dimensional window is generated from rotating the 1-dimensional
+%     output of the HWIN function, through each of the N-dimensions, and
+%     then shrinking it through each of its axes in order to fit the
+%     specified semi-widths (F). This is done in the included subfunction
+%     named NDWIND. In this way, the window has central symmetry and do not
+%     produce a phase shift on X data.
+%
+%     By default, the edges are padded with the values of X at the borders
+%     with the PADARRAY MATLAB's function. In this way, the edges are
+%     treated smoothly. When PADARRAY is not found, the program performs
+%     zero-padding.
+%
+%   Notes: 
+%     * The use of semi-widths F's is to force the generated window to be
+%       even and, therefore, the change of phase is null.  
+%     * The window function HWIN should output an even function, otherwise,
+%       it won't generate an error but the user should be aware that this
+%       program will consider only the last half of it.
+%     * The function window should return a monotonically decreasing
+%       result, this restriction is because I try to avoid the use of FZERO
+%       function, for example, to find the expanding/shrinking factors.
+%     * If the user has an already generated window, it can be used in HWIN
+%       instead of a function handle or name. 
+%     * Accepts empty value for any input. When X is empty, the program can
+%       be used as a N-dimensional window generator.
+%     * NaNs elements surrounded by no-NaNs elements (which will depend on
+%       window width) are the ones that will be interpolated. The others
+%       are leaved untouched.
+%     * When WNAN=2, the programs acts like an NAN-interpolat/GAP-filling,
+%       leaving untouched the no-NaNs elements but the filtering is
+%       perfomed anyway. I recomend the default behaviour (WNAN=0) in order
+%       to keep the filtered data in the workspace, and then use the code
+%       at the end of this function to get/remove the interpolated NaNs
+%       (see the example).
+%     * The program looks for the IMFILTER and PADARRAY functions from the
+%       Image Processing Toolbox. If not found, then CONVN is used instead
+%       (slower) and pads with zeros or the given value. In this latter
+%       case, if border elements are NaNs, the window won't work properly.
+%
+%   Example:
+%     FWIN = 'hamming';
+%     F = [13 8];
+%     N = 100;
+%     Pnoise = 0.30;
+%     PNaNs  = 0.20;
+%     X = peaks(N);                                     % original
+%     Y = X + ((rand(size(X))-0.5)*2)*max(X(:))*Pnoise; % add noise
+%     Y(round(1 + (N^2-1).*rand(N^2*PNaNs,1))) = NaN;   % add NaNs
+%     [Z0,W] = ndnanfilter(Y,FWIN,F);                   % filters
+%     Z1 = Z0; Z2 = Y; inan = isnan(Y);
+%     Z1(inan) = NaN;
+%     Z2(inan) = Z0(inan);  
+%     subplot(231), imagesc(X), clim = caxis; axis equal tight
+%                   title('Original data')
+%     subplot(232), imagesc(Y),  caxis(clim), axis equal tight 
+%                   title('Data + NOISE + NaNs')
+%     subplot(234), imagesc(Z0), caxis(clim), axis equal tight 
+%                   title('FILTERS + NaNs interpolation')
+%     subplot(235), imagesc(Z1), caxis(clim), axis equal tight 
+%                   title('FILTERS ignoring NaNs')
+%     subplot(236), imagesc(Z2), caxis(clim), axis equal tight 
+%                   title('GAP-filling with interpolated NaNs')
+%     subplot(233), imagesc(-F(1):F(1),-F(2):F(2),W), axis equal tight, 
+%                    title([upper(FWIN) ' 2D window']), view(2) 
+%
+%   See also: FILTER, FILTER2 and CONVN; WINDOW from the Signal Processing
+%   Toolbox; and FWIND1, FWIND2, FSPECIAL, IMFILTER and PADARRAY from the
+%   Image Processing Toolbox. 
+
+%   Copyright 2008 Carlos Adrian Vargas Aguilera
+%   $Revision: 1.2 $  $Date: 2008/06/30 18:00:00 $
+
+%   Written by
+%   M.S. Carlos Adrian Vargas Aguilera
+%   Physical Oceanography PhD candidate
+%   CICESE 
+%   Mexico, 2008
+%   nubeobscura@hotmail.com
+%
+%   Download from:
+%   http://www.mathworks.com/matlabcentral/fileexchange/loadAuthor.do?objec
+%   tType=author&objectId=1093874
+
+%   1.0    Release (2008/06/23 10:30:00)
+%   1.1    Fixed Bug adding an extra dimension of unitary width. 
+%   1.2    Fixed Bug with ynan.
+
+% Use the IMFILTER function? (faster than CONVN):
+yimfilter = (exist('imfilter','file')==2);
+
+% Use the PADARRAY function (or zero padding): 
+ypadarray = (exist('padarray','file')==2);
+
+% Check inputs and sets defaults of principal arguments:
+if nargin<3 || nargin>7
+ error('Filtern:IncorrectNumberOfInputs',...
+  'At least three inputs are needed and less than 7.')
+end
+if isempty(HWIN)
+ HWIN = 'rectwin';
+end
+if isempty(F)
+ F = 3;
+end
+N = length(F);
+S = size(X);
+% Secondary arguments:
+if N && (nargin<4 || isempty(DIM))
+ DIM = find(S~=1,1);   %  DIM = min(find(S~=1));
+ if isempty(DIM), DIM = 1; end
+end
+if nargin<5 || isempty(WINOPT)
+ WINOPT = {};
+end
+if nargin<6 || isempty(PADOPT)
+ if ypadarray
+  PADOPT = {'replicate'};
+ else
+  PADOPT = {0};
+ end
+elseif ~ypadarray && ~isnumeric(PADOPT{1})
+ PADOPT = {0};
+end
+if nargin<7 || isempty(WNAN)
+ WNAN = 0;
+end
+
+% Selects the 1-dimensional filter or set a row vector: 
+if N==1
+ a = zeros(1,DIM);
+ a(DIM) = F;
+ F = a;
+ clear a
+end
+
+% Checks if the window input is a function or an array:
+if ~isa(HWIN,'function_handle') && ~ischar(HWIN)
+ W = HWIN;
+else
+ W = [];
+end
+
+% If no input data but two outputs then generates the window only:
+if isempty(X)
+ Y = [];
+ if nargout==2 && ~isempty(W)
+  W = ndwind(HWIN,F,WINOPT{:});
+ end
+ return
+end
+
+% Generates the window:
+if isempty(W)
+ W = ndwind(HWIN,F,WINOPT{:});
+end
+
+% Check for NaN's:
+inan = isnan(X);
+ynan = any(inan(:));                       % Bug fixed 30/jun/2008
+if ynan
+ X(inan) = 0;
+else
+ factor = sum(W(:));
+end
+
+% Filtering:
+if yimfilter                                % Use IMFILTER (faster)
+ if ~isfloat(X)
+  X = double(X);
+ end
+ if ~isfloat(W)
+  W = double(W);
+ end
+ if ynan
+  Y = imfilter(X,W       ,PADOPT{:},'conv');
+ else
+  Y = imfilter(X,W/factor,PADOPT{:},'conv');
+ end
+else                                        % Use CONVN
+ % Sets F and S of equal sizes.
+ F = reshape(F,1,N);
+ Nx = numel(S);
+ if N<Nx
+  F(N+1:Nx) = 0;
+ elseif N>Nx
+  S(Nx+1:N) = 1;
+ end
+ F2 = 2*F;
+ % Pads the borders:
+ if ypadarray
+  ind    = padarray(false(S),F2,true     );    % Index of the padding.
+  Y      = padarray(X       ,F2,PADOPT{:});
+ elseif length(PADOPT{1})==1
+  ind2 = cell(N,1);
+  for n = 1:N
+   ind2{n} = F2(n) + (1:S(n)).';
+  end
+  ind          = repmat(true     ,2*F2+S);
+  Y            = repmat(PADOPT{1},2*F2+S);
+  ind(ind2{:}) = false;
+    Y(ind2{:}) = X;
+ else % No padding at all
+  Y    = X;
+  ind  = repmat(false,S); 
+  warning('Ndnanfilter:PaddingOption','Do not perfom any padding.')
+ end
+ % Convolutes both arrays:
+ if ynan
+  Y = convn(Y,W       ,'same');
+ else
+  Y = convn(Y,W/factor,'same');
+ end
+ %  Eliminates the padding:
+ Y(ind) = [];
+ Y      = reshape(Y,S);   
+end
+
+% Estimates the averages when NaNs are present:
+if ynan
+ if yimfilter
+  factor       = imfilter(double(~inan),W,PADOPT{:},'conv');
+ else
+  if ypadarray
+   factor      = padarray(~inan,F2,PADOPT{:});
+  elseif length(PADOPT{1})==1 % (won't work properly with NaNs at borders)
+   factor          = ind;
+   factor(ind2{:}) = ~inan;
+  else
+   factor = ~inan;
+  end
+  factor      = convn(factor,W,'same');
+  factor(ind) = [];
+  factor      = reshape(factor,S);
+ end
+ Y = Y./factor;
+end
+
+% What about NaNs?:
+if     WNAN == 1       % Leave NaNs elements untouched!
+ Y(inan) = NaN;
+elseif WNAN == 2       % Leave no-NaNs elements untouched!!!
+ X(inan) = Y(inan);
+ Y = X;
+end  
+
+
+function W = ndwind(HWIN,F,varargin)
+% NDWIND Generate a N-Dimensional zero-phase window.
+%
+%   Syntax:
+%     W = ndwind(HWIN,F);
+%     W = ndwind(HWIN,F,OPT);
+%
+%   Input:
+%     HWIN - Window function handle. See WINDOW for details. By default
+%            uses: @rectwin (a rectangular window).
+%     F    - A vector specifying the semiwidth of the window for each
+%            dimension. The window's width will be 2*F+1. By default uses:
+%            3 (i.e. a window of width 6). 
+%     OPT  - Cell array specifying optional arguments for the window
+%            function. By default uses: {[]} (window's defaults).
+%
+%   Output:
+%     W    - N-Dimensional window with central symmetry.
+%
+%   Description:
+%     In the axes of each dimension, W has a 1-D window defined as
+%              feval(HWIN,2*F(n)+1), n = 1,...,N.
+%     That is, they are defined by the same window function but have
+%     different widths. So, this program creates another widther window (at
+%     least 201 points), with the same definition, and finds how much the
+%     former windows should be expanded in order to fit the latter one. 
+%
+%     Afterwards, the coordinates of every point are expanded accordingly
+%     and the value of the window in those points are found by linear
+%     interpolation with the bigger window. 
+%
+%     In resume, it is like rotating this big window through every
+%     dimension and then shrinking it through each of its axes to fix the
+%     specified widths.
+%
+%   Notes: 
+%     * Because of the use of the semi-widths F's, all the generated
+%       windows are even. Therefore the change of phase is null. 
+%     * The window function HWIN should output an even function, otherwise,
+%       it won't generate an error but this program will consider only the
+%       last half of it.
+%     * The window should be monotonically decreasing.
+%     * Instead of the handle window, it can be given as a string:
+%       'hamming' instead of @hamming, for example.
+%     * Uses the MATLAB's function FUNC2STR.
+%
+%   Example:
+%     W = ndwind(@hamming,[3 2])
+%     % Results:
+%     W =
+%     
+%              0         0    0.0800         0         0
+%              0    0.1417    0.3100    0.1417         0
+%              0    0.3966    0.7700    0.3966         0
+%         0.0800    0.5400    1.0000    0.5400    0.0800
+%              0    0.3966    0.7700    0.3966         0
+%              0    0.1417    0.3100    0.1417         0
+%              0         0    0.0800         0         0
+%
+%
+%   See also: WINDOW from the Signal Processing Toolbox; and FWIND1,
+%   FWIND2, and FSPECIAL from the Image Processing Toolbox.
+
+%   Copyright 2008 Carlos Adrian Vargas Aguilera
+%   $Revision: 1.1 $  $Date: 2008/06/26 19:30:00 $
+
+%   Written by
+%   M.S. Carlos Adrian Vargas Aguilera
+%   Physical Oceanography PhD candidate
+%   CICESE 
+%   Mexico, 2008
+%   nubeobscura@hotmail.com
+%
+%   Download from:
+%   http://www.mathworks.com/matlabcentral/fileexchange/loadAuthor.do?objec
+%   tType=author&objectId=1093874
+
+%   1.0    Release (2008/06/23 10:30:00)
+%   1.1    Fixed Bug adding an extra dimension of unitary width.  
+
+% Check inputs:
+if nargin<1 || isempty(HWIN)
+ HWIN = 'rectwin';
+end
+if nargin<2 || isempty(F)
+ F = 3;
+end
+
+% Rectangular wind?:
+if isa(HWIN,'function_handle')
+ HWIN = func2str(HWIN);
+end
+if strcmpi(HWIN,'rectwin')
+ W = ones([2*F(:).'+1 1]);
+ return
+end
+
+% Generate the BIG window (only the last half):
+FBIG         = max([100; F(:)]);
+BIGw         = feval(HWIN,2*FBIG+1,varargin{:});
+BIGw(1:FBIG) = [];       % Deletes the first half.
+rBIGw        = 0:FBIG;   % Window argument (distance).
+
+% Axial windows widths:
+N  = numel(F);
+F  = reshape(F,1,N); 
+F  = [F 0];             % BUG fixed by adding an extra dimension.
+N  = N+1;
+F2 = 2*F+1;
+
+
+% Pre-allocates the final window and the expanded axis:
+W  = zeros(F2);
+An = cell(N,1);
+Ae = An;
+
+% Generates the index and expanded axes:
+for n = 1:N
+ 
+ % Generate temporally the window in the n-axis:
+ wn = feval(HWIN,F2(n),varargin{:});
+ 
+ % Finds the expansion factors (Note: the window should tends to zero):
+ if F(n)
+  piv = wn(end);
+  ind = (BIGw == piv);
+  if ~any(ind)
+   ind1 = (BIGw >= piv); ind1 = length(ind1(ind1));
+   ind2 = (BIGw <= piv); ind2 = length(ind2(~ind2))+1;
+   if ind2>FBIG+1
+    r = rBIGw(ind1);
+   else
+    r = interp1(BIGw([ind1 ind2]), rBIGw([ind1 ind2]),piv);
+   end
+  else
+   r = rBIGw(ind);
+  end
+  Ef = r/F(n);
+ else
+  Ef = 1;
+ end
+ 
+ % Reversed index and expanded n-axis (for the following grid):
+ An{n} = (F(n):-1:0);
+ Ae{n} = An{n}*Ef;
+ 
+end
+
+% Estimates the expanded distances outside the axes (only at the 1st
+% quarter):
+% Note: In a 2-Dimensional matrix, by the 1st quarter of a matrix I mean
+% the first 1/4 piece of the matrix after you divided it throuh the middle
+% row and column. In N-dimensions it would be the 1st 1/2^N part.
+gride4      = cell(N,1);
+[gride4{:}] = ndgrid(Ae{:});
+R4          = sqrt(sum(reshape([gride4{:}],prod(F+1),N).^2,2));
+
+% Generates the window and linear index in the 1st quarter:
+grid4     = cell(N,1);
+[grid4{:}]= ndgrid(An{:});
+in        = (R4<=rBIGw(end));           % Looks for elements inside window.
+W4        = zeros(F+1);                 % 1st quarter of the window.
+W4(in)    = interp1(rBIGw,BIGw,R4(in)); % Interpolates the window values.
+for n=1:N                               % Linear index on the 1st quarter.
+ grid4{n} = flipdim(grid4{n}+1,n);
+end
+ind4      = sub2ind(F2,grid4{:});
+
+% Index of permutations to fill the N-D window:
+np = 2^N-1;
+ip = zeros(1,np);
+for n = 1:N
+ ini  = 2^(n-1);
+ step = ini*2;
+ ip(ini:step:np) = n;
+end
+
+% Fills the N-D window by flipping W4 and the index: 
+ones4       = repmat(false,F2);    % Avoids using new FALSE function
+ones4(ind4) = true;
+W(ones4)    = W4;
+for kp = ip
+ W4         = flipdim(W4,kp);
+ ones4      = flipdim(ones4,kp);
+ W(ones4)   = W4;
+end
\ No newline at end of file
diff --git a/tools/filter/spikeRemoval.m b/tools/filter/spikeRemoval.m
new file mode 100644
index 0000000000000000000000000000000000000000..bd1d56126a9b84c0e7b0d8d10d82d71e4929b9c8
--- /dev/null
+++ b/tools/filter/spikeRemoval.m
@@ -0,0 +1,601 @@
+function [x,spikes] = spikeRemoval(w,varargin)
+%   DESPIKING DISCRETE-TIME SIGNAL USING HISTOGRAM METHOD (a.k.a. DELETE OUTLIERS)
+%
+%   Time series may contain undesired transients and spikes. This function
+%   replaces sudden spikes (outliers) exceeding the threshold value by
+%   either:
+%
+%   (1) interpolating among previous and subsequent data points, or
+%   (2) replacing them with NaN (delete outliers)
+%
+%   User needs to choose one of the above options; default is interpolation.
+%
+%   "w" is the input array and "x" is the spike removed output array. In
+%   addition, output argument "spikes" is a structure array, which returns
+%   the indices of spikes, corresponding values, and replacement values. If
+%   npass parameter is selected as 2 (it means the code will run twice),
+%   then "spikes" will be the structure array of results corresponding to
+%   each run.
+%
+%   The threshold is defined as mean +/- a number of standard deviations of
+%   windowed data centered at spike locations.
+%
+%   USAGE:
+%   [x,spikes] = spikeRemoval(w) 
+%
+%   or
+%   
+%   [x,spikes] = spikeRemoval(w,prop_name,prop_val)
+%
+%   STATIC INPUT:
+%            w = vector of time series data (1xn or nx1)
+%
+%   VALID PROP_NAME / PROP_VAL PAIRS:
+%   -----------------------------------------
+%   'wnsz'   --> (1x1)-[numeric]-[default:25]
+%   'nstd'   --> (1x2)-[numeric]-[default: 3]
+%   'nbins'  --> (1x2)-[numeric]-[default: 4]
+%   'ndata'  --> (1x1)-[numeric]-[default: 5]
+%   'npass'  --> (1x1)-[numeric]-[default: 2]
+%   'method' --> [text]-[default: 'Linear']
+%   'debug'  --> [text]-[default: 'True']
+%
+%  NOTES:
+%         x = spike removed time series
+%
+%    spikes = 3xn structure array, first column contains indexes of spikes,
+%             second column contains values of spikes and third column
+%             contains replacement values
+%
+%      wnsz = window size to compute mean and standard deviation for
+%             thresholding
+%
+%      nstd = number of standard deviations above and below mean
+%
+%     nbins = number of bins used in histogram method
+%
+%     ndata = number of neighbouring data points for spike replacement
+%
+%     npass = number of passes for spike detection and replacement (e.g.,
+%             1); Suggest npass = 2 or 3 for noisy data with back-to-back
+%             spikes
+%
+%    method = interpolation method (linear, spline, cubic, nearest). To
+%             delete outliers chose method as 'delete' 
+%             (e.g., 'method','delete').
+%
+%     debug = 'True' for print debug messages and plot results or '' for no
+%             debug messages or plots
+%
+%   WARNING: This function is for detecting sudden spikes, it may not be
+%            effective detecting long duration spike-like pulses.
+%
+%   EXAMPLE 1 (using defaults values):
+%
+%    w = rand(500,1)*10; w(ceil(rand(1,20)*500))=rand(1,20)*100;
+%    [x,spikes] = spikeRemoval(w);
+%
+%   EXAMPLE 2 (using user defined values for interpolation):
+%
+%    w = rand(500,1)*10; w(ceil(rand(1,20)*500))=rand(1,20)*100;
+%    [x,spikes] = spikeRemoval(w,'wnsz',20,'nstd',2,'npass',1);
+%
+%   EXAMPLE 3 (using user defined values for deleting outliers):
+%
+%    w = rand(500,1)*10; w(ceil(rand(1,20)*500))=rand(1,20)*100;
+%    [x,spikes] = spikeRemoval(w,'nstd',2,'npass',1,'method','delete');
+%
+%   REQUIREMENTS:
+%   spikeRemoval function doesn't require any MatLAB toolbox.
+%
+%   ACKNOWLEDGEMENTS:
+%   I would like thank Dr. Ayal Anis of Texas AM and Jeanne Jones of USGS
+%   for their valuable comments and suggestions, which helped improving
+%   this code significantly. Also, thanks to Jan Glscher of University of
+%   Hamburg for making his nanmean and nanstd codes available. Finally,
+%   thanks to James for making his cent_diff_n function available at MatLAB
+%   FEX.
+%
+%   REFERENCES:
+%
+%   Solomon, O. M., D. R. Larson, and N. G. Paulter (2001). Comparison of
+%   some algorithms to estimate the low and high state level of pulses,
+%   IEEE Instrumentation and Measurement Technology Conference, Vol. 1,
+%   Budapest, Hungary, 21?23 May 2001, 96?101.
+%
+%   cent_diff_n function is available at
+%   https://www.mathworks.com/matlabcentral/fileexchange/36123-n-point-central-differencing
+%   
+%   nanmean and nanstd functions are available at
+%   https://www.mathworks.com/matlabcentral/fileexchange/6837-nan-suite
+%
+%   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+%   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+%   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+%   NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY DIRECT, INDIRECT,
+%   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+%   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+%   OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+%   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+%   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+%   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+%   DAMAGE.
+%
+%   Written by Dr. Erol Kalkan, P.E. (kalkan76@gmail.com)
+%   Revision: 2.1.0
+%   Date: 2019/03/20 08:58:00 $
+
+%% DEFAULT PROPERTIES
+if (nargin >= 1)
+    wnsz = 25;
+    nstd = 3;
+    ndata = 5;
+    nbins = 4;
+    method = 'Linear';
+    debug = 'True';
+    npass = 2;
+else
+    error('spikeRemoval: First argument must be a waveform')
+end
+
+%% USER-DEFINED PROPERTIES
+if (nargin > 1)
+    v = varargin;
+    nv = nargin-1;
+    if ~rem(nv,2) == 0
+        error(['spikeRemoval: Arguments after wave must appear in ',...
+            'property name/val pairs'])
+    end
+    for n = 1:2:nv-1
+        name = lower(v{n});
+        val = v{n+1};
+        switch name
+            case 'nstd'
+                nstd = val;
+            case 'wnsz'
+                wnsz = val;
+            case 'ndata'
+                ndata = val;
+            case 'method'
+                method = val;
+            case 'debug'
+                debug = val;
+            case 'nbins'
+                nbins = val;
+            case 'npass'
+                npass = val;
+            otherwise
+                error('spikeRemoval: Property name not recognized')
+        end
+    end
+end
+
+% Initialize
+nspk  = 0; % number of spikes (outliers)
+x = w;
+
+for i = 1:npass
+    [x,spikes(i).results,nspk] = run(x,nspk,nstd,wnsz,ndata,method,debug,nbins);
+end
+if debug
+    fprintf('Number of spikes found and replaced = %4d\n',nspk);
+    figure(i+1);
+    subplot(311); plot(w,'r'); hold on; title('Original'); lim = ylim;
+    subplot(312); plot(x); title('SpikeRemoval'); ylim(lim);
+    subplot(313); plot(medfilt1_perso(w,nstd),'g');
+    title('MatLAB 1-D Median Filter'); ylim(lim);
+end
+end
+
+function [x,spikes,nspk] = run(x,nspk,nstd,wnsz,ndata,method,debug,nbins)
+% Enforce input as row vector
+if ~isrow(x)
+    x = x';
+end
+
+% Compute first derivative using central differentiation with order 3
+xdot = cent_diff_n(x,1,3);
+
+% Define spike locations via histogram method
+R = statelevel(abs(xdot),nbins);
+locs = find(abs(xdot) > R(1));
+
+if debug
+    figure(1); plot(xdot); hold on; plot(locs,xdot(locs),'ro');
+    title('Spikes to be checked on first derivative of time series');
+end
+
+for i = 1:length(locs)
+    [x,spike,flag] = spikeFix(x,locs(i),nstd,wnsz,ndata,method,debug);
+    nspk = nspk + flag;
+    if nspk ~= 0 && flag == 1
+        spikes(nspk,:) = spike;
+    end
+end
+if ~exist('spikes','var')
+    spikes = -1;
+end
+end
+
+function [x,spike,flag] = spikeFix(x,idx,nstd,wnsz,ndata,method,debug)
+%   Input:
+%         x = vector of time series (1xn)
+%
+%       idx = spike location
+%
+%   Output:
+%         x = vector of spiked removed time series
+%
+%      flag = 1 or 0; 1 means spike is found and fixed, 0 means no spike
+%             detected
+
+% If spike is at the beginning, replace it with the mean value of next
+% ndata points
+if idx <= 2*ndata
+    w = x(idx);
+    if method == 'delete'
+        x(idx) = NaN;
+    else
+        x(idx) = nanmean(x(idx+1:idx+ndata));
+    end
+    flag = 1;
+    spike = [idx,w,x(idx)];
+    if debug
+        fprintf('Peak of %5.2f @%5.0f is replaced by %5.2f\n',w,idx,x(idx));
+    end
+    % If spike is at the end, replace it with the mean value of previous
+    % ndata points
+elseif idx >= length(x)-2*ndata
+    w = x(idx);
+    if method == 'delete'
+        x(idx) = NaN;
+    else
+        x(idx) = nanmean(x(idx-ndata:idx-1));
+    end
+    flag = 1;
+    spike = [idx,w,x(idx)];
+    if debug
+        fprintf('Peak of %5.2f @%5.0f is replaced by %5.2f\n',w,idx,x(idx));
+    end
+else
+    % Set spike search region. Note: arr needs to be updated if n-point
+    % central difference method is used for differentiation, for instance
+    % if 5-point central difference method is used then arr =
+    % (idx-2:idx+1); the current implementation uses a 3-point central
+    % difference
+    arr = (idx-1:idx+1);
+    [~, idx] = max(abs(x(arr)));
+    i = arr(idx);
+    
+    % Overwrite window size if spike occurs within first or last data
+    % window
+    if i <= floor(wnsz/2)
+        wnsz = (i-1)*2;
+    elseif i >= length(x) - floor(wnsz/2)
+        wnsz = length(x) - i;
+    end
+    
+    % Overwrite ndata if it is bigger than half of window size
+    if ndata > floor(wnsz/2)
+        ndata = floor(wnsz/2)-1;
+    end
+    
+    % Compute mean and standard deviation of data window centered at spike
+    % by excluding spike
+    arr1 = [x((i - floor(wnsz/2)):(i-1)), x((i+1):(i + floor(wnsz/2)))];
+    mu_x = nanmean(arr1); std_x = nanstd(arr1);
+    
+    if debug
+        fprintf('wnsz =%5.0f, ndata =%5.0f, i = %5.0f, x(i) = %5.2f, mu_x = %5.2f, std_x = %5.2f\n', ...
+            wnsz,ndata,i,x(i),mu_x,std_x);
+    end
+    
+    % Check threshold exceedance
+    if x(i) >= mu_x + std_x*nstd || x(i) <= mu_x - std_x*nstd
+        w = x(i);
+        
+        if method == 'delete'
+            x(i) = NaN;
+        else
+            % Replace spike region using interpolation of neighbouring data
+            % points
+            x(i-1:i+1) = interp1([i-ndata-1:i-2,i+2:i+ndata+1],...
+                [x(i-ndata-1:i-2),x(i+2:i+ndata+1)],i-1:i+1,method);
+        end
+        flag = 1;
+        spike = [i,w,x(i)];
+        if debug
+            fprintf('Peak of %5.2f @%5.0f is replaced by %5.2f\n',w,i,x(i));
+        end
+    else
+        flag = 0;
+        spike = -1;
+    end
+end
+end
+
+function [levels, histogram, bins] = statelevel(y,n)
+ymax = max(y);
+ymin = min(y)-eps;
+
+% Compute histogram
+idx = ceil(n * (y-ymin)/(ymax-ymin));
+idx = idx(idx>=1 & idx<=n);
+histogram = zeros(n, 1);
+for i=1:numel(idx)
+    histogram(idx(i)) = histogram(idx(i)) + 1;
+end
+
+% Compute center of each bin
+ymin = min(y);
+Ry = ymax-ymin;
+dy = Ry/n;
+bins = ymin + ((1:n)-0.5)*dy;
+
+% Compute state levels
+iLowerRegion = find(histogram > 0, 1, 'first');
+iUpperRegion = find(histogram > 0, 1, 'last');
+
+iLow  = iLowerRegion(1);
+iHigh = iUpperRegion(1);
+
+% Define the lower and upper histogram regions halfway between the lowest
+% and highest nonzero bins.
+lLow  = iLow;
+lHigh = iLow + floor((iHigh - iLow)/2);
+uLow  = iLow + floor((iHigh - iLow)/2);
+uHigh = iHigh;
+
+% Upper and lower histograms
+lHist = histogram(lLow:lHigh, 1);
+uHist = histogram(uLow:uHigh, 1);
+
+levels = zeros(1,2);
+[~, iMax] = max(lHist(2:end));
+[~, iMin] = max(uHist);
+levels(1) = ymin + dy * (lLow + iMax(1) - 1.5);
+levels(2) = ymin + dy * (uLow + iMin(1) - 1.5);
+end
+
+function y = nanmean(x,dim)
+% FORMAT: Y = NANMEAN(X,DIM)
+% 
+%    Average or mean value ignoring NaNs
+%
+%    This function enhances the functionality of NANMEAN as distributed in
+%    the MATLAB Statistics Toolbox and is meant as a replacement (hence the
+%    identical name).  
+%
+%    NANMEAN(X,DIM) calculates the mean along any dimension of the N-D
+%    array X ignoring NaNs.  If DIM is omitted NANMEAN averages along the
+%    first non-singleton dimension of X.
+%
+%    Similar replacements exist for NANSTD, NANMEDIAN, NANMIN, NANMAX, and
+%    NANSUM which are all part of the NaN-suite.
+%
+%    See also MEAN
+
+% -------------------------------------------------------------------------
+%    author:      Jan Glscher
+%    affiliation: Neuroimage Nord, University of Hamburg, Germany
+%    email:       glaescher@uke.uni-hamburg.de
+%    
+%    $Revision: 1.1 $ $Date: 2004/07/15 22:42:13 $
+
+if isempty(x)
+	y = NaN;
+	return
+end
+
+if nargin < 2
+	dim = min(find(size(x)~=1));
+	if isempty(dim)
+		dim = 1;
+	end
+end
+
+% Replace NaNs with zeros.
+nans = isnan(x);
+x(isnan(x)) = 0; 
+
+% denominator
+count = size(x,dim) - sum(nans,dim);
+
+% Protect against a  all NaNs in one dimension
+i = find(count==0);
+count(i) = ones(size(i));
+
+y = sum(x,dim)./count;
+y(i) = i + NaN;
+% $Id: nanmean.m,v 1.1 2004/07/15 22:42:13 glaescher Exp glaescher $
+end
+
+function y = nanstd(x,dim,flag)
+% FORMAT: Y = NANSTD(X,DIM,FLAG)
+% 
+%    Standard deviation ignoring NaNs
+%
+%    This function enhances the functionality of NANSTD as distributed in
+%    the MATLAB Statistics Toolbox and is meant as a replacement (hence the
+%    identical name).  
+%
+%    NANSTD(X,DIM) calculates the standard deviation along any dimension of
+%    the N-D array X ignoring NaNs.  
+%
+%    NANSTD(X,DIM,0) normalizes by (N-1) where N is SIZE(X,DIM).  This make
+%    NANSTD(X,DIM).^2 the best unbiased estimate of the variance if X is
+%    a sample of a normal distribution. If omitted FLAG is set to zero.
+%    
+%    NANSTD(X,DIM,1) normalizes by N and produces the square root of the
+%    second moment of the sample about the mean.
+%
+%    If DIM is omitted NANSTD calculates the standard deviation along first
+%    non-singleton dimension of X.
+%
+%    Similar replacements exist for NANMEAN, NANMEDIAN, NANMIN, NANMAX, and
+%    NANSUM which are all part of the NaN-suite.
+%
+%    See also STD
+
+% -------------------------------------------------------------------------
+%    author:      Jan Glscher
+%    affiliation: Neuroimage Nord, University of Hamburg, Germany
+%    email:       glaescher@uke.uni-hamburg.de
+%    
+%    $Revision: 1.1 $ $Date: 2004/07/15 22:42:15 $
+
+if isempty(x)
+	y = NaN;
+	return
+end
+
+if nargin < 3
+	flag = 0;
+end
+
+if nargin < 2
+	dim = min(find(size(x)~=1));
+	if isempty(dim)
+		dim = 1; 
+	end	  
+end
+
+
+% Find NaNs in x and nanmean(x)
+nans = isnan(x);
+avg = nanmean(x,dim);
+
+% create array indicating number of element 
+% of x in dimension DIM (needed for subtraction of mean)
+tile = ones(1,max(ndims(x),dim));
+tile(dim) = size(x,dim);
+
+% remove mean
+x = x - repmat(avg,tile);
+
+count = size(x,dim) - sum(nans,dim);
+
+% Replace NaNs with zeros.
+x(isnan(x)) = 0; 
+
+
+% Protect against a  all NaNs in one dimension
+i = find(count==0);
+
+if flag == 0
+	y = sqrt(sum(x.*x,dim)./max(count-1,1));
+else
+	y = sqrt(sum(x.*x,dim)./max(count,1));
+end
+y(i) = i + NaN;
+
+% $Id: nanstd.m,v 1.1 2004/07/15 22:42:15 glaescher Exp glaescher $
+end
+
+function df = cent_diff_n(f,h,n)
+% df = cent_diff_n(f,h,n)
+% Computes an n-point central difference of function f with spacing h.
+% Returns a vector df of same size as f.
+% Input f must be a vector with evenly spaced points.
+% Input n must be 3,5,7, or 9.
+% All three inputs are required.
+%
+% Differences for points near the edges are calculated with lower order.
+% For example, if n=5 and length(f)=10, then 3-point central differencing is used
+% to calculate values at points 2 and 9, 2-point forward differencing is used for
+% point 1, 2-point backward differencing is used for point 10, and 5-point central
+% differencing is used for points 3-7.
+%
+% If f contains less than n points, the order will be downgraded to the
+% maximum possible.  Ex: if length(f) = 6, n will be downgraded to 5.
+%
+% Differencing formulae from: http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/central-differences/
+% Accessed 4/10/12.
+
+if nargin < 3
+    error('Not enough inputs.  See help documentation.')
+end
+
+if ~isscalar(h)
+    error('Input h must be a scalar value.')
+end
+
+possible_ns = [3,5,7,9];
+if ~ismember(n,possible_ns)
+    error('Input n must be 3,5,7, or 9')
+end
+
+numPts = length(f);
+if numPts < n
+    newN = max(possible_ns(possible_ns<=numPts));
+    warnstr = [num2str(n) '-point differencing was requested,\n'...
+        'but input function only has ' num2str(numPts) ' points.\n'...
+        'Switching to ' num2str(newN) '-point differencing.'];
+    warning(warnstr,'%s')
+    n = newN;
+end
+
+df_1 = b_diff(f,h);
+df_End = f_diff(f,h);
+
+% Calculate 3-point for all
+df_3pt = c_diff(f,h,3);
+
+if n >=5
+    df_5pt = c_diff(f,h,n);
+    % For the 2nd and next-to-last grid point, use 3-point differencing.
+    df_2 = df_3pt(1);
+    df_Endm1 = df_3pt(end);
+end
+if n >=7
+    df_7pt = c_diff(f,h,7);
+    % For the 3nd and 2nd from last grid point, use 5-point differencing.
+    df_3 = df_5pt(1);
+    df_Endm2 = df_5pt(end);
+end
+if n>= 9
+    df_9pt = c_diff(f,h,9);
+    % For the 4nd and 3rd from last grid point, use 7-point differencing.
+    df_4 = df_7pt(1);
+    df_Endm3 = df_7pt(end);
+end
+
+switch n
+    case 3
+        df = [df_1 df_3pt df_End];
+    case 5
+        df = [df_1 df_2 df_5pt df_Endm1 df_End];
+    case 7
+        df = [df_1 df_2 df_3 df_7pt df_Endm2 df_Endm1 df_End];
+    case 9
+        df = [df_1 df_2 df_3 df_4 df_9pt df_Endm3 df_Endm2 df_Endm1 df_End];
+end
+end
+
+function df = c_diff(f,h,n)
+midStartPoint = ceil(n/2); % First point at which full n points can be used
+midEndPoint = length(f)-midStartPoint+1; % Last point at which full n points can be used
+
+df = [];
+for k = midStartPoint:midEndPoint
+    switch n
+        case 3
+            df_k = (f(k+1) - f(k-1))/(2*h);
+        case 5
+            df_k = (f(k-2) - 8*f(k-1) + 8*f(k+1) - f(k+2))/(12*h);
+        case 7
+            df_k = (-f(k-3) + 9*f(k-2) - 45*f(k-1) + 45*f(k+1) - 9*f(k+2) + f(k+3))/(60*h);
+        case 9
+            df_k = (3*f(k-4) - 32*f(k-3) + 168*f(k-2) - 672*f(k-1) + 672*f(k+1) - 168*f(k+2) + 32*f(k+3) - 3*f(k+4))/(840*h);
+    end
+    df = [df df_k];
+end
+end
+
+function df1 = b_diff(f,h)
+df1 = (f(2)-f(1))/h;
+end
+
+function dfEnd = f_diff(f,h)
+dfEnd = (f(end)-f(end-1))/h;
+end
\ No newline at end of file
diff --git a/tools/filter/sw_svel.m b/tools/filter/sw_svel.m
new file mode 100644
index 0000000000000000000000000000000000000000..50aa6b5fb4165d03503e9090daf1063b9cdbd88f
--- /dev/null
+++ b/tools/filter/sw_svel.m
@@ -0,0 +1,166 @@
+
+function svel = sw_svel(S,T,P)
+
+% SW_SVEL    Sound velocity of sea water
+%=========================================================================
+% SW_SVEL  $Id: sw_svel.m,v 1.1 2003/12/12 04:23:22 pen078 Exp $
+%          Copyright (C) CSIRO, Phil Morgan 1993.
+%
+% USAGE:  svel = sw_svel(S,T,P)
+%
+% DESCRIPTION:
+%    Sound Velocity in sea water using UNESCO 1983 polynomial.
+%
+% INPUT:  (all must have same dimensions)
+%   S = salinity    [psu      (PSS-78)]
+%   T = temperature [degree C (ITS-90)]
+%   P = pressure    [db]
+%       (P may have dims 1x1, mx1, 1xn or mxn for S(mxn) )
+%
+% OUTPUT:
+%   svel = sound velocity  [m/s]
+%
+% AUTHOR:  Phil Morgan 93-04-20, Lindsay Pender (Lindsay.Pender@csiro.au)
+%
+% DISCLAIMER:
+%   This software is provided "as is" without warranty of any kind.
+%   See the file sw_copy.m for conditions of use and licence.
+%
+% REFERENCES:
+%    Fofonoff, P. and Millard, R.C. Jr
+%    Unesco 1983. Algorithms for computation of fundamental properties of
+%    seawater, 1983. _Unesco Tech. Pap. in Mar. Sci._, No. 44, 53 pp.
+%=========================================================================
+
+% Modifications
+% 99-06-25. Lindsay Pender, Fixed transpose of row vectors.
+% 03-12-12. Lindsay Pender, Converted to ITS-90.
+
+% CALLER: general purpose
+% CALLEE: none
+
+% UNESCO 1983. eqn.33  p.46
+
+%----------------------
+% CHECK INPUT ARGUMENTS
+%----------------------
+if nargin ~=3
+   error('sw_svel.m: Must pass 3 parameters')
+end %if
+
+% CHECK S,T,P dimensions and verify consistent
+[ms,ns] = size(S);
+[mt,nt] = size(T);
+[mp,np] = size(P);
+
+
+% CHECK THAT S & T HAVE SAME SHAPE
+if (ms~=mt) | (ns~=nt)
+   error('check_stp: S & T must have same dimensions')
+end %if
+
+% CHECK OPTIONAL SHAPES FOR P
+if     mp==1  & np==1      % P is a scalar.  Fill to size of S
+   P = P(1)*ones(ms,ns);
+elseif np==ns & mp==1      % P is row vector with same cols as S
+   P = P( ones(1,ms), : ); %   Copy down each column.
+elseif mp==ms & np==1      % P is column vector
+   P = P( :, ones(1,ns) ); %   Copy across each row
+elseif mp==ms & np==ns     % PR is a matrix size(S)
+   % shape ok
+else
+   error('check_stp: P has wrong dimensions')
+end %if
+
+%***check_stp
+
+%---------
+% BEGIN
+%--------
+
+P = P/10;  % convert db to bars as used in UNESCO routines
+T68 = T * 1.00024;
+
+%------------
+% eqn 34 p.46
+%------------
+c00 = 1402.388;
+c01 =    5.03711;
+c02 =   -5.80852e-2;
+c03 =    3.3420e-4;
+c04 =   -1.47800e-6;
+c05 =    3.1464e-9;
+
+c10 =  0.153563;
+c11 =  6.8982e-4;
+c12 = -8.1788e-6;
+c13 =  1.3621e-7;
+c14 = -6.1185e-10;
+
+c20 =  3.1260e-5;
+c21 = -1.7107e-6;
+c22 =  2.5974e-8;
+c23 = -2.5335e-10;
+c24 =  1.0405e-12;
+
+c30 = -9.7729e-9;
+c31 =  3.8504e-10;
+c32 = -2.3643e-12;
+
+Cw = ((((c32.*T68 + c31).*T68 + c30).*P + ...
+       ((((c24.*T68 + c23).*T68 + c22).*T68 + c21).*T68 + c20)).*P + ...
+       ((((c14.*T68 + c13).*T68 + c12).*T68 + c11).*T68 + c10)).*P + ...
+       ((((c05.*T68 + c04).*T68 + c03).*T68 + c02).*T68 + c01).*T68 + c00;
+
+%-------------
+% eqn 35. p.47
+%-------------
+a00 =  1.389;
+a01 = -1.262e-2;
+a02 =  7.164e-5;
+a03 =  2.006e-6;
+a04 = -3.21e-8;
+
+a10 =  9.4742e-5;
+a11 = -1.2580e-5;
+a12 = -6.4885e-8;
+a13 =  1.0507e-8;
+a14 = -2.0122e-10;
+
+a20 = -3.9064e-7;
+a21 =  9.1041e-9;
+a22 = -1.6002e-10;
+a23 =  7.988e-12;
+
+a30 =  1.100e-10;
+a31 =  6.649e-12;
+a32 = -3.389e-13;
+
+A = ((((a32.*T68 + a31).*T68 + a30).*P + ...
+    (((a23.*T68 + a22).*T68 + a21).*T68 + a20)).*P + ...
+    ((((a14.*T68 + a13).*T68 + a12).*T68 + a11).*T68 + a10)).*P + ...
+    (((a04.*T68 + a03).*T68 + a02).*T68 + a01).*T68 + a00;
+
+%------------
+% eqn 36 p.47
+%------------
+b00 = -1.922e-2;
+b01 = -4.42e-5;
+b10 =  7.3637e-5;
+b11 =  1.7945e-7;
+
+B = b00 + b01.*T68 + (b10 + b11.*T68).*P;
+
+%------------
+% eqn 37 p.47
+%------------
+d00 =  1.727e-3;
+d10 = -7.9836e-6;
+
+D = d00 + d10.*P;
+
+%------------
+% eqn 33 p.46
+%------------
+svel = Cw + A.*S + B.*S.*sqrt(S) + D.*S.^2;
+
diff --git a/tools/tide/check_order.m b/tools/tide/check_order.m
new file mode 100644
index 0000000000000000000000000000000000000000..e3586a73b0fbde8957f4c60e05ed59cd041ae7f7
--- /dev/null
+++ b/tools/tide/check_order.m
@@ -0,0 +1,34 @@
+function [n_out, w, trivalwin] = check_order(n_in)
+%CHECK_ORDER Checks the order passed to the window functions.
+% [N,W,TRIVALWIN] = CHECK_ORDER(N_ESTIMATE) will round N_ESTIMATE to the
+% nearest integer if it is not alreay an integer. In special cases (N is [],
+% 0, or 1), TRIVALWIN will be set to flag that W has been modified.
+
+%   Copyright 1988-2002 The MathWorks, Inc.
+%   $Revision: 1.6 $  $Date: 2002/04/15 01:07:36 $
+
+w = [];
+trivalwin = 0;
+
+% Special case of negative orders:
+if n_in < 0,
+   error('Order cannot be less than zero.');
+end
+
+% Check if order is already an integer or empty
+% If not, round to nearest integer.
+if isempty(n_in) | n_in == floor(n_in),
+   n_out = n_in;
+else
+   n_out = round(n_in);
+   warning('Rounding order to nearest integer.');
+end
+
+% Special cases:
+if isempty(n_out) | n_out == 0,
+   w = zeros(0,1);               % Empty matrix: 0-by-1
+   trivalwin = 1; 
+elseif n_out == 1,
+   w = 1;
+   trivalwin = 1;   
+end
diff --git a/tools/tide/hanning.m b/tools/tide/hanning.m
new file mode 100644
index 0000000000000000000000000000000000000000..84d82e25ba6881cf37798114e7b1a9d99f32c4e2
--- /dev/null
+++ b/tools/tide/hanning.m
@@ -0,0 +1,79 @@
+function w = hanning(varargin)
+%HANNING   Hanning window.
+%   HANNING(N) returns the N-point symmetric Hanning window in a column
+%   vector.  Note that the first and last zero-weighted window samples
+%   are not included.
+%
+%   HANNING(N,'symmetric') returns the same result as HANNING(N).
+%
+%   HANNING(N,'periodic') returns the N-point periodic Hanning window,
+%   and includes the first zero-weighted window sample.
+%
+%   NOTE: Use the HANN function to get a Hanning window which has the 
+%          first and last zero-weighted samples. 
+%
+%   See also BARTLETT, BLACKMAN, BOXCAR, CHEBWIN, HAMMING, HANN, KAISER
+%   and TRIANG.
+
+%   Copyright 1988-2004 The MathWorks, Inc.
+%   $Revision: 1.11.4.2 $  $Date: 2004/12/26 22:16:01 $
+
+% Check number of inputs
+error(nargchk(1,2,nargin));
+
+% Check for trivial order
+[n,w,trivialwin] = check_order(varargin{1});
+if trivialwin, return, end
+
+% Select the sampling option
+if nargin == 1,
+   sflag = 'symmetric';
+else
+   sflag = lower(varargin{2});
+end
+
+% Allow partial strings for sampling options
+allsflags = {'symmetric','periodic'};
+sflagindex = find(strncmp(sflag, allsflags, length(sflag)));
+if length(sflagindex)~=1         % catch 0 or 2 matches
+   error('Sampling flag must be either ''symmetric'' or ''periodic''.');
+end
+sflag = allsflags{sflagindex};
+
+% Evaluate the window
+switch sflag,
+case 'periodic'
+   % Includes the first zero sample
+   w = [0; sym_hanning(n-1)];
+case 'symmetric'
+   % Does not include the first and last zero sample
+   w = sym_hanning(n);
+end
+
+%---------------------------------------------------------------------
+function w = sym_hanning(n)
+%SYM_HANNING   Symmetric Hanning window. 
+%   SYM_HANNING Returns an exactly symmetric N point window by evaluating
+%   the first half and then flipping the same samples over the other half.
+
+if ~rem(n,2)
+   % Even length window
+   half = n/2;
+   w = calc_hanning(half,n);
+   w = [w; w(end:-1:1)];
+else
+   % Odd length window
+   half = (n+1)/2;
+   w = calc_hanning(half,n);
+   w = [w; w(end-1:-1:1)];
+end
+
+%---------------------------------------------------------------------
+function w = calc_hanning(m,n)
+%CALC_HANNING   Calculates Hanning window samples.
+%   CALC_HANNING Calculates and returns the first M points of an N point
+%   Hanning window.
+
+w = .5*(1 - cos(2*pi*(1:m)'/(n+1))); 
+
+% [EOF] hanning.m
diff --git a/tools/tide/psdesttype.m b/tools/tide/psdesttype.m
new file mode 100644
index 0000000000000000000000000000000000000000..1eae3d21e8e9474523df906becd1b96054747d88
--- /dev/null
+++ b/tools/tide/psdesttype.m
@@ -0,0 +1,29 @@
+function [esttype, arglist] = psdesttype(validtypes,arglist)
+%PSDESTTYPE - return the PSD estimation type option and remove it from the
+%argument list
+%
+%   validtypes  - a cell array of valid estimator types
+%                 (e.g. {'power','ms','psd'})
+%   varargin    - the input argument list
+%
+%   Errors out if different estimation types are specified in varargin.    
+
+%   Copyright 2012 The MathWorks, Inc.
+
+% use 'psd' by default
+esttype = 'psd'; 
+found = false; 
+
+for i=1:numel(validtypes) 
+matches = find(strcmpi(validtypes{i},arglist)); 
+if ~isempty(matches) 
+if ~found
+    found = true;
+    esttype = validtypes{i};
+    arglist(matches) = [];
+    else
+    error(message('signal:psdoptions:ConflictingEstTypes', ...
+                  esttype,validtypes{i}));
+    end
+end
+end 
\ No newline at end of file
diff --git a/tools/tide/pwelch.m b/tools/tide/pwelch.m
new file mode 100644
index 0000000000000000000000000000000000000000..cc312d6869b8a72ea7e6eb4a6377af1678aba967
--- /dev/null
+++ b/tools/tide/pwelch.m
@@ -0,0 +1,163 @@
+
+
+function varargout = pwelch(x,varargin)
+%PWELCH Power Spectral Density estimate via Welch's method.
+%   Pxx = PWELCH(X) returns the Power Spectral Density (PSD) estimate, Pxx,
+%   of a discrete-time signal, X, using Welch's averaged, modified
+%   periodogram method.  When X is a vector, it is converted to a column
+%   vector and treated as a single channel.  When X is a matrix, the PSD is
+%   computed independently for each column and stored in the corresponding
+%   column of Pxx.
+%
+%   By default, X is divided into the longest possible sections, to get as
+%   close to but not exceeding 8 segments with 50% overlap. A modified
+%   periodogram is computed for each segment using a Hamming window and all
+%   the resulting periodograms are averaged to compute the final spectral
+%   estimate. X is truncated if it cannot be divided into an integer number
+%   of segments.
+%
+%   Pxx is the distribution of power per unit frequency. For real signals,
+%   PWELCH returns the one-sided PSD by default; for complex signals, it
+%   returns the two-sided PSD.  Note that a one-sided PSD contains the
+%   total power of the input signal.
+%
+%   Note also that the default Hamming window has a 42.5 dB sidelobe
+%   attenuation. This may mask spectral content below this value (relative
+%   to the peak spectral content). Choosing different windows enables
+%   you to make tradeoffs between resolution (e.g., using a rectangular
+%   window) and sidelobe attenuation (e.g., using a Hann window). See
+%   WinTool for more details.
+%
+%   Pxx = PWELCH(X,WINDOW), when WINDOW is a vector, divides each column of
+%   X into overlapping sections of the same length as WINDOW, and then uses
+%   the vector to window each section. If WINDOW is an integer, PWELCH
+%   divides each column of X into sections of length WINDOW, and uses a
+%   Hamming window of the same length. If the length of X is such that it
+%   cannot be divided exactly into an integer number of sections with 50%
+%   overlap, X is truncated. A Hamming window is used if WINDOW is omitted
+%   or specified as empty.
+%
+%   Pxx = PWELCH(X,WINDOW,...,SPECTRUMTYPE) uses the window scaling
+%   algorithm specified by SPECTRUMTYPE when computing the power spectrum.
+%   SPECTRUMTYPE can be set to 'psd' or 'power':
+%     'psd'   - returns the power spectral density
+%     'power' - scales each estimate of the PSD by the equivalent noise
+%               bandwidth of the window (in hertz).  Use this option to
+%               obtain an estimate of the power at each frequency.
+%   The default value for SPECTRUMTYPE is 'psd'.
+%
+%   Pxx = PWELCH(X,WINDOW,NOVERLAP) uses NOVERLAP samples of overlap from
+%   section to section.  NOVERLAP must be an integer smaller than WINDOW if
+%   WINDOW is an integer, or smaller than the length of WINDOW if WINDOW is
+%   a vector. If NOVERLAP is omitted or specified as empty, it is set to
+%   obtain a 50% overlap.
+%
+%   [Pxx,W] = PWELCH(X,WINDOW,NOVERLAP,NFFT) specifies the number of FFT
+%   points used to calculate the PSD estimate.  For real X, Pxx has length
+%   (NFFT/2+1) if NFFT is even, and (NFFT+1)/2 if NFFT is odd. For complex
+%   X, Pxx always has length NFFT.  If NFFT is specified as empty, NFFT is
+%   set to either 256 or the next power of two greater than the length of
+%   each section of X, whichever is larger.
+%
+%   If NFFT is greater than the length of each section, the data is
+%   zero-padded. If NFFT is less than the section length, the segment is
+%   "wrapped" (using DATAWRAP) to make the length equal to NFFT. This
+%   produces the correct FFT when NFFT is smaller than the section length.
+%
+%   W is the vector of normalized frequencies at which the PSD is
+%   estimated.  W has units of radians/sample.  For real signals, W spans
+%   the interval [0,pi] when NFFT is even and [0,pi) when NFFT is odd.  For
+%   complex signals, W always spans the interval [0,2*pi).
+%
+%   [Pxx,W] = PWELCH(X,WINDOW,NOVERLAP,W) computes the two-sided PSD at the
+%   normalized angular frequencies contained in the vector W.  W must have 
+%   at least two elements.
+%
+%   [Pxx,F] = PWELCH(X,WINDOW,NOVERLAP,NFFT,Fs) returns a PSD computed as
+%   a function of physical frequency.  Fs is the sampling frequency
+%   specified in hertz.  If Fs is empty, it defaults to 1 Hz.
+%
+%   F is the vector of frequencies (in hertz) at which the PSD is
+%   estimated.  For real signals, F spans the interval [0,Fs/2] when NFFT
+%   is even and [0,Fs/2) when NFFT is odd.  For complex signals, F always
+%   spans the interval [0,Fs).
+%
+%   [Pxx,F] = PWELCH(X,WINDOW,NOVERLAP,F,Fs) computes the two-sided PSD at 
+%   the frequencies contained in the vector F.  F must have at least two
+%   elements and be expressed in hertz.
+%
+%   [Pxx,F,Pxxc] = PWELCH(...,'ConfidenceLevel',P), where P is a scalar
+%   between 0 and 1, returns the P*100% confidence interval for Pxx.  The
+%   default value for P is .95. Confidence intervals are computed using a
+%   chi-squared approach. Pxxc has twice as many columns as Pxx.
+%   Odd-numbered columns contains the lower bounds of the confidence
+%   intervals; even-numbered columns contain the upper bounds.  Thus,
+%   Pxxc(M,2*N-1) is the lower bound and Pxxc(M,2*N) is the upper bound
+%   corresponding to the estimate Pxx(M,N).
+%
+%   [...] = PWELCH(...,FREQRANGE)  returns the PSD over the specified range
+%   of frequencies based upon the value of FREQRANGE:
+%
+%      'onesided' - returns the one-sided PSD of a real input signal X.
+%         If NFFT is even, Pxx has length NFFT/2+1 and is computed over the
+%         interval [0,pi].  If NFFT is odd, Pxx has length (NFFT+1)/2 and
+%         is computed over the interval [0,pi). When Fs is specified, the
+%         intervals become [0,Fs/2) and [0,Fs/2] for even and odd NFFT,
+%         respectively.
+%
+%      'twosided' - returns the two-sided PSD for either real or complex
+%         input X.  Pxx has length NFFT and is computed over the interval
+%         [0,2*pi). When Fs is specified, the interval becomes [0,Fs).
+%
+%      'centered' - returns the centered two-sided PSD for either real or
+%         complex X.  Pxx has length NFFT and is computed over the interval
+%         (-pi, pi] for even length NFFT and (-pi, pi) for odd length NFFT.
+%         When Fs is specified, the intervals become (-Fs/2, Fs/2] and
+%         (-Fs/2, Fs/2) for even and odd NFFT, respectively.
+%
+%      FREQRANGE may be placed in any position in the input argument list
+%      after NOVERLAP.  The default value of FREQRANGE is 'onesided' when X
+%      is real and 'twosided' when X is complex.
+%
+%   [...] = PWELCH(...,TRACE) uses TRACE to combine spectra computed
+%   for each segment. TRACE can be set to 'mean', 'maxhold' or 'minhold':
+%     'mean'    - Average spectrum over segments for each frequency bin
+%     'maxhold' - Maximum spectrum over segments for each frequency bin
+%     'minhold' - Minimum spectrum over segments for each frequency bin
+%   The default value for TRACE is 'mean'.
+%
+%   PWELCH(...) with no output arguments plots the PSD estimate (in
+%   decibels per unit frequency) in the current figure window.
+%
+%   EXAMPLE:
+%      % Compute Welch's estimate of the periodogram of a 200 Hz sinusoid
+%      % in noise using default values for window, overlap and NFFT.
+%      Fs = 1000;   t = 0:1/Fs:.296;
+%      x = cos(2*pi*t*200)+randn(size(t));
+%      pwelch(x,[],[],[],Fs,'twosided');
+% 
+%   See also PERIODOGRAM, PCOV, PMCOV, PBURG, PYULEAR, PEIG, PMTM, PMUSIC.
+
+%   Copyright 1988-2014 The MathWorks, Inc.
+
+%   References:
+%     [1] Petre Stoica and Randolph Moses, Introduction To Spectral
+%         Analysis, Prentice-Hall, 1997, pg. 15
+%     [2] Monson Hayes, Statistical Digital Signal Processing and 
+%         Modeling, John Wiley & Sons, 1996.
+
+narginchk(1,9);
+nargoutchk(0,3);
+
+% look for psd, power, and ms window compensation flags
+[esttype, varargin] = psdesttype({'psd','power','ms'},varargin);
+
+% Possible outputs are:
+%       Plot
+%       Pxx
+%       Pxx, freq
+%       Pxx, freq, Pxxc
+[varargout{1:nargout}] = welch(x,esttype,varargin{:});
+
+% [EOF]
+
diff --git a/tools/tide/t_constituents.mat b/tools/tide/t_constituents.mat
new file mode 100644
index 0000000000000000000000000000000000000000..7d4c1098240c3fe8db93c2119a91ff0b83c5e388
Binary files /dev/null and b/tools/tide/t_constituents.mat differ
diff --git a/tools/tide/t_equilib.dat b/tools/tide/t_equilib.dat
new file mode 100644
index 0000000000000000000000000000000000000000..6a1ed449f9e65bb3be88a9aff23d9690b6dc2852
--- /dev/null
+++ b/tools/tide/t_equilib.dat
@@ -0,0 +1,51 @@
+% Equilibrium Tidal Potential
+%
+% Values taken from Appendix 1 of Godin, "The Analysis
+% of Tides", Univ. Toronto Press, 1972.
+%
+% Constants multiplied by 1e5.
+% Names as in tide3.dat
+%
+%Name Species A     B
+SA     0   1160     0
+SSA    0   7299     0
+MM     0   8254     0
+MSF    0   1376     0
+MF     0  15642     0
+ALP1   1      0   278
+2Q1    1      0   955
+SIG1   1      0  1153
+Q1     1      0  7216
+RHO1   1      0  1371
+O1     1      0 37689
+TAU1   1      0  -491
+BET1   1      0  -278
+NO1    1      0 -2964
+CHI1   1      0  -566
+PI1    1      0  1029
+P1     1      0 17584
+S1     1      0  -423
+K1     1      0-53050
+PSI1   1      0  -423
+THE1   1      0  -756
+J1     1      0 -2964
+OO1    1      0 -1623
+UPS1   1      0  -311
+OQ2    2    259     0
+EPS2   2    671     0
+2N2    2   2301     0
+MU2    2   2777     0
+N2     2  17387     0
+NU2    2   3303     0
+GAM2   2   -273     0
+H1     2   -314     0
+M2     2  90812     0
+H2     2    276     0
+LDA2   2   -670     0
+L2     2  -2567     0
+T2     2   2479     0
+S2     2  42358     0
+R2     2   -354     0
+K2     2  11506     0
+ETA2   2    643     0
+M3     3  -1188     0
diff --git a/tools/tide/t_getconsts.m b/tools/tide/t_getconsts.m
new file mode 100644
index 0000000000000000000000000000000000000000..34e21de7933215fec15c34118f3ff05712587d83
--- /dev/null
+++ b/tools/tide/t_getconsts.m
@@ -0,0 +1,176 @@
+function [const,sat,shallow]=t_getconsts(ctime);
+% T_GETCONSTS Gets constituent data structures
+% [CONST,SAT,SHALLOW]=T_GETCONSTS returns data structures holding
+% information for tidal analyses.
+%
+% Variables are loaded from 't_constituents.mat', otherwise the 
+% ascii files 'tide3.dat' (provided with the IOS analysis package)
+% and 't_equilib.dat' are read, and the results stored in 
+% 't_constituents.mat' for future use.
+%
+% [...]=T_GETCONSTS(TIME) recomputes the frequencies from the 
+% rates-of-change of astronomical parameters at the matlab TIME given.
+
+% R. Pawlowicz 11/8/99
+% Version 1.0
+
+
+if exist('t_constituents.mat','file');
+  load t_constituents
+else
+
+  nc=146;
+  empvec=zeros(nc,1)+NaN;
+  const=struct('name',setstr(zeros(nc,4)),... % constituent names
+               'freq',empvec,...              % and frequencies (cph)
+               'kmpr',setstr(zeros(nc,4)),... % names of comparisons
+	       'ikmpr',empvec,'df',empvec,... % ..and their index (into .name)
+	       'doodson',zeros(nc,6)+NaN,...  % doodson#s (when available)
+	       'semi',empvec,...              % phase offsets
+	       'isat',empvec,...              % index into "sat"
+	       'nsat',empvec,...              % # of associated satellites in "sat"
+	       'ishallow',empvec,...          % index in "shallow"
+	       'nshallow',empvec,...          % # of generating freqs in "shallow"
+	       'doodsonamp',empvec,...        % Equilibrium Amplitude (when available)
+	       'doodsonspecies',empvec);      % Species
+  nsat=162;
+  sat=struct('deldood',zeros(nsat,3),...  % changes in last 3 doodson#s 
+             'phcorr',zeros(nsat,1),...   % phase corrections
+             'amprat',zeros(nsat,1),...   % amplitude corrections
+	     'ilatfac',zeros(nsat,1),...  % latitude-dependent correction type
+	     'iconst',zeros(nsat,1));     % index of major (in const.)
+  
+  nshl=251;
+  shallow=struct('iconst',zeros(nshl,1),... % index of shallow constituent name
+                 'coef',zeros(nshl,1),...   % corresponding combination number and
+                 'iname',zeros(nshl,1));    % index of main constituent
+		 
+  
+  
+  fid=fopen('tide3.dat');
+  if fid==-1,
+    error('Can''t find constituent input file ''tide3.dat''!');
+  end;
+  l=fgetl(fid);
+  k=0;
+  while length(l)>24,
+    k=k+1;
+    const.name(k,:)=l(5:8);
+    const.freq(k)=sscanf(l(14:25),'%f');
+    nm=[l(30:end) '    '];
+    const.kmpr(k,:)=nm(1:4); 
+    l=fgetl(fid);
+  end
+
+  % Coefficients without comparison constituent are not used
+  % in the present configuration.
+
+  const.df=zeros(length(const.freq),1);
+  
+  for k=find(any(const.kmpr'~=' '));   
+    j1=strmatch(const.kmpr(k,:),const.name);
+    const.ikmpr(k)=j1;
+    const.df(k)=abs(const.freq(j1)-const.freq(k));
+  end
+  const.df(1)=0;  % Leave df(1)=0 to remove z0 from this list
+
+  % Skip blank lines.
+  l=fgetl(fid);l=fgetl(fid);l=fgetl(fid);
+ 
+  % Now decode the doodson# and satellite information.
+  
+  k=0;
+  while length(l)>10,
+    kon=l(7:10);
+    j1=strmatch(kon,const.name);
+    vals=sscanf(l(11:end),'%f');
+    const.doodson(j1,:)=vals(1:6);
+    const.semi(j1)=vals(7);
+    if vals(8)~=0,  % Satellite data follows
+      const.nsat(j1)=vals(8);
+      m=vals(8);sats=[];
+      while m>0,
+        l=fgetl(fid);
+	l=[l ' 0'];
+        for n=1:min(m,3);
+          if l(n*23+10)==' ',l(n*23+10)='0'; end;
+	  sats=[sats,l(n*23+[-11:8]) ' ' l(n*23+10),' '];
+	  m=m-1;
+	end;
+     end;
+      vals=sscanf(sats,'%f',[6 Inf])';
+      nst=size(vals,1); 
+      if nst~=const.nsat(j1), error('# of satellites does not match input'); end;
+      sat.deldood(k+[1:nst],:)=vals(:,1:3);
+      sat.phcorr(k+[1:nst])=vals(:,4);
+      sat.amprat(k+[1:nst])=vals(:,5);
+      sat.ilatfac(k+[1:nst])=vals(:,6);
+      sat.iconst(k+[1:nst])=j1;
+      const.isat(j1)=k+1;
+      k=k+nst;
+    end;
+    l=fgetl(fid);
+  end;
+  
+  % Shallow water constituents - we need to get these in terms
+  % of their original!
+  
+  l=fgetl(fid);
+  k=0;
+  while length(l)>3,
+    kon=l(7:10);
+    j1=strmatch(kon,const.name);
+    nsh=sscanf(l(11:12),'%d');
+    const.nshallow(j1)=nsh;
+    shallow.iconst(k+[1:nsh])=j1;
+    for m=1:nsh,
+     shallow.coef(k+m)=sscanf(l(m*15+[0:3]),'%f');
+     shallow.iname(k+m)=strmatch(l(m*15+[5:6]),const.name);
+    end;
+    const.ishallow(j1)=k+1;
+    k=k+nsh;
+    l=fgetl(fid);
+  end;  
+   
+  %% Get the equilibrium amplitudes from Doodson's development.
+  
+  fid=fopen('t_equilib.dat');
+  if fid==-1,
+    error('Can''t find equilibrium amplitude dataset');
+  end;
+
+  % Now parse file, which is in format Name species A B.
+  
+  l=fgetl(fid);
+  while l(1)=='%',
+    l=fgetl(fid);
+  end;
+  while length(l)>1,
+    j1=strmatch(l(1:4),const.name);
+    vals=sscanf(l(5:end),'%f');
+    if vals(2)~=0,
+      const.doodsonamp(j1)=vals(2)/1e5;
+      const.doodsonspecies(j1)=vals(1);
+    else
+      const.doodsonamp(j1)=vals(3)/1e5;
+      const.doodsonspecies(j1)=-vals(1);
+    end;
+    l=fgetl(fid);
+  end;
+   
+  save t_constituents const sat shallow
+end;
+
+if nargin==1 & ~isempty(ctime), % If no time, just take the "standard" frequencies,
+                                % otherwise compute them from derivatives of astro
+ [astro,ader]=t_astron(ctime);  % parameters. This is probably a real overkill - the
+ ii=finite(const.ishallow);     % diffs are in the 10th decimal place (9th sig fig).
+ const.freq(~ii) = (const.doodson(~ii,:)*ader)/(24);
+ for k=find(ii)',
+   ik=const.ishallow(k)+[0:const.nshallow(k)-1];
+   const.freq(k)=sum( const.freq(shallow.iname(ik)).*shallow.coef(ik) );
+ end;
+end; 
+
+
+
diff --git a/tools/tide/t_signal.m b/tools/tide/t_signal.m
new file mode 100644
index 0000000000000000000000000000000000000000..8b27224925e2eb43c71a47dc7f11822cd3750ed6
--- /dev/null
+++ b/tools/tide/t_signal.m
@@ -0,0 +1,1220 @@
+function varargout = t_signal(fcn,varargin);
+
+% T_SIGNAL  PSD and CSD from Signal Processing toolbox
+%
+% T_SIGNAL( Fcn , varargin )
+%
+
+%---------------------------------------------------------------------
+% SubFunctions from Signal and Matlab-Toolboxes
+%---------------------------------------------------------------------
+%
+% PSD        Power Spectral Density estimate.
+% CSD        Cross Spectral Density estimate.
+%
+% PSDCHK     Helper function for PSD, CSD, COHERE, and TFE.
+%
+% CHI2CONF   Confidence interval using inverse of chi-square cdf.
+% -CHI2INV   Inverse of the chi-square cumulative distribution function (cdf).
+% -GAMINV    Inverse of the gamma cumulative distribution function (cdf).
+% -NORMINV   Inverse of the normal cumulative distribution function (cdf).
+% -GAMCDF    Gamma cumulative distribution function.
+% -GAMPDF    Gamma probability density function.
+% -DISTCHCK  Checks the argument list for the probability functions.
+%
+% HAMMING       Hamming window.
+%
+% GENCOSWIN     Returns one of the generalized cosine windows.
+% -SYM_WINDOW   Symmetric generalized cosine window.
+% -CALC_WINDOW  Calculate the generalized cosine window samples.
+%
+% HANNING       Hanning window.
+% -SYM_HANNING  Symmetric Hanning window. 
+% -CALC_HANNING Calculates Hanning window samples.
+%
+% CHECK_ORDER   Checks the order passed to the window functions.
+%
+
+varargout = cell(1,nargout);
+
+[varargout{:}] = feval(['t_' fcn],varargin{:});
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function [Pxx, Pxxc, f] = t_psd(varargin)
+%PSD Power Spectral Density estimate.
+%   PSD has been replaced by SPECTRUM.WELCH.  PSD still works but may be
+%   removed in the future. Use SPECTRUM.WELCH (or its functional form
+%   PWELCH) instead. Type help SPECTRUM/WELCH for details.
+%
+%   See also SPECTRUM/PSD, SPECTRUM/MSSPECTRUM, SPECTRUM/PERIODOGRAM.
+
+%   Author(s): T. Krauss, 3-26-93
+%   Copyright 1988-2004 The MathWorks, Inc.
+%   $Revision: 1.12.4.3 $  $Date: 2004/10/18 21:09:08 $
+
+%   NOTE 1: To express the result of PSD, Pxx, in units of
+%           Power per Hertz multiply Pxx by 1/Fs [1].
+%
+%   NOTE 2: The Power Spectral Density of a continuous-time signal,
+%           Pss (watts/Hz), is proportional to the Power Spectral 
+%           Density of the sampled discrete-time signal, Pxx, by Ts
+%           (sampling period). [2] 
+%       
+%               Pss(w/Ts) = Pxx(w)*Ts,    |w| < pi; where w = 2*pi*f*Ts
+
+%   References:
+%     [1] Petre Stoica and Randolph Moses, Introduction To Spectral
+%         Analysis, Prentice hall, 1997, pg, 15
+%     [2] A.V. Oppenheim and R.W. Schafer, Discrete-Time Signal
+%         Processing, Prentice-Hall, 1989, pg. 731
+%     [3] A.V. Oppenheim and R.W. Schafer, Digital Signal
+%         Processing, Prentice-Hall, 1975, pg. 556
+
+error(nargchk(1,7,nargin))
+x = varargin{1};
+[msg,nfft,Fs,window,noverlap,p,dflag]=psdchk(varargin(2:end),x);
+error(msg)
+
+% compute PSD
+window = window(:);
+n = length(x);		    % Number of data points
+nwind = length(window); % length of window
+if n < nwind            % zero-pad x if it has length less than the window length
+    x(nwind)=0;  n=nwind;
+end
+% Make sure x is a column vector; do this AFTER the zero-padding 
+% in case x is a scalar.
+x = x(:);		
+
+k = fix((n-noverlap)/(nwind-noverlap));	% Number of windows
+                    					% (k = fix(n/nwind) for noverlap=0)
+%   if 0
+%       disp(sprintf('   x        = (length %g)',length(x)))
+%       disp(sprintf('   y        = (length %g)',length(y)))
+%       disp(sprintf('   nfft     = %g',nfft))
+%       disp(sprintf('   Fs       = %g',Fs))
+%       disp(sprintf('   window   = (length %g)',length(window)))
+%       disp(sprintf('   noverlap = %g',noverlap))
+%       if ~isempty(p)
+%           disp(sprintf('   p        = %g',p))
+%       else
+%           disp('   p        = undefined')
+%       end
+%       disp(sprintf('   dflag    = ''%s''',dflag))
+%       disp('   --------')
+%       disp(sprintf('   k        = %g',k))
+%   end
+
+index = 1:nwind;
+KMU = k*norm(window)^2;	% Normalizing scale factor ==> asymptotically unbiased
+% KMU = k*sum(window)^2;% alt. Nrmlzng scale factor ==> peaks are about right
+
+Spec = zeros(nfft,1); % Spec2 = zeros(nfft,1);
+for i=1:k
+    if strcmp(dflag,'none')
+        xw = window.*(x(index));
+    elseif strcmp(dflag,'linear')
+        xw = window.*detrend(x(index));
+    else
+        xw = window.*detrend(x(index),'constant');
+    end
+    index = index + (nwind - noverlap);
+    Xx = abs(fft(xw,nfft)).^2;
+    Spec = Spec + Xx;
+%     Spec2 = Spec2 + abs(Xx).^2;
+end
+
+% Select first half
+if ~any(any(imag(x)~=0)),   % if x is not complex
+    if rem(nfft,2),    % nfft odd
+        select = (1:(nfft+1)/2)';
+    else
+        select = (1:nfft/2+1)';
+    end
+    Spec = Spec(select);
+%     Spec2 = Spec2(select);
+%    Spec = 4*Spec(select);     % double the signal content - essentially
+% folding over the negative frequencies onto the positive and adding.
+%    Spec2 = 16*Spec2(select);
+else
+    select = (1:nfft)';
+end
+freq_vector = (select - 1)*Fs/nfft;
+
+% find confidence interval if needed
+if (nargout == 3) || ((nargout == 0) && ~isempty(p)),
+    if isempty(p),
+        p = .95;    % default
+    end
+    % Confidence interval from Kay, p. 76, eqn 4.16:
+    % (first column is lower edge of conf int., 2nd col is upper edge)
+    confid = Spec*chi2conf(p,k)/KMU;
+
+    if noverlap > 0
+        disp('Warning: confidence intervals inaccurate for NOVERLAP > 0.')
+    end
+end
+
+Spec = Spec*(1/KMU);   % normalize
+
+% set up output parameters
+if (nargout == 3),
+   Pxx = Spec;
+   Pxxc = confid;
+   f = freq_vector;
+elseif (nargout == 2),
+   Pxx = Spec;
+   Pxxc = freq_vector;
+elseif (nargout == 1),
+   Pxx = Spec;
+elseif (nargout == 0),
+   if ~isempty(p),
+       P = [Spec confid];
+   else
+       P = Spec;
+   end
+   newplot;
+   plot(freq_vector,10*log10(abs(P))), grid on
+   xlabel('Frequency'), ylabel('Power Spectrum Magnitude (dB)');
+end
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function [Pxy, Pxyc, f] = t_csd(varargin)
+%CSD Cross Spectral Density estimate.
+%   CSD has been replaced by CPSD.  CSD still works but may be removed in
+%   the future. Use CPSD instead.
+%
+%   CPSD does not support detrending.  Please use the DETREND function if
+%   you need to detrend your signal.  Type "help detrend" for details.
+%
+%   See also PWELCH, TFESTIMATE, MSCOHERE,
+%   ETFE, SPA, and ARX in the Identification Toolbox.
+
+%   Author(s): T. Krauss, 3-30-93
+%   Copyright 1988-2004 The MathWorks, Inc.
+%   $Revision: 1.8.4.4 $  $Date: 2004/12/26 22:15:35 $
+
+error(nargchk(2,8,nargin))
+x = varargin{1};
+y = varargin{2};
+[msg,nfft,Fs,window,noverlap,p,dflag]=psdchk(varargin(3:end),x,y);
+error(msg)
+    
+% compute CSD
+window = window(:);
+n = length(x);		% Number of data points
+nwind = length(window); % length of window
+if n < nwind    % zero-pad x , y if length is less than the window length
+    x(nwind)=0;
+    y(nwind)=0;  
+    n=nwind;
+end
+x = x(:);		% Make sure x is a column vector
+y = y(:);		% Make sure y is a column vector
+k = fix((n-noverlap)/(nwind-noverlap));	% Number of windows
+					% (k = fix(n/nwind) for noverlap=0)
+index = 1:nwind;
+KMU = k*norm(window)^2;	% Normalizing scale factor ==> asymptotically unbiased
+% KMU = k*sum(window)^2;% alt. Nrmlzng scale factor ==> peaks are about right
+
+Spec = zeros(nfft,1);
+for i=1:k
+    if strcmp(dflag,'none')
+        xw = window.*x(index);
+        yw = window.*y(index);
+    elseif strcmp(dflag,'linear')
+        xw = window.*detrend(x(index));
+        yw = window.*detrend(y(index));
+    else
+        xw = window.*detrend(x(index),0);
+        yw = window.*detrend(y(index),0);
+    end
+    index = index + (nwind - noverlap);
+    Xx = fft(xw,nfft);
+    Yy = fft(yw,nfft);
+    Xy2 = Yy.*conj(Xx);
+    Spec = Spec + Xy2;
+end
+
+% Select first half
+if ~any(any(imag([x y])~=0)),   % if x and y are not complex
+    if rem(nfft,2),    % nfft odd
+        select = 1:(nfft+1)/2;
+    else
+        select = 1:nfft/2+1;   % include DC AND Nyquist
+    end
+    Spec = Spec(select);
+else
+    select = 1:nfft;
+end
+freq_vector = (select - 1)'*Fs/nfft;
+
+% find confidence interval if needed
+if (nargout == 3) || ((nargout == 0) && ~isempty(p)),
+    if isempty(p),
+        p = .95;    % default
+    end
+    confid = Spec*chi2conf(p,k)/KMU;
+end
+
+Spec = Spec*(1/KMU);
+
+% set up output parameters
+if (nargout == 3),
+   Pxy = Spec;
+   Pxyc = confid;
+   f = freq_vector;
+elseif (nargout == 2),
+   Pxy = Spec;
+   Pxyc = freq_vector;
+elseif (nargout == 1),
+   Pxy = Spec;
+elseif (nargout == 0),
+   if ~isempty(p),
+       P = [Spec confid];
+   else
+       P = Spec;
+   end
+   newplot;
+   plot(freq_vector,10*log10(abs(P))), grid on
+   xlabel('Frequency'), ylabel('Cross Spectrum Magnitude (dB)');
+end
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function [msg,nfft,Fs,window,noverlap,p,dflag] = psdchk(P,x,y)
+%PSDCHK Helper function for PSD, CSD, COHERE, and TFE.
+%   [msg,nfft,Fs,window,noverlap,p,dflag]=PSDCHK(P,x,y) takes the cell 
+%   array P and uses each element as an input argument.  Assumes P has 
+%   between 0 and 7 elements which are the arguments to psd, csd, cohere
+%   or tfe after the x (psd) or x and y (csd, cohere, tfe) arguments.
+%   y is optional; if given, it is checked to match the size of x.
+%   x must be a numeric vector.
+%   Outputs:
+%     msg - error message, [] if no error
+%     nfft - fft length
+%     Fs - sampling frequency
+%     window - window vector
+%     noverlap - overlap of sections, in samples
+%     p - confidence interval, [] if none desired
+%     dflag - detrending flag, 'linear' 'mean' or 'none'
+
+%   Author(s): T. Krauss, 10-28-93
+%   Copyright 1988-2002 The MathWorks, Inc.
+%       $Revision: 1.6 $  $Date: 2002/04/15 01:08:06 $
+
+msg = [];
+
+if length(P) == 0 
+% psd(x)
+    nfft = min(length(x),256);
+    window = hanning(nfft);
+    noverlap = 0;
+    Fs = 2;
+    p = [];
+    dflag = 'none';
+elseif length(P) == 1
+% psd(x,nfft)
+% psd(x,dflag)
+    if isempty(P{1}),   dflag = 'none'; nfft = min(length(x),256); 
+    elseif isstr(P{1}), dflag = P{1};       nfft = min(length(x),256); 
+    else              dflag = 'none'; nfft = P{1};   end
+    Fs = 2;
+    window = hanning(nfft);
+    noverlap = 0;
+    p = [];
+elseif length(P) == 2
+% psd(x,nfft,Fs)
+% psd(x,nfft,dflag)
+    if isempty(P{1}), nfft = min(length(x),256); else nfft=P{1};     end
+    if isempty(P{2}),   dflag = 'none'; Fs = 2;
+    elseif isstr(P{2}), dflag = P{2};       Fs = 2;
+    else              dflag = 'none'; Fs = P{2}; end
+    window = hanning(nfft);
+    noverlap = 0;
+    p = [];
+elseif length(P) == 3
+% psd(x,nfft,Fs,window)
+% psd(x,nfft,Fs,dflag)
+    if isempty(P{1}), nfft = min(length(x),256); else nfft=P{1};     end
+    if isempty(P{2}), Fs = 2;     else    Fs = P{2}; end
+    if isstr(P{3})
+        dflag = P{3};
+        window = hanning(nfft);
+    else
+        dflag = 'none';
+        window = P{3};
+        if length(window) == 1, window = hanning(window); end
+        if isempty(window), window = hanning(nfft); end
+    end
+    noverlap = 0;
+    p = [];
+elseif length(P) == 4
+% psd(x,nfft,Fs,window,noverlap)
+% psd(x,nfft,Fs,window,dflag)
+    if isempty(P{1}), nfft = min(length(x),256); else nfft=P{1};     end
+    if isempty(P{2}), Fs = 2;     else    Fs = P{2}; end
+    window = P{3};
+    if length(window) == 1, window = hanning(window); end
+    if isempty(window), window = hanning(nfft); end
+    if isstr(P{4})
+        dflag = P{4};
+        noverlap = 0;
+    else
+        dflag = 'none';
+        if isempty(P{4}), noverlap = 0; else noverlap = P{4}; end
+    end
+    p = [];
+elseif length(P) == 5
+% psd(x,nfft,Fs,window,noverlap,p)
+% psd(x,nfft,Fs,window,noverlap,dflag)
+    if isempty(P{1}), nfft = min(length(x),256); else nfft=P{1};     end
+    if isempty(P{2}), Fs = 2;     else    Fs = P{2}; end
+    window = P{3};
+    if length(window) == 1, window = hanning(window); end
+    if isempty(window), window = hanning(nfft); end
+    if isempty(P{4}), noverlap = 0; else noverlap = P{4}; end
+    if isstr(P{5})
+        dflag = P{5};
+        p = [];
+    else
+        dflag = 'none';
+        if isempty(P{5}), p = .95;    else    p = P{5}; end
+    end
+elseif length(P) == 6
+% psd(x,nfft,Fs,window,noverlap,p,dflag)
+    if isempty(P{1}), nfft = min(length(x),256); else nfft=P{1};     end
+    if isempty(P{2}), Fs = 2;     else    Fs = P{2}; end
+    window = P{3};
+    if length(window) == 1, window = hanning(window); end
+    if isempty(window), window = hanning(nfft); end
+    if isempty(P{4}), noverlap = 0; else noverlap = P{4}; end
+    if isempty(P{5}), p = .95;    else    p = P{5}; end
+    if isstr(P{6})
+        dflag = P{6};
+    else
+        msg = 'DFLAG parameter must be a string.'; return
+    end
+end
+
+% NOW do error checking
+if (nfft<length(window)), 
+    msg = 'Requires window''s length to be no greater than the FFT length.';
+end
+if (noverlap >= length(window)),
+    msg = 'Requires NOVERLAP to be strictly less than the window length.';
+end
+if (nfft ~= abs(round(nfft)))|(noverlap ~= abs(round(noverlap))),
+    msg = 'Requires positive integer values for NFFT and NOVERLAP.';
+end
+if ~isempty(p),
+    if (prod(size(p))>1)|(p(1,1)>1)|(p(1,1)<0),
+        msg = 'Requires confidence parameter to be a scalar between 0 and 1.';
+    end
+end
+if min(size(x))~=1 | ~isnumeric(x) | length(size(x))>2
+    msg = 'Requires vector (either row or column) input.';
+end
+if (nargin>2) & ( (min(size(y))~=1) | ~isnumeric(y) | length(size(y))>2 )
+    msg = 'Requires vector (either row or column) input.';
+end
+if (nargin>2) & (length(x)~=length(y))
+    msg = 'Requires X and Y be the same length.';
+end
+
+dflag = lower(dflag);
+if strncmp(dflag,'none',1)
+      dflag = 'none';
+elseif strncmp(dflag,'linear',1)
+      dflag = 'linear';
+elseif strncmp(dflag,'mean',1)
+      dflag = 'mean';
+else
+    msg = 'DFLAG must be ''linear'', ''mean'', or ''none''.';
+end
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function c = chi2conf(conf,k);
+%CHI2CONF Confidence interval using inverse of chi-square cdf.
+%   C = CHI2CONF(P,K) is the confidence interval of an unbiased power spectrum 
+%   estimate made up of K independent measurements.  C is a two element
+%   vector.  We are P*100% confident that the true PSD lies in the interval
+%   [C(1)*X C(2)*X], where X is the PSD estimate.
+%
+%   Reference:
+%     Stephen Kay, "Modern Spectral Analysis, Theory & Application," 
+%     p. 76, eqn 4.16.
+
+%   Copyright 1988-2002 The MathWorks, Inc.
+%   $Revision: 1.6 $  $Date: 2002/04/15 01:07:39 $
+
+if nargin < 2, 
+    error('Requires two input arguments.');
+end
+
+v=2*k;
+alfa = 1 - conf;
+c=chi2inv([1-alfa/2 alfa/2],v);
+c=v./c;
+
+function x = chi2inv(p,v);
+%CHI2INV Inverse of the chi-square cumulative distribution function (cdf).
+%   X = CHI2INV(P,V)  returns the inverse of the chi-square cdf with V  
+%   degrees of freedom at the values in P. The chi-square cdf with V 
+%   degrees of freedom, is the gamma cdf with parameters V/2 and 2.   
+%
+%   The size of X is the common size of P and V. A scalar input
+%   functions as a constant matrix of the same size as the other input.   
+
+%   References:
+%      [1]  M. Abramowitz and I. A. Stegun, "Handbook of Mathematical
+%      Functions", Government Printing Office, 1964, 26.4.
+%      [2] E. Kreyszig, "Introductory Mathematical Statistics",
+%      John Wiley, 1970, section 10.2 (page 144)
+
+%  Was: Revision: 1.2, Date: 1996/07/25 16:23:36
+
+if nargin < 2, 
+    error('Requires two input arguments.');
+end
+
+[errorcode p v] = distchck(2,p,v);
+
+if errorcode > 0
+    error('Requires non-scalar arguments to match in size.');
+end
+
+% Call the gamma inverse function. 
+x = gaminv(p,v/2,2);
+
+% Return NaN if the degrees of freedom is not a positive integer.
+k = find(v < 0  |  round(v) ~= v);
+if any(k)
+    tmp  = NaN;
+    x(k) = tmp(ones(size(k)));
+end
+
+
+function x = gaminv(p,a,b);
+%GAMINV Inverse of the gamma cumulative distribution function (cdf).
+%   X = GAMINV(P,A,B)  returns the inverse of the gamma cdf with  
+%   parameters A and B, at the probabilities in P.
+%
+%   The size of X is the common size of the input arguments. A scalar input  
+%   functions as a constant matrix of the same size as the other inputs.    
+%
+%   GAMINV uses Newton's method to converge to the solution.
+
+%   References:
+%      [1]  M. Abramowitz and I. A. Stegun, "Handbook of Mathematical
+%      Functions", Government Printing Office, 1964, 6.5.
+
+%   B.A. Jones 1-12-93
+%   Was: Revision: 1.2, Date: 1996/07/25 16:23:36
+
+if nargin<3, 
+    b=1;
+end
+
+[errorcode p a b] = distchck(3,p,a,b);
+
+if errorcode > 0
+    error('The arguments must be the same size or be scalars.');
+end
+
+%   Initialize X to zero.
+x = zeros(size(p));
+
+k = find(p<0 | p>1 | a <= 0 | b <= 0);
+if any(k),
+    tmp = NaN;
+    x(k) = tmp(ones(size(k)));
+end
+
+% The inverse cdf of 0 is 0, and the inverse cdf of 1 is 1.  
+k0 = find(p == 0 & a > 0 & b > 0);
+if any(k0),
+    x(k0) = zeros(size(k0)); 
+end
+
+k1 = find(p == 1 & a > 0 & b > 0);
+if any(k1), 
+    tmp = Inf;
+    x(k1) = tmp(ones(size(k1))); 
+end
+
+% Newton's Method
+% Permit no more than count_limit interations.
+count_limit = 100;
+count = 0;
+
+k = find(p > 0  &  p < 1 & a > 0 & b > 0);
+pk = p(k);
+
+% Supply a starting guess for the iteration.
+%   Use a method of moments fit to the lognormal distribution. 
+mn = a(k) .* b(k);
+v = mn .* b(k);
+temp = log(v + mn .^ 2); 
+mu = 2 * log(mn) - 0.5 * temp;
+sigma = -2 * log(mn) + temp;
+xk = exp(norminv(pk,mu,sigma));
+
+h = ones(size(pk)); 
+
+% Break out of the iteration loop for three reasons:
+%  1) the last update is very small (compared to x)
+%  2) the last update is very small (compared to sqrt(eps))
+%  3) There are more than 100 iterations. This should NEVER happen. 
+
+while(any(abs(h) > sqrt(eps)*abs(xk))  &  max(abs(h)) > sqrt(eps)    ...
+                                 & count < count_limit), 
+                                 
+    count = count + 1;
+    h = (gamcdf(xk,a(k),b(k)) - pk) ./ gampdf(xk,a(k),b(k));
+    xnew = xk - h;
+    % Make sure that the current guess stays greater than zero.
+    % When Newton's Method suggests steps that lead to negative guesses
+    % take a step 9/10ths of the way to zero:
+    ksmall = find(xnew < 0);
+    if any(ksmall),
+        xnew(ksmall) = xk(ksmall) / 10;
+        h = xk-xnew;
+    end
+    xk = xnew;
+end
+
+
+% Store the converged value in the correct place
+x(k) = xk;
+
+if count == count_limit, 
+    fprintf('\nWarning: GAMINV did not converge.\n');
+    str = 'The last step was:  ';
+    outstr = sprintf([str,'%13.8f'],h);
+    fprintf(outstr);
+end
+
+function x = norminv(p,mu,sigma);
+%NORMINV Inverse of the normal cumulative distribution function (cdf).
+%   X = NORMINV(P,MU,SIGMA) finds the inverse of the normal cdf with
+%   mean, MU, and standard deviation, SIGMA.
+%
+%   The size of X is the common size of the input arguments. A scalar input  
+%   functions as a constant matrix of the same size as the other inputs.    
+%
+%   Default values for MU and SIGMA are 0 and 1 respectively.
+
+%   References:
+%      [1]  M. Abramowitz and I. A. Stegun, "Handbook of Mathematical
+%      Functions", Government Printing Office, 1964, 7.1.1 and 26.2.2
+
+%   Was: Revision: 1.2, Date: 1996/07/25 16:23:36
+
+if nargin < 3, 
+    sigma = 1;
+end
+
+if nargin < 2;
+    mu = 0;
+end
+
+[errorcode p mu sigma] = distchck(3,p,mu,sigma);
+
+if errorcode > 0
+    error('Requires non-scalar arguments to match in size.');
+end
+
+% Allocate space for x.
+x = zeros(size(p));
+
+% Return NaN if the arguments are outside their respective limits.
+k = find(sigma <= 0 | p < 0 | p > 1);
+if any(k)
+    tmp  = NaN;
+    x(k) = tmp(ones(size(k))); 
+end
+
+% Put in the correct values when P is either 0 or 1.
+k = find(p == 0);
+if any(k)
+    tmp  = Inf;
+    x(k) = -tmp(ones(size(k)));
+end
+
+k = find(p == 1);
+if any(k)
+    tmp  = Inf;
+    x(k) = tmp(ones(size(k))); 
+end
+
+% Compute the inverse function for the intermediate values.
+k = find(p > 0  &  p < 1 & sigma > 0);
+if any(k),
+    x(k) = sqrt(2) * sigma(k) .* erfinv(2 * p(k) - 1) + mu(k);
+end
+
+
+function p = gamcdf(x,a,b);
+%GAMCDF Gamma cumulative distribution function.
+%   P = GAMCDF(X,A,B) returns the gamma cumulative distribution
+%   function with parameters A and B at the values in X.
+%
+%   The size of P is the common size of the input arguments. A scalar input  
+%   functions as a constant matrix of the same size as the other inputs.    
+%
+%   Some references refer to the gamma distribution with a single
+%   parameter. This corresponds to the default of B = 1. 
+%
+%   GAMMAINC does computational work.
+
+%   References:
+%      [1]  L. Devroye, "Non-Uniform Random Variate Generation", 
+%      Springer-Verlag, 1986. p. 401.
+%      [2]  M. Abramowitz and I. A. Stegun, "Handbook of Mathematical
+%      Functions", Government Printing Office, 1964, 26.1.32.
+
+%   Was: Revision: 1.2, Date: 1996/07/25 16:23:36
+
+if nargin < 3, 
+    b = 1; 
+end
+
+if nargin < 2, 
+    error('Requires at least two input arguments.'); 
+end
+
+[errorcode x a b] = distchck(3,x,a,b);
+
+if errorcode > 0
+    error('Requires non-scalar arguments to match in size.');
+end
+
+%   Return NaN if the arguments are outside their respective limits.
+k1 = find(a <= 0 | b <= 0);     
+if any(k1)
+    tmp   = NaN;
+    p(k1) = tmp(ones(size(k1)));
+end
+
+% Initialize P to zero.
+p = zeros(size(x));
+
+k = find(x > 0 & ~(a <= 0 | b <= 0));
+if any(k), 
+    p(k) = gammainc(x(k) ./ b(k),a(k));
+end
+
+% Make sure that round-off errors never make P greater than 1.
+k = find(p > 1);
+if any(k)
+    p(k) = ones(size(k));
+end
+
+
+function y = gampdf(x,a,b)
+%GAMPDF Gamma probability density function.
+%   Y = GAMPDF(X,A,B) returns the gamma probability density function 
+%   with parameters A and B, at the values in X.
+%
+%   The size of Y is the common size of the input arguments. A scalar input  
+%   functions as a constant matrix of the same size as the other inputs.    
+%
+%   Some references refer to the gamma distribution with a single
+%   parameter. This corresponds to the default of B = 1.
+
+%   References:
+%      [1]  L. Devroye, "Non-Uniform Random Variate Generation", 
+%      Springer-Verlag, 1986, pages 401-402.
+
+%   Was: Revision: 1.2, Date: 1996/07/25 16:23:36
+
+if nargin < 3, 
+    b = 1; 
+end
+
+if nargin < 2, 
+    error('Requires at least two input arguments'); 
+end
+
+[errorcode x a b] = distchck(3,x,a,b);
+
+if errorcode > 0
+    error('Requires non-scalar arguments to match in size.');
+end
+
+% Initialize Y to zero.
+y = zeros(size(x));
+
+%   Return NaN if the arguments are outside their respective limits.
+k1 = find(a <= 0 | b <= 0);     
+if any(k1)
+    tmp = NaN;
+    y(k1) = tmp(ones(size(k1)));
+end
+
+k=find(x > 0 & ~(a <= 0 | b <= 0));
+if any(k)
+    y(k) = (a(k) - 1) .* log(x(k)) - (x(k) ./ b(k)) - gammaln(a(k)) - a(k) .* log(b(k));
+    y(k) = exp(y(k));
+end
+k1 = find(x == 0 & a < 1);
+if any(k1)
+  tmp = Inf;
+  y(k1) = tmp(ones(size(k1)));
+end
+k2 = find(x == 0 & a == 1);
+if any(k2)
+  y(k2) = (1./b(k2));
+end
+
+function [errorcode,out1,out2,out3,out4] = distchck(nparms,arg1,arg2,arg3,arg4)
+%DISTCHCK Checks the argument list for the probability functions.
+
+%   B.A. Jones  1-22-93
+%   Was: Revision: 1.2, Date: 1996/07/25 16:23:36
+
+errorcode = 0;
+
+if nparms == 1
+    out1 = arg1;
+    return;
+end
+    
+if nparms == 2
+    [r1 c1] = size(arg1);
+    [r2 c2] = size(arg2);
+    scalararg1 = (prod(size(arg1)) == 1);
+    scalararg2 = (prod(size(arg2)) == 1);
+    if ~scalararg1 & ~scalararg2
+        if r1 ~= r2 | c1 ~= c2
+            errorcode = 1;
+            return;         
+        end     
+    end
+    if scalararg1
+        out1 = arg1(ones(r2,1),ones(c2,1));
+    else
+        out1 = arg1;
+    end
+    if scalararg2
+        out2 = arg2(ones(r1,1),ones(c1,1));
+    else
+        out2 = arg2;
+    end
+end
+    
+if nparms == 3
+    [r1 c1] = size(arg1);
+    [r2 c2] = size(arg2);
+    [r3 c3] = size(arg3);
+    scalararg1 = (prod(size(arg1)) == 1);
+    scalararg2 = (prod(size(arg2)) == 1);
+    scalararg3 = (prod(size(arg3)) == 1);
+
+    if ~scalararg1 & ~scalararg2
+        if r1 ~= r2 | c1 ~= c2
+            errorcode = 1;
+            return;         
+        end
+    end
+
+    if ~scalararg1 & ~scalararg3
+        if r1 ~= r3 | c1 ~= c3
+            errorcode = 1;
+            return;                 
+        end
+    end
+
+    if ~scalararg3 & ~scalararg2
+        if r3 ~= r2 | c3 ~= c2
+            errorcode = 1;
+            return;         
+        end
+    end
+
+    if ~scalararg1
+      out1 = arg1;
+    end
+    if ~scalararg2
+      out2 = arg2;
+    end
+    if ~scalararg3
+      out3 = arg3;
+    end
+    rows = max([r1 r2 r3]);
+   columns = max([c1 c2 c3]);  
+       
+    if scalararg1
+        out1 = arg1(ones(rows,1),ones(columns,1));
+    end
+   if scalararg2
+        out2 = arg2(ones(rows,1),ones(columns,1));
+   end
+   if scalararg3
+       out3 = arg3(ones(rows,1),ones(columns,1));
+   end
+     out4 =[];
+    
+end
+
+if nparms == 4
+    [r1 c1] = size(arg1);
+    [r2 c2] = size(arg2);
+    [r3 c3] = size(arg3);
+    [r4 c4] = size(arg4);
+    scalararg1 = (prod(size(arg1)) == 1);
+    scalararg2 = (prod(size(arg2)) == 1);
+    scalararg3 = (prod(size(arg3)) == 1);
+    scalararg4 = (prod(size(arg4)) == 1);
+
+    if ~scalararg1 & ~scalararg2
+        if r1 ~= r2 | c1 ~= c2
+            errorcode = 1;
+            return;         
+        end
+    end
+
+    if ~scalararg1 & ~scalararg3
+        if r1 ~= r3 | c1 ~= c3
+            errorcode = 1;
+            return;                 
+        end
+    end
+
+    if ~scalararg1 & ~scalararg4
+        if r1 ~= r4 | c1 ~= c4
+            errorcode = 1;
+            return;                 
+        end
+    end
+
+    if ~scalararg3 & ~scalararg2
+        if r3 ~= r2 | c3 ~= c2
+            errorcode = 1;
+            return;         
+        end
+    end
+
+    if ~scalararg4 & ~scalararg2
+        if r4 ~= r2 | c4 ~= c2
+            errorcode = 1;
+            return;         
+        end
+    end
+
+    if ~scalararg3 & ~scalararg4
+        if r3 ~= r4 | c3 ~= c4
+            errorcode = 1;
+            return;         
+        end
+    end
+
+
+    if ~scalararg1
+       out1 = arg1;
+    end
+    if ~scalararg2
+       out2 = arg2;
+    end
+    if ~scalararg3
+      out3 = arg3;
+    end
+    if ~scalararg4
+      out4 = arg4;
+    end
+ 
+   rows = max([r1 r2 r3 r4]);
+   columns = max([c1 c2 c3 c4]);     
+    if scalararg1
+       out1 = arg1(ones(rows,1),ones(columns,1));
+   end
+   if scalararg2
+       out2 = arg2(ones(rows,1),ones(columns,1));
+   end
+   if scalararg3
+       out3 = arg3(ones(rows,1),ones(columns,1));
+   end
+   if scalararg4
+       out4 = arg4(ones(rows,1),ones(columns,1));
+   end
+end
+
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function w = hamming(varargin)
+%HAMMING   Hamming window.
+%   HAMMING(N) returns the N-point symmetric Hamming window in a column vector.
+% 
+%   HAMMING(N,SFLAG) generates the N-point Hamming window using SFLAG window
+%   sampling. SFLAG may be either 'symmetric' or 'periodic'. By default, a 
+%   symmetric window is returned. 
+%
+%   See also BLACKMAN, HANN, WINDOW.
+
+%   Copyright 1988-2002 The MathWorks, Inc.
+%   $Revision: 1.14 $  $Date: 2002/11/21 15:46:43 $
+
+% Check number of inputs
+error(nargchk(1,2,nargin));
+
+[w,msg] = gencoswin('hamming',varargin{:});
+error(msg);
+
+
+% [EOF] hamming.m
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function [w,msg] = gencoswin(varargin)
+%GENCOSWIN   Returns one of the generalized cosine windows.
+%   GENCOSWIN returns the generalized cosine window specified by the 
+%   first string argument. Its inputs can be
+%     Window name    - a string, any of 'hamming', 'hann', 'blackman'.
+%     N              - length of the window desired.
+%     Sampling flag  - optional string, one of 'symmetric', 'periodic'. 
+
+%   Copyright 1988-2002 The MathWorks, Inc.
+%   $Revision: 1.7 $  $Date: 2002/04/15 01:09:14 $ 
+
+% Parse the inputs
+window = varargin{1};
+n = varargin{2};
+msg = '';
+
+% Check for trivial orders
+[n,w,trivialwin] = check_order(n);
+if trivialwin, return, end;
+
+% Select the sampling option
+if nargin == 2, % no sampling flag specified, use default. 
+    sflag = 'symmetric';
+else
+    sflag = lower(varargin{3});
+end
+
+% Allow partial strings for sampling options
+allsflags = {'symmetric','periodic'};
+sflagindex = strmatch(sflag, allsflags);
+if length(sflagindex)~=1         % catch 0 or 2 matches
+    msg = 'Sampling flag must be either ''symmetric'' or ''periodic''.';
+    return;
+else	
+    sflag = allsflags{sflagindex};
+end
+
+% Evaluate the window
+switch sflag
+case 'periodic'
+    w = sym_window(n+1,window);
+    w(end) = [];
+case 'symmetric'
+    w = sym_window(n,window);
+end
+
+%---------------------------------------------------------------------
+function w = sym_window(n,window)
+%SYM_WINDOW   Symmetric generalized cosine window.
+%   SYM_WINDOW Returns an exactly symmetric N point generalized cosine 
+%   window by evaluating the first half and then flipping the same samples
+%   over the other half.
+
+if ~rem(n,2)
+    % Even length window
+    half = n/2;
+    w = calc_window(half,n,window);
+    w = [w; w(end:-1:1)];
+else
+    % Odd length window
+    half = (n+1)/2;
+    w = calc_window(half,n,window);
+    w = [w; w(end-1:-1:1)];
+end
+
+%---------------------------------------------------------------------
+function w = calc_window(m,n,window)
+%CALC_WINDOW   Calculate the generalized cosine window samples.
+%   CALC_WINDOW Calculates and returns the first M points of an N point
+%   generalized cosine window determined by the 'window' string.
+
+% For the hamming and blackman windows we force rounding in order to achieve
+% better numerical properties.  For example, the end points of the hamming 
+% window should be exactly 0.08.
+
+switch window
+case 'hann'
+    % Hann window
+    %    w = 0.5 * (1 - cos(2*pi*(0:m-1)'/(n-1))); 
+    a0 = 0.5;
+    a1 = 0.5;
+    a2 = 0;
+    a3 = 0;
+    a4 = 0;
+case 'hamming'
+    % Hamming window
+    %    w = (54 - 46*cos(2*pi*(0:m-1)'/(n-1)))/100;
+    a0 = 0.54;
+    a1 = 0.46;
+    a2 = 0;
+    a3 = 0;
+    a4 = 0;
+case 'blackman'
+    % Blackman window
+    %    w = (42 - 50*cos(2*pi*(0:m-1)/(n-1)) + 8*cos(4*pi*(0:m-1)/(n-1)))'/100;
+    a0 = 0.42;
+    a1 = 0.5;
+    a2 = 0.08;
+    a3 = 0;
+    a4 = 0;
+case 'flattopwin'
+    % Flattop window
+    % Original coefficients as defined in the reference (see flattopwin.m);
+    % a0 = 1;
+    % a1 = 1.93;
+    % a2 = 1.29;
+    % a3 = 0.388;
+    % a4 = 0.032;
+    %
+    % Scaled by (a0+a1+a2+a3+a4)
+    a0 = 0.2156;
+    a1 = 0.4160;
+    a2 = 0.2781;
+    a3 = 0.0836;
+    a4 = 0.0069;
+end
+
+x = (0:m-1)'/(n-1);
+w = a0 - a1*cos(2*pi*x) + a2*cos(4*pi*x) - a3*cos(6*pi*x) + a4*cos(8*pi*x);
+
+% [EOF] gencoswin.m
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function w = hanning(varargin)
+%HANNING   Hanning window.
+%   HANNING(N) returns the N-point symmetric Hanning window in a column
+%   vector.  Note that the first and last zero-weighted window samples
+%   are not included.
+%
+%   HANNING(N,'symmetric') returns the same result as HANNING(N).
+%
+%   HANNING(N,'periodic') returns the N-point periodic Hanning window,
+%   and includes the first zero-weighted window sample.
+%
+%   NOTE: Use the HANN function to get a Hanning window which has the 
+%          first and last zero-weighted samples. 
+%
+%   See also BARTLETT, BLACKMAN, BOXCAR, CHEBWIN, HAMMING, HANN, KAISER
+%   and TRIANG.
+
+%   Copyright 1988-2004 The MathWorks, Inc.
+%   $Revision: 1.11.4.2 $  $Date: 2004/12/26 22:16:01 $
+
+% Check number of inputs
+error(nargchk(1,2,nargin));
+
+% Check for trivial order
+[n,w,trivialwin] = check_order(varargin{1});
+if trivialwin, return, end
+
+% Select the sampling option
+if nargin == 1,
+   sflag = 'symmetric';
+else
+   sflag = lower(varargin{2});
+end
+
+% Allow partial strings for sampling options
+allsflags = {'symmetric','periodic'};
+sflagindex = find(strncmp(sflag, allsflags, length(sflag)));
+if length(sflagindex)~=1         % catch 0 or 2 matches
+   error('Sampling flag must be either ''symmetric'' or ''periodic''.');
+end
+sflag = allsflags{sflagindex};
+
+% Evaluate the window
+switch sflag,
+case 'periodic'
+   % Includes the first zero sample
+   w = [0; sym_hanning(n-1)];
+case 'symmetric'
+   % Does not include the first and last zero sample
+   w = sym_hanning(n);
+end
+
+%---------------------------------------------------------------------
+function w = sym_hanning(n)
+%SYM_HANNING   Symmetric Hanning window. 
+%   SYM_HANNING Returns an exactly symmetric N point window by evaluating
+%   the first half and then flipping the same samples over the other half.
+
+if ~rem(n,2)
+   % Even length window
+   half = n/2;
+   w = calc_hanning(half,n);
+   w = [w; w(end:-1:1)];
+else
+   % Odd length window
+   half = (n+1)/2;
+   w = calc_hanning(half,n);
+   w = [w; w(end-1:-1:1)];
+end
+
+%---------------------------------------------------------------------
+function w = calc_hanning(m,n)
+%CALC_HANNING   Calculates Hanning window samples.
+%   CALC_HANNING Calculates and returns the first M points of an N point
+%   Hanning window.
+
+w = .5*(1 - cos(2*pi*(1:m)'/(n+1))); 
+
+% [EOF] hanning.m
+
+%*****************************************************************
+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+function [n_out, w, trivalwin] = check_order(n_in)
+%CHECK_ORDER Checks the order passed to the window functions.
+% [N,W,TRIVALWIN] = CHECK_ORDER(N_ESTIMATE) will round N_ESTIMATE to the
+% nearest integer if it is not alreay an integer. In special cases (N is [],
+% 0, or 1), TRIVALWIN will be set to flag that W has been modified.
+
+%   Copyright 1988-2002 The MathWorks, Inc.
+%   $Revision: 1.6 $  $Date: 2002/04/15 01:07:36 $
+
+w = [];
+trivalwin = 0;
+
+% Special case of negative orders:
+if n_in < 0,
+   error('Order cannot be less than zero.');
+end
+
+% Check if order is already an integer or empty
+% If not, round to nearest integer.
+if isempty(n_in) | n_in == floor(n_in),
+   n_out = n_in;
+else
+   n_out = round(n_in);
+   warning('Rounding order to nearest integer.');
+end
+
+% Special cases:
+if isempty(n_out) | n_out == 0,
+   w = zeros(0,1);               % Empty matrix: 0-by-1
+   trivalwin = 1; 
+elseif n_out == 1,
+   w = 1;
+   trivalwin = 1;   
+end
diff --git a/tools/tide/t_tide.m b/tools/tide/t_tide.m
new file mode 100644
index 0000000000000000000000000000000000000000..97ca6e00ebf34e41d8bdc6b6f1cdbb93c5a18b27
--- /dev/null
+++ b/tools/tide/t_tide.m
@@ -0,0 +1,1331 @@
+function [nameu,fu,tidecon,xout]=t_tide(xin,varargin)
+% T_TIDE Harmonic analysis of a time series
+% [NAME,FREQ,TIDECON,XOUT]=T_TIDE(XIN) computes the tidal analysis 
+% of the (possibly complex) time series XIN.
+%
+% [TIDESTRUC,XOUT]=T_TIDE(XIN) returns the analysis information in
+% a structure formed of NAME, FREQ, and TIDECON.
+%
+% XIN can be scalar (e.g. for elevations), or complex ( =U+sqrt(-1)*V
+% for eastward velocity U and northward velocity V.
+%
+% Further inputs are optional, and are specified as property/value pairs
+% [...]=T_TIDE(XIN,property,value,property,value,...,etc.)
+%      
+% These properties are:
+%
+%       'interval'       Sampling interval (hours), default = 1. 
+%                        Interval can either be a scalar or a vector with 
+%                        length (length(XIN)-1). An irregularly-spaced time
+%                        vector 'time' in Matlab datenumbers (as often encountered 
+%                        with ADCP data) can be analyzed as follows. Note this
+%                        is a non-canonical t_tide extension by OpenEarthTools.
+%
+%                        T_TIDE(XIN,'int',diff(time).*24,'start',time(1),...)
+%          
+%   The next two are required if nodal corrections are to be computed,
+%   otherwise not necessary. If they are not included then the reported
+%   phases are raw constituent phases at the central time. 
+%
+%   If your time series is longer than 18.6 years then nodal corrections
+%   are not made -instead we fit directly to all satellites (start time
+%   is then just used to generate Greenwich phases).
+%
+%       'start time'     [year,month,day,hour,min,sec]
+%                        - min,sec are optional OR decimal day (matlab 
+%                        DATENUM scalar), When [], no nodal corrections are 
+%                        used (default: [], unlike t_predic).
+%       'latitude'       decimal degrees (+north) (default: none).
+%
+%   Where to send the output.
+%       'output'         where to send printed output:
+%                        'none'    (no printed output)
+%                        'screen'  (to screen) - default
+%                        FILENAME  (to a file)
+%                        FID       (to (externally) openend file)
+%
+%   Where to send the screen messages (using fprintf):
+%       'diary'          'none'    (no printed output)
+%                        'screen'  (to screen) - default
+%                        <output filename> (to same file as 'output')
+%                        FILENAME  (to another file)
+%                        FID       (to (externally) opened file)
+%
+%   How to sort the component dimension in tables
+%       'sort'          '<->fre<q>' (default), '<->amp<litude>','<->snr,
+%                       '<->pha<se>' where prefix <-> means descending.
+%                        Or <->column_number in tidecon array (see below).
+%
+%   Correction factor for prefiltering.
+%       'prefilt'        FS,CORR
+%                        If the time series has been passed through
+%                        a pre-filter of some kind (say, to reduce the
+%                        low-frequency variability), then the analyzed
+%                        constituents will have to be corrected for 
+%                        this. The correction transfer function 
+%                        (1/filter transfer function) has (possibly 
+%                        complex) magnitude CORR at frequency FS (cph). 
+%                        Corrections of more than a factor of 100 are 
+%                        not applied; it is assumed these refer to tidal
+%                        constituents that were intentionally filtered 
+%                        out, e.g., the fortnightly components.
+%
+%   Adjustment for long-term behavior ("secular" behavior).
+%       'secular'        'mean'   - assume constant offset (default).
+%                        'linear' - get linear trend.
+%                     
+%   Inference of constituents.
+%       'inference'      NAME,REFERENCE,AMPRAT,PHASE_OFFSET
+%                        where NAME is an array of the names of 
+%                        constituents to be inferred, REFERENCE is an 
+%                        array of the names of references, and AMPRAT 
+%                        and PHASE_OFFSET are the amplitude factor and
+%                        phase offset (in degrees)from the references. 
+%                        NAME and REFERENCE are Nx4 (max 4 characters
+%                        in name), and AMPRAT and PHASE_OFFSET are Nx1
+%                        (for scalar time series) and Nx2 for vector 
+%                        time series (column 1 is for + frequencies and
+%                        column 2 for - frequencies).
+%                        NB - you can only infer ONE unknown constituent
+%                        per known constituent (i.e. REFERENCE must not 
+%                        contain multiple instances of the same name).
+%
+%   Shallow water constituents
+%       'shallow'        NAME
+%                        A matrix whose rows contain the names of 
+%                        shallow-water constituents to analyze.
+%
+%   Resolution criterions for least-squares fit.        
+%       'rayleigh'       scalar - Rayleigh criteria, default = 1.
+%                        Matrix of strings or cellstr - names of constituents to use
+%                         (useful for testing). Note: case-sensitive, use UPPER case.
+%  
+%   Calculation of confidence limits (Boostrap not possible with non-equidistant time)
+%                        NB (1) the 2 *boot methods do not work with
+%                        non-equidistant time vectors.
+%                        NB (2) cboot/linear need a COMMERCIAL SIGNAL 
+%                        PROCESSING TOOLBOX LICENSE
+%       'error'           'wboot' - (1) Boostrapped confidence intervals 
+%                                   based on a correlated bivariate 
+%                                   white-noise model.
+%                         'cboot' - (1,2) Boostrapped confidence intervals 
+%                                   based on an uncorrelated bivariate 
+%                                   coloured-noise model (default).
+%                        'linear' - (2) Linearized error analysis that 
+%                                   assumes an uncorrelated bivariate 
+%                                   coloured noise model. 
+%                                   
+%   Computation of "predicted" tide is passed to t_predic, but note that the
+%                                    default for nodal factor usage differs.
+%       'synthesis'      0 - use all selected constituents
+%                        scalar>0 - use only those constituents with a 
+%                                   SNR greater than that given (1 or 2 
+%                                   are good choices, 2 is the default).
+%                              <0 - return result of least-squares fit 
+%                                   (should be the same as using '0', 
+%                                   except that NaN-holes in original 
+%                                   time series will remain and mean/trend
+%                                   are included).
+%
+%   Least squares soln computational efficiency parameter
+%         'lsq'          'direct' - use A\x fit
+%                       'normal'  - use (A'A)\(A'x) (may be necessary
+%                                   for very large input vectors since
+%                                   A'A is much smaller than A)
+%                         'best'  - automatically choose based on
+%                                   length of series (default).
+%
+%       It is possible to call t_tide without using property names,
+%       in which case the assumed calling sequence is
+%
+%          T_TIDE(XIN,INTERVAL,START_TIME,LATITUDE,RAYLEIGH)
+%
+%
+%  OUTPUT: 
+%
+%    nameu=list of constituents used
+%    fu=frequency of tidal constituents (cycles/hr)
+%    tidecon=[fmaj,emaj,fmin,emin,finc,einc,pha,epha] for vector xin
+%           =[fmaj,emaj,pha ,epha] for scalar (real) xin
+%       fmaj,fmin - constituent major and minor axes (same units as xin)       
+%       emaj,emin - 95% confidence intervals for fmaj,fmin
+%       finc - ellipse orientations (degrees)
+%       einc - 95% confidence intervals for finc
+%       pha - constituent phases (degrees relative to Greenwich)
+%       epha - 95% confidence intervals for pha
+%    xout=tidal prediction
+%
+% Note: Although missing data can be handled with NaN, it is wise not
+%       to have too many of them. If your time series has a lot of 
+%       missing data at the beginning and/or end, then truncate the 
+%       input time series.  The Rayleigh criterion is applied to 
+%       frequency intervals calculated as the inverse of the input 
+%       series length.
+%
+% Example: irregular interval from DART buoy off the coast of Seattle from NOAA OPeNDAP server
+%      Since R2012a Matlab handles OPeNDAP urls, otherwise download netCDF file locally.
+%
+%      url   = 'http://dods.ndbc.noaa.gov/thredds/dodsC/data/dart/46419/46419t2016.nc'
+%      %% or download and read local cache
+%      % urlwrite(url,fullfile(tempdir,filenameext(url)))
+%      % url = fullfile(tempdir,filenameext(url));
+%      D.h   = ncread(url,'height',[1 1 1],[1 1 1e4]); % total water column height, so huge A0
+%      D.day = double(ncread(url,'time',1,1e4))/3600/24; % sec since 1970
+%      D.t   = datenum(1970,1,D.day); % irregular: 15, 30, 60,... 900 sec
+%      D.lat = ncread(url,'latitude');
+%     [S,D.hfit] = t_tide(D.h(:),'lat',D.lat,'sort','amp','interval',diff(D.t)*24,'start',D.t(1),'err','lin')
+%      plot(D.t,D.h(:)-+S.z0,'DisplayName','observation');
+%      hold on;plot(D.t,D.hfit,'r','DisplayName','t\_tide');
+%      legend show; grid on
+%      datetick('x')
+%
+% A description of the theoretical basis of the analysis and some
+% implementation details can be found in:
+%
+% Pawlowicz, R., B. Beardsley, and S. Lentz, "Classical Tidal 
+%   "Harmonic Analysis Including Error Estimates in MATLAB 
+%    using <a href="http://www.eos.ubc.ca/~rich/#T_Tide">"T_TIDE"</a>, <a href="http://dx.doi.org/10.1016/S0098-3004(02)00013-4">Computers and Geosciences, 28, 929-937 (2002)</a>.
+%   (citation would be appreciated if you find the toolbox useful).
+%
+%See also: t_predic, tide, UTide, modulation, harmanal
+
+% R. Pawlowicz 11/ 8/99 - Completely rewritten from the transliterated-
+%                         to-matlab IOS/Foreman fortran code by S. Lentz
+%                         and B. Beardsley.
+%               3/ 3/00 - Redid errors to take into account covariances 
+%                         between u and v errors.
+%               7/21/00 - Found that annoying bug in error calc! 
+%              11/ 1/00 - Added linear error analysis.
+%               8/29/01 - Made synth=1 default, also changed behavior 
+%                         when no lat/time given so that phases are raw
+%                         at central time. 
+%               9/ 1/01 - Moved some SNR code to t_predic.
+%               9/28/01 - made sure you can't choose Z0 as constituent.
+%               6/12/01 - better explanation for variance calcs, fixed
+%                         bug in typed output (thanks Mike Cook).
+%               8/ 2/03 - Added block processing for long time series (thanks
+%                         to Derek Goring).
+%               9/ 2/03 - Beta version of 18.6 year series handling
+%              12/ 2/03 - Bug (x should be xin) fixed thanks to Mike Cook (again!)
+%               4/ 3/11 - Changed (old) psd to (new) pwelch calls, also
+%                         isfinite for finite.
+%              23/ 3/11 - Corrected my conversion from psd to pwelch, thanks
+%                         to Dan Codiga and (especially) Evan Haug!
+%                Jul/12 - added support for non-equidistant time vector (Gerben J. de Boer)
+%                Feb/13 - added option to sort screen/txt output for rapid assessment (Gerben J. de Boer)
+%                Feb/13 - added option return z0 (different than regular mean) (Gerben J. de Boer)
+%                Nov/16 - fetch issue non-equidistant dt wiith error estimate
+
+% Version 1.3+
+% $Id$
+% $HeadURL: https://svn.oss.deltares.nl/repos/openearthtools/trunk/matlab/applications/tide/nc_t_tide$
+
+
+
+% ----------------------Parse inputs-----------------------------------
+
+ray          = 1;
+dt           = 1;
+fid          = 1;
+diar         = 1;
+sor          = 'freq';
+stime        = [];
+lat          = [];
+corr_fs      = [0 1e6];
+corr_fac     = [1  1];
+secular      = 'mean';
+inf.iname    = [];
+inf.irefname = [];
+shallownames = [];
+constitnames = [];
+errcalc      = 'cboot';
+synth        = 2;
+lsq          = 'best';
+
+k=1; 
+while length(varargin)>0,
+    if ischar(varargin{1}),
+        switch lower(varargin{1}(1:3)),
+            case 'int',
+                dt=varargin{2};
+            case 'sta',
+                stime=varargin{2};
+                if length(stime)>1, 
+                stime=[stime(:)' zeros(1,6-length(stime))]; 
+                stime=datenum(stime(1),stime(2),stime(3),stime(4),stime(5),stime(6));
+                end;
+            case 'lat',
+                lat=varargin{2};
+            case 'out',
+                filen=varargin{2};
+                switch filen,
+                    case 'none',
+                        fid=-1;
+                    case 'screen',
+                        fid=1;
+                    otherwise
+                        if isnumeric(filen);
+                            fid=filen;
+                        else % filen is a file name
+                            [fid,mesg]=fopen(filen,'w');
+                            openinside=1;
+                            if fid==-1,
+                                error(mesg); 
+                            end;
+                        end;
+                end;
+            case 'dia',
+                diar=varargin{2};
+                switch diar
+                    case 'none'
+                        diar=0;
+                    case 'screen'
+                        diar=1;
+                    case filen
+                        diar=fid;
+                    otherwise
+                        if isnumeric(diar)
+                            diar=varargin{2};
+                        else
+                            diar=fopen(diar,'w');
+                            if diar==-1,
+                                error(mesg);
+                            end;
+                        end
+                end;
+            case 'ray',
+                if isnumeric(varargin{2}),
+                    ray=varargin{2};
+                else
+                    constitnames=varargin{2};
+                    if iscellstr(constitnames),
+                        constitnames=char(constitnames); 
+                    end;
+                end;
+            case 'pre',
+                corr_fs=varargin{2};
+                corr_fac=varargin{3};
+                varargin(1)=[];
+            case 'sec',
+                secular=varargin{2};
+            case 'inf',
+                inf.iname=varargin{2};
+                inf.irefname=varargin{3};
+                inf.amprat=varargin{4};
+                inf.ph=varargin{5};
+                varargin(1:3)=[];
+            case 'sha',
+                shallownames=varargin{2};
+            case 'err',
+                errcalc=varargin{2};
+            case 'syn',
+                synth=varargin{2};
+            case 'lsq',
+                lsq=varargin{2};	 
+            case 'sor',
+                sor=varargin{2};	 
+        otherwise,
+            error(['Can''t understand property:' varargin{1}]);
+        end;
+            varargin([1 2])=[]; 
+    else  
+        switch k,
+            case 1,
+                dt=varargin{1};
+            case 2,
+                stime=varargin{1};
+            case 3,
+                lat=varargin{1};
+            case 4,
+                ray=varargin{1};
+            otherwise
+                error('Too many input parameters');
+        end;
+        varargin(1)=[];
+    end;
+    k=k+1;
+end;
+
+%% checks
+[inn,inm]=size(xin);
+if ~(inn==1 || inm==1), error('Input time series is not a vector'); end;
+
+%%
+xin=xin(:); % makes xin a column vector
+nobs=length(xin);
+
+if strcmp(lsq(1:3),'bes'),  % Set matrix method if auto-choice.
+ if nobs>10000,
+    lsq='normal';
+ else
+    lsq='direct';
+ end;
+end;
+ 
+nobsu=nobs-rem(nobs-1,2);% makes series odd to give a center point
+
+if prod(length(dt)) >1
+   if ~(length(dt)==(nobs-1))
+      error('time vector should have same length as XIN')
+   end
+   t  = [0;cumsum(dt(:))];   % construct time vector
+   
+   tmean=mean(t(1:2*fix((length(t)-1)/2)+1));   
+
+   t  = t - tmean;     % Time vector for entire time series,
+                       % centered at series midpoint. 
+
+   % check whether full time series is equidistant at machine precision
+   % so we can perform the bootstrap error analysis after all
+   if max(abs(diff(dt))) > eps('single')
+      dt0 = dt;
+   end
+   dt = (t(end) - t(1))./(nobs-1); % average dt to ensure results for equidistant vector match scalar dt
+
+   % Make sure centraltime matches 'centraltime' (jdmid) in t_predic that has
+   % always been capable of handling non-equidistant time series
+   if ~isempty(stime),
+     centraltime=stime+tmean./24;
+   else
+     centraltime=[];
+     if (rem(nobs-1,2)) % even
+         t = t -dt/2;
+     end
+   end;
+   
+else
+   
+   t=dt*([1:nobs]'-ceil(nobsu/2));  % Time vector for entire time series,
+                                    % centered at series midpoint. 
+   if ~isempty(stime),
+     centraltime=stime+floor(nobsu./2)./24.0*dt;
+   else
+     centraltime=[];
+     if (rem(nobs-1,2)) % even
+         t = t -dt/2;
+     end     
+   end;
+end
+
+%check consistency with t_predic's centraltime
+if ~diar==0
+fprintf(diar,'   t_tide   centraltime =  %f (%s)\n',centraltime,datestr(centraltime));
+end
+if nobs*dt> 18.6*365.25*24,  % Long time series
+  longseries=1; ltype='full';
+else
+  longseries=0; ltype='nodal';
+end;
+        		
+% -------Get the frequencies to use in the harmonic analysis-----------
+
+[nameu,fu,ju,namei,fi,jinf,jref]=constituents(ray/(dt*nobsu),constitnames,...
+                                           shallownames,inf.iname,inf.irefname,centraltime);
+if ~diar==0
+fprintf(diar,'   number of standard constituents used: %s\n',int2str(length(ju)));
+end
+mu=length(fu); % # base frequencies
+mi=length(fi); % # inferred
+
+% Find the good data points (here I assume that in a complex time 
+% series, if u is bad, so is v).
+
+gd=find(isfinite(xin(1:nobsu)));
+ngood=length(gd);
+if ~diar==0
+fprintf(diar,'   Points used: %d of %d\n',ngood,nobs);
+end
+
+
+%----------------------------------------------------------------------
+% Now solve for the secular trend plus the analysis. Instead of solving
+% for + and - frequencies using exp(i*f*t), I use sines and cosines to 
+% keep tc real.  If the input series is real, than this will 
+% automatically use real-only computation (faster). However, for the analysis, 
+% it's handy to get the + and - frequencies ('ap' and 'am'), and so 
+% that's what we do afterwards.
+
+% The basic code solves the matrix problem Ac=x+errors where the functions to
+% use in the fit fill up the A matrix, which is of size (number points)x(number
+% constituents). This can get very, very large for long time series, and
+% for this the more complex block processing algorithm was added. It should
+% give identical results (up to roundoff error)
+
+if strcmp(lsq(1:3),'dir'),
+
+  if secular(1:3)=='lin',
+    tc=[ones(length(t),1) cos((2*pi)*t*fu') sin((2*pi)*t*fu') t*(2/dt/nobsu)];
+  else
+    tc=[ones(length(t),1) cos((2*pi)*t*fu') sin((2*pi)*t*fu') ];
+  end;
+  
+  coef=tc(gd,:)\xin(gd);
+
+  z0=coef(1);
+  ap=(coef(2:(1+mu))-1i*coef((2+mu):(1+2*mu)))/2;  % a+ amplitudes
+  am=(coef(2:(1+mu))+1i*coef((2+mu):(1+2*mu)))/2;  % a- amplitudes
+  if secular(1:3)=='lin',
+    dz0=coef(end);
+  else
+    dz0=0;
+  end;    
+  xout=tc*coef;  % This is the time series synthesized from the analysis
+
+else  % More complicated code required for long time series when memory may be
+      % a problem. Modified from code submitted by Derek Goring (NIWA Chrischurch)
+      
+      % Basically the normal equations are formed (rather than using Matlab's \
+      % algorithm for least squares); this can be done by adding up subblocks
+      % of data. Notice how the code is messier, and we have to recalculate everything
+      % to get the original fit.
+
+  nsub=5000;  % Block length - doesn't matter really but should be small enough to
+              % get allocated quickly	      
+  if secular(1:3)=='lin',
+    lhs=zeros(mu*2+2,mu*2+2); rhs=zeros(mu*2+2,1);
+    for j1=1:nsub:ngood
+      j2=min(j1 + nsub - 1,ngood);
+      E=[ones(j2-j1+1,1) cos((2*pi)*t(gd(j1:j2))*fu') sin((2*pi)*t(gd(j1:j2))*fu') t(gd(j1:j2))*(2/dt/nobsu)];
+      rhs=rhs + E'*xin(gd(j1:j2));
+      lhs=lhs + E'*E;
+    end;
+  else  
+    lhs=zeros(mu*2+1,mu*2+1); rhs=zeros(mu*2+1,1);
+    for j1=1:nsub:ngood
+      j2=min(j1 + nsub - 1,ngood);
+      E=[ones(j2-j1+1,1) cos((2*pi)*t(gd(j1:j2))*fu') sin((2*pi)*t(gd(j1:j2))*fu')];
+      rhs=rhs + E'*xin(gd(j1:j2));
+      lhs=lhs + E'*E;
+    end;
+  end;
+    
+  coef=lhs\rhs;
+  
+  z0=coef(1);
+  ap=(coef(2:(1+mu))-1i*coef((2+mu):(1+2*mu)))/2;  % a+ amplitudes
+  am=(coef(2:(1+mu))+1i*coef((2+mu):(1+2*mu)))/2;  % a- amplitudes
+  if secular(1:3)=='lin',
+    dz0=coef(end);
+  else
+    dz0=0;
+  end; 
+  
+  xout=xin; % Copies over NaN   
+  if secular(1:3)=='lin',
+    for j1=1:nsub:nobs
+      j2=min(j1 + nsub - 1,nobs);
+      E=[ones(j2-j1+1,1) cos((2*pi)*t(j1:j2)*fu') sin((2*pi)*t(j1:j2)*fu') t(j1:j2)*(2/dt/nobsu)];
+      xout(j1:j2)=E*coef;
+    end;
+  else  
+    for j1=1:nsub:nobs
+      j2=min(j1 + nsub - 1,nobs);
+      E=[ones(j2-j1+1,1) cos((2*pi)*t(j1:j2)*fu') sin((2*pi)*t(j1:j2)*fu')];
+      xout(j1:j2)=E*coef;
+    end;
+  end;
+
+end;
+
+   
+ 
+%----------------------------------------------------------------------
+% Check variance explained (but do this with the original fit).
+
+xres=xin-xout; % and the residuals!
+
+if isreal(xin),    % Real time series
+  varx=cov(xin(gd));varxp=cov(xout(gd));varxr=cov(xres(gd));
+  if ~diar==0
+  fprintf(diar,'   percent of var residual after lsqfit/var original: %5.2f %%\n',100*(varxr/varx));  
+  end
+else               % Complex time series
+  varx=cov(real(xin(gd)));varxp=cov(real(xout(gd)));varxr=cov(real(xres(gd)));
+  if ~diar==0
+  fprintf(diar,'   percent of X var residual after lsqfit/var original: %5.2f %%\n',100*(varxr/varx));
+  end
+
+  vary=cov(imag(xin(gd)));varyp=cov(imag(xout(gd)));varyr=cov(imag(xres(gd)));
+  if ~diar==0
+  fprintf(diar,'   percent of Y var residual after lsqfit/var original: %5.2f %%\n',100*(varyr/vary));
+  end
+end;
+
+
+%---------- Correct for prefiltering-----------------------------------
+
+corrfac=interp1(corr_fs,corr_fac,fu);
+% To stop things blowing up!
+corrfac(corrfac>100 | corrfac <.01 | isnan(corrfac))=1;
+
+ap=ap.*corrfac;
+am=am.*conj(corrfac);
+
+%---------------Nodal Corrections-------------------------------------- 						   
+% Generate nodal corrections and calculate phase relative to Greenwich. 						   
+% Note that this is a slightly weird way to do the nodal corrections,							   
+% but is 'traditional'.  The "right" way would be to change the basis							   
+% functions used in the least-squares fit above.									   
+
+if ~isempty(lat) && ~isempty(stime),   % Time and latitude								   
+  % Get nodal corrections at midpoint time.										   
+  [v,u,f]=t_vuf(ltype,centraltime,[ju;jinf],lat);									   
+
+  vu=(v+u)*360; % total phase correction (degrees)									   
+  nodcor=['Greenwich phase computed with nodal corrections applied to amplitude and phase relative to center time ',datestr(centraltime)];
+elseif ~isempty(stime),    % Time only  										   
+  % Get nodal corrections at midpoint time										   
+  [v,u,f]=t_vuf(ltype,centraltime,[ju;jinf]);										   
+  vu=(v+u)*360; % total phase correction (degrees)									   
+  nodcor=['Greenwich phase computed, no nodal corrections'];								   
+else   % No time, no latitude												   
+  vu=zeros(length(ju)+length(jinf),1);  										   
+  f=ones(length(ju)+length(jinf),1);											   
+  nodcor=['No nodal corrections, because of empty central time and latitude'];%['Phases at central time ',datestr(centraltime,0)]; % empty central time will be error in future releases
+end			
+if ~diar==0
+fprintf(diar,'   %s\n',nodcor);												   
+end
+
+%---------------Inference Corrections----------------------------------
+% Once again, the "right" way to do this would be to change the basis
+% functions.
+ii=find(isfinite(jref));
+if ii,
+    if ~diar==0
+    fprintf(diar,'   Do inference corrections\n');
+    end
+    snarg=nobsu*pi*(fi(ii)   -fu(jref(ii)) )*dt;
+    scarg=sin(snarg)./snarg;
+ 
+  if size(inf.amprat,2)==1,    % For real time series
+    pearg=     2*pi*(vu(mu+ii)-vu(jref(ii))+inf.ph(ii))/360;
+    pcfac=inf.amprat(ii).*f(mu+ii)./f(jref(ii)).*exp(1i*pearg);
+    pcorr=1+pcfac.*scarg;
+    mcfac=conj(pcfac);
+    mcorr=conj(pcorr);
+  else                          % For complex time series
+    pearg=     2*pi*(vu(mu+ii)-vu(jref(ii))+inf.ph(ii,1))/360;
+    pcfac=inf.amprat(ii,1).*f(mu+ii)./f(jref(ii)).*exp(1i*pearg);
+    pcorr=1+pcfac.*scarg;
+    mearg=    -2*pi*(vu(mu+ii)-vu(jref(ii))+inf.ph(ii,2))/360;
+    mcfac=inf.amprat(ii,2).*f(mu+ii)./f(jref(ii)).*exp(1i*mearg);
+    mcorr=1+mcfac.*scarg;
+  end;
+    
+  ap(jref(ii))=ap(jref(ii))./pcorr;   % Changes to existing constituents
+  ap=[ap;ap(jref(ii)).*pcfac];        % Inferred constituents
+
+  am(jref(ii))=am(jref(ii))./mcorr;
+  am=[am;am(jref(ii)).*mcfac];
+
+  fu=[fu;fi(ii)];
+  nameu=[nameu;namei(ii,:)];
+end;
+
+
+% --------------Error Bar Calculations---------------------------------
+%
+% Error bar calcs involve two steps:
+%      1) Estimate the uncertainties in the analyzed amplitude
+%         for both + and - frequencies (i.e., in 'ap' and 'am').
+%         A simple way of doing this is to take the variance of the
+%         original time series and divide it into the amount appearing
+%         in the bandwidth of the analysis (approximately 1/length).
+%         A more sophisticated way is to assume "locally white"
+%         noise in the vicinity of, e.g., the diurnal consistuents.
+%         This takes into account slopes in the continuum spectrum.
+%
+%      2) Transform those uncertainties into ones suitable for ellipse
+%         parameters (axis lengths, angles). This can be done 
+%         analytically for large signal-to-noise ratios. However, the 
+%         transformation is non-linear at lows SNR, say, less than 10
+%         or so.
+%
+
+xr=fixgaps(xres); % Fill in "internal" NaNs with linearly interpolated
+                  % values so we can fft things.
+nreal=1;
+if any(strmatch(errcalc(2:end),'boot')) && exist('dt0','var')
+   % non-equidistant time series do not allow for fft
+   warning(['When ''dt'' is a non-equidistant vector, error estimate with ''',errcalc,''' not possible, only ''lin'': all errors 0.'])
+   epsp = zeros(1,nreal);
+   epsm = zeros(1,nreal);
+elseif any(strmatch(errcalc(2:end),'boot')) && ~exist('dt0','var')
+    if ~diar==0
+    fprintf(diar,'   Using nonlinear bootstrapped error estimates\n');
+    end
+  
+  % "noise" matrices are created with the right covariance structure
+  % to add to the analyzed components to create 'nreal' REPLICATES. 
+  % 
+
+  nreal=300;             % Create noise matrices 
+  [NP,NM]=noise_realizations(xr(isfinite(xr)),fu,dt,nreal,errcalc);
+      
+  % All replicates are then transformed (nonlinearly) into ellipse 
+  % parameters.  The computed error bars are then based on the std
+  % dev of the replicates.
+
+  AP=ap(:,ones(1,nreal))+NP;        % Add to analysis (first column
+  AM=am(:,ones(1,nreal))+NM;        % of NM,NP=0 so first column of
+                                    % AP/M holds ap/m).
+  epsp=angle(AP)*180/pi;            % Angle/magnitude form:
+  epsm=angle(AM)*180/pi;
+  ap=abs(AP);
+  am=abs(AM);
+elseif any(strmatch(errcalc,'linear')) || exist('dt0','var') % non-equidistant time series do not allow for fft
+  if ~diar==0
+  fprintf(diar,'   Using linearized error estimates\n');
+  end
+  %
+  % Uncertainties in analyzed amplitudes are computed in different
+  % spectral bands. Real and imaginary parts of the residual time series
+  % are treated separately (no cross-covariance is assumed).
+  %
+  % Noise estimates are then determined from a linear analysis of errors,
+  % assuming that everything is uncorrelated. This is OK for scalar time
+  % series but can fail for vector time series if the noise is not 
+  % isotropic.
+  
+  [ercx,eicx]=noise_stats(xr(isfinite(xr)),fu,dt);
+  % Note - here we assume that the error in the cos and sin terms is 
+  % equal, and equal to total power in the encompassing frequency bin. 
+  % It seems like there should be a factor of 2 here somewhere but it 
+  % only works this way! <shrug>
+  [emaj,emin,einc,epha]=errell(ap+am,1i*(ap-am),ercx,ercx,eicx,eicx);
+
+  epsp=angle(ap)*180/pi;
+  epsm=angle(am)*180/pi;
+  ap=abs(ap);
+  am=abs(am);
+else
+  error(['Unrecognized type of error analysis: ''' errcalc ''' specified!']);
+end;
+
+%-----Convert complex amplitudes to standard ellipse parameters--------
+
+aap=ap./f(:,ones(1,nreal));	% Apply nodal corrections and
+aam=am./f(:,ones(1,nreal));	% compute ellipse parameters.
+
+fmaj=aap+aam;                   % major axis
+fmin=aap-aam;                   % minor axis
+
+gp=mod( vu(:,ones(1,nreal))-epsp ,360); % pos. Greenwich phase in deg.
+gm=mod( vu(:,ones(1,nreal))+epsm ,360); % neg. Greenwich phase in deg.
+
+finc= (epsp+epsm)/2;
+finc(:,1)=mod( finc(:,1),180 ); % Ellipse inclination in degrees
+				% (mod 180 to prevent ambiguity, i.e., 
+				% we always ref. against northern 
+				% semi-major axis.
+	
+finc=cluster(finc,180); 	% Cluster angles around the 'true' 
+                                % angle to avoid 360 degree wraps.
+
+pha=mod( gp+finc ,360); 	% Greenwich phase in degrees.
+
+pha=cluster(pha,360);		% Cluster angles around the 'true' angle
+				% to avoid 360 degree wraps.
+
+%----------------Generate 95% CI---------------------------------------
+%% For bootstrapped errors, we now compute limits of the distribution.
+if strmatch(errcalc(2:end),'boot'),
+     %% std dev-based estimates.
+     % The 95% CI are computed from the sigmas
+     % by a 1.96 fudge factor (infinite degrees of freedom).
+     % emaj=1.96*std(fmaj,0,2);
+     % emin=1.96*std(fmin,0,2);
+     % einc=1.96*std(finc,0,2);
+     % epha=1.96*std(pha ,0,2);
+     %% Median-absolute-deviation (MAD) based estimates.
+     % (possibly more stable?)
+      emaj=median(abs(fmaj-median(fmaj,2)*ones(1,nreal)),2)/.6375*1.96;
+      emin=median(abs(fmin-median(fmin,2)*ones(1,nreal)),2)/.6375*1.96;
+      einc=median(abs(finc-median(finc,2)*ones(1,nreal)),2)/.6375*1.96;
+      epha=median(abs( pha-median( pha,2)*ones(1,nreal)),2)/.6375*1.96;
+else
+   % In the linear analysis, the 95% CI are computed from the sigmas
+   % by this fudge factor (infinite degrees of freedom).
+   emaj=1.96*emaj;
+   emin=1.96*emin;
+   einc=1.96*einc;
+   epha=1.96*epha;
+end;
+				  
+if isreal(xin),
+    tidecon=[fmaj(:,1),emaj,pha(:,1),epha];
+else
+    tidecon=[fmaj(:,1),emaj,fmin(:,1),emin, finc(:,1),einc,pha(:,1),epha];
+end;
+
+% Sort results by frequency (needed if anything has been inferred since 
+% these are stuck at the end of the list by code above).
+if any(isfinite(jref)),
+ [fu,I]=sort(fu);
+ nameu=nameu(I,:);
+ tidecon=tidecon(I,:);
+end;
+
+snr=(tidecon(:,1)./tidecon(:,2)).^2;  % signal to noise ratio
+
+%--------Generate a 'prediction' using significant constituents----------
+xoutOLD=xout;
+if synth>=0,
+ if ~isempty(lat) && ~isempty(stime),
+     if ~diar==0
+     fprintf(diar,'   Generating prediction with nodal corrections, SNR is %f\n',synth);
+     end
+   [xout,centraltime]=t_predic(stime+(t(:)'-t(1))./24.0,nameu,fu,tidecon,'lat',lat,'synth',synth,'anal',ltype);
+ elseif ~isempty(stime),
+     if ~diar==0
+     fprintf(diar,'   Generating prediction without nodal corrections, SNR is %f\n',synth);
+     end
+   [xout,centraltime]=t_predic(stime+(t(:)'-t(1))./24.0,nameu,fu,tidecon,'synth',synth,'anal',ltype);
+ else
+     if ~diar==0
+     fprintf(diar,'   Generating prediction without nodal corrections, SNR is %f\n',synth);
+     end
+   [xout,centraltime]=t_predic(t/24.0,nameu,fu,tidecon,'synth',synth,'anal',ltype,'start',stime);
+ end;
+else
+    if ~diar==0
+    fprintf(diar,'   Returning fitted prediction\n');
+    end
+end;
+
+if ~isempty(centraltime)
+    if ~diar==0
+    fprintf(diar,'   t_predic centraltime =  %f (%s)\n',centraltime,datestr(centraltime));
+    end
+end
+
+%----------------------------------------------------------------------
+% Check variance explained (but now do this with the synthesized fit).
+xres=xin(:)-xout(:); % and the residuals!
+
+%error;
+
+if isreal(xin),    % Real time series
+  varx=cov(xin(gd));varxp=cov(xout(gd));varxr=cov(xres(gd));
+  if ~diar==0
+  fprintf(diar,'   percent of var residual after synthesis/var original: %5.2f %%\n',100*(varxr/varx));  
+  end
+else               % Complex time series
+  varx=cov(real(xin(gd)));varxp=cov(real(xout(gd)));varxr=cov(real(xres(gd)));
+  if ~diar==0
+  fprintf(diar,'   percent of X var residual after synthesis/var original: %5.2f %%\n',100*(varxr/varx));
+  end
+
+  vary=cov(imag(xin(gd)));varyp=cov(imag(xout(gd)));varyr=cov(imag(xres(gd)));
+  if ~diar==0
+  fprintf(diar,'   percent of Y var residual after synthesis/var original: %5.2f %%\n',100*(varyr/vary));
+  end
+end;
+
+%-----------------Sort   results---------------------------------------
+
+if isnumeric(sor)
+   if sor > 0
+   [~,index]=sort(tidecon(:,abs(sor)),1,'ascend' );
+   else
+   [~,index]=sort(tidecon(:,abs(sor)),1,'descend');
+   end
+else
+
+   if strcmpi(sor(1),'-');
+      order = 'descend' ;
+   else
+      order = 'ascend' ;
+   end
+
+   if      any(strfind(sor,'fre'));                            index =1:size(tidecon,1);% default
+   elseif  any(strfind(sor,'amp'))||any(strfind(sor,'fma'));[~,index]=sort(tidecon(:,1    ),1,order);
+   elseif  any(strfind(sor,'fmi'))                         ;[~,index]=sort(tidecon(:,3    ),1,order);
+   elseif  any(strfind(sor,'pha'))                         ;[~,index]=sort(tidecon(:,end-1),1,order);
+   elseif  any(strfind(sor,'snr'))                         ;[~,index]=sort(snr             ,1,order);
+   end   
+end
+
+   nameu   =   nameu(index,:);
+   fu      =      fu(index);
+   tidecon = tidecon(index,:);
+   snr     =     snr(index);
+
+%-----------------Output results---------------------------------------
+
+if fid>1,
+ fprintf(fid,'\n%s\n',['file name: ',filen]);
+elseif fid==1,
+ fprintf(fid,'-----------------------------------\n');
+end
+
+if fid>0,
+  fprintf(fid,'t_tide version: $Id$ \n');
+  fprintf(fid,'nobs = %d,  ngood = %d,  record length (days) = %.2f\n',nobs,ngood,length(xin)*dt/24);
+  if ~isempty(stime); fprintf(fid,'%s\n',['start time: ',datestr(stime)]); end
+  fprintf(fid,'rayleigh criterion = %.1f\n',ray);
+  fprintf(fid,'%s\n',nodcor);
+%  fprintf(fid,'\n     coefficients from least squares fit of x\n');
+%  fprintf(fid,'\n tide    freq        |a+|       err_a+      |a-|       err_a-\n');
+%  for k=1:length(fu);
+%    if ap(k)>eap(k) | am(k)>eam(k), fprintf('*'); else fprintf(' '); end;
+%    fprintf(fid,'%s  %8.5f  %9.4f  %9.4f  %9.4f  %9.4f\n',nameu(k,:),fu(k),ap(k),eap(k),am(k),eam(k));
+%  end
+  fprintf(fid,'\nx0= %.3g, x trend= %.3g\n',real(z0),real(dz0));
+  fprintf(fid,['\nvar(x)= ',num2str(varx),'   var(xp)= ',num2str(varxp),'   var(xres)= ',num2str(varxr) '\n']);
+  fprintf(fid,'percent var predicted/var original= %.1f %%\n',100*varxp/varx);
+
+  if isreal(xin)
+    fprintf(fid,'\n     tidal amplitude and phase with 95%% CI estimates\n');
+    fprintf(fid,'\ntide   freq       amp     amp_err    pha    pha_err     snr\n');
+    for k=1:length(fu);
+      if snr(k)>synth, fprintf(fid,'*'); else fprintf(fid,' '); end;
+      fprintf(fid,'%s %9.7f %9.4f %8.3f %8.2f %8.2f %8.2g\n',nameu(k,:),fu(k),tidecon(k,:),snr(k));
+    end
+  else
+    fprintf(fid,'\ny0= %.3g, x trend= %.3g\n',imag(z0),imag(dz0));
+    fprintf(fid,['\nvar(y)= ',num2str(vary),'    var(yp)= ',num2str(varyp),'  var(yres)= ',num2str(varyr) '\n']);
+    fprintf(fid,'percent var predicted/var original= %.1f %%\n',100*varyp/vary);
+    fprintf(fid,'\n%s\n',['ellipse parameters with 95%% CI estimates']);
+    fprintf(fid,'\n%s\n',['tide  freq        major   emaj    minor    emin   inc    einc      pha     epha   snr']);
+    for k=1:length(fu);
+      if snr(k)>synth, fprintf(fid,'*'); else fprintf(fid,' '); end;
+      fprintf(fid,'%s %9.7f %7.3f %7.3f %8.3f %7.3f %7.2f %7.2f %8.2f %7.2f %6.2g\n',...
+	nameu(k,:),fu(k),tidecon(k,:),snr(k));
+    end
+    fprintf(fid,['\ntotal var= ',num2str(varx+vary),'   pred var= ',num2str(varxp+varyp) '\n']);
+    fprintf(fid,'percent total var predicted/var original= %.1f %%\n\n',100*(varxp+varyp)/(varx+vary));
+  end
+
+  if exist('openinside','var') %Only close logfile when created inside t_tide, if created outside you should close it there as well.
+      st=fclose(fid);
+      clear('openinside'); 
+  end
+end;
+
+if isempty(stime)
+    period = []; % no nodal corrections
+else
+    period = stime+[0 (t(end)-t(1))]/24;
+end
+
+xout=reshape(xout,inn,inm);
+switch nargout,
+  case {0,3,4}
+  case {1}
+   nameu = struct('name',nameu,'freq',fu,'tidecon',tidecon,'type',ltype,'z0',z0,'dz0',dz0,...  % original t_tide fields
+       'lat',lat,'period',period);
+  case {2}   
+   nameu = struct('name',nameu,'freq',fu,'tidecon',tidecon,'type',ltype,'z0',z0,'dz0',dz0,...  % original t_tide fields
+       'lat',lat,'period',period);
+   fu=xout;
+end;
+   
+%----------------------------------------------------------------------
+function [nameu,fu,ju,namei,fi,jinf,jref]=constituents(minres,constit,...
+                                     shallow,infname,infref,centraltime);
+% [name,freq,kmpr]=constituents(minres,infname) loads tidal constituent
+% table (containing 146 constituents), then picks out only the '
+% resolvable' frequencies (i.e. those that are MINRES apart), base on 
+% the comparisons in the third column of constituents.dat. Only 
+% frequencies in the 'standard' set of 69 frequencies are actually used.
+% Also return the indices of constituents to be inferred.
+
+% If we have the mat-file, read it in, otherwise create it and read
+% it in!
+
+% R Pawlowicz 9/1/01 
+% Version 1.0
+%
+%    19/1/02 - typo fixed (thanks to  Zhigang Xu)
+
+% Compute frequencies from astronomical considerations.
+
+if minres>1/(18.6*365.25*24),                       % Choose only resolveable pairs for short
+    [const,sat,cshallow]=t_getconsts(centraltime);    % Time series   
+    ju=find(const.df>=minres);
+else                                                % Choose them all if > 18.6 years.
+    if isempty(centraltime)
+     error('time series longer than 18.6 years require keyword ''start time''')
+    end
+    [const,sat,cshallow]=t_get18consts(centraltime);
+    ju=[2:length(const.freq)]';  % Skip Z0
+    for ff=1:2,                  % loop twice to make sure of neighbouring pairs
+    jck=find(diff(const.freq(ju))<minres);
+    if (length(jck)>0)
+       jrm=jck;
+       jrm=jrm+(abs(const.doodsonamp(ju(jck+1)))<abs(const.doodsonamp(ju(jck))));
+       disp('  Warning! Following constituent pairs violate Rayleigh criterion');
+       for ick=1:length(jck);
+     disp(['     ',const.name(ju(jck(ick)),:),' vs ',const.name(ju(jck(ick)+1),:) ' - not using ',const.name(ju(jrm(ick)),:)]);
+       end;
+       ju(jrm)=[];
+    end
+    end;
+end;
+  
+if ~isempty(constit),     % Selected if constituents are specified in input.
+  ju=[];
+  for k=1:size(constit,1),
+   j1=strmatch(constit(k,:),const.name);
+   if isempty(j1),
+     disp(['   Can''t recognize name ' constit(k,:) ' for forced search']);
+   elseif j1==1,
+     disp('*************************************************************************');
+     disp('Z0 specification ignored - for non-tidal offsets see ''secular'' property');
+     disp('*************************************************************************');
+   else  
+     ju=[ju;j1];
+   end;
+  end;
+  [~,II]=sort(const.freq(ju)); % sort in ascending order of frequency.
+  ju=ju(II);
+end;
+
+if ~isempty(shallow),          % Add explictly selected shallow water constituents.
+ for k=1:size(shallow,1),
+   j1=strmatch(shallow(k,:),const.name);
+   if isempty(j1),
+     disp(['   Can''t recognize name ' shallow(k,:) ' for forced search']);
+   else
+     if isnan(const.ishallow(j1)),
+       disp(['   ',shallow(k,:),' Not a shallow-water constituent']);
+     end;
+     disp(['   Forced fit to ' shallow(k,:)]);
+     ju=[ju;j1];
+   end;
+ end;
+ 
+end;
+      
+nameu=const.name(ju,:);
+fu=const.freq(ju);
+
+
+% Check if neighboring chosen constituents violate Rayleigh criteria.
+jck=find(diff(fu)<minres);
+if (length(jck)>0)
+   disp('  Warning! Following constituent pairs violate Rayleigh criterion');
+   for ick=1:length(jck);
+   disp(['     ',nameu(jck(ick),:),'  ',nameu(jck(ick)+1,:)]);
+   end;
+end
+
+% For inference, add in list of components to be inferred.
+
+fi=[];namei=[];jinf=[];jref=[];
+if ~isempty(infname),
+  fi=zeros(size(infname,1),1);
+  namei=zeros(size(infname,1),4);
+  jinf=zeros(size(infname,1),1)+NaN;
+  jref=zeros(size(infname,1),1)+NaN;
+
+  for k=1:size(infname,1),
+   j1=strmatch(infname(k,:),const.name);
+   if isempty(j1),
+     disp(['Can''t recognize name' infname(k,:) ' for inference']);
+   else
+    jinf(k)=j1;
+    fi(k)=const.freq(j1);
+    namei(k,:)=const.name(j1,:);
+    j1=strmatch(infref(k,:),nameu);
+    if isempty(j1),
+      disp(['Can''t recognize name ' infref(k,:) ' for as a reference for inference']);
+    else
+      jref(k)=j1;
+      disp(['   Inference of %s using  %s\n',namei(k,:),nameu(j1,:)])
+%       if ~diar==0
+%       fprintf(diar,'   Inference of %s using  %s\n',namei(k,:),nameu(j1,:));
+%       end
+    end;
+   end;
+  end;    
+  jinf(isnan(jref))=NaN;
+end;
+
+%----------------------------------------------------------------------
+function y=fixgaps(x)
+% FIXGAPS: Linearly interpolates gaps in a time series
+% YOUT=FIXGAPS(YIN) linearly interpolates over NaN in the input time 
+% series (may be complex), but ignores trailing and leading NaNs.
+
+% R. Pawlowicz 11/6/99
+% Version 1.0
+
+y=x;
+
+bd=isnan(x);
+gd=find(~bd);
+
+bd([1:(min(gd)-1) (max(gd)+1):end])=0;
+
+
+y(bd)=interp1(gd,x(gd),find(bd)); 
+
+
+%----------------------------------------------------------------------
+function ain=cluster(ain,clusang)
+% CLUSTER: Clusters angles in rows around the angles in the first 
+% column. CLUSANG is the allowable ambiguity (usually 360 degrees but
+% sometimes 180).
+
+ii=(ain-ain(:,ones(1,size(ain,2))))>clusang/2;
+ain(ii)=ain(ii)-clusang;
+ii=(ain-ain(:,ones(1,size(ain,2))))<-clusang/2;
+ain(ii)=ain(ii)+clusang;
+
+
+%----------------------------------------------------------------------
+function [NP,NM]=noise_realizations(xres,fu,dt,nreal,errcalc)
+% NOISE_REALIZATIONS: Generates matrices of noise (with correct
+% cross-correlation structure) for bootstrap analysis.
+%
+
+% R. Pawlowicz 11/10/00
+% Version 1.0
+
+if strmatch(errcalc,'cboot'),
+  [fband,Pxrave,Pxiave,Pxcave]=residual_spectrum(xres,fu,dt);
+  
+  Pxcave=zeros(size(Pxcave));  %% For comparison with other technique!
+  %if ~diar==0
+  %fprintf(diar,'**** Assuming no covariance between u and v errors!*******\n');
+  %end
+elseif strmatch(errcalc,'wboot'),
+  fband=[0 .5];
+  nx=length(xres);
+  A=cov(real(xres),imag(xres))/nx;
+  Pxrave=A(1,1);Pxiave=A(2,2);Pxcave=A(1,2);
+else
+  error(['Unrecognized type of bootstap analysis specified: ''' errcalc '''']);
+end;
+  
+nfband=size(fband,1);
+
+Mat=zeros(4,4,nfband);
+for k=1:nfband,
+
+  % The B matrix represents the covariance matrix for the vector
+  % [Re{ap} Im{ap} Re{am} Im{am}]' where Re{} and Im{} are real and
+  % imaginary parts, and ap/m represent the complex constituent 
+  % amplitudes for positive and negative frequencies when the input
+  % is bivariate white noise. For a flat residual spectrum this works 
+  % fine.
+ 
+  % This is adapted here for "locally white" conditions, but I'm still
+  % not sure how to handle a complex sxy, so this is set to zero
+  % right now.
+  
+  p=(Pxrave(k)+Pxiave(k))/2;
+  d=(Pxrave(k)-Pxiave(k))/2;
+  sxy=Pxcave(k);
+  
+  B=[p    0   d   sxy;
+     0    p  sxy  -d;
+     d   sxy  p    0
+     sxy -d   0    p];
+
+  % Compute the transformation matrix that takes uncorrelated white 
+  % noise and makes noise with the same statistical structure as the 
+  % Fourier transformed noise.
+  [V,D]=eig(B);
+  Mat(:,:,k)=V*diag(sqrt(diag(D)));
+end;
+
+% Generate realizations for the different analyzed constituents.
+
+N=zeros(4,nreal);
+NM=zeros(length(fu),nreal);
+NP=NM;
+for k=1:length(fu);
+  l=find(fu(k)>fband(:,1) & fu(k)<fband(:,2));
+  N=[zeros(4,1),Mat(:,:,l)*randn(4,nreal-1)];
+  NP(k,:)=N(1,:)+1i*N(2,:);
+  NM(k,:)=N(3,:)+1i*N(4,:);
+end;
+
+%----------------------------------------------------------------------
+function [ercx,eicx]=noise_stats(xres,fu,dt)
+% NOISE_STATS: Computes statistics of residual energy for all 
+% constituents (ignoring any cross-correlations between real and
+% imaginary parts).
+
+% S. Lentz  10/28/99
+% R. Pawlowicz 11/1/00
+% Version 1.0
+
+[fband,Pxrave,Pxiave,Pxcave]=residual_spectrum(xres,fu,dt);
+nfband=size(fband,1);
+mu=length(fu);
+
+% Get the statistics for each component.
+ercx=zeros(mu,1);
+eicx=zeros(mu,1);
+for k1=1:nfband;
+   k=find(fu>=fband(k1,1) & fu<=fband(k1,2));
+   ercx(k)=sqrt(Pxrave(k1));
+   eicx(k)=sqrt(Pxiave(k1));
+end
+
+%----------------------------------------------------------------------
+function [fband,Pxrave,Pxiave,Pxcave]=residual_spectrum(xres,fu,dt)
+% RESIDUAL_SPECTRUM: Computes statistics from an input spectrum over
+% a number of bands, returning the band limits and the estimates for
+% power spectra for real and imaginary parts and the cross-spectrum.          
+%
+% Mean values of the noise spectrum are computed for the following 
+% 8 frequency bands defined by their center frequency and band width:
+% M0 +.1 cpd; M1 +-.2 cpd; M2 +-.2 cpd; M3 +-.2 cpd; M4 +-.2 cpd; 
+% M5 +-.2 cpd; M6 +-.21 cpd; M7 (.26-.29 cpd); and M8 (.30-.50 cpd). 
+
+% S. Lentz  10/28/99
+% R. Pawlowicz 11/1/00
+% Version 1.0
+
+% Define frequency bands for spectral averaging.
+fband =[.00010 .00417;
+        .03192 .04859;
+        .07218 .08884;
+        .11243 .12910;
+        .15269 .16936;
+        .19295 .20961;
+        .23320 .25100;
+        .26000 .29000;
+        .30000 .50000];
+
+% If we have a sampling interval> 1 hour, we might have to get
+% rid of some bins.
+%fband(fband(:,1)>1/(2*dt),:)=[];
+
+nfband=size(fband,1);
+nx=length(xres);
+
+% Spectral estimate (takes real time series only).
+
+
+% Matlab has changed their spectral estimator functions
+% To match the old code, I have to divide by 2*dt. This is because
+% 
+%  PSD*dt  is two-sided spectrum in units of power per hertz.
+%
+%  PWELCH is the one-sided spectrum in power per hertz
+%
+%  So PWELCH/2 = PSD*dt
+
+
+%[Pxr,fx]=psd(real(xres),nx,1/dt); % Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme. If you have an error here you are probably missing this toolbox
+%[Pxi,fx]=psd(imag(xres),nx,1/dt); % Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme.
+%[Pxc,fx]=csd(real(xres),imag(xres),nx,1/dt); % Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme.
+
+
+[Pxr,fx]=pwelch(real(xres),hanning(nx),ceil(nx/2),nx,1/dt); % Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme. If you have an error here you are probably missing this toolbox
+Pxr=Pxr/2/dt;
+[Pxi,fx]=pwelch(imag(xres),hanning(nx),ceil(nx/2),nx,1/dt); % Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme.
+Pxi=Pxi/2/dt;
+[Pxc,fx]=cpsd(real(xres),imag(xres),[],[],nx,1/dt); % Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme.
+Pxc=Pxc/2/dt;
+
+df=fx(3)-fx(2);
+if length(fu) <= length(fx) % prevent issue with low-res series as t=0:2:24 [for unit tests]
+Pxr(round(fu./df)+1)=NaN ; % Sets Px=NaN in bins close to analyzed frequencies
+Pxi(round(fu./df)+1)=NaN ; % (to prevent leakage problems?).
+Pxc(round(fu./df)+1)=NaN ; 
+end
+
+Pxrave=zeros(nfband,1);
+Pxiave=zeros(nfband,1);
+Pxcave=zeros(nfband,1);
+% Loop downwards in frequency through bands (cures short time series
+% problem with no data in lowest band).
+%
+% Divide by nx to get power per frequency bin, and multiply by 2
+% to account for positive and negative frequencies.
+%
+for k=nfband:-1:1,
+   jband=find(fx>=fband(k,1) & fx<=fband(k,2) & isfinite(Pxr));
+   if any(jband),
+     Pxrave(k)=mean(Pxr(jband))*2/nx;
+     Pxiave(k)=mean(Pxi(jband))*2/nx;
+     Pxcave(k)=mean(Pxc(jband))*2/nx;
+   elseif k<nfband,
+     Pxrave(k)=Pxrave(k+1);   % Low frequency bin might not have any points...
+     Pxiave(k)=Pxiave(k+1);   
+     Pxcave(k)=Pxcave(k+1);   
+   end;
+end
+ 
+
+%----------------------------------------------------------------------
+function [emaj,emin,einc,epha]=errell(cxi,sxi,ercx,ersx,ercy,ersy)
+% [emaj,emin,einc,epha]=errell(cx,sx,cy,sy,ercx,ersx,ercy,ersy) computes
+% the uncertainities in the ellipse parameters based on the 
+% uncertainities in the least square fit cos,sin coefficients.
+%
+%  INPUT:  cx,sx=cos,sin coefficients for x 
+%          cy,sy=cos,sin coefficients for y
+%          ercx,ersx=errors in x cos,sin coefficients
+%          ercy,ersy=errors in y cos,sin coefficients
+%          
+%  OUTPUT: emaj=major axis error
+%          emin=minor axis error
+%          einc=inclination error (deg)
+%          epha=pha error (deg)
+
+% based on linear error propagation, with errors in the coefficients 
+% cx,sx,cy,sy uncorrelated. 
+
+% B. Beardsley  1/15/99; 1/20/99
+% Version 1.0
+
+r2d=180./pi;
+cx=real(cxi(:));sx=real(sxi(:));cy=imag(cxi(:));sy=imag(sxi(:));
+ercx=ercx(:);ersx=ersx(:);ercy=ercy(:);ersy=ersy(:);
+
+rp=.5.*sqrt((cx+sy).^2+(cy-sx).^2);
+rm=.5.*sqrt((cx-sy).^2+(cy+sx).^2);
+ercx2=ercx.^2;ersx2=ersx.^2;
+ercy2=ercy.^2;ersy2=ersy.^2;
+
+% major axis error
+ex=(cx+sy)./rp;
+fx=(cx-sy)./rm;
+gx=(sx-cy)./rp;
+hx=(sx+cy)./rm;
+dcx2=(.25.*(ex+fx)).^2;
+dsx2=(.25.*(gx+hx)).^2;
+dcy2=(.25.*(hx-gx)).^2;
+dsy2=(.25.*(ex-fx)).^2;
+emaj=sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+% minor axis error
+dcx2=(.25.*(ex-fx)).^2;
+dsx2=(.25.*(gx-hx)).^2;
+dcy2=(.25.*(hx+gx)).^2;
+dsy2=(.25.*(ex+fx)).^2;
+emin=sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+% inclination error
+rn=2.*(cx.*cy+sx.*sy);
+rd=cx.^2+sx.^2-(cy.^2+sy.^2);
+den=rn.^2+rd.^2;
+dcx2=((rd.*cy-rn.*cx)./den).^2;
+dsx2=((rd.*sy-rn.*sx)./den).^2;
+dcy2=((rd.*cx+rn.*cy)./den).^2;
+dsy2=((rd.*sx+rn.*sy)./den).^2;
+einc=r2d.*sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+% phase error
+rn=2.*(cx.*sx+cy.*sy);
+rd=cx.^2-sx.^2+cy.^2-sy.^2;
+den=rn.^2+rd.^2;
+dcx2=((rd.*sx-rn.*cx)./den).^2;
+dsx2=((rd.*cx+rn.*sx)./den).^2;
+dcy2=((rd.*sy-rn.*cy)./den).^2;
+dsy2=((rd.*cy+rn.*sy)./den).^2;
+epha=r2d.*sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+
+
+
+
diff --git a/tools/tide/t_tidecb.m b/tools/tide/t_tidecb.m
new file mode 100644
index 0000000000000000000000000000000000000000..74a861f5efab6a3faad266a07eec851b0072ee4c
--- /dev/null
+++ b/tools/tide/t_tidecb.m
@@ -0,0 +1,1015 @@
+function [nameu,fu,tidecon,xout]=t_tidecb(xin,varargin);
+% T_TIDECB Harmonic analysis of a time series
+% [NAME,FREQ,TIDECON,XOUT]=T_TIDECB(XIN) computes the tidal analysis 
+% of the (possibly complex) time series XIN.
+%
+% [TIDESTRUC,XOUT]=T_TIDECB(XIN) returns the analysis information in
+% a structure formed of NAME, FREQ, and TIDECON.
+%
+% Further inputs are optional, and are specified as property/value pairs
+% [...]=T_TIDECB(XIN,property,value,property,value,...,etc.)
+%      
+% These properties are:
+%
+%       'interval'       Sampling interval (hours), default = 1. 
+%          
+%   The next two are required if nodal corrections are to be computed,
+%   otherwise not necessary. If they are not included then the reported
+%   phases are raw constituent phases at the central time. 
+%       'start time'     [year,month,day,hour,min,sec]
+%                        - min,sec are optional OR 
+%                        decimal day (matlab DATENUM scalar)
+%       'latitude'       decimal degrees (+north) (default: none).
+%
+%   Where to send the output.
+%       'output'         where to send printed output:
+%                        'none'    (no printed output)
+%                        'screen'  (to screen) - default
+%                        FILENAME   (to a file)
+%                        >FILENAME   (append to a file)
+%
+%   Correction factor for prefiltering.
+%       'prefilt'        FS,CORR
+%                        If the time series has been passed through
+%                        a pre-filter of some kind (say, to reduce the
+%                        low-frequency variability), then the analyzed
+%                        constituents will have to be corrected for 
+%                        this. The correction transfer function 
+%                        (1/filter transfer function) has (possibly 
+%                        complex) magnitude CORR at frequency FS (cph). 
+%                        Corrections of more than a factor of 100 are 
+%                        not applied; it is assumed these refer to tidal
+%                        constituents that were intentionally filtered 
+%                        out, e.g., the fortnightly components.
+%
+%   Adjustment for long-term behavior ("secular" behavior).
+%       'secular'        'mean'   - assume constant offset (default).
+%                        'linear' - get linear trend.
+%                     
+%   Inference of constituents.
+%       'inference'      NAME,REFERENCE,AMPRAT,PHASE_OFFSET
+%                        where NAME is an array of the names of 
+%                        constituents to be inferred, REFERENCE is an 
+%                        array of the names of references, and AMPRAT 
+%                        and PHASE_OFFSET are the amplitude factor and
+%                        phase offset (in degrees)from the references. 
+%                        NAME and REFERENCE are Nx4 (max 4 characters
+%                        in name), and AMPRAT and PHASE_OFFSET are Nx1
+%                        (for scalar time series) and Nx2 for vector 
+%                        time series (column 1 is for + frequencies and
+%                        column 2 for - frequencies).
+%
+%   Shallow water constituents
+%       'shallow'        NAME
+%                        A matrix whose rows contain the names of 
+%                        shallow-water constituents to analyze.
+%
+%   Resolution criterions for least-squares fit.        
+%       'rayleigh'       scalar - Rayleigh criteria, default = 1.
+%                        Matrix of strings - names of constituents to
+%                                   use (useful for testing purposes).
+%  
+%   Calculation of confidence limits.
+%       'error'          'wboot'  - Boostrapped confidence intervals 
+%                                   based on a correlated bivariate 
+%                                   white-noise model.
+%                        'cboot'  - Boostrapped confidence intervals 
+%                                   based on an uncorrelated bivariate 
+%                                   coloured-noise model (default).
+%                        'linear' - Linearized error analysis that 
+%                                   assumes an uncorrelated bivariate 
+%                                   coloured noise model. 
+%                                   
+%   Computation of "predicted" tide (passed to t_predic, but note that
+%                                    the default value is different).
+%       'synthesis'      0 - use all selected constituents
+%                        scalar>0 - use only those constituents with a 
+%                                   SNR greater than that given (1 or 2 
+%                                   are good choices, 2 is the default).
+%                              <0 - return result of least-squares fit 
+%                                   (should be the same as using '0', 
+%                                   except that NaN-holes in original 
+%                                   time series will remain).
+%
+%
+%       It is possible to call t_tide without using property names,
+%       in which case the assumed calling sequence is
+%
+%          T_TIDECB(XIN,INTERVAL,START_TIME,LATITUDE,RAYLEIGH)
+%
+%
+%  OUTPUT: 
+%
+%    nameu=list of constituents used
+%    fu=frequency of tidal constituents (cycles/hr)
+%    tidecon=[fmaj,emaj,fmin,emin,finc,einc,pha,epha] for vector xin
+%           =[fmaj,emaj,pha,epha] for scalar (real) xin
+%       fmaj,fmin - constituent major and minor axes (same units as xin)       
+%       emaj,emin - 95% confidence intervals for fmaj,fmin
+%       finc - ellipse orientations (degrees)
+%       einc - 95% confidence intervals for finc
+%       pha - constituent phases (degrees relative to Greenwich)
+%       epha - 95% confidence intervals for pha
+%    xout=tidal prediction
+%
+% Note: Although missing data can be handled with NaN, it is wise not
+%       to have too many of them. If your time series has a lot of 
+%       missing data at the beginning and/or end, then truncate the 
+%       input time series.  The Rayleigh criterion is applied to 
+%       frequency intervals calculated as the inverse of the input 
+%       series length.
+%
+% A description of the theoretical basis of the analysis and some
+% implementation details can be found in:
+%
+% Pawlowicz, R., B. Beardsley, and S. Lentz, "Classical Tidal 
+%   "Harmonic Analysis Including Error Estimates in MATLAB 
+%    using T_TIDE", Computers and Geosciences, 2002.
+%
+% (citation of this article would be appreciated if you find the
+%  toolbox useful).
+
+
+% R. Pawlowicz 11/8/99 - Completely rewritten from the transliterated-
+%                        to-matlab IOS/Foreman fortran code by S. Lentz
+%                        and B. Beardsley.
+%              3/3/00  - Redid errors to take into account covariances 
+%                        between u and v errors.
+%              7/21/00 - Found that annoying bug in error calc! 
+%              11/1/00 - Added linear error analysis.
+%              8/29/01 - Made synth=1 default, also changed behavior 
+%                        when no lat/time given so that phases are raw
+%                        at central time. 
+%              9/1/01  - Moved some SNR code to t_predic.
+%              9/28/01 - made sure you can't choose Z0 as constituent.
+%              6/12/01 - better explanation for variance calcs, fixed
+%                        bug in typed output (thanks Mike Cook).
+%
+% Version 1.02
+%
+%              6/06/07 - use PSD and CSD from T_SIGNAL  (C.Begler)
+%
+
+
+% ----------------------Parse inputs-----------------------------------
+
+ray=1;
+dt=1;
+fid=1;
+stime=[];
+lat=[];
+corr_fs=[0 1e6];
+corr_fac=[1  1];
+secular='mean';
+inf.iname=[];
+inf.irefname=[];
+shallownames=[];
+constitnames=[];
+errcalc='cboot';
+synth=2;
+
+k=1;
+while length(varargin)>0,
+  if ischar(varargin{1}),
+    switch lower(varargin{1}(1:3)),
+      case 'int',
+        dt=varargin{2};
+      case 'sta',
+        stime=varargin{2};
+	if length(stime)>1, 
+	  stime=[stime(:)' zeros(1,6-length(stime))]; 
+	  stime=datenum(stime(1),stime(2),stime(3),stime(4),stime(5),stime(6));
+	end;
+      case 'lat',
+         lat=varargin{2};
+      case 'out',
+         filen=varargin{2};
+	 switch filen,
+	   case 'none',
+	     fid=-1;
+	   case 'screen',
+	     fid=1;
+	   otherwise
+             md = 'w';
+             if filen(1) == '>' 
+                md = 'a';
+                filen = filen(2:end);
+             end
+	     [fid,mesg]=fopen(filen,md);
+	     if fid==-1, error(msg); end;
+	  end;
+      case 'ray',
+         if isnumeric(varargin{2}),
+           ray=varargin{2};
+	 else
+	   constitnames=varargin{2};
+	   if iscellstr(constitnames), constitnames=char(constitnames); end;
+	 end;
+       case 'pre',
+         corr_fs=varargin{2};
+	 corr_fac=varargin{3};
+         varargin(1)=[];
+      case 'sec',
+         secular=varargin{2};
+      case 'inf',
+         inf.iname=varargin{2};
+	 inf.irefname=varargin{3};
+	 inf.amprat=varargin{4};
+	 inf.ph=varargin{5};
+	 varargin(1:3)=[];
+      case 'sha',
+         shallownames=varargin{2};
+      case 'err',
+         errcalc=varargin{2};
+      case 'syn',
+         synth=varargin{2};
+      otherwise,
+         error(['Can''t understand property:' varargin{1}]);
+    end;
+    varargin([1 2])=[]; 
+  else  
+    switch k,
+      case 1,
+        dt=varargin{1};
+      case 2,
+        stime=varargin{1};
+      case 3,
+        lat=varargin{1};
+      case 4,
+        ray=varargin{1};
+      otherwise
+        error('Too many input parameters');
+     end;
+     varargin(1)=[];
+  end;
+  k=k+1;
+end;
+ 
+[inn,inm]=size(xin);
+if ~(inn==1 | inm==1), error('Input time series is not a vector'); end;
+
+xin=xin(:); % makes xin a column vector
+nobs=length(xin);
+
+nobsu=nobs-rem(nobs-1,2);% makes series odd to give a center point
+
+t=dt*([1:nobs]'-ceil(nobsu/2));  % Time vector for entire time series,
+                                 % centered at series midpoint. 
+
+if ~isempty(stime),
+  centraltime=stime+floor(nobsu./2)./24.0*dt;
+else
+  centraltime=[];
+end;
+
+% -------Get the frequencies to use in the harmonic analysis-----------
+
+[nameu,fu,ju,namei,fi,jinf,jref]=constituents(ray/(dt*nobsu),constitnames,...
+                                           shallownames,inf.iname,inf.irefname,centraltime);
+
+mu=length(fu); % # base frequencies
+mi=length(fi); % # inferred
+
+% Find the good data points (here I assume that in a complex time 
+% series, if u is bad, so is v).
+
+gd=find(finite(xin(1:nobsu)));
+ngood=length(gd);
+fprintf('   Points used: %d of %d\n',ngood,nobs)
+
+
+%----------------------------------------------------------------------
+% Now solve for the secular trend plus the analysis. Instead of solving
+% for + and - frequencies using exp(i*f*t), I use sines and cosines to 
+% keep tc real.  If the input series is real, than this will 
+% automatically use real-only computation. However, for the analysis, 
+% it's handy to get the + and - frequencies ('ap' and 'am'), and so 
+% that's what we do afterwards.
+
+if secular(1:3)=='lin',
+  tc=[ones(length(t),1) t*(2/dt/nobsu) cos((2*pi)*t*fu') sin((2*pi)*t*fu') ];
+
+  coef=tc(gd,:)\xin(gd);
+
+  z0=coef(1);dz0=coef(2);
+  ap=(coef(3:mu+2)-i*coef(mu+3:end))/2;  % a+ amplitudes
+  am=(coef(3:mu+2)+i*coef(mu+3:end))/2;  % a- amplitudes
+else
+  tc=[ones(length(t),1) cos((2*pi)*t*fu') sin((2*pi)*t*fu') ];
+  
+  coef=tc(gd,:)\xin(gd);
+
+  z0=coef(1);dz0=0;
+  ap=(coef(2:mu+1)-i*coef(mu+2:end))/2;  % a+ amplitudes
+  am=(coef(2:mu+1)+i*coef(mu+2:end))/2;  % a- amplitudes
+end;
+
+%----------------------------------------------------------------------
+% Check variance explained (but do this with the original fit).
+
+xout=tc*coef;  % This is the time series synthesized from the analysis
+xres=xin-xout; % and the residuals!
+
+if isreal(xin),    % Real time series
+  varx=cov(xin(gd));varxp=cov(xout(gd));varxr=cov(xres(gd));
+  fprintf('   percent of var residual after lsqfit/var original: %5.2f %%\n',100*(varxr/varx));  
+else               % Complex time series
+  varx=cov(real(xin(gd)));varxp=cov(real(xout(gd)));varxr=cov(real(xres(gd)));
+  fprintf('   percent of X var residual after lsqfit/var original: %5.2f %%\n',100*(varxr/varx));
+
+  vary=cov(imag(xin(gd)));varyp=cov(imag(xout(gd)));varyr=cov(imag(xres(gd)));
+  fprintf('   percent of Y var residual after lsqfit/var original: %5.2f %%\n',100*(varyr/vary));
+end;
+
+
+%---------- Correct for prefiltering-----------------------------------
+
+corrfac=interp1(corr_fs,corr_fac,fu);
+% To stop things blowing up!
+corrfac(corrfac>100 | corrfac <.01 | isnan(corrfac))=1;
+
+ap=ap.*corrfac;
+am=am.*conj(corrfac);
+
+
+%---------------Nodal Corrections--------------------------------------
+% Generate nodal corrections and calculate phase relative to Greenwich.
+% Note that this is a slightly weird way to do the nodal corrections,
+% but is 'traditional'.  The "right" way would be to change the basis 
+% functions used in the least-squares fit above.
+
+if ~isempty(lat) & ~isempty(stime),   % Time and latitude
+    
+  % Get nodal corrections at midpoint time.
+  [v,u,f]=t_vuf(centraltime,[ju;jinf],lat);
+  
+  vu=(v+u)*360; % total phase correction (degrees)
+  nodcor=['Greenwich phase computed with nodal corrections applied to amplitude \n and phase relative to center time'];  
+elseif ~isempty(stime),    % Time only
+  % Get nodal corrections at midpoint time
+  [v,u,f]=t_vuf(centraltime,[ju;jinf]);
+  vu=(v+u)*360; % total phase correction (degrees)
+  nodcor=['Greenwich phase computed, no nodal corrections'];  
+else   % No time, no latitude
+  vu=zeros(length(ju)+length(jinf),1);
+  f=ones(length(ju)+length(jinf),1);
+   nodcor=['Phases at central time'];  
+end
+fprintf(['   ',nodcor,'\n']);
+
+%---------------Inference Corrections----------------------------------
+% Once again, the "right" way to do this would be to change the basis
+% functions.
+ii=find(finite(jref));
+if ii,
+  fprintf('   Do inference corrections\n');
+  snarg=nobsu*pi*(fi(ii)   -fu(jref(ii)) )*dt;
+  scarg=sin(snarg)./snarg;
+ 
+  if size(inf.amprat,2)==1,    % For real time series
+    pearg=     2*pi*(vu(mu+ii)-vu(jref(ii))+inf.ph(ii))/360;
+    pcfac=inf.amprat(ii).*f(mu+ii)./f(jref(ii)).*exp(i*pearg);
+    pcorr=1+pcfac.*scarg;
+    mcfac=conj(pcfac);
+    mcorr=conj(pcorr);
+  else                          % For complex time series
+    pearg=     2*pi*(vu(mu+ii)-vu(jref(ii))+inf.ph(ii,1))/360;
+    pcfac=inf.amprat(ii,1).*f(mu+ii)./f(jref(ii)).*exp(i*pearg);
+    pcorr=1+pcfac.*scarg;
+    mearg=    -2*pi*(vu(mu+ii)-vu(jref(ii))+inf.ph(ii,2))/360;
+    mcfac=inf.amprat(ii,2).*f(mu+ii)./f(jref(ii)).*exp(i*mearg);
+    mcorr=1+mcfac.*scarg;
+  end;
+    
+  ap(jref(ii))=ap(jref(ii))./pcorr;   % Changes to existing constituents
+  ap=[ap;ap(jref(ii)).*pcfac];        % Inferred constituents
+
+  am(jref(ii))=am(jref(ii))./mcorr;
+  am=[am;am(jref(ii)).*mcfac];
+
+  fu=[fu;fi(ii)];
+  nameu=[nameu;namei(ii,:)];
+end;
+
+% --------------Error Bar Calculations---------------------------------
+%
+% Error bar calcs involve two steps:
+%      1) Estimate the uncertainties in the analyzed amplitude
+%         for both + and - frequencies (i.e., in 'ap' and 'am').
+%         A simple way of doing this is to take the variance of the
+%         original time series and divide it into the amount appearing
+%         in the bandwidth of the analysis (approximately 1/length).
+%         A more sophisticated way is to assume "locally white"
+%         noise in the vicinity of, e.g., the diurnal consistuents.
+%         This takes into account slopes in the continuum spectrum.
+%
+%      2) Transform those uncertainties into ones suitable for ellipse
+%         parameters (axis lengths, angles). This can be done 
+%         analytically for large signal-to-noise ratios. However, the 
+%         transformation is non-linear at lows SNR, say, less than 10
+%         or so.
+%
+
+xr=fixgaps(xres); % Fill in "internal" NaNs with linearly interpolated
+                  % values so we can fft things.
+nreal=1;
+
+if strmatch(errcalc(2:end),'boot'),
+  fprintf('   Using nonlinear bootstrapped error estimates\n');
+  
+  % "noise" matrices are created with the right covariance structure
+  % to add to the analyzed components to create 'nreal' REPLICATES. 
+  % 
+
+  nreal=300;             % Create noise matrices 
+  [NP,NM]=noise_realizations(xr(finite(xr)),fu,dt,nreal,errcalc);
+      
+  % All replicates are then transformed (nonlinearly) into ellipse 
+  % parameters.  The computed error bars are then based on the std
+  % dev of the replicates.
+
+  AP=ap(:,ones(1,nreal))+NP;        % Add to analysis (first column
+  AM=am(:,ones(1,nreal))+NM;        % of NM,NP=0 so first column of
+                                    % AP/M holds ap/m).
+  epsp=angle(AP)*180/pi;            % Angle/magnitude form:
+  epsm=angle(AM)*180/pi;
+  ap=abs(AP);
+  am=abs(AM);
+elseif strmatch(errcalc,'linear'),
+  fprintf('   Using linearized error estimates\n');
+  %
+  % Uncertainties in analyzed amplitudes are computed in different
+  % spectral bands. Real and imaginary parts of the residual time series
+  % are treated separately (no cross-covariance is assumed).
+  %
+  % Noise estimates are then determined from a linear analysis of errors,
+  % assuming that everything is uncorrelated. This is OK for scalar time
+  % series but can fail for vector time series if the noise is not 
+  % isotropic.
+  
+  [ercx,eicx]=noise_stats(xr(finite(xr)),fu,dt);
+  % Note - here we assume that the error in the cos and sin terms is 
+  % equal, and equal to total power in the encompassing frequency bin. 
+  % It seems like there should be a factor of 2 here somewhere but it 
+  % only works this way! <shrug>
+  [emaj,emin,einc,epha]=errell(ap+am,i*(ap-am),ercx,ercx,eicx,eicx);
+
+  epsp=angle(ap)*180/pi;
+  epsm=angle(am)*180/pi;
+  ap=abs(ap);
+  am=abs(am);
+else
+  error(['Unrecognized type of error analysis: ''' errcalc ''' specified!']);
+end;
+
+%-----Convert complex amplitudes to standard ellipse parameters--------
+
+aap=ap./f(:,ones(1,nreal));	% Apply nodal corrections and
+aam=am./f(:,ones(1,nreal));	% compute ellipse parameters.
+
+fmaj=aap+aam;                   % major axis
+fmin=aap-aam;                   % minor axis
+
+gp=mod( vu(:,ones(1,nreal))-epsp ,360); % pos. Greenwich phase in deg.
+gm=mod( vu(:,ones(1,nreal))+epsm ,360); % neg. Greenwich phase in deg.
+
+finc= (epsp+epsm)/2;
+finc(:,1)=mod( finc(:,1),180 ); % Ellipse inclination in degrees
+				% (mod 180 to prevent ambiguity, i.e., 
+				% we always ref. against northern 
+				% semi-major axis.
+	
+finc=cluster(finc,180); 	% Cluster angles around the 'true' 
+                                % angle to avoid 360 degree wraps.
+
+pha=mod( gp+finc ,360); 	% Greenwich phase in degrees.
+
+pha=cluster(pha,360);		% Cluster angles around the 'true' angle
+				% to avoid 360 degree wraps.
+
+%----------------Generate 95% CI---------------------------------------
+%% For bootstrapped errors, we now compute limits of the distribution.
+if strmatch(errcalc(2:end),'boot'),
+     %% std dev-based estimates.
+     % The 95% CI are computed from the sigmas
+     % by a 1.96 fudge factor (infinite degrees of freedom).
+     % emaj=1.96*std(fmaj,0,2);
+     % emin=1.96*std(fmin,0,2);
+     % einc=1.96*std(finc,0,2);
+     % epha=1.96*std(pha ,0,2);
+     %% Median-absolute-deviation (MAD) based estimates.
+     % (possibly more stable?)
+      emaj=median(abs(fmaj-median(fmaj,2)*ones(1,nreal)),2)/.6375*1.96;
+      emin=median(abs(fmin-median(fmin,2)*ones(1,nreal)),2)/.6375*1.96;
+      einc=median(abs(finc-median(finc,2)*ones(1,nreal)),2)/.6375*1.96;
+      epha=median(abs( pha-median( pha,2)*ones(1,nreal)),2)/.6375*1.96;
+else
+   % In the linear analysis, the 95% CI are computed from the sigmas
+   % by this fudge factor (infinite degrees of freedom).
+   emaj=1.96*emaj;
+   emin=1.96*emin;
+   einc=1.96*einc;
+   epha=1.96*epha;
+end;
+				  
+if isreal(xin),
+    tidecon=[fmaj(:,1),emaj,pha(:,1),epha];
+else
+    tidecon=[fmaj(:,1),emaj,fmin(:,1),emin, finc(:,1),einc,pha(:,1),epha];
+end;
+
+% Sort results by frequency (needed if anything has been inferred since 
+% these are stuck at the end of the list by code above).
+if any(finite(jref)),
+ [fu,I]=sort(fu);
+ nameu=nameu(I,:);
+ tidecon=tidecon(I,:);
+end;
+
+snr=(tidecon(:,1)./tidecon(:,2)).^2;  % signal to noise ratio
+
+%--------Generate a 'prediction' using significant constituents----------
+xoutOLD=xout;
+if synth>=0,
+ if ~isempty(lat) & ~isempty(stime),
+   fprintf('   Generating prediction with nodal corrections, SNR is %f\n',synth);
+   xout=t_predic(stime+[0:nobs-1]*dt/24.0,nameu,fu,tidecon,'lat',lat,'synth',synth);
+ elseif ~isempty(stime), 
+   fprintf('   Generating prediction without nodal corrections, SNR is %f\n',synth);
+   xout=t_predic(stime+[0:nobs-1]*dt/24.0,nameu,fu,tidecon,'synth',synth);
+ else
+   fprintf('   Generating prediction without nodal corrections, SNR is %f\n',synth);
+   xout=t_predic(t/24.0,nameu,fu,tidecon,'synth',synth);
+ end;
+else
+ fprintf('   Returning fitted prediction\n');
+end;
+
+%----------------------------------------------------------------------
+% Check variance explained (but now do this with the synthesized fit).
+xres=xin(:)-xout(:); % and the residuals!
+
+%error;
+
+if isreal(xin),    % Real time series
+  varx=cov(xin(gd));varxp=cov(xout(gd));varxr=cov(xres(gd));
+  fprintf('   percent of var residual after synthesis/var original: %5.2f %%\n',100*(varxr/varx));  
+else               % Complex time series
+  varx=cov(real(xin(gd)));varxp=cov(real(xout(gd)));varxr=cov(real(xres(gd)));
+  fprintf('   percent of X var residual after synthesis/var original: %5.2f %%\n',100*(varxr/varx));
+
+  vary=cov(imag(xin(gd)));varyp=cov(imag(xout(gd)));varyr=cov(imag(xres(gd)));
+  fprintf('   percent of Y var residual after synthesis/var original: %5.2f %%\n',100*(varyr/vary));
+end;
+
+
+%-----------------Output results---------------------------------------
+
+if fid>1,
+ fprintf(fid,'\n%s\n',['file name: ',filen]);
+elseif fid==1,
+ fprintf(fid,'-----------------------------------\n');
+end
+
+if fid>0,
+  fprintf(fid,'date: %s\n',date);
+  fprintf(fid,'nobs = %d,  ngood = %d,  record length (days) = %.2f\n',nobs,ngood,length(xin)*dt/24);
+  if ~isempty(stime); fprintf(fid,'%s\n',['start time: ',datestr(stime)]); end
+  fprintf(fid,'rayleigh criterion = %.1f\n',ray);
+  fprintf(fid,'%s\n',nodcor);
+%  fprintf(fid,'\n     coefficients from least squares fit of x\n');
+%  fprintf(fid,'\n tide    freq        |a+|       err_a+      |a-|       err_a-\n');
+%  for k=1:length(fu);
+%    if ap(k)>eap(k) | am(k)>eam(k), fprintf('*'); else fprintf(' '); end;
+%    fprintf(fid,'%s  %8.5f  %9.4f  %9.4f  %9.4f  %9.4f\n',nameu(k,:),fu(k),ap(k),eap(k),am(k),eam(k));
+%  end
+  fprintf(fid,'\nx0= %.3g, x trend= %.3g\n',real(z0),real(dz0));
+  fprintf(fid,['\nvar(x)= ',num2str(varx),'   var(xp)= ',num2str(varxp),'   var(xres)= ',num2str(varxr) '\n']);
+  fprintf(fid,'percent var predicted/var original= %.1f %%\n',100*varxp/varx);
+
+  if isreal(xin)
+    fprintf(fid,'\n     tidal amplitude and phase with 95%% CI estimates\n');
+    fprintf(fid,'\n tide   period    freq         amp      amp_err    pha    pha_err  pha_time    snr\n');
+    for k=1:length(fu);
+      if snr(k)>synth, fprintf(fid,'*'); else fprintf(fid,' '); end;
+      [v,fstr] = str2val(1/fu(k)/24,'time1');
+      [v,pstr] = str2val(1/fu(k)/24*tidecon(k,end-1)/360,'time1');
+      fprintf(fid,'%s %9s   %9.7f %9.4f %8.3f %8.2f %8.2f  %9s   %7.3f\n', ...
+                nameu(k,:),fstr,fu(k),tidecon(k,:),pstr,snr(k));
+    end
+  else
+    fprintf(fid,'\ny0= %.3g, x trend= %.3g\n',imag(z0),imag(dz0));
+    fprintf(fid,['\nvar(y)= ',num2str(vary),'    var(yp)= ',num2str(varyp),'  var(yres)= ',num2str(varyr) '\n']);
+    fprintf(fid,'percent var predicted/var original= %.1f %%\n',100*varyp/vary);
+    fprintf(fid,'\n%s\n',['ellipse parameters with 95%% CI estimates']);
+    fprintf(fid,'\n%s\n',['tide   freq      major  emaj    minor   emin     inc    einc     pha    epha      snr']);
+    for k=1:length(fu);
+      if snr(k)>synth, fprintf(fid,'*'); else fprintf(fid,' '); end;
+      fprintf(fid,'%s %9.7f %6.3f %7.3f %7.3f %6.2f %8.2f %6.2f %8.2f %6.2f %6.2g\n',...
+	nameu(k,:),fu(k),tidecon(k,:),snr(k));
+    end
+    fprintf(fid,['\ntotal var= ',num2str(varx+vary),'   pred var= ',num2str(varxp+varyp) '\n']);
+    fprintf(fid,'percent total var predicted/var original= %.1f %%\n\n',100*(varxp+varyp)/(varx+vary));
+  end
+
+  if fid~=1, st=fclose(fid); end
+end;
+
+xout=reshape(xout,inn,inm);
+switch nargout,
+  case {0,3,4}
+ case {1}
+   nameu = struct('name',nameu,'freq',fu,'tidecon',tidecon);
+  case {2}   
+   xout=reshape(xout,inn,inm);
+   nameu = struct('name',nameu,'freq',fu,'tidecon',tidecon);
+   fu=xout;
+end;
+   
+%----------------------------------------------------------------------
+function [nameu,fu,ju,namei,fi,jinf,jref]=constituents(minres,constit,...
+                                     shallow,infname,infref,centraltime);
+% [name,freq,kmpr]=constituents(minres,infname) loads tidal constituent
+% table (containing 146 constituents), then picks out only the '
+% resolvable' frequencies (i.e. those that are MINRES apart), base on 
+% the comparisons in the third column of constituents.dat. Only 
+% frequencies in the 'standard' set of 69 frequencies are actually used.
+% Also return the indices of constituents to be inferred.
+
+% If we have the mat-file, read it in, otherwise create it and read
+% it in!
+
+% R Pawlowicz 9/1/01 
+% Version 1.0
+%
+%    19/1/02 - typo fixed (thanks to  Zhigang Xu)
+
+% Compute frequencies from astronomical considerations.
+
+[const,sat,cshallow]=t_getconsts(centraltime);
+
+if isempty(constit),
+  ju=find(const.df>=minres);
+else
+  ju=[];
+  for k=1:size(constit,1),
+   j1=strmatch(constit(k,:),const.name);
+   if isempty(j1),
+     disp(['Can''t recognize name ' constit(k,:) ' for forced search']);
+   elseif j1==1,
+     disp(['*************************************************************************']);
+     disp(['Z0 specification ignored - for non-tidal offsets see ''secular'' property']);
+     disp(['*************************************************************************']);
+   else  
+     ju=[ju;j1];
+   end;
+  end;
+  [dum,II]=sort(const.freq(ju)); % sort in ascending order of frequency.
+  ju=ju(II);
+end;
+
+
+disp(['   number of standard constituents used: ',int2str(length(ju))])
+
+if ~isempty(shallow),
+ for k=1:size(shallow,1),
+   j1=strmatch(shallow(k,:),const.name);
+   if isempty(j1),
+     disp(['Can''t recognize name ' shallow(k,:) ' for forced search']);
+   else
+     if isnan(const.ishallow(j1)),
+       disp([shallow(k,:) ' Not a shallow-water constituent']);
+     end;
+     disp(['   Forced fit to ' shallow(k,:)]);
+     ju=[ju;j1];
+   end;
+ end;
+ 
+end;
+      
+nameu=const.name(ju,:);
+fu=const.freq(ju);
+
+
+% Check if neighboring chosen constituents violate Rayleigh criteria.
+jck=find(diff(fu)<minres);
+if (length(jck)>0)
+   disp(['  Warning! Following constituent pairs violate Rayleigh criterion']);
+   for ick=1:length(jck);
+   disp(['     ',nameu(jck(ick),:),'  ',nameu(jck(ick)+1,:)]);
+   end;
+end
+
+% For inference, add in list of components to be inferred.
+
+fi=[];namei=[];jinf=[];jref=[];
+if ~isempty(infname),
+  fi=zeros(size(infname,1),1);
+  namei=zeros(size(infname,1),4);
+  jinf=zeros(size(infname,1),1)+NaN;
+  jref=zeros(size(infname,1),1)+NaN;
+
+  for k=1:size(infname,1),
+   j1=strmatch(infname(k,:),const.name);
+   if isempty(j1),
+     disp(['Can''t recognize name' infname(k,:) ' for inference']);
+   else
+    jinf(k)=j1;
+    fi(k)=const.freq(j1);
+    namei(k,:)=const.name(j1,:);
+    j1=strmatch(infref(k,:),nameu);
+    if isempty(j1),
+      disp(['Can''t recognize name ' infref(k,:) ' for as a reference for inference']);
+    else
+      jref(k)=j1;
+      fprintf(['   Inference of ' namei(k,:) ' using ' nameu(j1,:) '\n']);
+    end;
+   end;
+  end;    
+  jinf(isnan(jref))=NaN;
+end;
+
+%----------------------------------------------------------------------
+function y=fixgaps(x);
+% FIXGAPS: Linearly interpolates gaps in a time series
+% YOUT=FIXGAPS(YIN) linearly interpolates over NaN in the input time 
+% series (may be complex), but ignores trailing and leading NaNs.
+
+% R. Pawlowicz 11/6/99
+% Version 1.0
+
+y=x;
+
+bd=isnan(x);
+gd=find(~bd);
+
+bd([1:(min(gd)-1) (max(gd)+1):end])=0;
+
+
+y(bd)=interp1(gd,x(gd),find(bd)); 
+
+
+%----------------------------------------------------------------------
+function ain=cluster(ain,clusang);
+% CLUSTER: Clusters angles in rows around the angles in the first 
+% column. CLUSANG is the allowable ambiguity (usually 360 degrees but
+% sometimes 180).
+
+ii=(ain-ain(:,ones(1,size(ain,2))))>clusang/2;
+ain(ii)=ain(ii)-clusang;
+ii=(ain-ain(:,ones(1,size(ain,2))))<-clusang/2;
+ain(ii)=ain(ii)+clusang;
+
+
+%----------------------------------------------------------------------
+function [NP,NM]=noise_realizations(xres,fu,dt,nreal,errcalc);
+% NOISE_REALIZATIONS: Generates matrices of noise (with correct
+% cross-correlation structure) for bootstrap analysis.
+%
+
+% R. Pawlowicz 11/10/00
+% Version 1.0
+
+if strmatch(errcalc,'cboot'),
+  [fband,Pxrave,Pxiave,Pxcave]=residual_spectrum(xres,fu,dt);
+  
+  Pxcave=zeros(size(Pxcave));  %% For comparison with other technique!
+  %fprintf('**** Assuming no covariance between u and v errors!*******\n');
+
+elseif strmatch(errcalc,'wboot'),
+  fband=[0 .5];
+  nx=length(xres);
+  A=cov(real(xres),imag(xres))/nx;
+  Pxrave=A(1,1);Pxiave=A(2,2);Pxcave=A(1,2);
+else
+  error(['Unrecognized type of bootstap analysis specified: ''' errcalc '''']);
+end;
+  
+nfband=size(fband,1);
+
+Mat=zeros(4,4,nfband);
+for k=1:nfband,
+
+  % The B matrix represents the covariance matrix for the vector
+  % [Re{ap} Im{ap} Re{am} Im{am}]' where Re{} and Im{} are real and
+  % imaginary parts, and ap/m represent the complex constituent 
+  % amplitudes for positive and negative frequencies when the input
+  % is bivariate white noise. For a flat residual spectrum this works 
+  % fine.
+ 
+  % This is adapted here for "locally white" conditions, but I'm still
+  % not sure how to handle a complex sxy, so this is set to zero
+  % right now.
+  
+  p=(Pxrave(k)+Pxiave(k))/2;
+  d=(Pxrave(k)-Pxiave(k))/2;
+  sxy=Pxcave(k);
+  
+  B=[p    0   d   sxy;
+     0    p  sxy  -d;
+     d   sxy  p    0
+     sxy -d   0    p];
+
+  % Compute the transformation matrix that takes uncorrelated white 
+  % noise and makes noise with the same statistical structure as the 
+  % Fourier transformed noise.
+  [V,D]=eig(B);
+  Mat(:,:,k)=V*diag(sqrt(diag(D)));
+end;
+
+% Generate realizations for the different analyzed constituents.
+
+N=zeros(4,nreal);
+NM=zeros(length(fu),nreal);
+NP=NM;
+for k=1:length(fu);
+  l=find(fu(k)>fband(:,1) & fu(k)<fband(:,2));
+  N=[zeros(4,1),Mat(:,:,l)*randn(4,nreal-1)];
+  NP(k,:)=N(1,:)+i*N(2,:);
+  NM(k,:)=N(3,:)+i*N(4,:);
+end;
+
+%----------------------------------------------------------------------
+function [ercx,eicx]=noise_stats(xres,fu,dt);
+% NOISE_STATS: Computes statistics of residual energy for all 
+% constituents (ignoring any cross-correlations between real and
+% imaginary parts).
+
+% S. Lentz  10/28/99
+% R. Pawlowicz 11/1/00
+% Version 1.0
+
+[fband,Pxrave,Pxiave,Pxcave]=residual_spectrum(xres,fu,dt);
+nfband=size(fband,1);
+mu=length(fu);
+
+% Get the statistics for each component.
+ercx=zeros(mu,1);
+eicx=zeros(mu,1);
+for k1=1:nfband;
+   k=find(fu>=fband(k1,1) & fu<=fband(k1,2));
+   ercx(k)=sqrt(Pxrave(k1));
+   eicx(k)=sqrt(Pxiave(k1));
+end
+
+%----------------------------------------------------------------------
+function [fband,Pxrave,Pxiave,Pxcave]=residual_spectrum(xres,fu,dt)
+% RESIDUAL_SPECTRUM: Computes statistics from an input spectrum over
+% a number of bands, returning the band limits and the estimates for
+% power spectra for real and imaginary parts and the cross-spectrum.          
+%
+% Mean values of the noise spectrum are computed for the following 
+% 8 frequency bands defined by their center frequency and band width:
+% M0 +.1 cpd; M1 +-.2 cpd; M2 +-.2 cpd; M3 +-.2 cpd; M4 +-.2 cpd; 
+% M5 +-.2 cpd; M6 +-.21 cpd; M7 (.26-.29 cpd); and M8 (.30-.50 cpd). 
+
+% S. Lentz  10/28/99
+% R. Pawlowicz 11/1/00
+% Version 1.0
+
+% Define frequency bands for spectral averaging.
+fband =[.00010 .00417;
+        .03192 .04859;
+        .07218 .08884;
+        .11243 .12910;
+        .15269 .16936;
+        .19295 .20961;
+        .23320 .25100;
+        .26000 .29000;
+        .30000 .50000];
+
+% If we have a sampling interval> 1 hour, we might have to get
+% rid of some bins.
+%fband(fband(:,1)>1/(2*dt),:)=[];
+
+nfband=size(fband,1);
+nx=length(xres);
+
+% Spectral estimate (takes real time series only).
+% Call to SIGNAL PROCESSING TOOLBOX - see note in t_readme.
+% If you have an error here you are probably missing this toolbox
+
+msg = {};
+try
+  [Pxr,fx]=psd(real(xres),nx,1/dt); 
+  [Pxi,fx]=psd(imag(xres),nx,1/dt); 
+  [Pxc,fx]=csd(real(xres),imag(xres),nx,1/dt);
+catch
+  msg = {lasterr};
+  try
+     [Pxr,fx]=t_signal('psd',real(xres),nx,1/dt);
+     [Pxi,fx]=t_signal('psd',imag(xres),nx,1/dt);
+     [Pxc,fx]=t_signal('csd',real(xres),imag(xres),nx,1/dt);
+     msg = {};
+  catch
+     msg = cat(1,msg,{lasterr});
+  end
+end
+
+if ~isempty(msg)
+    msg = sprintf('%s\n',msg{:});
+    error(sprintf('Error call PSD / CSD from SignalProcessingToolbox.\n%s',msg));
+end
+
+df=fx(3)-fx(2);
+Pxr(round(fu./df)+1)=NaN ; % Sets Px=NaN in bins close to analyzed frequencies
+Pxi(round(fu./df)+1)=NaN ; % (to prevent leakage problems?).
+Pxc(round(fu./df)+1)=NaN ; 
+
+Pxrave=zeros(nfband,1);
+Pxiave=zeros(nfband,1);
+Pxcave=zeros(nfband,1);
+% Loop downwards in frequency through bands (cures short time series
+% problem with no data in lowest band).
+%
+% Divide by nx to get power per frequency bin, and multiply by 2
+% to account for positive and negative frequencies.
+%
+for k=nfband:-1:1,
+   jband=find(fx>=fband(k,1) & fx<=fband(k,2) & finite(Pxr));
+   if any(jband),
+     Pxrave(k)=mean(Pxr(jband))*2/nx;
+     Pxiave(k)=mean(Pxi(jband))*2/nx;
+     Pxcave(k)=mean(Pxc(jband))*2/nx;
+   elseif k<nfband,
+     Pxrave(k)=Pxrave(k+1);   % Low frequency bin might not have any points...
+     Pxiave(k)=Pxiave(k+1);   
+     Pxcave(k)=Pxcave(k+1);   
+   end;
+end
+
+
+
+%----------------------------------------------------------------------
+function [emaj,emin,einc,epha]=errell(cxi,sxi,ercx,ersx,ercy,ersy)
+% [emaj,emin,einc,epha]=errell(cx,sx,cy,sy,ercx,ersx,ercy,ersy) computes
+% the uncertainities in the ellipse parameters based on the 
+% uncertainities in the least square fit cos,sin coefficients.
+%
+%  INPUT:  cx,sx=cos,sin coefficients for x 
+%          cy,sy=cos,sin coefficients for y
+%          ercx,ersx=errors in x cos,sin coefficients
+%          ercy,ersy=errors in y cos,sin coefficients
+%          
+%  OUTPUT: emaj=major axis error
+%          emin=minor axis error
+%          einc=inclination error (deg)
+%          epha=pha error (deg)
+
+% based on linear error propagation, with errors in the coefficients 
+% cx,sx,cy,sy uncorrelated. 
+
+% B. Beardsley  1/15/99; 1/20/99
+% Version 1.0
+
+r2d=180./pi;
+cx=real(cxi(:));sx=real(sxi(:));cy=imag(cxi(:));sy=imag(sxi(:));
+ercx=ercx(:);ersx=ersx(:);ercy=ercy(:);ersy=ersy(:);
+
+rp=.5.*sqrt((cx+sy).^2+(cy-sx).^2);
+rm=.5.*sqrt((cx-sy).^2+(cy+sx).^2);
+ercx2=ercx.^2;ersx2=ersx.^2;
+ercy2=ercy.^2;ersy2=ersy.^2;
+
+% major axis error
+ex=(cx+sy)./rp;
+fx=(cx-sy)./rm;
+gx=(sx-cy)./rp;
+hx=(sx+cy)./rm;
+dcx2=(.25.*(ex+fx)).^2;
+dsx2=(.25.*(gx+hx)).^2;
+dcy2=(.25.*(hx-gx)).^2;
+dsy2=(.25.*(ex-fx)).^2;
+emaj=sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+% minor axis error
+dcx2=(.25.*(ex-fx)).^2;
+dsx2=(.25.*(gx-hx)).^2;
+dcy2=(.25.*(hx+gx)).^2;
+dsy2=(.25.*(ex+fx)).^2;
+emin=sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+% inclination error
+rn=2.*(cx.*cy+sx.*sy);
+rd=cx.^2+sx.^2-(cy.^2+sy.^2);
+den=rn.^2+rd.^2;
+dcx2=((rd.*cy-rn.*cx)./den).^2;
+dsx2=((rd.*sy-rn.*sx)./den).^2;
+dcy2=((rd.*cx+rn.*cy)./den).^2;
+dsy2=((rd.*sx+rn.*sy)./den).^2;
+einc=r2d.*sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+% phase error
+rn=2.*(cx.*sx+cy.*sy);
+rd=cx.^2-sx.^2+cy.^2-sy.^2;
+den=rn.^2+rd.^2;
+dcx2=((rd.*sx-rn.*cx)./den).^2;
+dsx2=((rd.*cx+rn.*sx)./den).^2;
+dcy2=((rd.*sy-rn.*cy)./den).^2;
+dsy2=((rd.*cy+rn.*sy)./den).^2;
+epha=r2d.*sqrt(dcx2.*ercx2+dsx2.*ersx2+dcy2.*ercy2+dsy2.*ersy2);
+
+
+
+
+
diff --git a/tools/tide/t_xtide.mat b/tools/tide/t_xtide.mat
new file mode 100644
index 0000000000000000000000000000000000000000..87176f96ce33bf0e2c22245b6faedb898e21a60f
Binary files /dev/null and b/tools/tide/t_xtide.mat differ
diff --git a/tools/tide/tide3.dat b/tools/tide/tide3.dat
new file mode 100644
index 0000000000000000000000000000000000000000..8cfecdd93647dc2099aee68e2e1df45ce5556c2b
--- /dev/null
+++ b/tools/tide/tide3.dat
@@ -0,0 +1,367 @@
+    Z0       0.0             M2
+    SA       0.0001140741    SSA
+    SSA      0.0002281591    Z0
+    MSM      0.0013097808    MM
+    MM       0.0015121518    MSF
+    MSF      0.0028219327    Z0
+    MF       0.0030500918    MSF
+    ALP1     0.0343965699    2Q1
+    2Q1      0.0357063507    Q1
+    SIG1     0.0359087218    2Q1
+    Q1       0.0372185026    O1
+    RHO1     0.0374208736    Q1
+    O1       0.0387306544    K1
+    TAU1     0.0389588136    O1
+    BET1     0.0400404353    NO1
+    NO1      0.0402685944    K1
+    CHI1     0.0404709654    NO1
+    PI1      0.0414385130    P1
+    P1       0.0415525871    K1
+    S1       0.0416666721    K1
+    K1       0.0417807462    Z0
+    PSI1     0.0418948203    K1
+    PHI1     0.0420089053    K1
+    THE1     0.0430905270    J1
+    J1       0.0432928981    K1
+    2PO1     0.0443745198
+    SO1      0.0446026789    OO1
+    OO1      0.0448308380    J1
+    UPS1     0.0463429898    OO1
+    ST36     0.0733553835
+    2NS2     0.0746651643
+    ST37     0.0748675353
+    ST1      0.0748933234
+    OQ2      0.0759749451    EPS2
+    EPS2     0.0761773161    2N2
+    ST2      0.0764054753
+    ST3      0.0772331498
+    O2       0.0774613089
+    2N2      0.0774870970    MU2
+    MU2      0.0776894680    N2
+    SNK2     0.0787710897
+    N2       0.0789992488    M2
+    NU2      0.0792016198    N2
+    ST4      0.0794555670
+    OP2      0.0802832416
+    GAM2     0.0803090296    H1
+    H1       0.0803973266    M2
+    M2       0.0805114007    Z0
+    H2       0.0806254748    M2
+    MKS2     0.0807395598    M2
+    ST5      0.0809677189
+    ST6      0.0815930224
+    LDA2     0.0818211815    L2
+    L2       0.0820235525    S2
+    2SK2     0.0831051742
+    T2       0.0832192592    S2
+    S2       0.0833333333    M2
+    R2       0.0834474074    S2
+    K2       0.0835614924    S2
+    MSN2     0.0848454852    ETA2
+    ETA2     0.0850736443    K2
+    ST7      0.0853018034
+    2SM2     0.0861552660
+    ST38     0.0863576370
+    SKM2     0.0863834251
+    2SN2     0.0876674179
+    NO3      0.1177299033
+    MO3      0.1192420551    M3
+    M3       0.1207671010    M2
+    NK3      0.1207799950
+    SO3      0.1220639878    MK3
+    MK3      0.1222921469    M3
+    SP3      0.1248859204
+    SK3      0.1251140796    MK3
+    ST8      0.1566887168
+    N4       0.1579984976
+    3MS4     0.1582008687
+    ST39     0.1592824904
+    MN4      0.1595106495    M4
+    ST9      0.1597388086
+    ST40     0.1607946422
+    M4       0.1610228013    M3
+    ST10     0.1612509604
+    SN4      0.1623325821    M4
+    KN4      0.1625607413
+    MS4      0.1638447340    M4
+    MK4      0.1640728931    MS4
+    SL4      0.1653568858
+    S4       0.1666666667    MS4
+    SK4      0.1668948258    S4
+    MNO5     0.1982413039
+    2MO5     0.1997534558
+    3MP5     0.1999816149
+    MNK5     0.2012913957
+    2MP5     0.2025753884
+    2MK5     0.2028035475    M4
+    MSK5     0.2056254802
+    3KM5     0.2058536393
+    2SK5     0.2084474129    2MK5
+    ST11     0.2372259056
+    2NM6     0.2385098983
+    ST12     0.2387380574
+    2MN6     0.2400220501    M6
+    ST13     0.2402502093
+    ST41     0.2413060429
+    M6       0.2415342020    2MK5
+    MSN6     0.2428439828
+    MKN6     0.2430721419
+    ST42     0.2441279756
+    2MS6     0.2443561347    M6
+    2MK6     0.2445842938    2MS6
+    NSK6     0.2458940746
+    2SM6     0.2471780673    2MS6
+    MSK6     0.2474062264    2SM6
+    S6       0.2500000000
+    ST14     0.2787527046
+    ST15     0.2802906445
+    M7       0.2817899023
+    ST16     0.2830867891
+    3MK7     0.2833149482    M6
+    ST17     0.2861368809
+    ST18     0.3190212990
+    3MN8     0.3205334508
+    ST19     0.3207616099
+    M8       0.3220456027    3MK7
+    ST20     0.3233553835
+    ST21     0.3235835426
+    3MS8     0.3248675353
+    3MK8     0.3250956944
+    ST22     0.3264054753
+    ST23     0.3276894680
+    ST24     0.3279176271
+    ST25     0.3608020452
+    ST26     0.3623141970
+    4MK9     0.3638263489
+    ST27     0.3666482815
+    ST28     0.4010448515
+    M10      0.4025570033
+    ST29     0.4038667841
+    ST30     0.4053789360
+    ST31     0.4069168759
+    ST32     0.4082008687
+    ST33     0.4471596822
+    M12      0.4830684040
+    ST34     0.4858903367
+    ST35     0.4874282766
+ 
+  .7428797055  .7771900329  .5187051308  .3631582592  .7847990160  000GMT 1/1/76
+13.3594019864  .9993368945  .1129517942  .0536893056  .0000477414  INCR./365DAYS
+      Z0      0  0  0  0  0  0 0.0    0
+      SA      0  0  1  0  0 -1 0.0    0
+      SSA     0  0  2  0  0  0 0.0    0
+      MSM     0  1 -2  1  0  0  .00   0
+      MM      0  1  0 -1  0  0 0.0    0
+      MSF     0  2 -2  0  0  0 0.0    0
+      MF      0  2  0  0  0  0 0.0    0
+      ALP1    1 -4  2  1  0  0 -.25   2
+      ALP1  -1  0  0 .75 0.0360R1   0 -1  0 .00 0.1906
+      2Q1     1 -3  0  2  0  0-0.25   5
+      2Q1   -2 -2  0 .50 0.0063    -1 -1  0 .75 0.0241R1  -1  0  0 .75 0.0607R1
+      2Q1    0 -2  0 .50 0.0063     0 -1  0 .0  0.1885
+      SIG1    1 -3  2  0  0  0-0.25   4
+      SIG1  -1  0  0 .75 0.0095R1   0 -2  0 .50 0.0061     0 -1  0 .0  0.1884
+      SIG1   2  0  0 .50 0.0087
+      Q1      1 -2  0  1  0  0-0.25  10
+      Q1    -2 -3  0 .50 0.0007    -2 -2  0 .50 0.0039    -1 -2  0 .75 0.0010R1
+      Q1    -1 -1  0 .75 0.0115R1  -1  0  0 .75 0.0292R1   0 -2  0 .50 0.0057
+      Q1    -1  0  1 .0  0.0008     0 -1  0 .0  0.1884     1  0  0 .75 0.0018R1
+      Q1     2  0  0 .50 0.0028
+      RHO1    1 -2  2 -1  0  0-0.25   5
+      RHO1   0 -2  0 .50 0.0058     0 -1  0 .0  0.1882     1  0  0 .75 0.0131R1
+      RHO1   2  0  0 .50 0.0576     2  1  0 .0  0.0175
+      O1      1 -1  0  0  0  0-0.25   8
+      O1    -1  0  0 .25 0.0003R1   0 -2  0 .50 0.0058     0 -1  0 .0  0.1885
+      O1     1 -1  0 .25 0.0004R1   1  0  0 .75 0.0029R1   1  1  0 .25 0.0004R1
+      O1     2  0  0 .50 0.0064     2  1  0 .50 0.0010
+      TAU1    1 -1  2  0  0  0-0.75   5
+      TAU1  -2  0  0 .0  0.0446    -1  0  0 .25 0.0426R1   0 -1  0 .50 0.0284
+      TAU1   0  1  0 .50 0.2170     0  2  0 .50 0.0142
+      BET1    1  0 -2  1  0  0 -.75   1
+      BET1   0 -1  0 .00 0.2266
+      NO1     1  0  0  1  0  0-0.75   9
+      NO1   -2 -2  0 .50 0.0057    -2 -1  0 .0  0.0665    -2  0  0 .0  0.3596
+      NO1   -1 -1  0 .75 0.0331R1  -1  0  0 .25 0.2227R1  -1  1  0 .75 0.0290R1
+      NO1    0 -1  0 .50 0.0290     0  1  0 .0  0.2004     0  2  0 .50 0.0054
+      CHI1    1  0  2 -1  0  0-0.75   2
+      CHI1   0 -1  0 .50 0.0282     0  1  0 .0  0.2187
+      PI1     1  1 -3  0  0  1-0.25   1
+      PI1    0 -1  0 .50 0.0078
+      P1      1  1 -2  0  0  0-0.25   6
+      P1     0 -2  0 .0  0.0008     0 -1  0 .50 0.0112     0  0  2 .50 0.0004
+      P1     1  0  0 .75 0.0004R1   2  0  0 .50 0.0015     2  1  0 .50 0.0003
+      S1      1  1 -1  0  0  1-0.75   2
+      S1     0  0 -2 .0  0.3534     0  1  0 .50 0.0264
+      K1      1  1  0  0  0  0-0.75  10
+      K1    -2 -1  0 .0  0.0002    -1 -1  0 .75 0.0001R1  -1  0  0 .25 0.0007R1
+      K1    -1  1  0 .75 0.0001R1   0 -2  0 .0  0.0001     0 -1  0 .50 0.0198
+      K1     0  1  0 .0  0.1356     0  2  0 .50 0.0029     1  0  0 .25 0.0002R1
+      K1     1  1  0 .25 0.0001R1
+      PSI1    1  1  1  0  0 -1-0.75   1
+      PSI1   0  1  0 .0  0.0190
+      PHI1    1  1  2  0  0  0-0.75   5
+      PHI1  -2  0  0 .0  0.0344    -2  1  0 .0  0.0106     0  0 -2 .0  0.0132
+      PHI1   0  1  0 .50 0.0384     0  2  0 .50 0.0185
+      THE1    1  2 -2  1  0  0 -.75   4
+      THE1  -2 -1  0 .00  .0300    -1  0  0 .25 0.0141R1   0 -1  0 .50  .0317
+      THE1   0  1  0 .00  .1993
+      J1      1  2  0 -1  0  0-0.75  10
+      J1     0 -1  0 .50 0.0294     0  1  0 .0  0.1980     0  2  0 .50 0.0047
+      J1     1 -1  0 .75 0.0027R1   1  0  0 .25 0.0816R1   1  1  0 .25 0.0331R1
+      J1     1  2  0 .25 0.0027R1   2  0  0 .50 0.0152     2  1  0 .50 0.0098
+      J1     2  2  0 .50 0.0057
+      OO1     1  3  0  0  0  0-0.75   8
+      OO1   -2 -1  0 .50 0.0037    -2  0  0 .0  0.1496    -2  1  0 .0  0.0296
+      OO1   -1  0  0 .25 0.0240R1  -1  1  0 .25 0.0099R1   0  1  0 .0  0.6398
+      OO1    0  2  0 .0  0.1342     0  3  0 .0  0.0086
+      UPS1    1  4  0 -1  0  0 -.75   5
+      UPS1  -2  0  0 .00 0.0611     0  1  0 .00 0.6399     0  2  0 .00 0.1318
+      UPS1   1  0  0 .25 0.0289R1   1  1  0 .25 0.0257R1
+      OQ2     2 -3  0  3  0  0 0.0    2
+      OQ2   -1  0  0 .25 0.1042R2   0 -1  0 .50 0.0386
+      EPS2    2 -3  2  1  0  0 0.0    3
+      EPS2  -1 -1  0 .25 0.0075R2  -1  0  0 .25 0.0402R2   0 -1  0 .50 0.0373
+      2N2     2 -2  0  2  0  0 0.0    4
+      2N2   -2 -2  0 .50 0.0061    -1 -1  0 .25 0.0117R2  -1  0  0 .25 0.0678R2
+      2N2    0 -1  0 .50 0.0374
+      MU2     2 -2  2  0  0  0 0.0    3
+      MU2   -1 -1  0 .25 0.0018R2  -1  0  0 .25 0.0104R2   0 -1  0 .50 0.0375
+      N2      2 -1  0  1  0  0 0.0    4
+      N2    -2 -2  0 .50 0.0039    -1  0  1 .00 0.0008     0 -2  0 .00 0.0005
+      N2     0 -1  0 .50 0.0373
+      NU2     2 -1  2 -1  0  0 0.0    4
+      NU2    0 -1  0 .50 0.0373     1  0  0 .75 0.0042R2   2  0  0 .0  0.0042
+      NU2    2  1  0 .50 0.0036
+      GAM2    2  0 -2  2  0  0 -.50   3
+      GAM2  -2 -2  0 .00 0.1429    -1  0  0 .25 0.0293R2   0 -1  0 .50 0.0330
+      H1      2  0 -1  0  0  1-0.50   2
+      H1     0 -1  0 .50 0.0224     1  0 -1 .50 0.0447
+      M2      2  0  0  0  0  0 0.0    9
+      M2    -1 -1  0 .75 0.0001R2  -1  0  0 .75 0.0004R2   0 -2  0 .0  0.0005
+      M2     0 -1  0 .50 0.0373     1 -1  0 .25 0.0001R2   1  0  0 .75 0.0009R2
+      M2     1  1  0 .75 0.0002R2   2  0  0 .0  0.0006     2  1  0 .0  0.0002
+      H2      2  0  1  0  0 -1 0.0    1
+      H2     0 -1  0 .50 0.0217
+      LDA2    2  1 -2  1  0  0-0.50   1
+      LDA2   0 -1  0 .50 0.0448
+      L2      2  1  0 -1  0  0-0.50   5
+      L2     0 -1  0 .50 0.0366     2 -1  0 .00 0.0047     2  0  0 .50 0.2505
+      L2     2  1  0 .50 0.1102     2  2  0 .50 0.0156
+      T2      2  2 -3  0  0  1 0.0    0
+      S2      2  2 -2  0  0  0 0.0    3
+      S2     0 -1  0 .0  0.0022     1  0  0 .75 0.0001R2   2  0  0 .0  0.0001
+      R2      2  2 -1  0  0 -1-0.50   2
+      R2     0  0  2 .50 0.2535     0  1  2 .0  0.0141
+      K2      2  2  0  0  0  0 0.0    5
+      K2    -1  0  0 .75 0.0024R2  -1  1  0 .75 0.0004R2   0 -1  0 .50 0.0128
+      K2     0  1  0 .0  0.2980     0  2  0 .0  0.0324
+      ETA2    2  3  0 -1  0  0 0.0    7
+      ETA2   0 -1  0 .50 0.0187     0  1  0 .0  0.4355     0  2  0 .0  0.0467
+      ETA2   1  0  0 .75 0.0747R2   1  1  0 .75 0.0482R2   1  2  0 .75 0.0093R2
+      ETA2   2  0  0 .50 0.0078
+      M3      3  0  0  0  0  0 -.50   1
+      M3     0 -1  0 .50  .0564
+ 
+      2PO1 2   2.0 P1        -1.0 O1
+      SO1  2   1.0 S2        -1.0 O1
+      ST36 3   2.0 M2         1.0 N2        -2.0 S2
+      2NS2 2   2.0 N2        -1.0 S2
+      ST37 2   3.0 M2        -2.0 S2
+      ST1  3   2.0 N2         1.0 K2        -2.0 S2
+      ST2  4   1.0 M2         1.0 N2         1.0 K2        -2.0 S2
+      ST3  3   2.0 M2         1.0 S2        -2.0 K2
+      O2   1   2.0 O1
+      ST4  3   2.0 K2         1.0 N2        -2.0 S2
+      SNK2 3   1.0 S2         1.0 N2        -1.0 K2
+      OP2  2   1.0 O1         1.0 P1
+      MKS2 3   1.0 M2         1.0 K2        -1.0 S2
+      ST5  3   1.0 M2         2.0 K2        -2.0 S2
+      ST6  4   2.0 S2         1.0 N2        -1.0 M2        -1.0 K2
+      2SK2 2   2.0 S2        -1.0 K2
+      MSN2 3   1.0 M2         1.0 S2        -1.0 N2
+      ST7  4   2.0 K2         1.0 M2        -1.0 S2        -1.0 N2
+      2SM2 2   2.0 S2        -1.0 M2
+      ST38 3   2.0 M2         1.0 S2        -2.0 N2
+      SKM2 3   1.0 S2         1.0 K2        -1.0 M2
+      2SN2 2   2.0 S2        -1.0 N2
+      NO3  2   1.0 N2         1.0 O1
+      MO3  2   1.0 M2         1.0 O1
+      NK3  2   1.0 N2         1.0 K1
+      SO3  2   1.0 S2         1.0 O1
+      MK3  2   1.0 M2         1.0 K1
+      SP3  2   1.0 S2         1.0 P1
+      SK3  2   1.0 S2         1.0 K1
+      ST8  3   2.0 M2         1.0 N2        -1.0 S2
+      N4   1   2.0 N2
+      3MS4 2   3.0 M2        -1.0 S2
+      ST39 4   1.0 M2         1.0 S2         1.0 N2        -1.0 K2
+      MN4  2   1.0 M2         1.0 N2
+      ST40 3   2.0 M2         1.0 S2        -1.0 K2
+      ST9  4   1.0 M2         1.0 N2         1.0 K2        -1.0 S2
+      M4   1   2.0 M2
+      ST10 3   2.0 M2         1.0 K2        -1.0 S2
+      SN4  2   1.0 S2         1.0 N2
+      KN4  2   1.0 K2         1.0 N2
+      MS4  2   1.0 M2         1.0 S2
+      MK4  2   1.0 M2         1.0 K2
+      SL4  2   1.0 S2         1.0 L2
+      S4   1   2.0 S2
+      SK4  2   1.0 S2         1.0 K2
+      MNO5 3   1.0 M2         1.0 N2         1.0 O1
+      2MO5 2   2.0 M2         1.0 O1
+      3MP5 2   3.0 M2        -1.0 P1
+      MNK5 3   1.0 M2         1.0 N2         1.0 K1
+      2MP5 2   2.0 M2         1.0 P1
+      2MK5 2   2.0 M2         1.0 K1
+      MSK5 3   1.0 M2         1.0 S2         1.0 K1
+      3KM5 3   1.0 K2         1.0 K1         1.0 M2
+      2SK5 2   2.0 S2         1.0 K1
+      ST11 3   3.0 N2         1.0 K2        -1.0 S2
+      2NM6 2   2.0 N2         1.0 M2
+      ST12 4   2.0 N2         1.0 M2         1.0 K2        -1.0 S2
+      ST41 3   3.0 M2         1.0 S2        -1.0 K2
+      2MN6 2   2.0 M2         1.0 N2
+      ST13 4   2.0 M2         1.0 N2         1.0 K2        -1.0 S2
+      M6   1   3.0 M2
+      MSN6 3   1.0 M2         1.0 S2         1.0 N2
+      MKN6 3   1.0 M2         1.0 K2         1.0 N2
+      2MS6 2   2.0 M2         1.0 S2
+      2MK6 2   2.0 M2         1.0 K2
+      NSK6 3   1.0 N2         1.0 S2         1.0 K2
+      2SM6 2   2.0 S2         1.0 M2
+      MSK6 3   1.0 M2         1.0 S2         1.0 K2
+      ST42 3   2.0 M2         2.0 S2        -1.0 K2
+      S6   1   3.0 S2
+      ST14 3   2.0 M2         1.0 N2         1.0 O1
+      ST15 3   2.0 N2         1.0 M2         1.0 K1
+      M7   1   3.5 M2
+      ST16 3   2.0 M2         1.0 S2         1.0 O1
+      3MK7 2   3.0 M2         1.0 K1
+      ST17 4   1.0 M2         1.0 S2         1.0 K2         1.0 O1
+      ST18 2   2.0 M2         2.0 N2
+      3MN8 2   3.0 M2         1.0 N2
+      ST19 4   3.0 M2         1.0 N2         1.0 K2        -1.0 S2
+      M8   1   4.0 M2
+      ST20 3   2.0 M2         1.0 S2         1.0 N2
+      ST21 3   2.0 M2         1.0 N2         1.0 K2
+      3MS8 2   3.0 M2         1.0 S2
+      3MK8 2   3.0 M2         1.0 K2
+      ST22 4   1.0 M2         1.0 S2         1.0 N2         1.0 K2
+      ST23 2   2.0 M2         2.0 S2
+      ST24 3   2.0 M2         1.0 S2         1.0 K2
+      ST25 3   2.0 M2         2.0 N2         1.0 K1
+      ST26 3   3.0 M2         1.0 N2         1.0 K1
+      4MK9 2   4.0 M2         1.0 K1
+      ST27 3   3.0 M2         1.0 S2         1.0 K1
+      ST28 2   4.0 M2         1.0 N2
+      M10  1   5.0 M2
+      ST29 3   3.0 M2         1.0 N2         1.0 S2
+      ST30 2   4.0 M2         1.0 S2
+      ST31 4   2.0 M2         1.0 N2         1.0 S2         1.0 K2
+      ST32 2   3.0 M2         2.0 S2
+      ST33 3   4.0 M2         1.0 S2         1.0 K1
+      M12  1   6.0 M2
+      ST34 2   5.0 M2         1.0 S2
+      ST35 4   3.0 M2         1.0 N2         1.0 K2         1.0 S2
+ 
+