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 + +