diff --git a/applications/+iceweb/apply_calib.m b/applications/+iceweb/apply_calib.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/apply_filter.m b/applications/+iceweb/apply_filter.m old mode 100644 new mode 100755 index 9091572..089cc0a --- a/applications/+iceweb/apply_filter.m +++ b/applications/+iceweb/apply_filter.m @@ -1,4 +1,7 @@ function w = apply_filter(w, PARAMS) + if ~exist('PARAMS','var') + PARAMS.filterObj = filterobject('h',0.1,2); + end for c=1:numel(w) try w(c) = filtfilt(PARAMS.filterObj, w(c)); diff --git a/applications/+iceweb/calculatePanelPositions.m b/applications/+iceweb/calculatePanelPositions.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/datenum2julday.m b/applications/+iceweb/datenum2julday.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/extended_spectralobject_colormap.m b/applications/+iceweb/extended_spectralobject_colormap.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/get_channeltags_active.m b/applications/+iceweb/get_channeltags_active.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/get_spectrogram_filename.m b/applications/+iceweb/get_spectrogram_filename.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/iceweb.m b/applications/+iceweb/iceweb.m old mode 100644 new mode 100755 index 22c902e..7cc462d --- a/applications/+iceweb/iceweb.m +++ b/applications/+iceweb/iceweb.m @@ -190,12 +190,12 @@ function iceweb_helper(paths, PARAMS, subnets, tw, ds) % load state statefile = sprintf('iceweb_%s_state.mat',subnet); if exist(statefile, 'file') - load(statefile) - if strcmp(subnet, subnet0) - if snum < snum0 % skip - %continue - end - end + load(statefile) + if strcmp(subnet, subnet0) + if snum < snum0 % skip + %continue + end + end end % save state diff --git a/applications/+iceweb/iceweb2017.m b/applications/+iceweb/iceweb2017.m new file mode 100755 index 0000000..2728137 --- /dev/null +++ b/applications/+iceweb/iceweb2017.m @@ -0,0 +1,316 @@ +function iceweb2017(PRODUCTS_TOP_DIR, subnetName, ds, ChannelTagList, ... + snum, enum, nummins, products) + debug.printfunctionstack('>'); + + % make the directory under which all products will be stored + try + mkdir(PRODUCTS_TOP_DIR); + catch + error(sprintf('Cannot make directory %s',PRODUCTS_TOP_DIR)); + end + +% % load state +% statefile = fullfile(PRODUCTS_TOP_DIR, sprintf('iceweb_%s_state.mat',subnetName)); +% if exist(statefile, 'file') +% load(statefile) +% end + + % end time + if enum==0 + enum = utnow - delaymins/1440; + end + + % generate list of timewindows + timewindows = iceweb.get_timewindow(enum, nummins, snum); + + networkName = getmostcommon(ChannelTagList); + + % loop over timewindows + for count = 1:length(timewindows.start) + hh = datestr(timewindows.start(count),'HH'); + mm = datestr(timewindows.start(count),'MM'); + if strcmp(hh,'00') && strcmp(mm, '00') || count==1 + fprintf('\n%s ',datestr(timewindows.start(count),26)); + end + if strcmp(mm,'00') + fprintf('%s ',hh); + end + process_timewindow(PRODUCTS_TOP_DIR, networkName, subnetName, ChannelTagList, timewindows.start(count), timewindows.stop(count), ds, products); + %anykey = input('Press any key to continue'); + end + debug.printfunctionstack('<'); +end + + +function process_timewindow(PRODUCTS_TOP_DIR, networkName, subnetName, ChannelTagList, snum, enum, ds, products) + debug.printfunctionstack('>'); + % changed for compatibility with Pensive, which names files by endtime +% filedate = datestr(snum,'yyyy-mm-dd'); +% filetime = datestr(snum,'yyyymmdd-HHMM'); + filedate = datestr(enum,'yyyy-mm-dd'); + filetime = datestr(enum,'yyyymmdd-HHMM'); + + MILLISECOND_IN_DAYS = (1 / 86400000); + enum = enum - MILLISECOND_IN_DAYS; % try to skip last sample + +% % load state +% statefile = fullfile(PRODUCTS_TOP_DIR, sprintf('%s_state.mat',subnetName)); +% if exist(statefile, 'file') +% load(statefile) +% if snum < snum0 +% return +% end +% end +% +% % save state +% ds0=ds; ChannelTagList0=ChannelTagList; snum0=snum; enum0=enum; subnetName0 = subnetName; +% mkdir(fileparts(statefile)); +% save(statefile, 'ds0', 'ChannelTagList0', 'snum0', 'enum0', 'subnetName0'); +% clear ds0 ChannelTagList0 snum0 enum0 subnetName0 + + %% Save raw waveform data to MAT file + jjj = datenum2julday(snum); + wavrawmat = fullfile(PRODUCTS_TOP_DIR, 'waveforms', subnetName, filedate, sprintf('%s_%s_raw.mat',subnetName,filetime)); + if ~exist(wavrawmat,'file') + %% Get waveform data + debug.print_debug(1, sprintf('%s %s: Getting waveforms for %s from %s to %s at %s',mfilename, datestr(utnow), subnetName , datestr(snum), datestr(enum))); + w = waveform(ds, ChannelTagList, snum, enum); + %w = iceweb.waveform_wrapper(ds, ChannelTagList, snum, enum); % returns 1 waveform per channeltag, in same order + if isempty(w) + debug.print_debug(1, 'No waveform data returned - here are the waveform() parameters:'); + save failedwaveformcall.mat ds ChannelTagList snum enum + debug.printfunctionstack('<'); + return + end + mkdir(fileparts(wavrawmat)); + debug.print_debug(1,sprintf('Saving waveform data to %s',wavrawmat)); + save(wavrawmat,'w'); + else + return + end + debug.printfunctionstack('<'); + + % Save the cleaned waveform data to MAT file + wavcleanmat = fullfile(PRODUCTS_TOP_DIR, 'waveforms', subnetName, filedate, sprintf('%s_%s_clean.mat',subnetName,filetime)); + if ~exist(wavcleanmat,'file') + + % Eliminate empty waveform objects + w = iceweb.waveform_remove_empty(w); + if numel(w)==0 + debug.print_debug(1, 'All waveform objects were empty - skipping this time window'); + return + end + + % Clean the waveforms + w = clean(w); + + % Apply filterobject if exists + if ~isempty(products.waveform.filterobject) + w = filtfilt(products.waveform.filterobject, w); + end + + % Pad all waveforms to same start/end + [wsnum wenum] = gettimerange(w); % assume gaps already filled, signal + w = pad(w, min([snum wsnum]), max([enum wenum]), 0); + + mkdir(fileparts(wavcleanmat)); + debug.print_debug(1,sprintf('Saving waveform data to %s',wavcleanmat)); + save(wavcleanmat,'w'); + else + return + end + + %% ICEWEB PRODUCTS + + % WAVEFORM PLOT + if products.waveform_plot.doit + fname = fullfile(PRODUCTS_TOP_DIR, 'waveforms', subnetName, filedate, sprintf('%s_%s.png',subnetName,filetime)); + if ~exist(fname,'file') + close all + plot_panels(w, 'visible', 'off') + orient tall; + iceweb.saveImageFile(fname, 72); % this should make directory tree too + end + end + + % RSAM + if products.rsam.doit + for measureNum = 1:numel(products.rsam.measures) + measure = products.rsam.measures{measureNum}; + if numel(products.rsam.samplingIntervalSeconds)>1 + samplingInterval = products.rsam.samplingIntervalSeconds(measureNum); + else + samplingInterval = products.rsam.samplingIntervalSeconds; + end + rsamobj = waveform2rsam(w, measure, samplingInterval); + %rsamobj.plot_panels() + rsamobj.save_to_bob_file(fullfile(PRODUCTS_TOP_DIR, 'bobfiles', subnetName, 'SSSS.CCC.YYYY.MMMM.bob')); + end + end + + + % PLOT SPECTROGRAMS + if products.spectrograms.doit + % filepath is compatible with Pensive + spectrogramFilename = fullfile(PRODUCTS_TOP_DIR, 'spectrograms', networkName, subnetName, filedate, sprintf('%s_%s.png',subnetName, filetime) ); + debug.print_debug(1, sprintf('Creating %s',spectrogramFilename)); + close all + spectrogramFraction = 0.75; + dbLims = [products.spectrograms.dBmin products.spectrograms.dBmax]; + specObj = spectralobject(1024, 924, products.spectrograms.fmax, dbLims); + + % if any channels have units 'Pa', multiple by 1000 so they are + % visible + for wavnum=1:numel(w) + thischan = get(w(wavnum),'channel'); + if strcmp(get(w(wavnum),'units'), 'Pa') || strcmp(thischan(1:2),'BD') + w(wavnum) = w(wavnum) * 1000; + w(wavnum) = set(w(wavnum), 'units', 'mPa'); + end + end + + [sgresult, Tcell, Fcell, Ycell] = iceweb.spectrogram_iceweb(specObj, w, 'spectrogramFraction', spectrogramFraction, 'colormap', iceweb.extended_spectralobject_colormap); + + + if sgresult > 0 % sgresult = number of waveforms for which a spectrogram was successfully plotted + % SAVE SPECTROGRAM PLOT TO IMAGE FILE AND CREATE THUMBNAIL + orient tall; + + if iceweb.saveImageFile(spectrogramFilename, 72) + + fileinfo = dir(spectrogramFilename); % getting a weird Index exceeds matrix dimensions error here. + debug.print_debug(1, sprintf('%s %s: spectrogram PNG size is %d',mfilename, datestr(utnow), fileinfo.bytes)); + + % make thumbnails + iceweb.makespectrogramthumbnails(spectrogramFilename, spectrogramFraction); + + % save spectral data + if products.spectral_data.doit + frequency_index_divider = 5.0; % Hz + for spi = 1:numel(Fcell) + thisY = Ycell{spi}; + thisF = Fcell{spi}; + thisT = Tcell{spi}; + thisCtag = get(w(spi),'ChannelTag'); + + % peakF and meanF for each spectrogram window + [Ymax,imax] = max(thisY); + PEAK_F = thisF(imax); + MEAN_F = (thisF' * thisY)./sum(thisY); + + % frequency index for each spectrogram window + fUpperIndices = find(thisF > frequency_index_divider); + fLowerIndices = find(thisF < frequency_index_divider); + fupper = sum(thisY(fUpperIndices,:)); + flower = sum(thisY(fLowerIndices,:)); + F_INDEX = log2(fupper ./ flower); + F_RATIO = fupper ./ flower; + + % Peak spectral value in each frequency bin - or in + % each minute? + + % Now downsample to 1 sample per minute + dnum = unique(floorminute(thisT)); + for k=1:length(dnum) + p = find(floorminute(thisT) == dnum(k)); + downsampled_peakf(k) = nanmean(PEAK_F(p)); + downsampled_meanf(k) = nanmean(MEAN_F(p)); + downsampled_findex(k) = nanmean(F_INDEX(p)); + downsampled_fratio(k) = nanmean(F_RATIO(p)); + suby = thisY(:,p); + max_in_each_freq_band(:,k) = max(suby'); + end + +% % Plot for verification +% close all +% subplot(2,1,1),plot(dnum,downsampled_peakf,'o');datetick('x'); +% hold on +% plot(dnum,downsampled_meanf,'*');datetick('x');legend('peakf','meanf') +% subplot(2,1,2),plot(dnum,downsampled_findex,'+');datetick('x'); +% hold on + + +% plot(dnum,downsampled_fratio,'*');datetick('x');legend('findex','fratio') +% anykey = input('Press any key to continue'); + + % Save data + r1 = rsam(dnum, downsampled_peakf, 'ChannelTag', thisCtag, ... + 'measure', 'peakf', ... + 'units', 'Hz'); + r1.save_to_bob_file(fullfile(PRODUCTS_TOP_DIR, 'bobfiles', subnetName, 'SSSS.CCC.YYYY.peakf.bob')) + + r2 = rsam(dnum, downsampled_meanf, 'ChannelTag', thisCtag, ... + 'measure', 'meanf', ... + 'units', 'Hz'); + r2.save_to_bob_file(fullfile(PRODUCTS_TOP_DIR, 'bobfiles', subnetName, 'SSSS.CCC.YYYY.meanf.bob')) + + r3 = rsam(dnum, downsampled_findex, 'ChannelTag', thisCtag, ... + 'measure', 'findex', ... + 'units', 'none'); + r3.save_to_bob_file(fullfile(PRODUCTS_TOP_DIR, 'bobfiles', subnetName, 'SSSS.CCC.YYYY.findex.bob')) + + r4 = rsam(dnum, downsampled_fratio, 'ChannelTag', thisCtag, ... + 'measure', 'fratio', ... + 'units', 'none'); + r4.save_to_bob_file(fullfile(PRODUCTS_TOP_DIR, 'bobfiles', subnetName, 'SSSS.CCC.YYYY.fratio.bob')) + + + specdatafilename = fullfile(PRODUCTS_TOP_DIR, 'spectrograms', networkName, subnetName, filedate, sprintf('%s_%s.dat',thisCtag.scn(), filedate) ); + + specdatadir = fileparts(specdatafilename); % make the directory in case it does not exist + mkdir(specdatadir); % make the directory in case it does not exist + fspec = fopen(specdatafilename,'a'); + fprintf(fspec, '%13.6f', min(dnum)); + fprintf(fspec, ' %6.2e', max_in_each_freq_band); + fprintf(fspec, '\n', max_in_each_freq_band); + fclose(fspec); + %save(specdatafilename, 'dnum', 'max_in_each_freq_band') + clear r1 r2 r3 k p downsampled_peakf downsampled_meanf ... + downsampled_findex fUpperIndices fLowerIndices flower ... + fupper F_INDEX PEAK_F MEAN_F Ymax imax thisY thisF thisT ... + thisCtag suby max_in_each_freq_band + + end + end + end + end + end + + % SOUND FILES + if products.soundfiles.doit + try + % 20120221 Added a "sound file" like 201202211259.sound which simply records order of stachans in waveform object so + % php script can match spectrogram panel with appropriate wav file + % 20121101 GTHO COmment: Could replace use of bnameroot below with strrep, since it is just used to change file extensions + % e.g. strrep(spectrogramFilename, '.png', sprintf('_%s_%s.wav', sta, chan)) + soundfileroot = fullfile('iceweb', 'soundfiles', subnetName, datestr(snum, 26) ); + [dname, bnameroot, bnameext] = fileparts(soundfileroot); + soundfilelist = fullfile(soundfiles, sprintf('%s.sound',datestr(snum,30))); + fsound = fopen(soundfilelist,'a'); + for c=1:length(w) + soundfilename = fullfile(soundfileroot, sprintf('%s_%s_%s.wav',datestr(snum,30), get(w(c),'station'), get(w(c), 'channel') ) ); + fprintf(fsound,'%s\n', soundfilename); + debug.print_debug(0, sprintf('Writing to %s',soundfilename)); + isSuccessful = waveform2sound(w(c), 60, soundfilename) + end + fclose(fsound); + end + + end + debug.printfunctionstack('<'); +end + +function s = getmostcommon(ctags) + if numel(ctags)==1 + s = get(ctags,'network'); + return + end + x = get(ctags,'network'); + y = unique(x); + n = zeros(length(y), 1); + for iy = 1:length(y) + n(iy) = length(find(strcmp(y{iy}, x))); + end + [~, itemp] = max(n); + s= y{itemp}; +end \ No newline at end of file diff --git a/applications/+iceweb/iceweb2017_extraproducts.m b/applications/+iceweb/iceweb2017_extraproducts.m new file mode 100755 index 0000000..437f878 --- /dev/null +++ b/applications/+iceweb/iceweb2017_extraproducts.m @@ -0,0 +1,303 @@ +function iceweb2017(thissubnet, ds, ChannelTagList, ... + snum, enum, nummins, products, PARAMS, paths) + debug.printfunctionstack('>'); + + % load state + statefile = sprintf('iceweb_%s_state.mat',thissubnet); + if exist(statefile, 'file') && ~strcmp(PARAMS.runmode, 'test') + load(statefile) + end + + % end time + if enum==0 + enum = utnow - delaymins/1440; + end + + + % loop one day at a time + for dnum = floor(snum):ceil(enum) + disp(datestr(dnum)) + +% % figure out which channeltags are active for this day? +% ctags_thisday = get_channeltags_thisday(ds, ctags, dnum); % subset to channeltags valid +% if isempty(todaysites) +% continue; +% end + +% % subset to sites for which the files pointed to by the +% % wfdisc table, actually exist +% m = listMiniseedFiles(ds, chantag, dnum, dnum+1); +% ctags_thisday = ctags_thisday([m.exists]==2); + + % get timewindow for this day + timewindows = get_timewindow(min([enum dnum+1]), nummins, max([dnum snum])); + + if ~strcmp(PARAMS.runmode,'test') + % loop over timewindows + for count = 1:length(timewindows.start) + this_timewindow.start = timewindows.start(count); + this_timewindow.stop = timewindows.stop(count); + iceweb_helper(paths, PARAMS, newsubnets, this_timewindow, ds, products); + end + end + end + debug.printfunctionstack('<'); +end + + +function iceweb_helper(paths, PARAMS, subnets, tw, ds, products) + debug.printfunctionstack('>'); + + MILLISECOND_IN_DAYS = (1 / 86400000); + + makeSamFiles = false; + makeSoundFiles = true; + + + snum = tw.start; + enum = tw.stop - MILLISECOND_IN_DAYS; % try to skip last sample + + % load state + statefile = sprintf('iceweb_%s_state.mat',subnet); + if exist(statefile, 'file') + load(statefile) + if snum < snum0 + continue + end + end + + % save state + ds0=ds; sites0=sites; snum0=snum; enum0=enum; subnet0 = subnet; + save(statefile, 'ds0', 'sites0', 'snum0', 'enum0', 'subnet0'); + clear ds0 sites0 snum0 enum0 subnet0 + + + + + %% Save raw waveform data to 1 hour MAT file + dv = datevec(snum); + yyyy = dv(1); + jjj = floor(snum - datenum(yyyy,1,1) + 1); + hh = dv(4); + wavematdir = 'wavemat'; + wavrawmat = sprintf('%s/%s/%04d/%03d/%s.%04d.%03d.%02d.raw.mat',wavematdir,subnet,yyyy,jjj,subnet,yyyy,jjj,hh); + if ~exist(wavrawmat,'file') + %% Get waveform data + debug.print_debug(0, sprintf('%s %s: Getting waveforms for %s from %s to %s at %s',mfilename, datestr(utnow), subnet , datestr(snum), datestr(enum))); + w = waveform_wrapper(ds, ChannelTagList, snum, enum); + mkdir(fileparts(wavmatfile)); + disp(sprintf('Saving waveform data to %s',wavrawmat)); + save(wavrawmat); + end + + % continue + + % Save the cleaned waveform data to MAT file + wavcleanmat = sprintf('%s/%s/%04d/%03d/%s.%04d.%03d.%02d.clean.mat',wavematdir,subnet,yyyy,jjj,subnet,yyyy,jjj,hh); + if ~exist(wavcleanmat,'file') + + % Eliminate empty waveform objects + w = waveform_remove_empty(w); + if numel(w)==0 + debug.print_debug(0, 'No waveform data returned - skipping'); + continue + end + + % Clean the waveforms + w = fillgaps(w, 'interp'); + w = detrend(w); + + % Apply calibs which should be stored within sites structure to + % waveform objects to convert from counts to real physical + % units + w = apply_calib(w, sites); + + % Pad all waveforms to same start/end + [wsnum wenum] = gettimerange(w); % assume gaps already filled, signal + w = pad(w, min([snum wsnum]), max([enum wenum]), 0); + + % Apply filter to all signals + w = apply_filter(w, PARAMS); + + mkdir(fileparts(wavmatfile)); + disp(sprintf('Saving waveform data to %s',wavcleanmat)); + save(wavcleanmat); + end + + %% ICEWEB PRODUCTS + + % WAVEFORM PLOT + if products.waveform_plot.doit + close all + plot_panels(w) + [spdir,spbase,spext] = fileparts(spectrogramFilename); + fname = fullfile('plots', 'waveforms', subnet, sprintf('%s.png',datestr(snum,31)) ); + orient tall; + saveImageFile(fname, 72); % this should make directory tree too + end + + % RSAM + if products.rsam.doit + for measure = products.rsam.measures + rsamobj = waveform2rsam(w); + rsamobj.save_to_bob_file(fullfile('data', 'rsam', subnet, 'SSSS.CCC.YYYY.MMMM.bob')); + end + end + + + % CREATE & SAVE HELICORDER PLOT + if products.helicorders.doit + close all + for wi=1:numel(w) + heliplot = helicorder(w(wi),'mpl',3); + build(heliplot) + ct = get(w(wi),'channeltag'); + helicorderFilename = fullfile(spdir, sprintf('heli_%s_%s.%s.%s',spbase,ct.station,ct.channel,spext)); + orient tall; + saveImageFile(helicorderFilename, 72); + clear ct helicorderFilename + end + clear wi + end + + + % PLOT SPECTROGRAMS + if products.spectrograms.doit + debug.print_debug(1, sprintf('Creating %s',spectrogramFilename)) + waveSp = w; + + % restrict spectrogram to Z channels only + w2=[]; + for wi=1:numel(w) + ct=get(w(wi),'channeltag'); + if strfind(ct.channel,'Z') + w2 = [w2 w(wi)]; + end + end + waveSp = w2; + + % split into 10-minute timewindows + for someloopover10mintimewindows + % make 10-minute spectrogram + close all + spectrogramFraction = 0.75; + [sgresult, Tcell, Fcell, Ycell] = spectrogram_iceweb(PARAMS.spectralobject, waveSp, spectrogramFraction, extended_spectralobject_colormap); + clear w2 wi ct + + if sgresult > 0 % sgresult = number of waveforms for which a spectrogram was successfully plotted + % SAVE SPECTROGRAM PLOT TO IMAGE FILE AND CREATE THUMBNAIL + orient tall; + + if saveImageFile(spectrogramFilename, 72) + + fileinfo = dir(spectrogramFilename); % getting a weird Index exceeds matrix dimensions error here. + debug.print_debug(0, sprintf('%s %s: spectrogram PNG size is %d',mfilename, datestr(utnow), fileinfo.bytes)); + + % make thumbnails + makespectrogramthumbnails(spectrogramFilename, spectrogramFraction); + + end + + % save spectral data + if products.spectral_data.doit + frequency_index_divider = 5.0; % Hz + for spi = 1:numel(Fcell) + thisY = Ycell{spi}; + thisF = Fcell{spi}; + thisT = Tcell{spi}; + sta = get(w(spi),'station'); + chan = get(w(spi),'channel'); + + % peakF and meanF for each spectrogram window + [Ymax,imax] = max(thisY); + PEAK_F = thisF(imax); + MEAN_F = (thisF' * thisY)./sum(thisY); + + % frequency index for each spectrogram window + fUpperIndices = find(thisF > frequency_index_divider); + fLowerIndices = find(thisF < frequency_index_divider); + fupper = sum(thisY(fUpperIndices,:)); + flower = sum(thisY(fLowerIndices,:)); + F_INDEX = log2(fupper ./ flower); + + % Peak spectral value in each frequency bin - or in + % each minute? + + % Now downsample to 1 sample per minute + dnum = unique(floorminute(thisT)); + for k=1:length(dnum) + p = find(floorminute(thisT) == dnum(k)); + downsampled_peakf(k) = nanmean(PEAK_F(p)); + downsampled_meanf(k) = nanmean(MEAN_F(p)); + downsampled_findex(k) = nanmean(F_INDEX(p)); + suby = thisY(:,p); + if size(suby,2) == 1 + max_in_each_freq_band(:,k) = suby; + else + max_in_each_freq_band(:,k) = max(suby'); + end + end + + % % Plot for verification + % close all + % subplot(2,1,1),plot(dnum,downsampled_peakf,':');datetick('x'); + % hold on + % plot(dnum,downsampled_meanf);datetick('x'); + % subplot(2,1,2),plot(dnum,downsampled_findex);datetick('x'); + % anykey = input('Press any key to continue'); + + % Save data + r1 = rsam(dnum, downsampled_peakf, 'sta', sta, ... + 'chan', chan, 'measure', 'peakf', ... + 'units', 'Hz', 'snum', min(dnum), 'enum', max(dnum)); + r1.save_to_bob_file(fullfile('spectrograms', subnet, 'SSSS.CCC.YYYY.peakf')) + + r2 = rsam(dnum, downsampled_meanf, 'sta', sta, ... + 'chan', chan, 'measure', 'meanf', ... + 'units', 'Hz', 'snum', min(dnum), 'enum', max(dnum)); + r2.save_to_bob_file(fullfile('spectrograms', subnet, 'SSSS.CCC.YYYY.meanf')) + + r3 = rsam(dnum, downsampled_findex, 'sta', sta, ... + 'chan', chan, 'measure', 'findex', ... + 'units', 'none', 'snum', min(dnum), 'enum', max(dnum)); + r3.save_to_bob_file(fullfile('spectrograms', subnet, 'SSSS.CCC.YYYY.findex')) + + specdatafilename = fullfile('data', 'spectral', subnet, datestr(min(dnum),'yyyy/mm/dd'), sprintf( '%s_%s_%s.mat', datestr(min(dnum),30), sta, chan) ); + specdatadir = fileparts(specdatafilename); % make the directory in case it does not exist + mkdir(specdatadir); % make the directory in case it does not exist + save(specdatafilename, 'dnum', 'max_in_each_freq_band') + clear r1 r2 r3 k p downsampled_peakf downsampled_meanf ... + downsampled_findex fUpperIndices fLowerIndices flower ... + fupper F_INDEX PEAK_F MEAN_F Ymax imax thisY thisF thisT ... + suby max_in_each_freq_band + + end + end + end + end + end + + % SOUND FILES + if products.soundfiles.doit + try + % 20120221 Added a "sound file" like 201202211259.sound which simply records order of stachans in waveform object so + % php script can match spectrogram panel with appropriate wav file + % 20121101 GTHO COmment: Could replace use of bnameroot below with strrep, since it is just used to change file extensions + % e.g. strrep(spectrogramFilename, '.png', sprintf('_%s_%s.wav', sta, chan)) + soundfileroot = fullfile('soundfiles', subnet, datestr(get(w(c),'start'),31), + [dname, bnameroot, bnameext] = fileparts(soundfileroot); + soundfilelist = sprintf('%s.sound',soundfileroot); + fsound = fopen(soundfilelist,'a'); + for c=1:length(w) + soundfilename = fullfile('soundfiles', sprintf('%s_%s_%s.wav',bnameroot, get(w(c),'station'), get(w(c), 'channel') ) ); + fprintf(fsound,'%s\n', soundfilename); + debug.print_debug(0, sprintf('Writing to %s',soundfilename)); + isSuccessful = waveform2sound(w(c), 60, soundfilename) + end + fclose(fsound); + end + end + + debug.printfunctionstack('<'); +end + diff --git a/applications/+iceweb/iceweb_wrapper.m b/applications/+iceweb/iceweb_wrapper.m new file mode 100755 index 0000000..068d51d --- /dev/null +++ b/applications/+iceweb/iceweb_wrapper.m @@ -0,0 +1,68 @@ +function iceweb_wrapper(PRODUCTS_TOP_DIR, subnetName, datasourceObject, ChannelTagList, ... + startTime, endTime) +%ICEWEB_WRAPPER Run IceWeb for long time intervals +% iceweb_wrapper(subnetName, datasourceObject, ChannelTagList, ... +% startTime, endTime) +% +% iceweb_wrapper(...) is a wrapper designed to move sequentially through +% days/weeks/months of data, load waveform data into waveform objects, +% compute various IceWeb products from those waveform objects +% and save data into binary "BOB" files. +% +% Inputs: +% +% subnetName - (string) usually the name of a volcano +% +% datasourceObject - (datasource) tells waveform where to load data +% from (e.g. IRIS, Earthworm, Antelope, Miniseed). +% See WAVEFORM, DATASOURCE. +% +% ChannelTagList - (ChannelTag.array) tells waveform which +% network-station-location-channel combinations to +% load data for. See CHANNELTAG. +% +% startTime - the date/time to begin at in datenum format. See +% DATENUM. +% +% endTime - the date/time to end at in datenum format. See +% DATENUM. +% +% See also: iceweb.iceweb2017 + +% set up products structure for iceweb +products.waveform_plot.doit = true; +products.rsam.doit = true; +products.rsam.samplingIntervalSeconds = [10 60 600]; +products.rsam.measures = {'max';'mean';'median'}; +products.spectrograms.doit = true; +products.spectrograms.timeWindowMinutes = 10; +%products.spectrograms.fmin = 0.5; +products.spectrograms.fmax = 10; % Hz +products.spectrograms.dBmin = 60; % white level +products.spectrograms.dBmax = 120; % pink level +products.spectral_data.doit = true; +products.spectral_data.samplingIntervalSeconds = 60; +products.reduced_displacement.doit = false; +products.reduced_displacement.samplingIntervalSeconds = 60; +products.helicorders.doit = true; +products.helicorders.timeWindowMinutes = 10; +products.soundfiles.doit = true; + +gulpMinutes = products.spectrograms.timeWindowMinutes; + +% call iceweb_wrapper +tic; +iceweb.iceweb2017(PRODUCTS_TOP_DIR, subnetName, datasourceObject, ChannelTagList, startTime, endTime, gulpMinutes, products) +toc + +% % create 24h spectrograms +% if products.spectral_data.doit +% iceweb.make_24h_spectrograms(subnetName, ChannelTagList, startTime, endTime); +% end +% +% % create 24h helicorders +% if products.helicorders.doit +% iceweb.make_24h_helicorders(subnetName, ChannelTagList, startTime, endTime); +% end + +disp('COMPLETED') diff --git a/applications/+iceweb/makespectrogramthumbnails.m b/applications/+iceweb/makespectrogramthumbnails.m old mode 100644 new mode 100755 index 8974aa4..c219419 --- a/applications/+iceweb/makespectrogramthumbnails.m +++ b/applications/+iceweb/makespectrogramthumbnails.m @@ -1,7 +1,5 @@ function makespectrogramthumbnails(spectrogramFilename, spectrogramFraction) -import debug.* -printfunctionstack('>'); -%print_debug('> makespectrogramthumbnails', 2); +debug.printfunctionstack('>'); % figure 1 should be a large spectrogram with traces, cropped nicely. Now remove labels and maximise panels. @@ -23,7 +21,7 @@ function makespectrogramthumbnails(spectrogramFilename, spectrogramFraction) % Move panels for channelNum = 1:numchannels - [spectrogramPosition, tracePosition] = calculatePanelPositions(numchannels, numchannels - channelNum + 1, spectrogramFraction, 0.0, 0.0, 1, 1); + [spectrogramPosition, tracePosition] = iceweb.calculatePanelPositions(numchannels, numchannels - channelNum + 1, spectrogramFraction, 0.0, 0.0, 1, 1); set(ax(channelNum*2 - 1), 'position', tracePosition); set(ax(channelNum*2), 'position', spectrogramPosition); end @@ -33,7 +31,7 @@ function makespectrogramthumbnails(spectrogramFilename, spectrogramFraction) tmpfile = sprintf('%s/%s_labelless%s',tmppath,tmpbase,tmpext); % print large labelless PNG -saveImageFile(tmpfile, 72); +iceweb.saveImageFile(tmpfile, 72); % load then delete temporary file I = imread(tmpfile); @@ -48,8 +46,8 @@ function makespectrogramthumbnails(spectrogramFilename, spectrogramFraction) %thumbnailfile = sprintf('%s/smaller_%s%s',tmppath, tmpbase, tmpext); %imwrite(X,map,thumbnailfile,'PNG'); [X,map] = rgb2ind(imresize(I, [198 151]), 256); -thumbnailfile = sprintf('%s/small_%s%s',tmppath, tmpbase, tmpext); +thumbnailfile = sprintf('%s/%s_thumb%s',tmppath, tmpbase, tmpext); imwrite(X,map,thumbnailfile,'PNG'); close; -printfunctionstack('<'); +debug.printfunctionstack('<'); diff --git a/applications/+iceweb/montserrat_remove_analog_sites.m b/applications/+iceweb/montserrat_remove_analog_sites.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/rsam_wrapper.m b/applications/+iceweb/rsam_wrapper.m new file mode 100755 index 0000000..2fc4555 --- /dev/null +++ b/applications/+iceweb/rsam_wrapper.m @@ -0,0 +1,92 @@ +function rsam_wrapper(TOP_DIR, subnetName, datasourceObject, ChannelTagList, ... + startTime, endTime, gulpMinutes, samplingIntervalSeconds, measures) +%RSAM_WRAPPER Compute RSAM data for long time intervals +% rsam_wrapper(subnetName, datasourceObject, ChannelTagList, ... +% startTime, endTime, gulpMinutes, samplingIntervalSeconds, measures) +% +% rsam_wrapper(...) is a wrapper designed to move sequentially through +% days/weeks/months of data, load waveform data into waveform objects, +% compute RSAM objects from those waveform objects (using waveform2rsam) +% and then save data from those RSAM objects into binary "BOB" files. +% +% rsam_wrapper is actually a driver for iceweb2017. iceweb2017 +% will drive other products such as spectrograms and helicorders if +% asked. But rsam_wrapper asks it only to compute RSAM data. +% +% Inputs: +% +% subnetName - (string) usually the name of a volcano +% +% datasourceObject - (datasource) tells waveform where to load data +% from (e.g. IRIS, Earthworm, Antelope, Miniseed). +% See WAVEFORM, DATASOURCE. +% +% ChannelTagList - (ChannelTag.array) tells waveform which +% network-station-location-channel combinations to +% load data for. See CHANNELTAG. +% +% startTime - the date/time to begin at in datenum format. See +% DATENUM. +% +% endTime - the date/time to end at in datenum format. See +% DATENUM. +% +% gulpMinutes - swallow data in chunks of this size. Minimum is 10 +% minutes, maximum is 2 hours. Other good choices are +% 30 minutes and 1 hour. +% +% samplingIntervalSeconds - compute RSAM with 1 sample from this many +% seconds of waveform data. Usually 60 seconds. +% See also WAVEFORM2RSAM. If multiple measures +% are used (see below) you can give multiple +% sampling intervals, e.g. +% measures = {'mean';'max';'median'} +% samplingIntervalSeconds = [60, 10, 600] +% But if you want them all to be the same, +% you only need pass a scalar. +% +% measures - each RSAM sample is usually the 'mean' of each 60 second +% timewindow. But other stats are probably better. For +% events, 'max' works better. For tremor, 'median' works +% better. To compute multiple versions of RSAM using +% different stats, use a cell array, +% e.g. measures = {'max';'median'}. +% +% Example: +% datasourceObject = datasource('antelope', '/raid/data/sakurajima/db') +% ChannelTagList(1) = ChannelTag('JP.SAKA.--.HHZ'); +% ChannelTagList(2) = ChannelTag('JP.SAKB.--.HHZ'); +% startTime = datenum(2015,5,28); +% endTime = datenum(2015,6,8); +% gulpMinutes = 10; +% samplingIntervalSeconds = 60; +% measures = {'mean'}; +% rsam_wrapper('Sakurajima', datasourceObject, ChannelTagList, ... +% startTime, endTime, gulpMinutes, ... +% samplingIntervalSeconds, measures) +% +% See also: iceweb.iceweb2017 + +if ~isa(measures,'cell') + measures = {measures}; +end + +% set up products structure for iceweb +products.waveform_plot.doit = true; +products.rsam.doit = true; +products.rsam.samplingIntervalSeconds = samplingIntervalSeconds; +products.rsam.measures = measures; +products.spectrograms.doit = false; +products.spectrograms.timeWindowMinutes = 10; +products.spectral_data.doit = false; +products.spectral_data.samplingIntervalSeconds = samplingIntervalSeconds; +products.reduced_displacement.doit = false; +products.reduced_displacement.samplingIntervalSeconds = samplingIntervalSeconds; +products.helicorders.doit = false; +products.helicorders.timeWindowMinutes = []; +products.soundfiles.doit = false; + +% call iceweb_wrapper +iceweb.iceweb2017(TOP_DIR, subnetName, datasourceObject, ChannelTagList, startTime, endTime, gulpMinutes, products) + +disp('COMPLETED') \ No newline at end of file diff --git a/applications/+iceweb/setup.m b/applications/+iceweb/setup.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/show_sites.m b/applications/+iceweb/show_sites.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/spectrogram_iceweb.m b/applications/+iceweb/spectrogram_iceweb.m old mode 100644 new mode 100755 index c4b8e53..86fd190 --- a/applications/+iceweb/spectrogram_iceweb.m +++ b/applications/+iceweb/spectrogram_iceweb.m @@ -1,17 +1,21 @@ -function [result,Tcell,Fcell,Ycell] = spectrogram_iceweb(s, w, spectrogramFraction, mycolormap) +function [result,Tcell,Fcell,Ycell,meanF,peakF] = spectrogram_iceweb(s, w, varargin) % SPECTROGRAM_ICEWEB produce an multi-channel spectrogram plot in the style of % AVO web (IceWeb) spectrograms, by wrapping the default MATLAB spectrogram % function % % Usage: -% [result, Tcell, Fcell, Ycell] =spectrogram_iceweb(s, w, spectrogramFraction, mycolormap) +% [result, Tcell, Fcell, Ycell] =spectrogram_iceweb(s, w, 'spectrogramFraction', 0.75, 'colormap', mycolormap, 'plot_metrics', 0) % % Inputs: % w - a vector of waveform objects % s - a spectralobject % spectrogramFraction - fraction of a panel height the spectrogram should take up (default: 0.8). % The waveform trace takes up the remaining fraction. -% mycolormap - (Optional) a user-defined colormap +% +% Name/Value pairs: +% 'mycolormap' - (Optional) a user-defined colormap +% 'plot_metrics' - superimpose a plot of mean & dominant frequency. +% 0 or 1 (default 0) % % Outputs: % (other than a figure on the screen) @@ -26,11 +30,11 @@ debug.printfunctionstack('>'); - +% varargin +% mycolormap result = 0; - % % reverse the order of the waveforms so they plot top to bottom in same % % order as in mulplt and as in waveform vector % w = fliplr(w); don't need this with calculatePanelPositions2 @@ -39,23 +43,40 @@ if numw==0 return; end -if ~exist('s','var') - s = spectralobject(1024, 924, 10, [60 120]); -end -if ~exist('spectrogramFraction','var') - spectrogramFraction = 1; -end -if ~exist('mycolormap', 'var') - mycolormap = jet; % should be using SPECTRAL_MAP here? -end +% if ~exist('s','var') +% s = spectralobject(1024, 924, 10, [60 120]); +% end +% if ~exist('spectrogramFraction','var') +% spectrogramFraction = 1; +% end +nfft = 1024; +overlap = 924; +fmax = 10; +dbLims = [60 120]; + +p = inputParser; +p.addParameter('spectrogramFraction', 0.75, @isnumeric); +p.addParameter('colormap', jet, @isnumeric); +p.addParameter('plot_metrics', 0, @isnumeric); +p.parse(varargin{:}); +spectrogramFraction = p.Results.spectrogramFraction; + + +% if nargin>=4 +% if strcmp( +% if ~exist('mycolormap', 'var') +% mycolormap = jet; % should be using SPECTRAL_MAP here? +% end %save lastspecgramcall.mat s w spectrogramFraction mycolormap debug.print_debug(2, sprintf('%d waveform objects',numel(w))); -% Default colormap is JET. Override that here. -if exist('mycolormap', 'var') - setmap(s, mycolormap); -end +% % Default colormap is JET. Override that here. +% if exist('mycolormap', 'var') +% setmap(s, mycolormap); +% end + +setmap(s, p.Results.colormap); % To get a colorbar, stop the function here and set colorbar option to vert @@ -63,7 +84,7 @@ nfft = round(get(s,'nfft')); overlap = floor(get(s, 'over')); -dBlims = get(s, 'dBlims') +dBlims = get(s, 'dBlims'); fmax = get(s, 'freqmax'); % Set appropriate date ticks @@ -81,61 +102,81 @@ [S,F,T] = spectrogram(data, nfft, nfft/2, nfft, fsamp); Y = 20*log10(abs(S)+eps); - fmax - max(F) - index = find(F <= fmax) + index = find(F <= fmax); if F(1)==0, F(1)=0.001; end - [spectrogramPosition, tracePosition] = iceweb.calculatePanelPositions(numw, c, spectrogramFraction, 0.08, 0.05, 0.88, 0.95); + [spectrogramPosition, tracePosition] = iceweb.calculatePanelPositions(numw, c, spectrogramFraction, 0.12, 0.05, 0.80, 0.9); axes('position', spectrogramPosition); T = wt.start + T/86400; F = F(1:max(index)); Y = Y(1:max(index),:); S = S(1:max(index),:); - if isempty(dBlims) - imagesc(T,F,abs(S)); - % mean frequency - numerator = abs(S)' * F; - denominator = sum(abs(S),1); - meanF = numerator./denominator'; - hold on; plot(T,meanF,'k','LineWidth',3); - % peak frequency - [maxvalue,maxindex] = max(abs(S)); - size(maxindex) - fmax = F(maxindex); - size(fmax) - hold on; plot(T,fmax,'r','LineWidth',3); + + % mean frequency + %minS = min(min(abs(S)+eps)); + minS = prctile(reshape(S,numel(S),1),40); + S2 = abs(S)-minS; + S2(S2 12) c = c + 1; + if c >= numel(stepMinOptions) + c = numel(stepMinOptions); + break; + end end stepMins = stepMinOptions(c); diff --git a/applications/+iceweb/testday.m b/applications/+iceweb/testday.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/unrest.m b/applications/+iceweb/unrest.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/waveform_remove_empty.m b/applications/+iceweb/waveform_remove_empty.m old mode 100644 new mode 100755 diff --git a/applications/+iceweb/waveform_wrapper.m b/applications/+iceweb/waveform_wrapper.m old mode 100644 new mode 100755 index 170003f..687388f --- a/applications/+iceweb/waveform_wrapper.m +++ b/applications/+iceweb/waveform_wrapper.m @@ -41,7 +41,7 @@ get(chantag(c),'channel') ); chantag(c) = thischantag; else - class(chantag(c)) + class(chantag(c)); end end diff --git a/applications/rockets/ensemble/ensemble_analysis.m b/applications/rockets/ensemble/ensemble_analysis.m new file mode 100644 index 0000000..1e0f8d8 --- /dev/null +++ b/applications/rockets/ensemble/ensemble_analysis.m @@ -0,0 +1,294 @@ +close all +clear all +clc +mkdir('ensemble1') +cd('ensemble1') + +%% 0. have we run this before and saved the data? +if exist('rocketmaster.mat', 'file') + load rocketmaster.mat +else + + %% 1. Define datasources, channels etc + dbpath = '/raid/data/rockets/rocketmaster2'; + ds = datasource('antelope', dbpath); + + %% 2. Load arrival table into an Arrival object + arrivalobj = Arrival.retrieve('antelope', dbpath ); + if isempty(arrivalobj) + error('No arrivals loaded') + end + + %% 3. Add a short waveform for each arrival for arrival waveform metrics + pretrigsecs = 5; posttrigsecs = 90; + arrivalobj = arrivalobj.addwaveforms(ds, pretrigsecs, posttrigsecs); + + %% 4. Compute an amplitude (and other waveform metrics) for each arrival + max_time_diff = 1.0; % max seconds between min and max + arrivalobj = arrivalobj.addmetrics(max_time_diff); + + %% 5. Associate arrivals into events in a Catalog + % 20 seconds seems to be most from BCHH to TANK infrasound + catalogobj = arrivalobj.associate(30*60); + + %% 6. Add waveforms for each event in the Catalog + pretriggersecs = 30; posttriggersecs = 120; + ctag = ChannelTag({'FL.BCHH.*.*'}); + catalogobj = catalogobj.addwaveforms(ds, ctag, pretriggersecs, posttriggersecs); + + %% 7. Add waveform metrics to event waveforms in Catalog + for eventnum = 1:catalogobj.numberOfEvents + w = [catalogobj.waveforms{eventnum}]; + maxTimeDiff = 2.0; + catalogobj.waveforms{eventnum} = addmetrics(w, maxTimeDiff); + end + + %% 8. save the data + save rocketmaster.mat + + %% 9. write Catalog to Antelope db + [dname, dfile] = fileparts(dbpath); + dbpath2 = sprintf('./%s_new',dfile); + catalogobj.write('antelope',dbpath2); + !chmod 775 *_new* *.png + !chgrp seismiclab *_new* + + +end + +%% 10. Make seismograms, spectrograms and sound files +close all +publish = 0; +for eventnum = 2:catalogobj.numberOfEvents + + % get waveform + w = [catalogobj.waveforms{eventnum}]; + thisA = catalogobj.arrivals{eventnum}; + dstr = datestr(min(get(w,'start')),'yyyymmdd.HHMM'); + tstr = sprintf('Event %2d: %s', eventnum, dstr); + + % plot seismograms + plot_panels(w(:),'alignWaveforms',1,'arrivals',thisA); + suptitle(sprintf('Raw waveform plot\n%s',tstr)); + if publish + snapnow; + else + print('-dpng', sprintf('%s_raw_waveform.png',dstr) ); + end + close + + % clean data + w=clean(w); + + % plot spectrograms of infrasound channels + figure + [Tcelli, Fcelli, Ycelli, meanfi, peakfi] = spectrogram(w(1:3), 'spectralobject', spectralobject(1024,1000,100,[30 80]) , 'plot_metrics', 0 ); + suptitle(sprintf('Infrasound Spectrogram\n%s',tstr)); + if publish + snapnow; + else + print('-dpng', sprintf('%s_infrasound_spectrogram.png',dstr) ); + end + close + + % plot spectrograms of seismic channels + figure + [Tcells, Fcells, Ycells, meanfs, peakfs] = spectrogram(w(4:end), 'spectralobject', spectralobject(1024,1000,100,[70 140]), 'plot_metrics', 0 ); + suptitle(sprintf('Seismic Spectrogram\n%s',tstr)); + if publish + snapnow; + else + print('-dpng', sprintf('%s_seismic_spectrogram.png',dstr) ); + end + close + + % make sound files + infrasoundwavname = sprintf('%s_infrasound.wav',dstr ); + waveform2sound(w(3), 30, infrasoundwavname); + seismicwavname = sprintf('%s_seismic.wav',dstr ); + waveform2sound(w(end), 30, seismicwavname); + + % frequency metrics + + % average meanf and peakf + % - infrasound + mfi = mean(cell2mat(meanfi),2); + pfi = mean(cell2mat(peakfi),2); + thisyi = Ycelli{1}; + for count=2:numel(Ycelli) + thisyi = thisyi + Ycelli{count}; + end + thisyi = thisyi / numel(Ycelli); + % - seismic + mfs = mean(cell2mat(meanfs),2); + pfs = mean(cell2mat(peakfs),2); + thisys = Ycells{1}; + for count=2:numel(Ycells) + thisys = thisys + Ycells{count}; + end + thisys = thisys / numel(Ycells); + + % replace small spectrogram amplitudes with NaN + % - infrasound + minyi = min(min(thisyi)); + maxyi = max(max(thisyi)); + yithresh = minyi + (maxyi-minyi) * 0.5; %* 0.675; + mfi(yithresh > max(thisyi)) = NaN; + pfi(yithresh > max(thisyi)) = NaN; + % - seismic + minys = min(min(thisys)); + maxys = max(max(thisys)); + ysthresh = minys + (maxys-minys) * 0.4; % 0.58; + mfs(ysthresh > max(thisys)) = NaN; + pfs(ysthresh > max(thisys)) = NaN; + + % smooth the plots + smoothsize = 20; + mfsi = smooth(mfi, smoothsize); + mfsi(find(isnan(mfi)))=NaN; + pfsi = smooth(pfi, smoothsize); + pfsi(find(isnan(pfi)))=NaN; + mfss = smooth(mfs, smoothsize); + mfss(find(isnan(mfs)))=NaN; + pfss = smooth(pfs, smoothsize); + pfss(find(isnan(pfs)))=NaN; + + % plot + figure + hmfi=plot(Tcelli{1}, mfsi,'k'); + set(hmfi, 'LineWidth',4); + datetick('x') + hold on + hmfs=plot(Tcells{1}, mfss,'b'); + set(hmfs, 'LineWidth',0.4); + datetick('x') + hpfi=plot(Tcelli{1}, pfsi); + set(hpfi, 'LineWidth',3,'Color',[0.8 0.8 0.8]); + datetick('x'); + hpfs=plot(Tcelli{1}, pfss); + set(hpfs, 'LineWidth',0.3,'Color',[0 0 0.8]); + datetick('x'); + ylabel('Frequency (Hz)') + legend('mean F - infrasound','mean F - seismic', 'peak F - infrasound','peak F - seismic'); + set(gca,'XLim',[Tcelli{1}(1) Tcelli{1}(end)], 'YLim', [0 50]); + hold off + suptitle(sprintf('Frequency analysis\n%s',tstr)); + if publish + snapnow; + else + print('-dpng', sprintf('%s_frequency_parameters.png',dstr) ); + end + %input(' to continue') + close + + % frequency to velocity + speed_of_sound = 350; + T=Tcelli{1}; + + [maxf,maxf_index]=max(mfsi); + velocity = speed_of_sound * (maxf./mfsi - 1); + velocity(1:maxf_index)=0; + vals = find(velocity>0); + X1 = velocity(1:max(vals)); + T1 = T(1:max(vals)); + X1(isnan(X1)) = interp1(find(~isnan(X1)), X1(~isnan(X1)), find(isnan(X1)),'linear'); + + [maxf,maxf_index]=max(pfsi); + velocity = speed_of_sound * (maxf./pfsi - 1); + velocity(1:maxf_index)=0; + vals = find(velocity>0); + X2 = velocity(1:max(vals)); + T2 = T(1:max(vals)); + X2(isnan(X2)) = interp1(find(~isnan(X2)), X2(~isnan(X2)), find(isnan(X2)),'linear'); + + figure + subplot(2,1,1) + h1=plot(T1,X1/1000); + set(h1, 'LineWidth',3,'Color',[0 0 0]); + datetick('x') + hold on + h2=plot(T2,smooth(X2/1000)); + set(h2, 'LineWidth',3,'Color',[0.8 0.8 0.8]); + ylabel('Speed (km/s)'); + datetick('x') + + samprate = 1/0.2560; + subplot(2,1,2) + h1=plot(T1,cumsum(X1/1000)/samprate); + set(h1, 'LineWidth',3,'Color',[0 0 0]); + datetick('x') + hold on + subplot(2,1,2) + h2=plot(T2,cumsum(smooth(X2/1000))/samprate); + set(h2, 'LineWidth',3,'Color',[0.8 0.8 0.8]); + ylabel('Distance (km)'); + datetick('x') + %input(' to continue') + suptitle(sprintf('Rocket trajectory\n%s',tstr)); + + + if publish + snapnow; + else + print('-dpng', sprintf('%s_trajectory.png',dstr) ); + end + close + + % envelope plot + figure + ax1=subplot(2,1,1); + plot(smooth(hilbert(clean(w(1:3))),1000),'axeshandle',ax1); + title(sprintf('infrasound amplitude starting at %s',dstr)) + ax2=subplot(2,1,2); + plot(smooth(hilbert(clean(w(4:end))),1000),'axeshandle',ax2); + title(sprintf('seismic amplitude starting at %s',dstr)) + suptitle(sprintf('envelope %s',tstr)); + if publish + snapnow; + else + print('-dpng', sprintf('%s_envelope.png',dstr) ); + end + close + + + + !chmod 775 *.wav *.png + !chgrp seismiclab *.wav *.png + + +end + +%% +plot_waveform_metrics(catalogobj) + + +%% make a plot of 1 infrasound & 1 seismic for each event +winfra=[]; +wseismic=[]; +for eventnum = 1:catalogobj.numberOfEvents + % get waveform + w = [catalogobj.waveforms{eventnum}]; + index1 = find(ismember(get(w,'channel'),'HD1_00')); + index2 = find(ismember(get(w,'channel'),'HHZ_00')); + if eventnum>1 + daysdiff = abs(get(winfra(end),'start') - get(w(index1),'start')); + if daysdiff>1 + winfra = [winfra w(index1)]; + wseismic = [wseismic w(index2)]; + end + else + winfra = w(index1); + wseismic = w(index2); + end + +end +plot_panels(winfra,'alignWaveforms',1); +plot_panels(wseismic,'alignWaveforms',1); + + +%% +plot_average_frequency(wseismic) + +%% + + diff --git a/applications/rockets/ensemble/load_all_rocket_events2.m b/applications/rockets/ensemble/load_all_rocket_events2.m new file mode 100755 index 0000000..7c4b709 --- /dev/null +++ b/applications/rockets/ensemble/load_all_rocket_events2.m @@ -0,0 +1,125 @@ +close all +clear all +clc +mkdir('ensemble1') +cd('ensemble1') + +%% 0. have we run this before and saved the data? +if exist('rocketmaster.mat', 'file') + load rocketmaster.mat +else + + %% 1. Define datasources, channels etc + dbpath = '/raid/data/rockets/rocketmaster2'; + ds = datasource('antelope', dbpath); + + %% 2. Load arrival table into an Arrival object + arrivalobj = Arrival.retrieve('antelope', dbpath); + if isempty(arrivalobj) + error('No arrivals loaded') + end + + %% 3. Add a short waveform for each arrival for arrival waveform metrics + pretrigsecs = 5; posttrigsecs = 90; + arrivalobj = arrivalobj.addwaveforms(ds, pretrigsecs, posttrigsecs); + + %% 4. Compute an amplitude (and other waveform metrics) for each arrival + max_time_diff = 1.0; % max seconds between min and max + arrivalobj = arrivalobj.addmetrics(max_time_diff); + + %% 5. Associate arrivals into events in a Catalog + % 20 seconds seems to be most from BCHH to TANK infrasound + catalogobj = arrivalobj.associate(20); + + %% 6. Add waveforms for each event in the Catalog + pretriggersecs = 30; posttriggersecs = 120; + ctag = ChannelTag({'FL.BCHH.*.*'}); + catalogobj = catalogobj.addwaveforms(ds, ctag, pretriggersecs, posttriggersecs); + + %% 7. Add waveform metrics to event waveforms in Catalog + for eventnum = 1:catalogobj.numberOfEvents + w = [catalogobj.waveforms{eventnum}]; + maxTimeDiff = 2.0; + catalogobj.waveforms{eventnum} = addmetrics(w, maxTimeDiff); + end + + %% 8. save the data + save rocketmaster.mat + + %% 9. write Catalog to Antelope db + [dname, dfile] = fileparts(dbpath); + dbpath2 = sprintf('./%s_new',dfile); + %antelope.dbcp(dbpath, dbpath2); + %% + catalogobj.write('antelope',dbpath2); +end + +%% 10. Make seismograms, spectrograms and sound files +for eventnum = 1:catalogobj.numberOfEvents + % get waveform + w = [catalogobj.waveforms{eventnum}]; + thisA = catalogobj.arrivals{eventnum}; + dstr = sprintf('Event %2d: %s', eventnum, datestr(min(get(w,'start')), 26)); + + % plot seismograms + plot_panels(w(:),'alignWaveforms',1,'arrivals',thisA); + suptitle(dstr); snapnow; close + + % clean data + w=clean(w); + + % plot spectrograms of infrasound channels + figure + spectrogram(w(1:3), 'spectralobject', spectralobject(256,250,100,[30 100]) , 'plot_metrics', 1 ); + suptitle(dstr); + print('-dpng', sprintf('%s_infrasound.png',datestr(get(w(1),'start'))) ); + snapnow; close + + % plot spectrograms of seismic channels + figure + spectrogram(w(4:end), 'spectralobject', spectralobject(256,250,100,[70 140]), 'plot_metrics', 1 ); + suptitle(dstr); + print('-dpng',sprintf('%s_seismic.png',datestr(get(w(1),'start'))) ); + snapnow; close + + % make sound files + infrasoundwavname = sprintf('%s_infrasound.wav',datestr(get(w(1),'start')) ); + waveform2sound(w(3), 30, infrasoundwavname); + seismicwavname = sprintf('%s_seismic.wav',datestr(get(w(4),'start')) ); + waveform2sound(w(end), 30, seismicwavname); + +end + +%% +plot_waveform_metrics(catalogobj) + + +%% make a plot of 1 infrasound & 1 seismic for each event +winfra=[]; +wseismic=[]; +for eventnum = 1:catalogobj.numberOfEvents + % get waveform + w = [catalogobj.waveforms{eventnum}]; + index1 = find(ismember(get(w,'channel'),'HD1_00')); + index2 = find(ismember(get(w,'channel'),'HHZ_00')); + if eventnum>1 + daysdiff = abs(get(winfra(end),'start') - get(w(index1),'start')); + if daysdiff>1 + winfra = [winfra w(index1)]; + wseismic = [wseismic w(index2)]; + end + else + winfra = w(index1); + wseismic = w(index2); + end + +end +plot_panels(winfra,'alignWaveforms',1); +plot_panels(wseismic,'alignWaveforms',1); + + +%% +plot_average_frequency(wseismic) + + + diff --git a/applications/rockets/ensemble/plot_rocket_launch_stats.m b/applications/rockets/ensemble/plot_rocket_launch_stats.m new file mode 100644 index 0000000..4fa1ae4 --- /dev/null +++ b/applications/rockets/ensemble/plot_rocket_launch_stats.m @@ -0,0 +1,55 @@ +%% Import data from spreadsheet +% Script for importing data from the following spreadsheet: +% +% Workbook: /Users/gt/Dropbox/KSC_rocket_launches_2016_onwards_v3.xlsx +% Worksheet: Sheet1 +% +% To extend the code for use with different selected data or a different +% spreadsheet, generate a function instead of a script. + +% Auto-generated by MATLAB on 2017/11/27 16:46:08 + +%% Import the data +[~, ~, raw] = xlsread('/Users/gt/Dropbox/KSC_rocket_launches_2016_onwards_v3.xlsx','Sheet1'); +raw = raw(2:end,:); +raw(cellfun(@(x) ~isempty(x) && isnumeric(x) && isnan(x),raw)) = {''}; +stringVectors = string(raw(:,[3,4,5,8,13])); +stringVectors(ismissing(stringVectors)) = ''; +raw = raw(:,[1,2,6,7,10,11,12,13]); + +%% Replace non-numeric cells with NaN +R = cellfun(@(x) ~isnumeric(x) && ~islogical(x),raw); % Find non-numeric cells +raw(R) = {NaN}; % Replace non-numeric cells + +%% Create output variable +data = reshape([raw{:}],size(raw)); + +%% Create table +cols = table; + +%% Allocate imported array to column variable names +cols.DATE = data(:,1); +cols.TIME = data(:,2); +cols.COMPANY = categorical(stringVectors(:,1)); +cols.ROCKETTYPE = categorical(stringVectors(:,2)); +cols.PAYLOAD = stringVectors(:,3); +cols.MASSt = data(:,3); +cols.SLC = data(:,4); +cols.ORBIT = categorical(stringVectors(:,4)); +cols.RECORDED = data(:,5); +cols.SPACEX = data(:,6); +cols.ALL = data(:,7); +cols.OTHER = data(:,8); + + +%% Clear temporary variables +clearvars data raw stringVectors R; + +%% plot +dnum=cols.DATE-1 + datenum(1899,12,31) + cols.TIME; +figure +plot(dnum,cols.ALL,'b^-',dnum,cols.SPACEX,'g^-',dnum,cols.RECORDED,'r^-',dnum,cols.OTHER,'r*-'); +datetick('x','keeplimits') +ylabel('Cumulative number') +xlabel('Date') +legend('Launches - any', 'Launches - SpaceX', 'Launches recorded', 'Other events recorded') \ No newline at end of file diff --git a/applications/rockets/ensemble/preprocess_rocketmaster.m b/applications/rockets/ensemble/preprocess_rocketmaster.m new file mode 100755 index 0000000..281734d --- /dev/null +++ b/applications/rockets/ensemble/preprocess_rocketmaster.m @@ -0,0 +1,108 @@ +if ismac + % Code to run on Mac plaform + cd('~/Dropbox/scratch_matlab') +elseif isunix + % Code to run on Linux plaform + localgismo + cd('~/Dropbox/scratch_matlab') +elseif ispc + % Code to run on Windows platform +else + disp('Platform not supported') +end + +close all +clear all +clc +mkdir('ensemble1') +cd('ensemble1') + + + +%% 1. Define datasources, channels etc +dbpath = '/Volumes/data/rockets/rocketmaster2'; +ds = datasource('antelope', dbpath); + +if exist('rocketmaster.mat','file') + load rocketmaster.mat +else + + %% 2. Load arrival table into an Arrival object + arrivalobj = Arrival.retrieve('antelope', dbpath); + + %% 3. Add a short waveform for each arrival for arrival waveform metrics + pretrigsecs = 2; posttrigsecs = 4; + arrivalobj = arrivalobj.addwaveforms(ds, pretrigsecs, posttrigsecs); + save rocketmaster.mat + + %% 4. Compute an amplitude (and other waveform metrics) for each arrival + max_time_diff = 1.0; % max seconds between min and max + arrivalobj = arrivalobj.addmetrics(max_time_diff); + + %% 5. Associate arrivals into events in a Catalog + % 20 seconds seems to be most from BCHH to TANK infrasound + catalogobj = arrivalobj.associate(20); + + %% 6. Add waveforms for each event in the Catalog + pretriggersecs = 30; posttriggersecs = 120; % before and after first arrival in event + ctag = ChannelTag({'FL.BCHH.*.*'}); + catalogobj = catalogobj.addwaveforms(ds, ctag, pretriggersecs, posttriggersecs); + + %% 7. Add waveform metrics to event waveforms in Catalog + for eventnum = 1:catalogobj.numberOfEvents + w = [catalogobj.waveforms{eventnum}]; + catalogobj.waveforms{eventnum} = addmetrics(w); + end + + %% 8. save the data + save rocketmaster.mat + +end + +%% 9. write Catalog to Antelope db +[dname, dfile] = fileparts(dbpath); +dbpath2 = sprintf('./%s3',dfile); +if ~exist(dbpath2,'file') + antelope.dbcp(dbpath, dbpath2); % we need a copy of the site tables + catalogobj.write('antelope',dbpath2,'overwrite'); % we can overwrite or append to existing tables +end + +%% 10. List waveform metrics +catalogobj.list_waveform_metrics() + +%% 11. Make seismograms, spectrograms and sound files +for eventnum = 1:catalogobj.numberOfEvents + % get waveform + w = [catalogobj.waveforms{eventnum}]; + thisA = catalogobj.arrivals{eventnum}; + dstr = sprintf('Event %2d: %s', eventnum, datestr(min(get(w,'start')), 26)); + + % plot seismograms + plot_panels(w(:),'alignwaveforms',1,'arrivals',thisA); + suptitle(dstr); snapnow; close + + % clean data + w = fillgaps(w, 'interp'); + w = detrend(w); + + % plot spectrograms of infrasound channels + figure + spectrogram(w(1:3), spectralobject(256,250,100,[30 100]) ); + suptitle(dstr); snapnow; close + + % plot spectrograms of seismic channels + figure + spectrogram(w(4:end), spectralobject(256,250,100,[70 140]) ); + suptitle(dstr); snapnow; close + + % make sound files + infrasoundwavname = sprintf('%s_infrasound.wav',datestr(get(w(3),'start')) ); + waveform2sound(w(3), 30, infrasoundwavname); + seismicwavname = sprintf('%s_seismic.wav',datestr(get(w(3),'start')) ); + waveform2sound(w(end), 30, seismicwavname); + +end + +%% +%plot_waveform_metrics(catalogobj) + diff --git a/applications/rockets/ensemble/rocket_airwave_event_analysis.m b/applications/rockets/ensemble/rocket_airwave_event_analysis.m new file mode 100755 index 0000000..51eca84 --- /dev/null +++ b/applications/rockets/ensemble/rocket_airwave_event_analysis.m @@ -0,0 +1,473 @@ +%% setup +if ~admin.antelope_exists() + addpath('/opt/antelope/5.7') + setup +end + +% run setup_rocket_event_YYYYMMDD.m first +[~,figureOutDirectory] = fileparts(dbpath); +figureOutDirectory = sprintf('%s_v8',figureOutDirectory) +matfilename = fullfile(figureOutDirectory, 'eventmatfile.mat'); +mkdir('.',figureOutDirectory); +diary(fullfile(figureOutDirectory, 'diary.txt')); +diary on + +% in NASA data, the bearing the data is coming from is recorded, so need to +% add 180 to get where it is going +wind_direction = mod(wind_direction_from + 180, 360); +% NASA wind speed is in knots, convert into m/s +wind_speed = wind_speed_knots * 0.514444; + +% coords is copied directly out of Excel and is for seismometer, and then +% RWB infrasound. this is how to convert into channel order +lat = [coords(2:4,1); repmat(coords(1,1),3,1) ]; +lon = [coords(2:4,2); repmat(coords(1,2),3,1) ]; + +%% compute speed of sound based on temperature & rel. humidity +temperatureC = fahrenheit2celsius(temperatureF); +speed_of_sound = computeSpeedOfSound(temperatureC, relativeHumidity); +disp(sprintf('speed of sound at %.1f Celsius and %f percent relative humidity is %.1f',temperatureC, relativeHumidity, speed_of_sound)); +savedata + +%% load waveform data +disp('Loading waveform data...') +w=waveform(ds,scnl,snum,enum); +savedata + +%% Filter waveform data to center around zero +wfilt = butterworthFilter(w, 'h', 0.5, 3) +savedata + +%% plot raw waveform data +if make_figures + plot_panels(w); + outfile = sprintf('%s/waveforms_raw.png',figureOutDirectory) + feval('print', '-dpng', outfile); + close +end + +%% plot filtered waveform data +if make_figures + plot_panels(wfilt); + outfile = sprintf('%s/waveforms_filt.png',figureOutDirectory); + feval('print', '-dpng', outfile); + close +end + +%% compute predicted travel times for infrasound waves based on GPS coords & wind +%% also add lat, lon, distance and bacaz fields to waveform objects +predictedTravelTimes; +savedata + +%% compute Eastings & Northings +[easting,northing]=latlon2eastingsNorthings(source.lat, source.lon, lat, lon) +savedata + +% %% SPACEX ONLY 2016/09/01 override Easting & Northing with data from Laura GPS sruevy, which use WGS84 UTM Zone 17N ellipsoid +% % also see online tools +% source.lon = -80.57719; %-80.57718; +% source.lat = 28.56195; %28.562106; +% +% easting = [461.091 473.067 447.133 465.217 465.217 465.217]; +% northing = [1343.9 1306.23 1320.01 1321.26 1321.26 1321.26]; + +%% plot array map & compute eastings and northings +eventMap; +savedata + +%% Load arrivals +disp('Loading arrivals...') +% add check for arrivals table here +if ~(antelope.dbtable_present(dbpath, 'arrival')) + return +end +arrivals=Arrival.retrieve('antelope', dbpath); +savedata + +%% Subset out N-wave arrivals +disp('Subsetting arrivals...') +arrivals = arrivals.subset('iphase', 'N'); +savedata + +%% Associate events +maxTimeDiffForAssoc = 0.1; +[cobj, numEvents] = arrivals.associate(maxTimeDiffForAssoc); +savedata + +%% Segment event waveforms +pretrigger = 0.2; +posttrigger = 0.3; +wevent = segment_event_waveforms(wfilt, cobj, pretrigger, posttrigger); +savedata + +%% Plot infrasound events +if make_figures + plot_events(wevent, 'waveforms_infrasoundEvent', figureOutDirectory); +end + +%% Make automatic measurements +infrasoundEvent = auto_measure_amplitudes(infrasoundEvent, wevent, 'auto_measure_event1', predicted_traveltime_seconds, figureOutDirectory); +save%data + +%% Let's add snr values too +for c=1:numEvents + infrasoundEvent(c).snr = infrasoundEvent(c).p2p./infrasoundEvent(c).rms; +end + +%% correlate +infrasoundEvent = xcorr3C(wevent, infrasoundEvent, make_figures, figureOutDirectory, pretrigger) +save%data + +%% plot normalized correlation values (3x3 matrix for each event) +a=[infrasoundEvent.maxCorr]; +figure +for row=1:3 + for col=1:3 + ind = (row-1) * 3 + col; + subplot(3,3,ind) + plot(a(ind:9:end),'.') + ylabel('r'); + xlabel('event #');% meanSecsDiff - an array of size N*N where N = number of array +% components. Each element represents mean travel +% time difference between the array elements +% represented by that row and column + end +end +suptitle('Normalized correlation values for infrasound component pairs'); + +%% beamform each event, searching for azimuth & speed +disp('Beamforming to estimate backazimuth of source from array & wave speed') +for eventnum = 1:numel(infrasoundEvent) + [bestbackaz,bestsoundspeed,distanceDiff,speedMatrix] = beamform2d(easting(1:3), northing(1:3), infrasoundEvent(eventnum).secsDiff); +% [bestbackaz,bestsoundspeed,distanceDiff,speedMatrix] = beamform2d(easting(1:3), northing(1:3), infrasoundEvent(eventnum).secsDiff, 0, mean(effective_speed)); +% [bestbackaz,bestsoundspeed,distanceDiff,speedMatrix] = beamform2d(easting(1:3), northing(1:3), infrasoundEvent(eventnum).secsDiff, mean(backaz)); + infrasoundEvent(eventnum).bestbackaz = bestbackaz; + infrasoundEvent(eventnum).bestsoundspeed = bestsoundspeed; + infrasoundEvent(eventnum).distanceDiff = distanceDiff; + infrasoundEvent(eventnum).speedMatrix = speedMatrix; +end +savedata + +%% let's choose events only with high correlation values +meanCorrAll=[infrasoundEvent.meanCorr]; +good = find(meanCorrAll>=0.8); +bestsoundspeedAll=[infrasoundEvent(good).bestsoundspeed]; +bestsoundspeed = median(bestsoundspeedAll); +bestbackazAll=[infrasoundEvent(good).bestbackaz]; +bestbackaz = median(bestbackazAll); +disp(sprintf('Based on %d events with a mean xcorr of %.1f or greater, best backaz = %.1f degrees, best sound speed = %.1f m/s',numel(good),0.8,bestbackaz,bestsoundspeed)); +save%data + +%% compute distance from source to array channels +for chanNum = 1:6 + array_distance_in_km(chanNum) = sqrt(easting(chanNum).^2 + northing(chanNum).^2)/1000.0; +end +save%data + +%% compute reduced Pressure +for c=1:numEvents + infrasoundEvent(c).reducedPressure = infrasoundEvent(c).p2p(1:3).*array_distance_in_km(1:3); +end +save%data + +%% compute energies +densityEarth = 2000; % (kg/m3) sandstone is 2000-2650, limestone 2000, wet sand 1950 +%pWaveSpeed = 2000; % (m/s) sandstone 2000-3500, limestone 3500-6000, wet sand 1500-2000 +densityAir = 1.225; % (kg/m3) +pWaveSpeed = 885; % from onset sub-event +for c=1:numEvents + ev = infrasoundEvent(c); + for chanNum = 1:3 + infrasoundEnergy(chanNum) = 2 * pi * (array_distance_in_km(chanNum).^2 * 1e6) * ev.energy(chanNum) / (densityAir * speed_of_sound); + end + seismicEnergy = sum(ev.energy(4:6)) * 2 * pi * (array_distance_in_km(6).^2 * 1e6) * densityEarth * pWaveSpeed *1e-18; + infrasoundEvent(c).infrasoundEnergy = median(infrasoundEnergy); + infrasoundEvent(c).seismicEnergy = seismicEnergy; +end +save%data + +%% get event times +etime=[infrasoundEvent.FirstArrivalTime]; +etime_good = etime(good); + +%% plot meanSecsDiff vs event time - proof of well correlated events +meanSecsDiff = [infrasoundEvent.meanSecsDiff]; +meanSecsDiff_good = [infrasoundEvent(good).meanSecsDiff]; + +figure +semilogy(etime,meanSecsDiff,'r.'); +hold on; +semilogy(etime_good,meanSecsDiff_good,'kx'); +datetick('x'); +axis tight; +ylabel('Mean Secs Diff'); +xlabel('Time'); +feval('print','-dpng',fullfile(figureOutDirectory,'meansecsdiff.png')); + +%% plot amplitudes vs event time - well correlated events have higher amplitudes +p2p = [infrasoundEvent.p2p]; + +p2p_good = [infrasoundEvent(good).p2p]; +figure +for chanNum = 1:3 + subplot(3,1,chanNum ); + semilogy(etime,p2p(chanNum:6:end),'r.'); + hold on; + semilogy(etime_good,p2p_good(chanNum:6:end),'kx'); + datetick('x'); + axis tight; + if chanNum==2 + ylabel('Pressure change Pa'); + end + if chanNum==3 + xlabel('Time'); + end +end +suptitle('N wave peak-trough amplitude') +feval('print','-dpng',fullfile(figureOutDirectory,'amplitude_infrasound.png')); + + +%% +figure +for chanNum = 4:6 + subplot(3,1,chanNum-3); + semilogy(etime,p2p(chanNum:6:end),'r.'); + hold on; + semilogy(etime_good,p2p_good(chanNum:6:end),'kx'); + datetick('x'); + axis tight; + if chanNum==5 + ylabel('Velocity change nm/s'); + end + if chanNum==6 + xlabel('Time'); + end +end +suptitle('N wave peak-trough amplitude') +feval('print','-dpng',fullfile(figureOutDirectory,'amplitude_seismic.png')); + +%% plot amplitudes vs event time - well correlated events have higher amplitudes +p2p = [infrasoundEvent.p2p]; +p2p_good = [infrasoundEvent(good).p2p]; +figure +p2p1 = p2p(1:6:end); +p2p2 = p2p(2:6:end); +p2p3 = p2p(3:6:end); +p2pbest = median([p2p1; p2p2; p2p3],1); +p2pbestgood = p2pbest(good); +subplot(2,1,1); +semilogy(etime,p2pbest,'kx','MarkerSize',4); +hold on; +semilogy(etime_good,p2pbestgood,'ko','MarkerSize',6,'MarkerFaceColor','k'); +datetick('x'); +axis tight; +ylabel(sprintf('Pressure change\n(Pa)')); +xlabel('Time'); +set(gca,'XLim',[datenum(2016,9,1,13,0,0) datenum(2016,9,1,13,36,0)]); +%suptitle('peak to peak amplitude') +% ylims=get(gca,'YLim'); +% set(gca,'YLim',[ylims(1)/2,ylims(2)*2]); +% yticks=logspace(log10(ylims(1)/2),log10(ylims(2)*2),7); +% set(gca,'YTick',round(yticks)); +set(gca,'YLim',[1 2000]); +set(gca,'YTick',[1 10 100 1000]); + +p2p1 = p2p(4:6:end); +p2p2 = p2p(5:6:end); +p2p3 = p2p(6:6:end); +p2pbest = sqrt(p2p1.^2 + p2p2.^2 + p2p3.^2); +p2pbestgood = p2pbest(good); +subplot(2,1,2); +semilogy(etime,p2pbest,'kx','MarkerSize',4); +hold on; +semilogy(etime_good,p2pbestgood,'ko','MarkerSize',6,'MarkerFaceColor','k'); +datetick('x'); +axis tight; +ylabel(sprintf('Ground velocity\n(nm/sec)')); +xlabel('Time'); +%suptitle('N wave peak-trough amplitude') +set(gca,'XLim',[datenum(2016,9,1,13,0,0) datenum(2016,9,1,13,36,0)]); +set(gca,'YLim',[1e4 1e7]); +feval('print','-dpng',fullfile(figureOutDirectory,'N_wave_amplitude.png')); +%% +figure +for chanNum = 4:6 + subplot(3,1,chanNum-3); + semilogy(etime,p2p(chanNum:6:end),'r.'); + hold on; + semilogy(etime_good,p2p_good(chanNum:6:end),'kx'); + datetick('x'); + axis tight; + if chanNum==5 + ylabel('Velocity change nm/s'); + end + if chanNum==6 + xlabel('Time'); + end +end +suptitle('N wave peak-trough amplitude') +feval('print','-dpng',fullfile(figureOutDirectory,'amplitude_seismic.png')); + + +%% plot signal-to-noise vs event time - not all well correlated events have good SNR (especially seismic) +snrall = [infrasoundEvent.snr]; +snr_good = [infrasoundEvent(good).snr]; + +figure +for chanNum = 1:3 + subplot(3,1,chanNum ); + plot(etime,snrall(chanNum:6:end),'r.'); + hold on; + plot(etime_good,snr_good(chanNum:6:end),'kx'); + datetick('x'); + axis tight; + if chanNum==2 + ylabel('SNR - Infrasound'); + end + if chanNum==3 + xlabel('Time'); + end +end +feval('print','-dpng',fullfile(figureOutDirectory,'snr_infrasound.png')); +%% +figure +for chanNum = 4:6 + subplot(3,1,chanNum-3); + plot(etime,snrall(chanNum:6:end),'r.'); + hold on; + plot(etime_good,snr_good(chanNum:6:end),'kx'); + datetick('x'); + axis tight; + if chanNum==5 + ylabel('SNR - Seismic'); + end + if chanNum==6 + xlabel('Time'); + end +end +feval('print','-dpng',fullfile(figureOutDirectory,'snr_seismic.png')); + +%% Make master event +masterEvent = make_master_event(infrasoundEvent(good)) +savedata + +%% beamform master +[Mbestbackaz,Mbestsoundspeed,MdistanceDiff,MspeedMatrix] = beamform2d(easting(1:3), northing(1:3), masterEvent.secsDiff); +MspeedMatrix +savedata + +%% relative calibrations +figure; +ratio1v3 = p2p_good(1:6:end)./p2p_good(3:6:end); +ratio2v3 = p2p_good(2:6:end)./p2p_good(3:6:end); +subplot(2,1,1) +plot(etime_good, ratio1v3,'x' ); +datetick('x') +ylabel('Amplitude BD1 / BD3') +subplot(2,1,2) +plot(etime_good, ratio2v3,'x' ); +ylabel('Amplitude BD2 / BD3') +datetick('x') +suptitle('Calibrations relative to BD3 - based on good events') +disp(sprintf('Amp ratio of BD1 vs BD3: mean %.3f, median %.3f, stdev %.3f',mean(ratio1v3), median(ratio1v3), std(ratio1v3))); +disp(sprintf('Amp ratio of BD2 vs BD3: mean %.3f, median %.3f, stdev %.3f',mean(ratio2v3), median(ratio2v3), std(ratio2v3))); +feval('print','-dpng',fullfile(figureOutDirectory,'calibrations.png')); + +%% Compute best sound speed given actual coordinates of source and array +for row=1:3 + for col=1:3 + distDiff(row,col) = arclen(row) - arclen(col); + end +end +for c=1:numEvents + d=distDiff./infrasoundEvent(c).secsDiff; + infrasoundEvent(c).apparentSpeed = nanmean(nanmean(d(1:end))); + infrasoundEvent(c).apparentSpeedError = nanstd(d(1:end)); +end +save%data + +%% write events to csv file +writeEvents(fullfile(figureOutDirectory, 'catalog.csv'), infrasoundEvent, array_distance_in_km); + +%% particle motion for all good events +for c=1:numEvents + thisw = wevent{c}; + t = threecomp(thisw([6 5 4])',199); + tr = t.rotate() + tr2 = tr.particlemotion(); + tr2.plotpm() + feval('print','-dpng',fullfile(figureOutDirectory,sprintf('pm_event%03d.png',c))); + close +end + +% %% spectrum for all good events +% for c=1:numEvents +% plot_spectrum(wevent{c}); +% feval('print','-dpng',fullfile(figureOutDirectory,sprintf('spectrum_event%03d.png',c))); +% close +% end + +%% time shift traces & produce a stacked infrasound trace, and save them + + +%% look for P arrival by stacking on ground coupled airwaves for good events +% let's plot the Z channel first for all events with a normalized waveform +% plot + +%% + +if 0 + %% plot largest event + wlargestevent = extract(wfilt, 'time', datenum(2016,9,1,13,7,10), datenum(2016,9,1,13,7,45)); + plot_panels(wlargestevent); + %% + plot_spectrum(wlargestevent([2 6])); + legend('Infrasound 2 (Pa/Hz)','Seismic Z (nm/sec/Hz)','location','south') + grid on + %% + t = threecomp(wlargestevent([6 5 4])',199.5); + tr = t.rotate() + tr2 = tr.particlemotion(); + tr2.plotpm() + + %% plot buildup + wbu = extract(wfilt, 'time', datenum(2016,9,1,13,7,21), datenum(2016,9,1,13,7,28)); + plot_panels(wbu); + plot_spectrum(wbu([2 6])); + legend('Infrasound 2 (Pa/Hz)','Seismic Z (nm/sec/Hz)','location','south') + grid on + t = threecomp(wbu([6 5 4])',199.5); + tr = t.rotate() + tr2 = tr.particlemotion(); + tr2.plotpm() + %% + scroll(wbu,16) + + %% Helicorder plots for whole sequence + plot_helicorder(wfilt(2),'scale',20,'mpl',1); + plot_helicorder(wfilt(6),'scale',20,'mpl',1); + %% Plot ZRT seismogram + t = threecomp(wfilt([6 5 4])',199.5); + tr = t.rotate() + tr2 = tr.particlemotion(); + tr2.plotpm() + %% Plot R helicorder + wzrt=get(tr,'waveform'); + %plot_helicorder(wzrt(2),'scale',10,'mpl',1); + %% + mkdir('sacfiles') + savesac(wzrt(1,1), 'sacfiles', 'wz.sac'); + savesac(wzrt(1,2), 'sacfiles', 'wr.sac'); + savesac(wzrt(1,3), 'sacfiles', 'wt.sac'); + %% + for c=1:numel(wzrt) + 0 + wzrt(1,c) + 1 + ct = get(wzrt(c),'ChannelTag') + 2 + savesac(wzrt(c), 'sacfiles', sprintf('%s.sac',ct.string())); + end + %% +end +diary off \ No newline at end of file diff --git a/applications/rockets/ensemble/rocketsimulation1d.m b/applications/rockets/ensemble/rocketsimulation1d.m new file mode 100644 index 0000000..1aad1a7 --- /dev/null +++ b/applications/rockets/ensemble/rocketsimulation1d.m @@ -0,0 +1,41 @@ +clear all +close all +%% Falcon 9 full thrust +thrust = 7.607e6; % N +m_initial = 549054; % kg, mass of fully laden Falcon 9 +burntime = 162; % s +m_1ststage_empty = 22200; % kg +m_2ndstage_full = 115000; % kg +m_payload = 1700; % kg +m_lost = m_initial - m_1ststage_empty - m_2ndstage_full - m_payload; +k = m_lost / burntime; % kg/s +g = 9.8055; %m/s +b = 0.4 * k; % drag constant, kg/s +v(1)=0; +a(1)=0; +d(1)=0; +m(1)=m_initial; +g(1) = 9.8055; % m/s^2 +R=6.371e6; %m +f(1)=60; % Hz +c(1)=340; % speed of sound, m/s +for t=1:burntime + g(t+1) = g(1) * R^2/(d(t) + R)^2; + m(t+1) = m(t) - k; + drag = b * v(t); + a(t+1) = (thrust - drag) / mean([m(t) m(t+1)]) - mean([g(t) g(t+1)]) ; + v(t+1) = v(t) + a(t); + c(t+1) = max([c(1) - d(t)/500 * 2 300]); % based on tables of elevation, pressure, temperature & c + f(t+1) = f(1) * c(t+1) / (c(t+1) + v(t+1) ); + d(t+1) = d(t) + mean([v(t) v(t+1)]); +end +figure +t=0:burntime; +subplot(2,2,1),plot(t,a); +xlabel('time (s)'); ylabel('acceleration (m/s^2)') +subplot(2,2,2),plot(t,v/1000); +xlabel('time (s)'); ylabel('velocity (km/s)') +subplot(2,2,3),plot(t,d/1000); +xlabel('time (s)'); ylabel('altitude (km)') +subplot(2,2,4),plot(t,f); +xlabel('time (s)'); ylabel('Doppler-shifted frequency (Hz)'); set(gca, 'YLim', [0 f(1)]); \ No newline at end of file diff --git a/applications/rockets/ensemble/rocketsimulation2d.m b/applications/rockets/ensemble/rocketsimulation2d.m new file mode 100644 index 0000000..29118d7 --- /dev/null +++ b/applications/rockets/ensemble/rocketsimulation2d.m @@ -0,0 +1,95 @@ +clear all +close all +%% Falcon 9 full thrust +thrust = 7.607e6; % N +m_initial = 549054; % kg, mass of fully laden Falcon 9 +burntime = 162; % s +m_1ststage_empty = 22200; % kg +m_2ndstage_full = 115000; % kg +m_payload = 1700; % kg +m_lost = m_initial - m_1ststage_empty - m_2ndstage_full - m_payload; +k = m_lost / burntime; % kg/s +b = 0.001 * k; % drag constant, kg/s +vy(1)=0; +vx(1)=0; +v(1)=0; +ay(1)=0; +ax(1)=0; +a(1)=0; +dy(1)=0; +dx(1)=0; +d(1)=0; +m(1)=m_initial; +g(1) = 9.8055; % m/s^2 +R=6.371e6; %m +f(1)=60; % Hz +c(1)=340; % speed of sound, m/s +theta(1)=0; +for t=1:burntime + theta(t+1)=min([t 80]); % thrust angle gradually increases from 0-80 degrees from the vertical + theta(t+1)=27; + g(t+1) = g(1) * R^2 / (dy(t) + R)^2; + m(t+1) = m(t) - k; + drag = b * v(t) * exp(-dy(t)/8000); + cos(deg2rad(theta(t+1))); + thrust - drag; + mean([m(t:t+1)]); + mean([g(t:t+1)]); + disp(drag) + ay(t+1) = cos(deg2rad(theta(t+1))) * (thrust - drag) / mean([m(t:t+1)]) - mean([g(t:t+1)]) ; + ax(t+1) = sin(deg2rad(theta(t+1))) * (thrust - drag) ; + vy(t+1) = vy(t) + mean([ay(t) ay(t+1)]); + vx(t+1) = vx(t) + mean([ax(t) ax(t+1)]); + dy(t+1) = dy(t) + mean([vy(t) vy(t+1)]); + dx(t+1) = dx(t) + mean([vx(t) vx(t+1)]); + a(t+1) = sqrt(ay(t+1)^2 + ax(t+1)^2); + v(t+1) = sqrt(vy(t+1)^2 + vx(t+1)^2); + d(t+1) = sqrt(dy(t+1)^2 + dx(t+1)^2); + + % now compute doppler shifted frequency - for this need to know velocity along the + % angle from launchpad to rocket + c(t+1) = max([c(1) - d(t)/500 * 2 300]); % based on tables of elevation, pressure, temperature & c + alpha(t+1) = atan(dy(t+1)/dx(t+1)); + phi(t+1) = atan(vy(t+1)/vx(t+1)); + vaway(t+1) = cos(alpha(t+1)-phi(t+1)) * v(t+1); + f(t+1) = f(1) - vaway(t+1) / c(t+1); +end +figure(1) +t=0:burntime; +subplot(2,2,1),plot(t,a); +xlabel('time (s)'); ylabel('acceleration (m/s^2)') +subplot(2,2,2),plot(t,v/1000); +xlabel('time (s)'); ylabel('speed (km/s)') +subplot(2,2,3),plot(t,d/1000); +xlabel('time (s)'); ylabel('distance travelled (km)') +subplot(2,2,4),plot(t,f); +xlabel('time (s)'); ylabel('Doppler-shifted frequency (Hz)') + +figure(2) +subplot(3,2,1),plot(t,ax); +xlabel('time (s)'); ylabel('horizontal acceleration (m/s^2)') +subplot(3,2,2),plot(t,ay); +xlabel('time (s)'); ylabel('vertical acceleration (m/s^2)') +subplot(3,2,3),plot(t,vx); +xlabel('time (s)'); ylabel('horizontal speed (m/s)') +subplot(3,2,4),plot(t,vy); +xlabel('time (s)'); ylabel('vertical speed (m/s)') +subplot(3,2,5),plot(t,dx); +xlabel('time (s)'); ylabel('horizontal distance (m)') +subplot(3,2,6),plot(t,dy); +xlabel('time (s)'); ylabel('vertical distance (m)') + +figure(3) +subplot(3,2,1),plot(t,90-theta); +xlabel('time (s)'); ylabel('thrust angle') +subplot(3,2,2),plot(t,rad2deg(phi)); +xlabel('time (s)'); ylabel('velocity angle') +subplot(3,2,3),plot(t,rad2deg(alpha)); +xlabel('time (s)'); ylabel('position angle') +subplot(3,2,4),plot(dx,dy); +xlabel('Horizontal distance'); ylabel('Height'); axis equal; +subplot(3,2,5),plot(t,c); +xlabel('time (s)'); ylabel('Speed of sound (m/s)') +subplot(3,2,6),plot(t,f); +xlabel('time (s)'); ylabel('Doppler-shifted Frequency (Hz)') + diff --git a/applications/rockets/explosion/bacthjob.m b/applications/rockets/explosion/bacthjob.m new file mode 100644 index 0000000..d2d7ab7 --- /dev/null +++ b/applications/rockets/explosion/bacthjob.m @@ -0,0 +1,3 @@ +addpath('/Users/gt/Dropbox/scratch_matlab/RocketSeismology') +sync_spacex_videos_usfdata +sync_youtube_video_usfdata \ No newline at end of file diff --git a/applications/rockets/explosion/plot_main_explosion_signal.m b/applications/rockets/explosion/plot_main_explosion_signal.m new file mode 100644 index 0000000..ee755c6 --- /dev/null +++ b/applications/rockets/explosion/plot_main_explosion_signal.m @@ -0,0 +1,27 @@ +close all + +dbpath = '/raid/data/rockets/rocketmaster2'; +%dbpath = '/raid/data/rockets/rocketmaster'; +ds = datasource('antelope', dbpath); +snum=datenum(2016,9,1,13,7,0); +enum = snum + 1/1440; +scnl = scnlobject('BCHH', '*', 'FL'); +wf = clean(waveform(ds, scnl, snum, enum)); +%% +plot_panels(wf) +suptitle('1st stage explosion infrasound') +%% +wf2=extract(wf,'time',snum+10/86400,snum+19.4/86400); +plot_panels(wf2); +suptitle('2nd stage explosion infrasound') +%% +wf3=extract(wf,'time',snum+10/86400,snum+15.8/86400); +plot_panels(wf3); +suptitle(sprintf('before 2nd stage explosion infrasound. \nseismic? infrasound precursor?')) +%% +wf4=extract(wf,'time',snum+15.8/86400,snum+16.3/86400); +plot_panels(wf4); +suptitle('2nd stage explosion infrasound zoom') + +%% +load_all_rocket_events2 \ No newline at end of file diff --git a/applications/rockets/explosion/read_spacex_video.m b/applications/rockets/explosion/read_spacex_video.m new file mode 100644 index 0000000..f42982e --- /dev/null +++ b/applications/rockets/explosion/read_spacex_video.m @@ -0,0 +1,21 @@ +vidObj = VideoReader('f9-29_ne_twr.mov'); +save vidfile.mat vidObj +%% +% Specify that reading should start at 0.5 seconds from the +% beginning. +%vidObj.CurrentTime = 0.5; + +% Create an axes +currAxes = axes; +figure +fnum = 1; +while hasFrame(vidObj) + vidFrame = readFrame(vidObj); + %image(vidFrame, 'Parent', currAxes); + fnum + imagesc(vidFrame) + %stop + %currAxes.Visible = 'off'; + pause(2.0/vidObj.FrameRate); + fnum=fnum+1; +end diff --git a/applications/rockets/explosion/setup_rocket_event_20160901.m b/applications/rockets/explosion/setup_rocket_event_20160901.m new file mode 100755 index 0000000..c07cf05 --- /dev/null +++ b/applications/rockets/explosion/setup_rocket_event_20160901.m @@ -0,0 +1,36 @@ +% waveform data parameters +%clear all +close all +%startup +make_figures = true; + +% Geographical coordinates +% coords = ... +% [28.574013, -80.57236; +% 28.574182, -80.57241; +% 28.573894, -80.572352; +% 28.574004, -80.572561]; +coords = ... + [28.5740171611, -80.5723755; + 28.5742216111, -80.5724168556; + 28.5738811889, -80.572295775; + 28.5740064361, -80.57256045]; + + +% SLC40 - SpaceX launch complex +% source.lat = 28.562106; +% source.lon = -80.57718; +source.lon = -80.57719; %-80.57718; +source.lat = 28.56195; %28.562106; + +easting = [461.091 473.067 447.133 465.217 465.217 465.217]; +northing = [1343.9 1306.23 1320.01 1321.26 1321.26 1321.26]; +% Wind tower data - could read this from Excel instead +% get Excel file from +relativeHumidity = 89.5; % percent from NASA weather tower data +temperatureF = 80.65; % 80 Fahrenheit according to weather tower data from NASA +wind_direction_from = 144; % degrees - this is the direction FROM according to NASA, see Lisa Huddleston email of Oct 10th +wind_speed_knots = 5.5; % knots + +% now call +rocket_airwave_event_analysis \ No newline at end of file diff --git a/applications/rockets/explosion/spaceXplosion.m b/applications/rockets/explosion/spaceXplosion.m new file mode 100755 index 0000000..d6a0392 --- /dev/null +++ b/applications/rockets/explosion/spaceXplosion.m @@ -0,0 +1,486 @@ +%% SPACEXPLOSION Analyze the SpaceX explosion that occurred on 1 Sep 2016 +% All supporting functions are in the infrasoundGT directory (or GISMO) +% + +%% setup +clear all +close all +matfile = '/Users/glennthompson/Dropbox/Rockets/analysis/20160901_SpaceXplosion/explosion2.mat'; +if exist(matfile,'file') + load(matfile); +else + + % waveform data parameters + %ds = datasource('antelope', '/raid/data/rockets/dbspacexplosion'); + ds = datasource('antelope', '/Users/glennthompson/Dropbox/Rockets/db/20160901_explosion'); + snum=datenum(2016,9,1,13,0,0); + enum = snum + 1/24; + scnl = scnlobject('BCHH', '*', 'FL'); + + % Geographical coordinates + % %%%%%%%%%%%%% SCAFFOLD - could load these from Antelope or Excel + lat = [28.574182 28.573894 28.574004 28.574013 28.574013 28.574013]; + lon = [-80.572410 -80.572352 -80.572561 -80.572360 -80.572360 -80.572360]; + source.lat = 28.562106; % SLC40 - SpaceX launch complex + source.lon = -80.57718; + % Wind tower data - could read this from Excel instead + relativeHumidity = 92; % percent from NASA weather tower data + temperatureF = 80; % 80 Fahrenheit according to weather tower data from NASA + wind_direction_from = 150; % degrees - this is the direction FROM according to NASA, see Lisa Huddleston email of Oct 10th + wind_direction = mod(wind_direction_from + 180, 360); + wind_speed_knots = 10; % knots + wind_speed = wind_speed_knots * 0.514444; % m/s + % rmpath(genpath('/raid/apps/src/GISMO')) + + %% compute speed of sound based on temperature & rel. humidity + temperatureC = fahrenheit2celsius(temperatureF); + speed_of_sound = computeSpeedOfSound(temperatureC, relativeHumidity); + disp(sprintf('speed of sound at %.1f Celsius and %f percent relative humidity is %.1f',temperatureC, relativeHumidity, speed_of_sound)); + + %% load waveform data + disp('Loading waveform data...') + w=waveform(ds,scnl,snum,enum); + + save(matfile); +end + +make_figures = true +%addpath('infrasoundGT') +figureOutDirectory = '20160901_results'; +mkdir('.',figureOutDirectory); + + +save(matfile) + +%% plot raw waveform data +if make_figures + figure + plot_panels(w); + outfile = sprintf('%s/waveforms_raw.png',figureOutDirectory); + feval('print', '-dpng', outfile); + close +end + +%% compute predicted travel times for infrasound waves based on GPS coords & wind +%% also add lat, lon, distance and bacaz fields to waveform objects +disp('Predicting travel times based on GPS coordinates and wind vector...') +fprintf('\n_______________________________________________\n'); +fprintf('PREDICTED TRAVEL TIME BASED ON:\n'); +fprintf(' sound speed %.1fm/s\n', speed_of_sound); +fprintf(' wind speed %.1fm/s\n', wind_speed); +fprintf(' wind direction %.1f degrees\n', wind_direction); +fprintf('------\t--------\t-----------\t----------\n'); +fprintf('Channel\tDistance\tBackAzimuth\tTravelTime\n'); +fprintf('------\t--------\t-----------\t----------\n'); +for c=1:length(lat) + [arclen(c), backaz(c)] = distance(lat(c), lon(c), source.lat, source.lon, 'degrees'); + arclen(c) = deg2km(arclen(c))*1000; + effective_speed = speed_of_sound + wind_speed * cos(deg2rad( (180+backaz(c)) - wind_direction) ); + predicted_traveltime_seconds(c) = arclen(c)/effective_speed; + fprintf('%s\t%.1fm\t\t%.1f degrees\t%.3fs\n',get(w(c),'channel'), arclen(c), backaz(c), predicted_traveltime_seconds(c)); + w(c) = addfield(w(c), 'lat', lat(c)); + w(c) = addfield(w(c), 'lon', lon(c)); + w(c) = addfield(w(c), 'distance', arclen(c)); + w(c) = addfield(w(c), 'backaz', backaz(c)); + +end +fprintf('_______________________________________________\n'); +fprintf('Program name: %s\n',mfilename('fullpath')) +save spacexplosion.mat + + +%% plot array map & compute eastings and northings +if make_figures + disp('Plotting array map') + close all + deg2m = deg2km(1) * 1000; + cols = 'rwbggg'; + for c=1:length(lat) + chan = get(w(c),'channel'); + easting(c) = distance(lat(c), lon(c), lat(c), source.lon) * deg2m; + northing(c) = distance(lat(c), lon(c), source.lat, lon(c)) * deg2m; + plot(easting(c),northing(c),'o','MarkerFaceColor',cols(c),'MarkerSize',10) + hold on + quiver(easting(c),northing(c),-easting(c)/100,-northing(c)/100,0); % /100 just gives arrow length + text(easting(c)+1,northing(c),chan(1:3)); + end + grid on + quiver(440,1325,wind_speed*sin(deg2rad(wind_direction)), wind_speed*cos(deg2rad(wind_direction)) ,0,'k'); + text(440,1325,'wind') + hold off + title('Beach House array position relative to SLC40'); + xlabel('metres east'); + ylabel('metres north'); + axis equal; + outfile = sprintf('%s/arraymap.png',figureOutDirectory); + feval('print', '-dpng', outfile); + close + save spacexplosion.mat +end + +%% Load arrivals +disp('Loading arrivals...') +arrivals=Arrival.retrieve('antelope', '/raid/data/rockets/dbspacexplosion'); +save spacexplosion.mat + +%% Subset out X1 arrivals +disp('Subsetting arrivals...') +arrivals = arrivals.subset('iphase', 'X1'); +save spacexplosion.mat + +%% Associate events +disp('Associating arrivals into events...') +maxTimeDiff = 1; % seconds +eventOn = false; +eventNumber = 0; +for c=2:numel(arrivals.daynumber) + if arrivals.daynumber(c-1) + maxTimeDiff/86400 > arrivals.daynumber(c) + if ~eventOn % start new event + eventOn = true; + eventNumber = eventNumber + 1; + infrasoundEvent(eventNumber).FirstArrivalTime = arrivals.daynumber(c-1); + else % event already in progress + end + infrasoundEvent(eventNumber).LastArrivalTime = arrivals.daynumber(c); + else + if eventOn % write out last event + eventOn = false; + end + end +end +numEvents = numel(infrasoundEvent); +save spacexplosion.mat + + +%% Filter waveform data to center around zero +disp('Filtering waveform data...') +wfilt = detrend(w); +f=filterobject('h',[10],3); +wfilt=filtfilt(f,wfilt); +save spacexplosion.mat + +%% Segment event waveforms +pretrigger = 1; +posttrigger = 1; +wevent = segment_event_waveforms(wfilt, infrasoundEvent, pretrigger, posttrigger); +save spacexplosion.mat + +%% Plot infrasound events +if make_figures + plot_events(wevent, 'waveforms_infrasoundEvent', figureOutDirectory); +end + +%% correlate +% loop through infrasound channels +% take a 0.3-second snippet starting 0.1s before FirstArrivalTime, till 0.2s +% after it +% correlate this against the whole wevent for each infrasound +% trace +% this should result in a correlation matrix +% from this record the time lag matrix for each infrasound channel against each other +% infrasound channel +disp('CORRELATION ...') +disp('_______________') +%infrasoundEvent = xcorr3C(wevent, infrasoundEvent, make_figures, figureOutDirectory, pretrigger); +infrasoundEvent = xcorr3C(wevent, infrasoundEvent, false, figureOutDirectory, pretrigger); + +% for eventNumber=1:numEvents +% fprintf('- processing event %d of %d\n', eventNumber, numEvents); +% haystacks = wevent{eventNumber}; +% infrasoundEvent(eventNumber).maxCorr = eye(3); +% infrasoundEvent(eventNumber).secsDiff = eye(3); +% precorrtime = 0.1; % NEEDLE seconds of data to add before first arrival +% postcorrtime = 0.2; % NEEDLE seconds of data to add after first arrival +% for chanNumber=1:3 +% needle = extract(haystacks(chanNumber), 'time', infrasoundEvent(eventNumber).FirstArrivalTime-precorrtime/86400, infrasoundEvent(eventNumber).FirstArrivalTime+postcorrtime/86400); +% for haystackNum = 1:3 +% fprintf(' - looking for needle %d in haystack %d\n', chanNumber, haystackNum); +% haystack = haystacks(haystackNum); +% [acor,lag] = xcorr(get(needle,'data'),get(haystack,'data')); +% [m,I] = max(abs(acor)); +% infrasoundEvent(eventNumber).maxCorr(chanNumber,haystackNum) = m; +% infrasoundEvent(eventNumber).secsDiff(chanNumber,haystackNum) = lag(I)/get(haystack,'freq') + pretrigger - precorrtime; +% if make_figures +% figure; +% subplot(3,1,1),plot(haystack); +% subplot(3,1,2),plot(needle); +% subplot(3,1,3),plot(lag,acor); +% outfile = sprintf('figs/xcorr_infrasoundEvent%03d_%d_%d.png',eventNumber,chanNumber,haystackNum); +% feval('print', '-dpng', outfile); +% close +% end +% end +% end +% end +save spacexplosion.mat + +%% Construct a master infrasound event, from all the individual ones +disp('Constructing master infrasound event from individual event statistics') +masterEvent.FirstArrivalTime = infrasoundEvent(1).FirstArrivalTime; +masterEvent.LastArrivalTime = infrasoundEvent(1).LastArrivalTime; + +% find the mean xcorr time lag difference for non-identical infrasound components - it should be close to zero if only one event in time window +% e.g. needle 1 and haystack 2 should have same magnitude but opposite sign +% time delay to needle 2 and haystack 1 if there is only one clear N wave +% in the haystacks +disp('- finding events with a mean time lag difference of close to 0 - these are the events we can use') +indexes = find(abs([infrasoundEvent.meanSecsDiff]) < 0.01); % these are events with probably only one event in wevent time window +fprintf('- found %d events we can use\n', numel(indexes)); +disp('- event indexes to use'); +disp(indexes) + +masterEvent.secsDiff = zeros(3,3); +masterEvent.stdSecsDiff = zeros(3,3); +for row=1:3 + for column=1:3 + a = []; + for eventNumber = indexes + thisEvent = infrasoundEvent(eventNumber); + a = [a thisEvent.secsDiff(row, column)]; + end + + % eliminate any events which have a difference from the mean of + % greater than the standard deviation + diffa = abs( (a-mean(a))/std(a) ); + + % now set the mean and std for the master event + masterEvent.secsDiff(row, column) = mean(a(diffa<1.0)); + masterEvent.stdSecsDiff(row, column) = std(a(diffa<1.0)); + end +end +disp(' - mean:'); +disp(masterEvent.secsDiff) +disp(' - std:') +disp(masterEvent.stdSecsDiff) +disp(' - fractional std:') +disp(masterEvent.stdSecsDiff ./ masterEvent.secsDiff) + +%% compute sound speed based on GPS coordinates and master event differential travel times +disp('- Estimating sound speed for each component pair using GPS coordinates') +clear speed +speed = zeros(3,3)*NaN; +for row=1:3 + for column = 1:3 + if row ~= column + radialDistanceDifference = ( get(w(row),'distance') - get(w(column),'distance') ); + timeDifference = masterEvent.secsDiff(row, column); + s = radialDistanceDifference / timeDifference; + disp(sprintf('row %d column %d distance difference %.1f time difference %.4f speed %.1fm/s',row,column,radialDistanceDifference,timeDifference,s)); + speed(row,column) = s; + end + end +end +speed +meanspeed = nanmean(nanmean(abs(speed))); +stdspeed = nanstd(nanstd(abs(speed))); +fprintf('- mean sound speed %.1f, std sound speed %.1f\n', meanspeed, stdspeed); +save spacexplosion.mat + +%% beamforming +disp('Beamforming to estimate backazimuth of source from array') +[bestbackaz,bestsoundspeed,distanceDiff,speedMatrix] = beamform2(easting(1:3), northing(1:3), masterEvent.secsDiff, 199.0, 348.6); +distanceDiff +speedMatrix +sourceDist = get(w(2),'distance'); +[beamformingsourcelat, beamformingsourcelon] = reckon(lat(2), lon(2), km2deg(sourceDist/1000), bestbackaz); +distFromSLC40 = deg2km(distance(beamformingsourcelat, beamformingsourcelon, source.lat, source.lon)) * 1000; +disp(sprintf('- source location estimated to be lat = %.4f lon = %.4f, which is %1.fm from true position',beamformingsourcelat, beamformingsourcelon,distFromSLC40)); + +%% +if make_figures + outfile = sprintf('%s/beamforming.png',figureOutDirectory); + feval('print', '-dpng', outfile); + close +end +save spacexplosion.mat + +%% estimate travel times from cross-correlation derived differential travel times and actual source location, wind speed, and predicted sound speed +[minimumPredictedTravelTime,index] = min(predicted_traveltime_seconds); +for component=1:3 + traveltime_secs(component) = minimumPredictedTravelTime + mean([masterEvent.secsDiff(component,index) -masterEvent.secsDiff(index,component)]); +end +traveltime_secs(4) = minimumPredictedTravelTime + (get(w(4),'distance') - get(w(index),'distance')) / bestsoundspeed; +traveltime_secs(5) = traveltime_secs(4); +traveltime_secs(6) = traveltime_secs(4); +save spacexplosion.mat + +%% Shift waveforms based on travel times +wshift = wfilt; +for c=1:numel(wshift) + starttime = get(w(c), 'start'); + newstarttime = starttime - traveltime_secs(c)/86400; + disp(sprintf('moving channel %d from %s to %s\n', c, datestr(starttime, 'HH:MM:SS.FFF'), datestr(newstarttime, 'HH:MM:SS.FFF'))); + wshift(c)=set(wshift(c),'start', newstarttime); + disp(sprintf('%s\n',datestr(get(wshift(c),'start')))); +end +save spacexplosion.mat + +%% Segment time shifted event waveforms +disp('Segmenting traveltime-corrected event waveforms...') +arrivalTimeCorrection = minimumPredictedTravelTime; +preplot = 0.15; +postplot = 0.15; +weventshift = segment_event_waveforms(wshift, infrasoundEvent, preplot, postplot, arrivalTimeCorrection); +save spacexplosion.mat + + +%% Plot shifted events +if make_figures + plot_events(weventshift, 'waveforms_infrasoundEvent_shifted', figureOutDirectory) +end + + + +%% Pick events +close all +for eventNumber=1:numEvents + + % plot waveforms for this event + w2=weventshift{eventNumber}; + fh=plot_panels(w2); + ah=get(fh,'Children'); + set(fh, 'Position', [0 0 1600 1000]); + infrasoundEvent(eventNumber).maxAmp=zeros(1,6); + infrasoundEvent(eventNumber).minAmp=zeros(1,6); + infrasoundEvent(eventNumber).maxTime=zeros(1,6); + infrasoundEvent(eventNumber).minTime=zeros(1,6); + + for chanNum=1:6 + + % scan over this event on this channel and find the greatest + % max/min difference in 0.1s + y = get(w2(chanNum),'data'); + fs = get(w2(chanNum),'freq'); + numSamples = length(y); + windowSize = round(fs/25); + maxA = 0; + for startSamp = 1:windowSize:round(numSamples*0.7)-windowSize + samples = startSamp:startSamp+windowSize-1; + [maxy, maxindex] = max(y(samples)); + [miny, minindex] = min(y(samples)); + if (maxy-miny) > maxA + maxSecs = ((maxindex+samples(1)-1)/fs); + minSecs = ((minindex+samples(1)-1)/fs); + maxA = maxy-miny; + + infrasoundEvent(eventNumber).maxTime(chanNum) = tstart + maxSecs/86400; + infrasoundEvent(eventNumber).minTime(chanNum) = tstart + minSecs/86400; + infrasoundEvent(eventNumber).maxAmp(chanNum) = maxy; + infrasoundEvent(eventNumber).minAmp(chanNum) = miny; + end + end + + % plot max & min + axisnum = 8 - chanNum; + axes(ah(axisnum)); + hold on + plot(ah(axisnum),maxSecs, infrasoundEvent(eventNumber).maxAmp(chanNum), 'g*'); + plot(ah(axisnum),minSecs, infrasoundEvent(eventNumber).minAmp(chanNum), 'r*'); + + end + feval('print', '-dpng', sprintf('%s/picked_event_%03d',figureOutDirectory, eventNumber) ); + close +end +save spacexplosion.mat + +%% Compute relative calibrations of infrasound sensors +clc +tolerance = 0.02; % times have to be within 2 hundreds of a second +component_pairs = [ [1,3]; [1,2]; [2,3] ]; + +for component_pair_num = 1:length(component_pairs) + chanNum = nan(2,1); + chanNum(1) = component_pairs(component_pair_num, 1); + chanNum(2) = component_pairs(component_pair_num, 2); + maxA = nan(2, numEvents); + minA = nan(2, numEvents); + for eventNum = 1:numEvents + ev = infrasoundEvent(eventNum); + if std(ev.maxTime) < tolerance/86400 & std(ev.minTime) < tolerance/86400 + for c=1:2 + maxA(c, eventNum) = ev.maxAmp(chanNum(c)); + minA(c, eventNum) = ev.minAmp(chanNum(c)); + end + end + end + avA = (maxA - minA)/2; + + % first compute raw ratio, mean and std + ratio = avA(1,:)./avA(2,:); + m=nanmean(ratio); + s=nanstd(ratio); + + % now recompute after throwing out any measurement that has an error larger + % than the standard deviation + ratio2 = ratio(ratio>m-s & ratio endtime + endtime = thistime; + end + save( fullfile(OUTDIR,'matfiles',sprintf('%s.%s.mat',d(c).name,datestr(thistime,'HHMMSS.FFF'))), 'thisframe'); + if mod(k,FPS)==0 + fprintf('%d ',k/FPS); + end + k = k+1; + end +end +%% +clear s c k crap thisframe thistime + +%% Define the figure panel setup +close all +vw = v.Width/2; +vh = v.Height/2; +imacaspectratio=16/9; +figureHeight=1152; % previously figure was 1360 x 1160 +figureWidth=figureHeight*imacaspectratio; % important for iMovie +fh=figure('Units','pixels','Position',[10 10 figureWidth figureHeight]); +spacer = 40; +ax(1)=axes('Units','pixels','Position',[spacer (spacer+vh)*2-spacer/2 vw vh]); +ax(2)=axes('Units','pixels','Position',[spacer*1.5+vw (spacer+vh)*2-spacer/2 vw vh]); +ax(3)=axes('Units','pixels','Position',[spacer (spacer+vh)*1 vw vh]); +ax(4)=axes('Units','pixels','Position',[spacer*1.5+vw (spacer+vh)*1 vw vh]); +for c=1:4 + set(ax(c),'XTick',[],'YTick',[]); +end +ax(5)=axes('Units','pixels','Position',[spacer (spacer+vh)*0.55 vw*2+spacer/2 vh*0.45]); +ax(6)=axes('Units','pixels','Position',[spacer spacer vw*2+spacer/2 vh*0.45]); +% time label +%ax(7) = axes('Units','pixels','Position',[vw-spacer spacer+vh*2 spacer*4 spacer],'Visible','off'); +ax(7) = axes('Units','pixels','Position',[spacer*2 vh spacer*4 spacer],'Visible','off'); + +%% Load the seismic and infrasound data corresponding to the time window of the video files +dbpath = '/Volumes/data/rockets/rocketmaster'; +ds = datasource('antelope', dbpath); +snum=datenum(2016,9,1,13,07,06.167); +enum=datenum(2016,9,1,13,07,31.433); +chantag=ChannelTag('FL.BCHH.*.*') +w=waveform(ds,chantag,snum,enum) +w=clean(w); + +%% plot the seismic and infrasound data in panels 6 and 5 respectively +ph1=plot(ax(5),get(w(1),'timevector'),get(w(1),'data')); +xlim(ax(5),[snum enum]); +datetick(ax(5),'x','keeplimits') +ylabel(ax(5),'Pa') +pylims=get(ax(5),'YLim'); +ph2=plot(ax(6),get(w(6),'timevector'),1e-6*get(w(6),'data')); +xlim(ax(6),[snum enum]); +datetick(ax(6),'x','keeplimits') +ylabel(ax(6),'mm/s'); +sylims=get(ax(6),'YLim'); + + + +%% Now loop over time from first frame to final frame +realstart = min(starttime); +kmax = floor((endtime - realstart) * SECONDS_PER_DAY * FPS) +for k=1:kmax + realtime = realstart + (k-1)/FPS/SECONDS_PER_DAY; + + % draw line to mark where on seismic and infrasound traces the video + % frames being shown right now are + lh1=line(ax(5),[realtime realtime], pylims,'LineWidth',2,'Color','k'); + lh2=line(ax(6),[realtime realtime], sylims,'LineWidth',2,'Color','k'); + infratime = realtime + 4/SECONDS_PER_DAY; + if infratime < endtime + lh3=line(ax(5),[infratime infratime], pylims,'LineWidth',2,'Color','r'); + lh4=line(ax(6),[infratime infratime], sylims,'LineWidth',2,'Color','r'); + end + uistack(ph1); + uistack(ph2); + + % add time + th1=text(ax(7),0,0,datestr(realtime,'HH:MM:SS.FFF'),'FontSize',32); + + % find matfiles (i.e. frames) for this time sample + dstr = datestr(realtime,'HHMMSS.FFF'); + filepattern = fullfile(OUTDIR,'matfiles',sprintf('*%s.mat',dstr)); + df=dir(filepattern); + if numel(df)>0 + for c=1:numel(df) + load(fullfile(df(c).folder,df(c).name)); + fparts = split(df(c).name,'.mov'); + + % decide which panel they belong to based on which video they + % came from + switch fparts{1} + case 'f9-29_ne_twr', posn=2; + case 'f9-29_nw_ptz', posn=1; + case 'f9-29_ucs3', posn=3; + case 'f9-29_west_fixed', posn=4; + otherwise + posn=0; + end + + % plot the frame in the appropriate panel corresponding to posn + % variable + if posn>0 + disp(sprintf('%s %s.%02d %d',dstr,datestr(realtime,'HHMMSS'), round(mod(realtime*SECONDS_PER_DAY,1)*30)+1, df(c).name, posn)); + image(ax(posn),thisframe) + title(ax(posn),fparts{1},'Interpreter','None'); + set(ax(posn),'XTick',[],'YTick',[]); + end + end + + % now all panels are plotted, save the figure window as a new jpeg + % file + jpgfile = fullfile(OUTDIR,'images',sprintf('%s.jpg',dstr)); + disp(jpgfile) + print('-djpeg',jpgfile); + end + + % delete the lines we drew to mark the time on the seismic and + % infrasound panels + delete(lh1) + delete(lh2) + if infratime < endtime + delete(lh3) + delete(lh4) + end + delete(th1) +end + +%% Write video file from the JPG images - no longer need to use ImageJ which never seems to export all the images +d=dir(fullfile(OUTDIR,'images','*.jpg')); +v=VideoWriter(fullfile(OUTDIR,'uncompressed.avi'),'Uncompressed AVI') +open(v) +for c=1:numel(d) + disp(sprintf('Processing frame %d of %d',c,numel(d))) + a=imread(fullfile(d(c).folder,d(c).name)); + writeVideo(v,a); +end +close(v); + +%% create wav audio files for infrasound and seismic traces +fmmod_waveform(w(1),16,fullfile(OUTDIR,'infra.wav')); +fmmod_waveform(w(6),20,fullfile(OUTDIR,'seismic.wav')); + + +%% +% QUICK TIME then be used to compress the 15 GB AV file to a < 1 GB .mov +% file +% iMovie can then be used to combine the .mov and .wav files, and write +% them back out to a new .mov file. + +%% --------------------- FUNCTIONS FOLLOW ----------------------------- %% +function w2=fmmod_waveform(w,n,outfile) + w=interp_waveform(w,n); + for c=1:numel(w) + thisw=w(c); + fs=get(thisw,'freq') + fc=fs*0.4; + thisw=taper(normalize(detrend(thisw)),0.1); + x=get(thisw,'data'); + y=fmmod(x,fc,fs,fc/4); + w2(c)=thisw; + w2(c)=set(w2(c),'data',y); + %sound(y,fs) + audiowrite(outfile,y,fs); + end +end + +function w2=interp_waveform(w,n) + SECONDS_PER_DAY = 86400; + for c=1:numel(w) + x=get(w(c),'data'); + fs=get(w(c),'freq'); + t=get(w(c),'timevector'); + t2=t(1)+(1/SECONDS_PER_DAY)*(0:1/fs/n:(length(t)-1)*1/fs); + x2=interp1(t,x,t2); + w2(c) = w(c); + w2(c)=set(w2(c),'data',x2); + w2(c)=set(w2(c),'freq',fs*n); + end +end + + + diff --git a/applications/rockets/explosion/sync_youtube_video_usfdata.m b/applications/rockets/explosion/sync_youtube_video_usfdata.m new file mode 100644 index 0000000..60a09fa --- /dev/null +++ b/applications/rockets/explosion/sync_youtube_video_usfdata.m @@ -0,0 +1,379 @@ +% +Improvements: +1. on zoomed plots, add 5-s to time on xticks +2. move to red lines to where they would be based on speed of sound travel time, so marks line up on each infrasound component + + + +%% This is where all output will go +OUTDIR='~/Movies/RocketSeismology/SpaceXplosion'; +mkdir(fullfile(OUTDIR,'ymatfiles')); +mkdir(fullfile(OUTDIR,'yimages')); +SECONDS_PER_DAY = 86400; + +%% locate the video clips from SpaceX and provide their start times (all different) +fname = fullfile(OUTDIR,'SpaceXStaticFireAnomalyAMOS_6_Youtube.mov'); +FPS=30; % frames per second +starttime = datenum(2016,9,1,13,06,00)+(1-1)/FPS/SECONDS_PER_DAY; + +%% loop over each file, importing each frame and saving it as a mat-file +start_recording_time = datenum(2016,9,1,13,07,05); +end_recording_time = datenum(2016,9,1,13,07,45); +endtime = 0; + +v=VideoReader( fname ); +d=dir(fullfile(OUTDIR,'ymatfiles')); +if numel(d)==0 + s = struct('cdata',zeros(v.Height,v.Width,3,'uint8'),... + 'colormap',[]); + k = 1; + while hasFrame(v) & endtime < end_recording_time + s(k).cdata = readFrame(v); + thisframe = s(k).cdata; + thistime = starttime + (k-1)/FPS/SECONDS_PER_DAY; + if thistime > endtime + endtime = thistime; + end + if thistime >= start_recording_time + %save( fullfile(OUTDIR,'ymatfiles',sprintf('%s.mat',datestr(thistime,'HHMMSS.FFF'))), 'thisframe'); + end + if mod(k,FPS)==0 + fprintf('%d ',k/FPS); + end + k = k+1; + end + + %% + clear s c k crap thisframe thistime +end + +%% Define the figure panel setup +close all +rf = 0.5; % reduction factor +vw = v.Width * rf; +vh = v.Height *rf; +spacer = 40 ;%* rf; +imacaspectratio=16/9; +traceHeight = 200 *rf; +figureHeight = vh + 2 * traceHeight + 4 * spacer; +figureWidth = figureHeight * imacaspectratio; +zoomspacer = traceHeight * 0.6; +zoombottom = spacer*3+traceHeight*2; +zoomtraceHeight = traceHeight * 0.45; +fh=figure('Units','pixels','Position',[10 10 figureWidth figureHeight]); % figure + +ax(1)=axes('Units','pixels','Position',[spacer*1.0 spacer*3+traceHeight*2 vw vh]); % video +set(ax(1),'XTick',[],'YTick',[]); +ax(2)=axes('Units','pixels','Position',[spacer*2.0 spacer*2+traceHeight vw-spacer traceHeight]); % overall infrasound trace +ax(3)=axes('Units','pixels','Position',[spacer*2.0 spacer vw-spacer traceHeight]); % overall seismic trace +% time label +ax(4) = axes('Units','pixels','Position',[vw*.85-spacer spacer*2.5+traceHeight*2 spacer*4 spacer],'Visible','off'); % time stamp +ax(5) = axes('Units','pixels','Position',[3*spacer+vw zoombottom+zoomspacer*5+spacer/2 figureWidth-spacer*4-vw zoomtraceHeight]); % infra 1 +ax(6) = axes('Units','pixels','Position',[3*spacer+vw zoombottom+zoomspacer*4+spacer/2 figureWidth-spacer*4-vw zoomtraceHeight]); % infra 2 +ax(7) = axes('Units','pixels','Position',[3*spacer+vw zoombottom+zoomspacer*3+spacer/2 figureWidth-spacer*4-vw zoomtraceHeight]); % infra 3 +ax(8) = axes('Units','pixels','Position',[3*spacer+vw zoombottom+zoomspacer*2 figureWidth-spacer*4-vw zoomtraceHeight]); % seismic Z +ax(9) = axes('Units','pixels','Position',[3*spacer+vw zoombottom+zoomspacer*1 figureWidth-spacer*4-vw zoomtraceHeight]); % seismic R +ax(10) = axes('Units','pixels','Position',[3*spacer+vw zoombottom+zoomspacer*0 figureWidth-spacer*4-vw zoomtraceHeight]); % seismic T +ax(11) = axes('Units','pixels','Position',[3*spacer+vw spacer*1.5 figureWidth-spacer*4-vw traceHeight*2]); % rectilinearity +%ax(12) = axes('Units','pixels','Position',[spacer+vw+spacer spacer figureWidth-spacer*3-vw traceHeight]); % ?? + +%% Load the seismic and infrasound data corresponding to the time window of the video files +windowlength = 5; % time (s) for showing in the zoomed plots +dbpath = '/Volumes/data/rockets/rocketmaster2'; +ds = datasource('antelope', dbpath); +chantag=ChannelTag('FL.BCHH.*.*') +w=waveform(ds,chantag,start_recording_time,end_recording_time+windowlength/SECONDS_PER_DAY); +if isempty(w) + error('no waveform data') +end +w=clean(w); + +%% Rotate seismogram +figure +t = threecomp(w([6 5 4])',199.5); +tr = t.rotate() +tr2 = tr.particlemotion(); +%tr2.plotpm() +wzrt=get(tr,'waveform'); + + +%% plot the seismic and infrasound data in panels 3 & 2 respectively +ph1=plot(ax(2),get(w(1),'timevector'),get(w(1),'data'),'LineWidth',2); % infrasound trace +xlim(ax(2),[start_recording_time end_recording_time]); +xticks = start_recording_time:5/86400:end_recording_time;%-5/86400; +yticks = [-200:200:800]; +ylim(ax(2),[-200 800]); +set(ax(2),'XTick',xticks,'YTick',yticks); +datetick(ax(2),'x','keepticks') +ylabel(ax(2),'Pa') +text(ax(2),0.1,0.9,'infraBSU 1','FontSize',20,'Units','normalized'); +pylims=get(ax(2),'YLim'); +ph2=plot(ax(3),get(wzrt(1),'timevector'),1e-6*get(w(6),'data'),'LineWidth',2); % vertical seismic trace +xlim(ax(3),[start_recording_time end_recording_time]); +yticks = [-3:1:3]; +ylim(ax(3),[-3 3]); +set(ax(3),'XTick',xticks,'YTick',yticks); +datetick(ax(3),'x','keepticks') +ylabel(ax(3),'mm/s'); +text(ax(3),0.1,0.9,'seismic vertical','FontSize',20,'Units','normalized'); +sylims=get(ax(3),'YLim'); + + + +%% Now loop over time from first frame to final frame +kmax = floor((end_recording_time - start_recording_time) * SECONDS_PER_DAY * FPS); +for k=1:kmax + realtime = start_recording_time + (k-1)/FPS/SECONDS_PER_DAY; + disp(sprintf('Processing frame %d of %d, corresponding to %s',k,kmax,datestr(realtime))); + + % draw line to mark where on seismic and infrasound traces the video + % frames being shown right now are + lh1=line(ax(2),[realtime realtime], pylims,'LineWidth',3,'Color','k'); + lh2=line(ax(3),[realtime realtime], sylims,'LineWidth',3,'Color','k'); + infratime = realtime + 4/SECONDS_PER_DAY; % predicted airwave time if something happens at realtime + if infratime < end_recording_time + windowlength/SECONDS_PER_DAY + lh3=line(ax(2),[infratime infratime], pylims,'LineWidth',3,'Color','r'); + lh4=line(ax(3),[infratime infratime], sylims,'LineWidth',3,'Color','r'); + end + seismictime = realtime + 1/SECONDS_PER_DAY; % predicted P wave time if something happens at realtime + if seismictime < end_recording_time + windowlength/SECONDS_PER_DAY + lh5=line(ax(3),[seismictime seismictime], sylims,'LineWidth',3,'Color','g'); + end + uistack(ph1); + uistack(ph2); + + % add time + th1=text(ax(4),0,0,datestr(realtime,'HH:MM:SS.FFF'),'FontSize',28); + + % add lines to these plots + startsecs = (realtime - start_recording_time) * SECONDS_PER_DAY; + for axnum=5:11 + %hold(ax(axnum), 'on') + lh6(axnum)=line(ax(axnum), [startsecs startsecs], ylim(ax(axnum)),'LineWidth',2,'Color','k'); + lh7(axnum)=line(ax(axnum),[startsecs+4 startsecs+4], ylim(ax(axnum)),'LineWidth',2,'Color','r'); + end + for axnum=8:11 + lh8(axnum)=line(ax(axnum),[startsecs+1 startsecs+1], ylim(ax(axnum)),'LineWidth',2,'Color','g'); + end + + % add zoomed infrasound traces + xlims = SECONDS_PER_DAY * ([realtime - start_recording_time realtime + windowlength/SECONDS_PER_DAY - start_recording_time]); + wiz = extract(w(1:3),'time',realtime,realtime+windowlength/SECONDS_PER_DAY); + t = ( get(wiz(1), 'timevector') - start_recording_time) * 86400; + + hold(ax(5),'on') + hi1=plot(ax(5), t, get(wiz(1), 'data'),'b'); + set(ax(5),'XLim',xlims); + set(hi1,'LineWidth',2) + title(ax(5),'Zoomed infrasound plots'); + ylabel(ax(5),'Pa'); + set(ax(5),'XTickLabel',{}) + %legend(ax(5),hi1,'HD1'); + + hold(ax(6),'on') + hi2=plot(ax(6), t, get(wiz(2), 'data'),'b'); + set(hi2,'LineWidth',2) + set(ax(6),'XLim',xlims); + set(ax(6),'XTickLabel',{}) + ylabel(ax(6),'Pa'); + %legend(ax(6),hi2,'HD2'); + + hold(ax(7),'on') + hi3=plot(ax(7), t, get(wiz(3), 'data'),'b'); + set(hi3,'LineWidth',2) + set(ax(7),'XLim',xlims); + ylabel(ax(7),'Pa'); + %legend(ax(7),hi3,'HD3'); + + % add zoomed seismic traces + wsz = extract(wzrt,'time',realtime,realtime+windowlength/SECONDS_PER_DAY); + + hold(ax(8),'on') + hs1=plot(ax(8), t, get(wsz(1), 'data')/1000,'b'); + set(hs1,'LineWidth',2) + set(ax(8),'XLim',xlims); + ylabel(ax(8),'\mum/s'); + title(ax(8),'Zoomed seismic plots') + text(ax(8),0.1,0.9,'vertical','Units','normalized','FontSize',18) + set(ax(8),'XTickLabel',{}) + %legend(ax(8),hs1,'HHZ'); + + hold(ax(9),'on') + hs2=plot(ax(9), t, get(wsz(2), 'data')/1000,'b'); + set(hs2,'LineWidth',2) + set(ax(9),'XLim',xlims); + ylabel(ax(9),'\mum/s'); + text(ax(9),0.1,0.9,'radial','Units','normalized','FontSize',18) + set(ax(9),'XTickLabel',{}) + %legend(ax(9),hs2,'HHR'); + + hold(ax(10),'on') + hs3=plot(ax(10), t, get(wsz(3), 'data')/1000,'b'); + set(ax(10),'XLim',xlims); + set(hs3,'LineWidth',2) + ylabel(ax(10),'\mum/s'); + text(ax(10),0.1,0.9,'transverse','Units','normalized','FontSize',18) + %legend(ax(10),hs3,'HHT'); + xlabel('Seconds'); + + % rectilinearity plot + wsz2 = extract(w([6 5 4]),'time',realtime,realtime+windowlength/SECONDS_PER_DAY); + tc = threecomp(wsz2',199.5); + tc1 = tc.rotate(); + tc2 = tc1.particlemotion(); + rl=get(tc2, 'rectilinearity'); + pl=get(tc2, 'planarity'); + tv = get(rl,'timevector'); + tv = (tv - start_recording_time)*SECONDS_PER_DAY; + + hold(ax(11),'on') + hrl=plot(ax(11), tv,get(rl,'data'),'m-','LineWidth',2); + text(ax(11),0.1,0.9,'rectilinearity','Color','m','Units','normalized','FontSize',18) + hold(ax(11),'on'); + hpl=plot(ax(11), tv, get(pl,'data'),'c-','LineWidth',2); + text(ax(11),0.5,0.9,'planarity','Color','c','Units','normalized','FontSize',18) + xlabel(ax(11),'Seconds'); + set(ax(11),'XLim',xlims); + %legend(ax(11),[hrl hpl],'Rectilinearity','Planarity'); + title(ax(11),''); + set(ax(11),'XGrid','on','YLim',[0 1]) + hold(ax(11),'off') + +% +% uistack(hi1); +% uistack(hi2); +% uistack(hi3); +% uistack(hs1); +% uistack(hs2); +% uistack(hs3); +% uistack(hrl); +% uistack(hpl); + + + % make sure there are no extra figures - there should just be 1 - these + % can otherwise mean printing the wrong figure to jpg/png + hfigs = findobj('type','figure'); + nfigs = length(hfigs); + if nfigs>1 + for fignum=2:nfigs + close(fignum) + end + end + + % find matfiles (i.e. frames) for this time sample + dstr = datestr(realtime,'HHMMSS.FFF'); + filepattern = fullfile(OUTDIR,'ymatfiles',sprintf('*%s.mat',dstr)); + df=dir(filepattern); + if numel(df)>0 + load(fullfile(df(1).folder,df(1).name)); + + + % plot the frame in the appropriate panel corresponding to posn + % variable + disp(sprintf('%s %s.%02d',dstr,datestr(realtime,'HHMMSS'), round(mod(realtime*SECONDS_PER_DAY,1)*30)+1, df(1).name)); + image(ax(1),thisframe) + set(ax(1),'XTick',[],'YTick',[]); + + % set font sizes + set(0,'DefaultAxesFontSize',16); + + % now all panels are plotted, save the figure window as a new jpeg + % file + + jpgfile = fullfile(OUTDIR,'yimagesjpg',sprintf('%s.jpg',dstr)); + disp(jpgfile) + print('-djpeg','-f1',jpgfile); + pngfile = fullfile(OUTDIR,'yimagespng',sprintf('%s.png',dstr)); + disp(pngfile) + print('-dpng','-f1',pngfile); + end + + % delete the lines we drew to mark the time on the seismic and + % infrasound panels + delete(lh1) + delete(lh2) + if infratime < end_recording_time + windowlength/SECONDS_PER_DAY + delete(lh3) + delete(lh4) + end + if seismictime < end_recording_time + windowlength/SECONDS_PER_DAY + delete(lh5) + end + set(th1,'String',' : : . ', 'FontSize',32); + delete(th1) + delete(lh6) + delete(lh7) + delete(lh8) + delete(hrl) + delete(hpl) + + % set hold off + for axnum=5:11 + hold(ax(axnum),'off'); + end +end + +%% Write video file from the JPG images - no longer need to use ImageJ which never seems to export all the images +d=dir(fullfile(OUTDIR,'yimagespng','*.png')); +v=VideoWriter(fullfile(OUTDIR,'yuncompressed2.avi'),'Uncompressed AVI') +open(v) +skippedframes = 0; +savedframes = 0; +for c=1:numel(d) + disp(sprintf('Processing frame %d of %d',c,numel(d))) + a=imread(fullfile(d(c).folder,d(c).name)); + if size(a)==[1500 2667 3] + writeVideo(v,a); + savedframes = savedframes + 1; + else + skippedframes = skippedframes + 1; + disp(sprintf('- wrong size. frame is %dx%dx%d',size(a))); + end + disp(sprintf('- Skipped %d, Saved %d', skippedframes, savedframes)); +end +close(v); + +%% create wav audio files for infrasound and seismic traces +fmmod_waveform(w(1),16,fullfile(OUTDIR,'yinfra.wav')); +fmmod_waveform(w(6),20,fullfile(OUTDIR,'yseismic.wav')); + + +%% +% QUICK TIME then be used to compress the 15 GB AV file to a < 1 GB .mov +% file +% iMovie can then be used to combine the .mov and .wav files, and write +% them back out to a new .mov file. + +%% --------------------- FUNCTIONS FOLLOW ----------------------------- %% +function w2=fmmod_waveform(w,n,outfile) + w=interp_waveform(w,n); + for c=1:numel(w) + thisw=w(c); + fs=get(thisw,'freq') + fc=fs*0.4; + thisw=taper(normalize(detrend(thisw)),0.1); + x=get(thisw,'data'); + y=fmmod(x,fc,fs,fc/4); + w2(c)=thisw; + w2(c)=set(w2(c),'data',y); + %sound(y,fs) + audiowrite(outfile,y,fs); + end +end + +function w2=interp_waveform(w,n) + SECONDS_PER_DAY = 86400; + for c=1:numel(w) + x=get(w(c),'data'); + fs=get(w(c),'freq'); + t=get(w(c),'timevector'); + t2=t(1)+(1/SECONDS_PER_DAY)*(0:1/fs/n:(length(t)-1)*1/fs); + x2=interp1(t,x,t2); + w2(c) = w(c); + w2(c)=set(w2(c),'data',x2); + w2(c)=set(w2(c),'freq',fs*n); + end +end + + + diff --git a/applications/rockets/infrasoundgt/.dropbox.attr b/applications/rockets/infrasoundgt/.dropbox.attr new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/applications/rockets/infrasoundgt/.dropbox.attr @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/addtoplotpanels.m b/applications/rockets/infrasoundgt/addtoplotpanels.m new file mode 100755 index 0000000..6b4e33f --- /dev/null +++ b/applications/rockets/infrasoundgt/addtoplotpanels.m @@ -0,0 +1,28 @@ +% THIS CODE IS FOR PLOTTING METRICS ON WAVEFORM +% THIS COULD BE ABSORBED INTO PLOT PANELS +% for arrivalnum=1:numel(arrivalobj.amp) +% fprintf('.'); +% thisA = arrivalobj.subset(arrivalnum); +% thisW = detrend(fillgaps(w(arrivalnum),'interp')); % make sure there is no trend or offset +% +% % plot waveform for arrival +% fh=plot_panels(thisW, false, thisA); +% ah=get(fh,'Children'); +% set(fh, 'Position', [0 0 1600 1000]); +% hold on +% plot(maxSecs, misc_fields.maxAmp(arrivalnum), 'g*'); +% plot(minSecs, misc_fields.minAmp(arrivalnum), 'r*'); +% teststr = sprintf('maxTime = %s, minTime = %s, timeDiff = %.3f s\namp = %.2e, maxAmp = %.2e, minAmp = %.2e\n rms = %.2e, energy = %.2e', ... +% datestr(maxTime,'HH:MM:SS.FFF'), ... +% datestr(minTime,'HH:MM:SS.FFF'), ... +% 86400*(maxTime-minTime), ... +% amp, ... +% maxAmp, ... +% minAmp, ... +% stdev, ... +% energy); +% text(0.1, 0.1, teststr, 'units', 'normalized') +% dummy=input('Any key to continue'); +% close +% +% end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/analyze_infrasound_event.m b/applications/rockets/infrasoundgt/analyze_infrasound_event.m new file mode 100755 index 0000000..5c8e93e --- /dev/null +++ b/applications/rockets/infrasoundgt/analyze_infrasound_event.m @@ -0,0 +1,247 @@ +function infrasoundEvent = analyze_infrasound_event(dbpath, infrasoundEvent) +%analyze_infrasound_event Perfrom a suite of analyses on a single +%infrasound event recorded on an infrasound array (or network of arrays) +% +% results = analyze_infrasound_event(dbpath, infrasoundEvent) given a CSS3.0 +% database path containing a wfdisc table, attempt to load waveform data +% for the event and perform additional analyses. + + %% load waveform data from an infrasound array + disp('Loading waveform data...') + ds = datasource('antelope', dbpath); + pretrigger = 10; % seconds before first arrival time + posttrigger = 10; % seconds after last arrival time + snum = infrasoundEvent.firstArrivalTime - pretrigger/86400; + enum = infrasoundEvent.lastArrivalTime + posttrigger/86400; + ctag = unique([arrivals.channelinfo]); + event_waveform_vector = waveform(ds,ctag,snum,enum); + numInfrasoundChannels = numel(w); + + %% high-pass filter + event_waveform_vector = butterworthFilter(event_waveform_vector, 'h', 0.5, 3); % highpass at 0.5 Hz + infrasoundEvent.waveform_vector = detrend(event_waveform_vector); + clear event_waveform_vector + + %% cross-correlate to find best time delays + infrasoundEvent = xcorr_all_components(w, infrasoundEvent, pretrigger); + + %% solve for best fitting direction & sound speed given time delays and coordinates + infrasoundEvent = solve_for_direction(infrasoundEvent, easting(1:numInfrasoundChannels), northing(1:numInfrasoundChannels)); + +% %% solve for best distance and elevation in best fitting direction +% infrasoundEvent = solve_for_location(infrasoundEvent, easting(1:numInfrasoundChannels), northing(1:numInfrasoundChannels)); + + %% compute amplitude, energy, frequency waveform parameters + % compute distance from source to array channels + for chanNum = 1:numInfrasoundChannels + array_distance_in_km(chanNum) = sqrt(easting(chanNum).^2 + northing(chanNum).^2)/1000.0; + end + %infrasoundEvent = auto_measure_amplitudes(infrasoundEvent, wevent, 'auto_measure_event1', predicted_traveltime_seconds, figureOutDirectory); + infrasoundEvent.reducedPressure = infrasoundEvent.p2p(1:numInfrasoundChannels).*array_distance_in_km(1:numInfrasoundChannels); + infrasoundEvent = compute_energy(infrasoundEvent, array_distance_in_km) +end + +%% +function infrasoundEvent = xcorr_all_components(w, infrasoundEvent, pretrigger) +%xcorr_all_components Cross-correlate an event recorded on n infrasound components +% infrasoundEvent = xcorr_all_components(infrasoundEvent) +% Input: +% wevent - a cell array where each component is a vector of n +% waveform objects, 1 per infrasound channel +% infrasoundEvent is a structure containing two elements: +% FirstArrivalTime +% LastArrivalTime +% make_figures - if true, a figure is generated for each xcorr pair +% +% Output: +% infrasoundEvent with some additional elements added +% maxCorr - a nxn array of the maximum cross correlation values +% secsDiff - a nxn array of the time lags corresponding to +% maxCorr +% meanSecsDiff - the mean of secsDiff for non-diagonal components +% +% each component is cross correlated with each component, hence nxn + + disp('CORRELATION ...') + disp('_______________') + haystacks = w; + numchannels = numel(w); + infrasoundEvent.maxCorr = eye(3); + infrasoundEvent.secsDiff = eye(3); + precorrtime = 0.1; % NEEDLE seconds of data to add before first arrival + postcorrtime = 0.2; % NEEDLE seconds of data to add after first arrival + for chanNumber=1:numchannels + % needle has length precorrtime + postcorrtime, starting at + % FirstArrivalTime - precorrtime + needle = extract(haystacks(chanNumber), 'time', infrasoundEvent.FirstArrivalTime-precorrtime/86400, infrasoundEvent.FirstArrivalTime+postcorrtime/86400); + needle_data = detrend(get(needle, 'data')); + for haystackNum = 1:numchannels + fprintf(' - looking for needle %d in haystack %d\n', chanNumber, haystackNum); + % haystack is a whole waveform + haystack = haystacks(haystackNum); + haystack_data = get(haystack,'data'); + [acor,lag] = xcorr(needle_data, haystack_data); + cxx0 = sum(abs(needle_data).^2); + cyy0 = sum(abs(haystack_data).^2); + scale = sqrt(cxx0*cyy0); + acor = acor./scale; + [m,I] = max(abs(acor)); + infrasoundEvent.maxCorr(chanNumber,haystackNum) = m; + infrasoundEvent.secsDiff(chanNumber,haystackNum) = lag(I)/get(haystack,'freq') + pretrigger - precorrtime; + end + end + infrasoundEvent.meanCorr = mean(infrasoundEvent.maxCorr(:)); + infrasoundEvent.stdCorr = std(infrasoundEvent.maxCorr(:)); + infrasoundEvent.meanSecsDiff = mean(infrasoundEvent.secsDiff(:)); + infrasoundEvent.stdSecsDiff = std(infrasoundEvent.secsDiff(:)); +end + + +%% +function infrasoundEvent = solve_for_direction(infrasoundEvent, easting, northing, fixbackaz, fixspeed) +%SOLVE_FOR_DIRECTION compute back azimuth of source from travel time differences +%between each component. Plane waves are assumed (i.e. source at infinite +%distance). 2D assumes flat topography, does not search over a vertical +%incident angle. +% +% infrasoundEvent = solve_for_direction(infrasoundEvent, easting, northing) +% For each possible back azimuth, compute the distances between array +% components resolved in that direction. +% Based on differential travel times (meanSecsDiff), compute +% speedMatrix. Take average and stdev of speedMatrix, and compute +% fractional deviation. +% Choose the best back azimuth (bestbackaz) as the back azimuth for which the fractional +% deviation is least. Return this and the mean speed (bestspeed). +% +% Inputs: +% infrasoundEvent - a structure for this infrasound event, which +% contains secsDiff matrix (delay times between +% components) +% easting, northing - GPS coordinates of array components +% +% Outputs: +% bestbackaz - back azimuth of the source that best fits inputs +% bestspeed - pressure wave speed across array that best fits inputs +% +% infrasoundEvent = solve_for_direction(infrasoundEvent, easting, northing, fixbackaz) +% fixbackaz - fix the back azimuth to this value +% Only iterate from fixbackaz-1 to fixbackaz+1, rather than from +% 0.1 to 360. +% +% +% infrasoundEvent = solve_for_direction(infrasoundEvent, easting, northing, 0, fixspeed) +% fixspeed - return the back azimuth that best fits this speed. + + meanSecsDiff = infrasoundEvent.secsDiff; + bestbackaz = NaN; + bestspeed = NaN; + + % First we use travel time ratios to find back azimuthal angle of the beam + % this means we do not need to know speed + N=numel(easting); + if numel(northing)~=N + error('length of easting and northing must be same') + end + if (size(meanSecsDiff) ~= [N N]) + size(easting) + size(northing) + size(meanSecsDiff) + error('wrong dimensions for meanSecsDiff') + end + + if exist('fixspeed','var') + clear fixbackaz + warning('You can only set fixbackaz or fixspeed, not both. Ignoring fixbackaz') + end + + if exist('fixbackaz', 'var') + backaz = fixbackaz - 1.0: 0.1: fixbackaz + 1.0; + else + backaz = 0.1:0.1:360; + end + unit_vector_easting = -sin(deg2rad(backaz)); + unit_vector_northing = -cos(deg2rad(backaz)); + + for row=1:N + for column=1:N + eastingDiff(row, column) = easting(row) - easting(column); + northingDiff(row, column) = northing(row) - northing(column); + end + end + + %for thisaz = backaz + for c=1:length(backaz) + thisaz = backaz(c); + for row=1:N + for column=1:N + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(c) unit_vector_northing(c)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; + a =[]; + for row=1:N + for column=1:N + if row~=column + a = [a speedMatrix(row, column)]; + end + end + end + %meanspeed(thisaz) = mean(a); + %stdspeed(thisaz) = std(a); + meanspeed(c) = mean(a); + stdspeed(c) = std(a); + + end + fractional_error = stdspeed ./ meanspeed; +% figure +% subplot(2,1,1),bar(backaz, meanspeed); +% xlabel('Back azimuth (degrees)') +% ylabel('Sound speed (m/s)'); +% subplot(2,1,2),semilogy(backaz, abs(fractional_error)); +% xlabel('Back azimuth (degrees)') +% ylabel('Sound speed fractional error'); + + + % return variables + fractional_error(meanspeed<0) = Inf; % eliminate -ve speeds as solutions + if exist('fixspeed','var') + [~,index] = min(abs(meanspeed-fixspeed)); + fractional_error(index) = 0; % force this speed to be used + end + [~,bestindex] = min(abs(fractional_error)); + bestbackaz = backaz(bestindex); + bestspeed = meanspeed(bestindex); + + fprintf('Source is at back azimuth %.1f and wave travels at speed of %.1fm/s\n',bestbackaz,bestspeed); + for row=1:N + for column=1:N + %distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestbackaz) unit_vector_northing(bestbackaz)] ); + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestindex) unit_vector_northing(bestindex)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; + + % add these to the infrasoundEvent structure + infrasoundEvent.bestbackaz = bestbackaz; + infrasoundEvent.bestsoundspeed = bestsoundspeed; + infrasoundEvent.distanceDiff = distanceDiff; + infrasoundEvent.speedMatrix = speedMatrix; +end + +%% +function infrasoundEvent = compute_energy(infrasoundEvent, array_distance_in_km) +%COMPUTE_ENERGY +% infrasoundEvent = compute_energy(infrasoundEvent, array_distance_in_km) + densityEarth = 2000; % (kg/m3) sandstone is 2000-2650, limestone 2000, wet sand 1950 + %pWaveSpeed = 2000; % (m/s) sandstone 2000-3500, limestone 3500-6000, wet sand 1500-2000 + densityAir = 1.225; % (kg/m3) + pWaveSpeed = 885; % from onset sub-event + for chanNum = 1:numInfrasoundChannels + infrasoundEnergy(chanNum) = 2 * pi * (array_distance_in_km(chanNum).^2 * 1e6) * infrasoundEvent.energy(chanNum) / (densityAir * speed_of_sound); + end + seismicEnergy = sum(ev.energy(4:6)) * 2 * pi * (array_distance_in_km(6).^2 * 1e6) * densityEarth * pWaveSpeed *1e-18; + + % add to infrasoundEvent structure + infrasoundEvent.infrasoundEnergy = median(infrasoundEnergy); + infrasoundEvent.seismicEnergy = seismicEnergy; +end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/apparentspeed.m b/applications/rockets/infrasoundgt/apparentspeed.m new file mode 100644 index 0000000..0367f4d --- /dev/null +++ b/applications/rockets/infrasoundgt/apparentspeed.m @@ -0,0 +1,18 @@ +hdistance1 = 1393.5; +hdistance2 = 1425.2; +sourceheight=0:500; +soundspeed=350.4; +pathlength1 = sqrt(hdistance1^2 + sourceheight.^2); +traveltime1 = pathlength1 ./ soundspeed; +pathlength2 = sqrt(hdistance2^2 + sourceheight.^2); +traveltime2 = pathlength2 ./ soundspeed; +apparentC = (hdistance2 - hdistance1)./(traveltime2-traveltime1); +plot(sourceheight, apparentC) +xlabel('Source Height (m)'); +ylabel('Apparent sound speed (m/sec)') + +figure +theta = 180 * atan(sourceheight./hdistance1) / pi; +plot(theta, apparentC) +xlabel('Incidence angle (degrees)'); +ylabel('Apparent sound speed (m/sec)') \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/auto_measure_amplitudes.m b/applications/rockets/infrasoundgt/auto_measure_amplitudes.m new file mode 100755 index 0000000..f995eac --- /dev/null +++ b/applications/rockets/infrasoundgt/auto_measure_amplitudes.m @@ -0,0 +1,18 @@ +function infrasoundEventOut = auto_measure_amplitudes(infrasoundEvent, wevent, titleStr, predicted_traveltime_seconds, figureOutDirectory) +numEvents = numel(infrasoundEvent); +close all +min_traveltime = min(predicted_traveltime_seconds); +relative_traveltimes = predicted_traveltime_seconds - min_traveltime; +infrasoundEventOut = []; +for eventNumber=1:numEvents + thisEvent = infrasoundEvent(eventNumber); + thisW=wevent{eventNumber}; + + thisEvent = auto_measure_minmax3(thisW, thisEvent, relative_traveltimes) + ah=get(gcf,'Children'); + title(ah(7),sprintf('Event %d',eventNumber)); + + feval('print', '-dpng', sprintf('%s/%s_%03d',figureOutDirectory, titleStr, eventNumber) ); + close + infrasoundEventOut = [infrasoundEventOut thisEvent]; +end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/auto_measure_minmax3.m b/applications/rockets/infrasoundgt/auto_measure_minmax3.m new file mode 100755 index 0000000..0bfaf29 --- /dev/null +++ b/applications/rockets/infrasoundgt/auto_measure_minmax3.m @@ -0,0 +1,83 @@ +function thisEvent = auto_measure_minmax(thisW, thisEvent, relative_traveltimes) +% for each channel find the minimum and maximum value that lie within +% MAX_TIME_DIFF seconds of each other + +MAX_TIME_DIFF = 0.03; % max time diff between min & max amp is MAX_TIME_DIFF seconds +SECONDS_PER_DAY = 86400; +SECONDS_BEFORE_FIRST_ARRIVAL_FOR_TIMEWINDOW_START = 0.05; +SECONDS_AFTER_FIRST_ARRIVAL_FOR_TIMEWINDOW_END = 0.15; + +thisW = detrend(thisW); % make sure there is no trend or offset +wstart = get(thisW,'start'); % vector of waveform start times +wstd = std(thisW); % vector of waveform standard deviations - for noise estimation + +% plot waveforms for this event +fh=plot_panels(thisW); +ah=get(fh,'Children'); +set(fh, 'Position', [0 0 1600 1000]); +thisEvent.maxAmp=zeros(1,6); +thisEvent.minAmp=zeros(1,6); +thisEvent.maxTime=zeros(1,6); +thisEvent.minTime=zeros(1,6); + +for chanNum=1:6 + + % GET THE DATA + y = get(thisW(chanNum),'data'); +% ydiff = [0; diff(y)]; + + % DEFINE THE MAIN TIME WINDOW TO SEARCH OVER + time_to_begin_at = thisEvent.FirstArrivalTime + relative_traveltimes(chanNum)/SECONDS_PER_DAY - SECONDS_BEFORE_FIRST_ARRIVAL_FOR_TIMEWINDOW_START/SECONDS_PER_DAY; + time_to_end_at = time_to_begin_at + SECONDS_AFTER_FIRST_ARRIVAL_FOR_TIMEWINDOW_END/SECONDS_PER_DAY; + fs = get(thisW(chanNum),'freq'); + numSamples = length(y); + seconds_begin_offset = (time_to_begin_at - wstart(chanNum)) * SECONDS_PER_DAY; + seconds_end_offset = (time_to_end_at - wstart(chanNum)) * SECONDS_PER_DAY; + sample_to_begin_at = max( [round( seconds_begin_offset * fs) 1]); + sample_to_end_at = min( [round( seconds_end_offset * fs) numSamples]); + + % LOOP OVER SUBWINDOWS + % find p2p amplitude in each, compare to highest p2p found so far + subWindowSize = round(fs * MAX_TIME_DIFF); + maxA = 0; + for startSamp = sample_to_begin_at:1:sample_to_end_at - subWindowSize + samples = startSamp:startSamp + subWindowSize-1; + [maxy, maxindex] = max(y(samples)); + [miny, minindex] = min(y(samples)); + if (maxy-miny) > maxA % THE BIGGEST PEAK2PEAK SO FAR - SO UPDATE THE INFRASOUND OBJECT + maxSecs = ((maxindex+samples(1)-1)/fs); + minSecs = ((minindex+samples(1)-1)/fs); + maxA = maxy-miny; + + % SAVE THE MIN AND MAX VALUES & CORRESPONDING TIMES + thisEvent.maxTime(chanNum) = wstart(chanNum) + maxSecs/SECONDS_PER_DAY; + thisEvent.minTime(chanNum) = wstart(chanNum) + minSecs/SECONDS_PER_DAY; + thisEvent.maxAmp(chanNum) = maxy; + thisEvent.minAmp(chanNum) = miny; + thisEvent.p2p(chanNum) = maxy - miny; + end + + end + +% % second algorithm - find the greatest number of consecutive points +% % with a positive gradient, and with a negative gradient +% a = (ydiff>0); +% [pos, neg] = longest_sequence(a(sample_to_begin_at:sample_to_end_at)); +% pos.Start = pos.Start - 1 + sample_to_begin_at; +% pos.End = pos.End - 1 + sample_to_begin_at; +% neg.Start = neg.Start - 1 + sample_to_begin_at; +% neg.End = neg.End - 1 + sample_to_begin_at; + + % ADD OTHER METRICS TO THE INFRASOUND OBJECT + thisEvent.rms(chanNum) = wstd(chanNum); % stdev of whole trace - noise level estimate + thisEvent.energy(chanNum) = sum(y(sample_to_begin_at:sample_to_end_at).^2)/fs; + + % MARK THE MIN AND MAX TIMES ON THE WAVEFORM PANEL PLOT + axisnum = 8 - chanNum; + axes(ah(axisnum)); + hold on + plot(ah(axisnum),maxSecs, thisEvent.maxAmp(chanNum), 'g*'); + plot(ah(axisnum),minSecs, thisEvent.minAmp(chanNum), 'r*'); +% plot(ah(axisnum),[pos.Start/fs pos.End/fs], [0 0], 'b-'); +% plot(ah(axisnum),[neg.Start/fs neg.End/fs], [0 0], 'k-'); +end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/beamform.m b/applications/rockets/infrasoundgt/beamform.m new file mode 100755 index 0000000..13f4f98 --- /dev/null +++ b/applications/rockets/infrasoundgt/beamform.m @@ -0,0 +1,94 @@ +function [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform(easting, northing, meanSecsDiff); +% tmp=easting(2); +% easting(2)=easting(3); +% easting(3)=tmp; +% tmp=northing(2); +% northing(2)=northing(3); +% northing(3)=tmp; +%BEAMFORM compute back azimuth of source from travel time differences +%between each component. Plane waves are assumed (i.e. source at infinite +%distance). +% +% sourcebackaz = beamform(easting, northing, meanSecsDiff) +% +% Inputs: +% easting, northing - GPS coordinates of array components +% meanSecsDiff - an array of size N*N where N = number of array +% components. Each element represents mean travel +% time difference between the array elements +% represented by that row and column +% +% Outputs: +% sourcebackaz - back azimuth of the source +% +% + bestbackaz = NaN; + bestspeed = NaN; + + % First we use travel time ratios to find back azimuthal angle of the beam + % this means we do not need to know speed + N=numel(easting); + if numel(northing)~=N + error('length of easting and northing must be same') + end + if (size(meanSecsDiff) ~= [N N]) + size(easting) + size(northing) + size(meanSecsDiff) + error('wrong dimensions for meanSecsDiff') + end + + backaz = 1:360; + unit_vector_easting = -sin(deg2rad(backaz)); + unit_vector_northing = -cos(deg2rad(backaz)); + + for row=1:N + for column=1:N + eastingDiff(row, column) = easting(row) - easting(column); + northingDiff(row, column) = northing(row) - northing(column); + end + end + + for thisaz = backaz + for row=1:N + for column=1:N + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(thisaz) unit_vector_northing(thisaz)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; + a =[]; + for row=1:N + for column=1:N + if row~=column + a = [a speedMatrix(row, column)]; + end + end + end + meanspeed(thisaz) = mean(a); + stdspeed(thisaz) = std(a); + + end + fractional_error = stdspeed ./ meanspeed; + figure + subplot(2,1,1),bar(backaz, meanspeed); + xlabel('Back azimuth (degrees)') + ylabel('Sound speed (m/s)'); + subplot(2,1,2),semilogy(backaz, abs(fractional_error)); + xlabel('Back azimuth (degrees)') + ylabel('Sound speed fractional error'); + + + % return variables + fractional_error(meanspeed<0) = Inf; % eliminate -ve speeds as solutions + [~,bestbackaz] = min(abs(fractional_error)); + bestspeed = meanspeed(bestbackaz); + + fprintf('Source is at back azimuth %.1f and wave travels at speed of %.1fm/s\n',bestbackaz,bestspeed); + for row=1:N + for column=1:N + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestbackaz) unit_vector_northing(bestbackaz)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; +end + diff --git a/applications/rockets/infrasoundgt/beamform2.m b/applications/rockets/infrasoundgt/beamform2.m new file mode 100755 index 0000000..46b0a50 --- /dev/null +++ b/applications/rockets/infrasoundgt/beamform2.m @@ -0,0 +1,128 @@ +function [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff, fixbackaz, fixspeed); +%BEAMFORM2D compute back azimuth of source from travel time differences +%between each component. Plane waves are assumed (i.e. source at infinite +%distance). 2D assumes flat topography, does not search over a vertical +%incident angle. +% +% [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff) +% For each possible back azimuth, compute the distances between array +% components resolved in that direction. +% Based on differential travel times (meanSecsDiff), compute +% speedMatrix. Take average and stdev of speedMatrix, and compute +% fractional deviation. +% Choose the best back azimuth (bestbackaz) as the back azimuth for which the fractional +% deviation is least. Return this and the mean speed (bestspeed). +% +% Inputs: +% easting, northing - GPS coordinates of array components +% meanSecsDiff - an array of size N*N where N = number of array +% components. Each element represents mean travel +% time difference between the array elements +% represented by that row and column +% +% Outputs: +% bestbackaz - back azimuth of the source that best fits inputs +% bestspeed - pressure wave speed across array that best fits inputs +% +% [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff,fixbackaz) +% fixbackaz - fix the back azimuth to this value +% Only iterate from fixbackaz-1 to fixbackaz+1, rather than from +% 0.1 to 360. +% +% +% [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff,0,fixspeed) +% fixspeed - return the back azimuth that best fits this speed. + + bestbackaz = NaN; + bestspeed = NaN; + disp('goit here') + + % First we use travel time ratios to find back azimuthal angle of the beam + % this means we do not need to know speed + N=numel(easting); + if numel(northing)~=N + error('length of easting and northing must be same') + end + if (size(meanSecsDiff) ~= [N N]) + size(easting) + size(northing) + size(meanSecsDiff) + error('wrong dimensions for meanSecsDiff') + end + + if exist('fixspeed','var') + clear fixbackaz + warning('You can only set fixbackaz or fixspeed, not both. Ignoring fixbackaz') + end + + if exist('fixbackaz', 'var') + backaz = fixbackaz - 1.0: 0.1: fixbackaz + 1.0; + else + backaz = 0.1:0.1:360; + end + unit_vector_easting = -sin(deg2rad(backaz)); + unit_vector_northing = -cos(deg2rad(backaz)); + + for row=1:N + for column=1:N + eastingDiff(row, column) = easting(row) - easting(column); + northingDiff(row, column) = northing(row) - northing(column); + end + end + eastingDiff + northingDiff + meanSecsDiff + + %for thisaz = backaz + for c=1:length(backaz) + thisaz = backaz(c); + for row=1:N + for column=1:N + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(c) unit_vector_northing(c)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; + a =[]; + for row=1:N + for column=1:N + if row~=column + a = [a speedMatrix(row, column)]; + end + end + end + %meanspeed(thisaz) = mean(a); + %stdspeed(thisaz) = std(a); + meanspeed(c) = mean(a); + stdspeed(c) = std(a); + + end + fractional_error = stdspeed ./ meanspeed; + figure + subplot(2,1,1),bar(backaz, meanspeed); + xlabel('Back azimuth (degrees)') + ylabel('Sound speed (m/s)'); + subplot(2,1,2),semilogy(backaz, abs(fractional_error)); + xlabel('Back azimuth (degrees)') + ylabel('Sound speed fractional error'); + + + % return variables + fractional_error(meanspeed<0) = Inf; % eliminate -ve speeds as solutions + if exist('fixspeed','var') + [~,index] = min(abs(meanspeed-fixspeed)); + fractional_error(index) = 0; % force this speed to be used + end + [~,bestindex] = min(abs(fractional_error)); + bestbackaz = backaz(bestindex); + bestspeed = meanspeed(bestindex); + + fprintf('Source is at back azimuth %.1f and wave travels at speed of %.1fm/s\n',bestbackaz,bestspeed); + for row=1:N + for column=1:N + %distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestbackaz) unit_vector_northing(bestbackaz)] ); + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestindex) unit_vector_northing(bestindex)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; +end + diff --git a/applications/rockets/infrasoundgt/beamform2d.m b/applications/rockets/infrasoundgt/beamform2d.m new file mode 100755 index 0000000..a541b4c --- /dev/null +++ b/applications/rockets/infrasoundgt/beamform2d.m @@ -0,0 +1,127 @@ +function [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff, fixbackaz, fixspeed) +%BEAMFORM2D compute back azimuth of source from travel time differences +%between each component. Plane waves are assumed (i.e. source at infinite +%distance). 2D assumes flat topography, does not search over a vertical +%incident angle. +% +% [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff) +% For each possible back azimuth, compute the distances between array +% components resolved in that direction. +% Based on differential travel times (meanSecsDiff), compute +% speedMatrix. Take average and stdev of speedMatrix, and compute +% fractional deviation. +% Choose the best back azimuth (bestbackaz) as the back azimuth for which the fractional +% deviation is least. Return this and the mean speed (bestspeed). +% +% Inputs: +% easting, northing - GPS coordinates of array components +% meanSecsDiff - an array of size N*N where N = number of array +% components. Each element represents mean travel +% time difference between the array elements +% represented by that row and column +% +% Outputs: +% bestbackaz - back azimuth of the source that best fits inputs +% bestspeed - pressure wave speed across array that best fits inputs +% +% [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff,fixbackaz) +% fixbackaz - fix the back azimuth to this value +% Only iterate from fixbackaz-1 to fixbackaz+1, rather than from +% 0.1 to 360. +% +% +% [bestbackaz, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff,0,fixspeed) +% fixspeed - return the back azimuth that best fits this speed. + + bestbackaz = NaN; + bestspeed = NaN; + + % First we use travel time ratios to find back azimuthal angle of the beam + % this means we do not need to know speed + N=numel(easting); + if numel(northing)~=N + error('length of easting and northing must be same') + end + if (size(meanSecsDiff) ~= [N N]) + size(easting) + size(northing) + size(meanSecsDiff) + error('wrong dimensions for meanSecsDiff') + end + + if exist('fixspeed','var') + clear fixbackaz + warning('You can only set fixbackaz or fixspeed, not both. Ignoring fixbackaz') + end + + + if exist('fixbackaz', 'var') + backaz = fixbackaz - 1.0: 0.1: fixbackaz + 1.0; + else + backaz = 0.1:0.1:360; + end + unit_vector_easting = -sin(deg2rad(backaz)); + unit_vector_northing = -cos(deg2rad(backaz)); + + for row=1:N + for column=1:N + eastingDiff(row, column) = easting(row) - easting(column); + northingDiff(row, column) = northing(row) - northing(column); + end + end + + %for thisaz = backaz + for c=1:length(backaz) + thisaz = backaz(c); + for row=1:N + for column=1:N + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(c) unit_vector_northing(c)] ); + end + %distanceDiff + end + + speedMatrix = distanceDiff ./ meanSecsDiff; + a =[]; + for row=1:N + for column=1:N + if row~=column + a = [a speedMatrix(row, column)]; + end + end + end + %meanspeed(thisaz) = mean(a); + %stdspeed(thisaz) = std(a); + meanspeed(c) = mean(a); + stdspeed(c) = std(a); + + end + fractional_error = stdspeed ./ meanspeed; +% figure +% subplot(2,1,1),bar(backaz, meanspeed); +% xlabel('Back azimuth (degrees)') +% ylabel('Sound speed (m/s)'); +% subplot(2,1,2),semilogy(backaz, abs(fractional_error)); +% xlabel('Back azimuth (degrees)') +% ylabel('Sound speed fractional error'); + + + % return variables + fractional_error(meanspeed<0) = Inf; % eliminate -ve speeds as solutions + if exist('fixspeed','var') + [~,index] = min(abs(meanspeed-fixspeed)); + fractional_error(index) = 0; % force this speed to be used + end + [~,bestindex] = min(abs(fractional_error)); + bestbackaz = backaz(bestindex); + bestspeed = meanspeed(bestindex); + + fprintf('Source is at back azimuth %.1f and wave travels at speed of %.1fm/s\n',bestbackaz,bestspeed); + for row=1:N + for column=1:N + %distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestbackaz) unit_vector_northing(bestbackaz)] ); + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestindex) unit_vector_northing(bestindex)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; +end + diff --git a/applications/rockets/infrasoundgt/beamform2dfull.m b/applications/rockets/infrasoundgt/beamform2dfull.m new file mode 100644 index 0000000..a468136 --- /dev/null +++ b/applications/rockets/infrasoundgt/beamform2dfull.m @@ -0,0 +1,121 @@ +function [backazimuth, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff, fixazimuth, fixspeed) +%BEAMFORM2D compute back azimuthguess of source from travel time differences +%between each component. Plane waves are assumed (i.e. source at infinite +%distance). 2D assumes flat topography, does not search over a vertical +%incident angle. +% +% [backazimuth, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff) +% For each possible back azimuthguess, compute the distances between array +% components resolved in that direction. +% Based on differential travel times (meanSecsDiff), compute +% speedMatrix. Take average and stdev of speedMatrix, and compute +% fractional deviation. +% Choose the azimuthguess for which the fractional +% deviation is least. Return this and the mean speed (bestspeed). +% +% Inputs: +% easting, northing - GPS coordinates of array components +% meanSecsDiff - an array of size N*N where N = number of array +% components. Each element represents mean travel +% time difference between the array elements +% represented by that row and column +% +% Outputs: +% backazimuthguess - back azimuthguess of the source that best fits inputs +% bestspeed - pressure wave speed across array that best fits inputs +% +% [backazimuth, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff,fixazimuthguess) +% fixazimuth - fix the back azimuthguess to this value +% Only iterate from fixazimuth-1 to fixazimuth+1, rather than from +% 0.1 to 360. +% +% +% [backazimuth, bestspeed,distanceDiff,speedMatrix] = beamform2d(easting, northing, meanSecsDiff,0,fixspeed) +% fixspeed - return the back azimuthguess that best fits this speed. + + backazimuth = NaN; + bestspeed = NaN; + + % First we use travel time ratios to find back azimuthguessal angle of the beam + % this means we do not need to know speed + N=numel(easting); + if numel(northing)~=N + error('length of easting and northing must be same') + end + if (size(meanSecsDiff) ~= [N N]) + size(easting) + size(northing) + size(meanSecsDiff) + error('wrong dimensions for meanSecsDiff') + end + + if exist('fixspeed','var') + clear fixazimuthguess + warning('You can only set fixazimuthguess or fixspeed, not both. Ignoring fixazimuthguess') + end + + + if exist('fixazimuthguess', 'var') + azimuthguess = mod(180 + fixazimuthguess - 1.0: 0.1: fixazimuthguess + 1.0,360); + else + azimuthguess = 0.1:0.1:360; + end + unit_vector_easting = -sin(deg2rad(azimuthguess)); + unit_vector_northing = -cos(deg2rad(azimuthguess)); + + for row=1:N + for column=1:N + eastingDiff(row, column) = easting(row) - easting(column); + northingDiff(row, column) = northing(row) - northing(column); + end + end + + %for thisaz = azimuthguess + for c=1:length(azimuthguess) + thisaz = azimuthguess(c); + for row=1:N + for column=1:N + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(c) unit_vector_northing(c)] ); + end + %distanceDiff + end + + speedMatrix = distanceDiff ./ meanSecsDiff; + meanspeed(c) = mean(speedMatrix([2 3 4 6 7 8])); + stdspeed(c) = std(speedMatrix([2 3 4 6 7 8])); + if meanspeed(c)<0 + stdspeed(c)=Inf; + end + + end + fractional_error = stdspeed ./ meanspeed; +% figure +% subplot(2,1,1),bar(azimuthguess, meanspeed); +% xlabel('Back azimuthguess (degrees)') +% ylabel('Sound speed (m/s)'); +% subplot(2,1,2),semilogy(azimuthguess, abs(fractional_error)); +% xlabel('Back azimuthguess (degrees)') +% ylabel('Sound speed fractional error'); + + + % return variables + fractional_error(meanspeed<0) = Inf; % eliminate -ve speeds as solutions + if exist('fixspeed','var') + [~,index] = min(abs(meanspeed-fixspeed)); + fractional_error(index) = 0; % force this speed to be used + end + %[~,bestindex] = min(abs(fractional_error)); + [~,bestindex] = min(abs(stdspeed)); + backazimuth = mod(180+azimuthguess(bestindex),360); + bestspeed = meanspeed(bestindex); + + + for row=1:N + for column=1:N + %distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestazimuthguess) unit_vector_northing(bestazimuthguess)] ); + distanceDiff(row, column) = dot( [eastingDiff(row, column) northingDiff(row, column)], [unit_vector_easting(bestindex) unit_vector_northing(bestindex)] ); + end + end + speedMatrix = distanceDiff ./ meanSecsDiff; +end + diff --git a/applications/rockets/infrasoundgt/butterworthFilter.m b/applications/rockets/infrasoundgt/butterworthFilter.m new file mode 100755 index 0000000..3df8ce1 --- /dev/null +++ b/applications/rockets/infrasoundgt/butterworthFilter.m @@ -0,0 +1,5 @@ +function wfilt = butterworthFilter(w, filterType, corners, numpoles) +disp('Filtering waveform data...') +wfilt = detrend(w); +f=filterobject(filterType, corners, numpoles); +wfilt=filtfilt(f,wfilt); diff --git a/applications/rockets/infrasoundgt/computeSpeedOfSound.m b/applications/rockets/infrasoundgt/computeSpeedOfSound.m new file mode 100755 index 0000000..e631960 --- /dev/null +++ b/applications/rockets/infrasoundgt/computeSpeedOfSound.m @@ -0,0 +1,2 @@ +function c = computeSpeedOfSound(temperatureC, relativeHumidity) +c = 331.3 + 0.606 * temperatureC + 1.26 * relativeHumidity/100; \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/compute_infrasound_energy.m b/applications/rockets/infrasoundgt/compute_infrasound_energy.m new file mode 100755 index 0000000..a291826 --- /dev/null +++ b/applications/rockets/infrasoundgt/compute_infrasound_energy.m @@ -0,0 +1,4 @@ +function e = compute_infrasound_energy(dataInPascals, samplingFrequency, distanceInMetres, densityAir, speed_of_sound); +% densityAir = 1.225; % (kg/m3) +dimensionlessEnergy = dataInPascals.^2 / samplingFrequency; +e = 2 * pi * distanceInMetres^2 * dimensionlessEnergy / (densityAir * speed_of_sound); diff --git a/applications/rockets/infrasoundgt/eventMap.m b/applications/rockets/infrasoundgt/eventMap.m new file mode 100755 index 0000000..102d573 --- /dev/null +++ b/applications/rockets/infrasoundgt/eventMap.m @@ -0,0 +1,24 @@ +%% plot array map & compute eastings and northings +if make_figures + disp('Plotting array map') + close all + cols = 'rwbggg'; + for c=1:length(lat) + chan = get(w(c),'channel');; + plot(easting(c),northing(c),'o','MarkerFaceColor',cols(c),'MarkerSize',10) + hold on + quiver(easting(c),northing(c),-easting(c)/100,-northing(c)/100,0); % /100 just gives arrow length + text(easting(c)+1,northing(c),chan(1:3)); + end + grid on + quiver(440,1325,wind_speed*sin(deg2rad(wind_direction)), wind_speed*cos(deg2rad(wind_direction)) ,0,'k'); + text(440,1325,'wind') + hold off + title('Beach House array position relative to SLC40'); + xlabel('metres east'); + ylabel('metres north'); + axis equal; + outfile = sprintf('%s/arraymap.png',figureOutDirectory); + feval('print', '-dpng', outfile); + close +end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/fahrenheit2celsius.m b/applications/rockets/infrasoundgt/fahrenheit2celsius.m new file mode 100755 index 0000000..2a871d3 --- /dev/null +++ b/applications/rockets/infrasoundgt/fahrenheit2celsius.m @@ -0,0 +1,2 @@ +function tempC = fahrenheit2celsius(tempF) +tempC = (tempF - 32) * 5/9; \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/infrasoundEvent2catalog.m b/applications/rockets/infrasoundgt/infrasoundEvent2catalog.m new file mode 100755 index 0000000..e69de29 diff --git a/applications/rockets/infrasoundgt/latlon2eastingsNorthings.m b/applications/rockets/infrasoundgt/latlon2eastingsNorthings.m new file mode 100755 index 0000000..7980fd4 --- /dev/null +++ b/applications/rockets/infrasoundgt/latlon2eastingsNorthings.m @@ -0,0 +1,11 @@ +function [easting,northing]=latlon2eastingsNorthings(sourcelat, sourcelon, lat, lon) +% Convert lat,lon to eastings,northings with sourcelat, sourcelon as origin +% inputs are in degrees, output is in metres relative to origin +% [easting,northing]=latlon2eastingsNorthings(sourcelat, sourcelon, lat, lon) +deg2m = deg2km(1) * 1000; +for c=1:length(lat) + e = distance(lat(c), lon(c), lat(c), sourcelon) * deg2m; + easting(c) = e * sign(lon(c)-sourcelon); + n = distance(lat(c), lon(c), sourcelat, lon(c)) * deg2m; + northing(c) = n * sign(lat(c)-sourcelat); +end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/make_master_event.m b/applications/rockets/infrasoundgt/make_master_event.m new file mode 100755 index 0000000..ccd7090 --- /dev/null +++ b/applications/rockets/infrasoundgt/make_master_event.m @@ -0,0 +1,44 @@ +function masterEvent = make_master_event(infrasoundEvent) +%% Construct a master infrasound event, from all the individual ones +disp('Constructing master infrasound event from individual event statistics') +masterEvent.FirstArrivalTime = infrasoundEvent(1).FirstArrivalTime; +masterEvent.LastArrivalTime = infrasoundEvent(1).LastArrivalTime; + +% find the mean xcorr time lag difference for non-identical infrasound components - it should be close to zero if only one event in time window +% e.g. needle 1 and haystack 2 should have same magnitude but opposite sign +% time delay to needle 2 and haystack 1 if there is only one clear N wave +% in the haystacks +disp('- finding events with a mean time lag difference of close to 0 - these are the events we can use') +indexes = find(abs([infrasoundEvent.meanSecsDiff]) < 0.01); % these are events with probably only one event in wevent time window +fprintf('- found %d events we can use\n', numel(indexes)); +disp('- event indexes to use'); +disp(indexes) + +masterEvent.secsDiff = zeros(3,3); +masterEvent.stdSecsDiff = zeros(3,3); +for row=1:3 + for column=1:3 + a = []; + for eventNumber = indexes + thisEvent = infrasoundEvent(eventNumber); + a = [a thisEvent.secsDiff(row, column)]; + end + +% % eliminate any events which have a difference from the mean of +% % greater than the standard deviation +% diffa = abs( (a-mean(a))/std(a) ); +% +% % now set the mean and std for the master event +% masterEvent.secsDiff(row, column) = mean(a(diffa<1.0)); +% masterEvent.stdSecsDiff(row, column) = std(a(diffa<1.0)); + + masterEvent.secsDiff(row, column) = mean(a); + masterEvent.stdSecsDiff(row, column) = std(a); + end +end +disp(' - mean:'); +disp(masterEvent.secsDiff) +disp(' - std:') +disp(masterEvent.stdSecsDiff) +disp(' - fractional std:') +disp(masterEvent.stdSecsDiff ./ masterEvent.secsDiff) \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/plotIS.m b/applications/rockets/infrasoundgt/plotIS.m new file mode 100755 index 0000000..4646dae --- /dev/null +++ b/applications/rockets/infrasoundgt/plotIS.m @@ -0,0 +1,19 @@ +function plotIS(w) +r=waveform2rsam(w([2 6]),'max',0.1); +yi = r(1).data; +ys = r(2).data; +t = r(1).dnum; + +figure +subplot(2,1,1), semilogy(t,yi); +ytickpos = get(gca,'YTick'); +set(gca,'YTickLabels',ytickpos); +ylabel('Pressure change (Pa)'); +datetick('x','keeplimits') + +subplot(2,1,2), semilogy(t,ys); +ytickpos = get(gca,'YTick'); +set(gca,'YTickLabels',ytickpos); +ylabel('Ground velocity (nm/sec)'); +xlabel(sprintf('Date/Time starting at %s',datestr(t(1),'yyyy-mm-dd HH:MM:SS.FFF'))); +datetick('x','keeplimits') \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/plot_events.m b/applications/rockets/infrasoundgt/plot_events.m new file mode 100755 index 0000000..3bc0d0c --- /dev/null +++ b/applications/rockets/infrasoundgt/plot_events.m @@ -0,0 +1,11 @@ +function plot_events(wevent, filepattern, figureOutDirectory) +numEvents = numel(wevent); +for eventNumber=1:numEvents + fprintf('- plotting event %d of %d\n',eventNumber,numEvents ); + plot_panels(wevent{eventNumber}); + ah=get(gcf,'Children'); + title(ah(7),sprintf('Event %d',eventNumber)); + outfile = sprintf('%s/%s%03d.png',figureOutDirectory, filepattern, eventNumber); + feval('print', '-dpng', outfile); + close +end \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/predictedTravelTimes.m b/applications/rockets/infrasoundgt/predictedTravelTimes.m new file mode 100755 index 0000000..fa4ed6b --- /dev/null +++ b/applications/rockets/infrasoundgt/predictedTravelTimes.m @@ -0,0 +1,26 @@ +%% compute predicted travel times for infrasound waves based on GPS coords & wind +%% also add lat, lon, distance and backaz fields to waveform objects +disp('Predicting travel times based on GPS coordinates and wind vector...') +fout = fopen(fullfile(figureOutDirectory, 'predictedTravelTimes.txt'), 'w'); +fprintf(fout,'\n_______________________________________________\n'); +fprintf(fout,'PREDICTED TRAVEL TIME BASED ON:\n'); +fprintf(fout,' sound speed(c) %.1fm/s\n', speed_of_sound); +fprintf(fout,' wind speed %.1fm/s\n', wind_speed); +fprintf(fout,' wind direction %.1f degrees\n', wind_direction); +fprintf(fout,'------\t--------\t-----------\t----------\t-----------\n'); +fprintf(fout,'Channel\tDistance\tBackAzimuth\tTravelTime\tc_effective\n'); +fprintf(fout,'------\t--------\t-----------\t----------\t-----------\n'); +for c=1:length(lat) + [arclen(c), backaz(c)] = distance(lat(c), lon(c), source.lat, source.lon, 'degrees'); + arclen(c) = deg2km(arclen(c))*1000; + effective_speed(c) = speed_of_sound + wind_speed * cos(deg2rad( (180+backaz(c)) - wind_direction) ); + predicted_traveltime_seconds(c) = arclen(c)/effective_speed(c); + fprintf(fout,'%s\t%.1fm\t\t%.1f degrees\t%.3fs\t\t%.1fm/s\n',get(w(c),'channel'), arclen(c), backaz(c), predicted_traveltime_seconds(c),effective_speed(c)); + w(c) = addfield(w(c), 'lat', lat(c)); + w(c) = addfield(w(c), 'lon', lon(c)); + w(c) = addfield(w(c), 'distance', arclen(c)); + w(c) = addfield(w(c), 'backaz', backaz(c)); +end +fprintf(fout,'_______________________________________________\n'); +fprintf(fout,'Program name: %s\n',mfilename('fullpath')); +fclose(fout); \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/predictedTravelTimesNE.m b/applications/rockets/infrasoundgt/predictedTravelTimesNE.m new file mode 100755 index 0000000..b329af2 --- /dev/null +++ b/applications/rockets/infrasoundgt/predictedTravelTimesNE.m @@ -0,0 +1,28 @@ +%% compute predicted travel times for infrasound waves based on eastings & northings & wind +%% also add lat, lon, distance and backaz fields to waveform objects +disp('Predicting travel times based on GPS coordinates and wind vector...') +fout = fopen(fullfile(figureOutDirectory, 'predictedTravelTimes.txt'), 'w'); +fprintf(fout,'\n_______________________________________________\n'); +fprintf(fout,'PREDICTED TRAVEL TIME BASED ON:\n'); +fprintf(fout,' sound speed(c) %.1fm/s\n', speed_of_sound); +fprintf(fout,' wind speed %.1fm/s\n', wind_speed); +fprintf(fout,' wind direction %.1f degrees\n', wind_direction); +fprintf(fout,'------\t--------\t-----------\t----------\t-----------\n'); +fprintf(fout,'Channel\tDistance\tBackAzimuth\tTravelTime\tc_effective\n'); +fprintf(fout,'------\t--------\t-----------\t----------\t-----------\n'); +for c=1:length(lat) + [arclen(c), backaz(c)] = distance(lat(c), lon(c), source.lat, source.lon, 'degrees'); + arclen(c) = deg2km(arclen(c))*1000; + arclen(c) = sqrt(easting(c).^2 + northing(c).^2); + backaz(c) = 180+180/pi*atan(easting(c)/northing(c)); + effective_speed(c) = speed_of_sound + wind_speed * cos(deg2rad( (180+backaz(c)) - wind_direction) ); + predicted_traveltime_seconds(c) = arclen(c)/effective_speed(c); + fprintf(fout,'%s\t%.1fm\t\t%.1f degrees\t%.3fs\t\t%.1fm/s\n',get(w(c),'channel'), arclen(c), backaz(c), predicted_traveltime_seconds(c),effective_speed(c)); + w(c) = addfield(w(c), 'lat', lat(c)); + w(c) = addfield(w(c), 'lon', lon(c)); + w(c) = addfield(w(c), 'distance', arclen(c)); + w(c) = addfield(w(c), 'backaz', backaz(c)); +end +fprintf(fout,'_______________________________________________\n'); +fprintf(fout,'Program name: %s\n',mfilename('fullpath')); +fclose(fout); \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/pressure_v_elevation.m b/applications/rockets/infrasoundgt/pressure_v_elevation.m new file mode 100644 index 0000000..2888152 --- /dev/null +++ b/applications/rockets/infrasoundgt/pressure_v_elevation.m @@ -0,0 +1,25 @@ +% Section 2.8 of https://www.ohio.edu/mechanical/thermo/Intro/Chapt.1_6/Chapter2b.html +close all + +T0 = 0 + 273; % kelvin, temperature at sea level +a = -0.00651; % Kelvin/m, a is lapse rate +P0 = 101000; % Pa, standard atmosphere pressure +g = 9.81; %m/s-2 +R = 287; %J/kg.Kelvin gas constant for air + +count=1; +z = 0:3000; % height +T = T0 + a * z; +P = P0 * power(T/T0, -g/(R*a) ); + +subplot(3,1,1),plot(z, T-273); ylabel('Temperature (C)'); +subplot(3,1,2),plot(z, P); ylabel('Pressure (Pa)'); + + +z=z(1)+0.5:z(end)-0.5; +subplot(3,1,3),plot(z,diff(P)); +xlabel('Altitude (m)') +ylabel('Pressure gradient (Pa/m)') + + + diff --git a/applications/rockets/infrasoundgt/savedata.m b/applications/rockets/infrasoundgt/savedata.m new file mode 100755 index 0000000..3dc69e6 --- /dev/null +++ b/applications/rockets/infrasoundgt/savedata.m @@ -0,0 +1,2 @@ +fprintf('Saving data to matfile %s\n',matfilename); +feval('save', matfilename) \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/segment_event_waveforms.m b/applications/rockets/infrasoundgt/segment_event_waveforms.m new file mode 100755 index 0000000..dea4364 --- /dev/null +++ b/applications/rockets/infrasoundgt/segment_event_waveforms.m @@ -0,0 +1,26 @@ +function wevent = segment_event_waveforms(w, cobj, pretrigger, posttrigger, arrivalTimeCorrection) +%SEGMENT_EVENT_WAVEFORMS take a continuous vector of waveforms, and based +% arrival times in the infrasoundEvent vector, generate a cell vector where +% each element is a waveform vector extracted around the arrival times +% given +% Usage: +% wevent = segment_event_waveforms(w, infrasoundEvent, pretrigger, +% posttrigger,arrivalTimeCorrection) +% pretrigger and posttrigger are in seconds (e.g. 1) +if ~exist('arrivalTimeCorrection','var') + arrivalTimeCorrection = 0.0; +end +disp('Segmenting event waveforms...') +numEvents = numel(infrasoundEvent); +for eventNumber=1:numEvents + fprintf('- segmenting infrasound event %d of %d\n',eventNumber,numEvents ); + time1 = infrasoundEvent(eventNumber).FirstArrivalTime-pretrigger/86400-arrivalTimeCorrection/86400; +% if arrivalTimeCorrection==0.0 +% time2 = infrasoundEvent(eventNumber).LastArrivalTime+posttrigger/86400-arrivalTimeCorrection/86400; +% else + time2 = infrasoundEvent(eventNumber).FirstArrivalTime+posttrigger/86400-arrivalTimeCorrection/86400; +% end + w2 = detrend(extract(w, 'time', time1, time2)); + wevent{eventNumber} = w2; + clear w2 +end diff --git a/applications/rockets/infrasoundgt/speed_versus_pressure.m b/applications/rockets/infrasoundgt/speed_versus_pressure.m new file mode 100755 index 0000000..db6d1d8 --- /dev/null +++ b/applications/rockets/infrasoundgt/speed_versus_pressure.m @@ -0,0 +1,34 @@ +function speed_versus_pressure() +% from Marchetti & Yasur 2013 GRL +% compute Mach number (M) as a function of overpressure (P0) +speed_of_sound = 348.8; % m/sec +gamma = 1.4; % heat capacity for dry air +r = 1400; %m +ambient_pressure = 1e5; % Pascals +overpressure = logspace(0,5); +underpressure = sort(-overpressure); +overpressure = [underpressure overpressure]; +%P0=ambient_pressure*10.^(-0.9:0.001:0.9); +%M=sqrt( (gamma+1)/(2*gamma) * (P0/ambient_pressure - 1) + 1); +M=sqrt( (gamma+1)/(2*gamma) * overpressure/ambient_pressure + 1); +close all +figure +subplot(3,1,1) +loglog(overpressure,M); +xlabel('Overpressure (Pa)') +ylabel('Mach number'); + +p=1000; % Pa +p_reduced = p * r; +r1 = 10:1:r; +p1 = p_reduced./r1; +subplot(3,1,2),semilogy(r1,p1); +xlabel('Distance (m)') +ylabel('Overpressure (Pa)') + +M1=sqrt( (gamma+1)/(2*gamma) * p1/ambient_pressure + 1); +subplot(3,1,3),semilogy(r1,M1); +xlabel('Distance (m)') +ylabel('Mach number') +disp(sprintf('At array: Mach number = %.3f, mean speed = %.1f',M1(end),1.6+M1(end)*speed_of_sound)) +disp(sprintf('Along raypath: Mean Mach number = %.3f, mean speed = %.1f',mean(M1),1.6+mean(M1*speed_of_sound))) diff --git a/applications/rockets/infrasoundgt/writeEvents.m b/applications/rockets/infrasoundgt/writeEvents.m new file mode 100755 index 0000000..a9b9036 --- /dev/null +++ b/applications/rockets/infrasoundgt/writeEvents.m @@ -0,0 +1,60 @@ +function writeEvents(filepath, ev, array_distance_in_km, speed_of_sound) +fout = fopen(filepath, 'w'); +fprintf(fout, 'Arrival Time,Pressure (Pa),Reduced Pressure (Pa.km),Infrasound Energy (J),P_SNR,Vertical Seismic Amplitude (um/s),Seismic Energy (J), S_SNR,Energy Ratio,mean correlation,time error (s),good event?,back azimuth (degrees),sound speed (m/s),predicted origin time,apparent speed, apparent speed error,apparent origin time\n'); +for c=1:numel(ev) + fprintf(fout, '"%s"', datestr(ev(c).FirstArrivalTime,'yyyy-mm-dd HH:MM:SS.FFF')); + fprintf(fout,','); + fprintf(fout, '%7.1f', median(ev(c).p2p(1:3))); + fprintf(fout,','); + fprintf(fout, '%7.1f', median(ev(c).reducedPressure)); + fprintf(fout,','); + fprintf(fout, '%4.2e', ev(c).infrasoundEnergy); + fprintf(fout,','); + fprintf(fout, '%5.1f', median(ev(c).snr(1:3))); + fprintf(fout,','); + fprintf(fout, '%7.1f', ev(c).p2p(6)/1000); + fprintf(fout,','); + fprintf(fout, '%4.2e', ev(c).seismicEnergy); + fprintf(fout,','); + fprintf(fout, '%5.1f', ev(c).snr(6)); + fprintf(fout,','); + fprintf(fout, '%5.1f', ev(c).infrasoundEnergy/ev(c).seismicEnergy); + fprintf(fout,','); + fprintf(fout, '%4.2f', ev(c).meanCorr); + fprintf(fout, ','); + fprintf(fout, '%7.3f', ev(c).meanSecsDiff); + fprintf(fout, ','); + goodEvent = (ev(c).meanSecsDiff<0.001 && ev(c).meanCorr >= 0.7);% && ev(c).bestsoundspeed > 300.0 && ev(c).bestsoundspeed < 400.0); + fprintf(fout, '%7.3f', goodEvent); + fprintf(fout, ','); + if goodEvent + fprintf(fout, '%6.1f', ev(c).bestbackaz); + fprintf(fout,','); + fprintf(fout, '%7.1f', ev(c).bestsoundspeed); + fprintf(fout,','); + daysDiff = (min(array_distance_in_km)*1000/ev(c).bestsoundspeed)/86400; + fprintf(fout, '"%s"', datestr(ev(c).FirstArrivalTime-daysDiff,'yyyy-mm-dd HH:MM:SS.FFF')); + fprintf(fout, ','); + fprintf(fout, '%7.1f', ev(c).apparentSpeed); + fprintf(fout, ','); + fprintf(fout, '%7.1f', ev(c).apparentSpeedError); + fprintf(fout, ','); + daysDiff = (min(array_distance_in_km)*1000/ev(c).apparentSpeed)/86400; + fprintf(fout, '"%s"', datestr(ev(c).FirstArrivalTime-daysDiff,'yyyy-mm-dd HH:MM:SS.FFF')); + else + fprintf(fout, ''); + fprintf(fout,','); + fprintf(fout, ''); + fprintf(fout,','); + fprintf(fout, ''); + fprintf(fout, '%7.1f', 0); + fprintf(fout, ','); + fprintf(fout, '%7.1f', 0); + fprintf(fout, ','); + fprintf(fout, ''); + end + fprintf(fout, '\n'); + +end +fclose(fout); + \ No newline at end of file diff --git a/applications/rockets/infrasoundgt/xcorr3C.m b/applications/rockets/infrasoundgt/xcorr3C.m new file mode 100755 index 0000000..0faef43 --- /dev/null +++ b/applications/rockets/infrasoundgt/xcorr3C.m @@ -0,0 +1,83 @@ +function infrasoundEvent = xcorr3C(wevent, infrasoundEvent, make_figures, figureOutDirectory, pretrigger) +%XCORR3C Cross-correlation an event recorded on 3 infrasound components +% infrasoundEvent = xcorr3C(infrasoundEvent) +% Input: +% wevent - a cell array where each component is a vector of 3 +% waveform objects, 1 per infrasound channel +% infrasoundEvent is a structure containing two elements: +% FirstArrivalTime +% LastArrivalTime +% make_figures - if true, a figure is generated for each xcorr pair +% +% Output: +% infrasoundEvent with some additional elements added +% maxCorr - a 3x3 array of the maximum cross correlation values +% secsDiff - a 3x3 array of the time lags corresponding to +% maxCorr +% meanSecsDiff - the mean of secsDiff for non-diagonal components +% +% each component is cross correlated with each component, hence 3x3 + + %% correlate + % loop through infrasound channels + % take a 0.3-second snippet starting 0.1s before FirstArrivalTime, till 0.2s + % after it + % correlate this against the whole wevent for each infrasound + % trace + % this should result in a correlation matrix + % from this record the time lag matrix for each infrasound channel against each other + % infrasound channel + disp('CORRELATION ...') + disp('_______________') + numEvents = numel(infrasoundEvent); + for eventNumber=1:numEvents + fprintf('- processing event %d of %d\n', eventNumber, numEvents); + haystacks = wevent{eventNumber}; + infrasoundEvent(eventNumber).maxCorr = eye(3); + infrasoundEvent(eventNumber).secsDiff = eye(3); + precorrtime = 0.1; % NEEDLE seconds of data to add before first arrival + postcorrtime = 0.2; % NEEDLE seconds of data to add after first arrival + %postcorrtime = 0.4; + for chanNumber=1:3 + needle = extract(haystacks(chanNumber), 'time', infrasoundEvent(eventNumber).FirstArrivalTime-precorrtime/86400, infrasoundEvent(eventNumber).FirstArrivalTime+postcorrtime/86400); + needle_data = detrend(get(needle, 'data')); +% % upsample by a factor of 8 +% nx = get(needle,'timevector'); +% nxx = nx(1):(nx(2) - nx(1))/8:nx(end); +% nyy = spline(nx,needle_data,nxx); + for haystackNum = 1:3 + fprintf(' - looking for needle %d in haystack %d\n', chanNumber, haystackNum); + haystack = haystacks(haystackNum); + haystack_data = get(haystack,'data'); +% % upsample by a factor of 8 +% hx = get(haystack,'timevector'); +% hxx = hx(1):(hx(2) - hx(1))/8:hx(end); +% hyy = spline(hx,haystack_data,hxx); + [acor,lag] = xcorr(needle_data, haystack_data); +% [acor,lag] = xcorr(nyy, hyy); + cxx0 = sum(abs(needle_data).^2); + cyy0 = sum(abs(haystack_data).^2); +% cxx0 = sum(abs(nyy).^2); +% cyy0 = sum(abs(hyy).^2); + scale = sqrt(cxx0*cyy0); + acor = acor./scale; + [m,I] = max(abs(acor)); + infrasoundEvent(eventNumber).maxCorr(chanNumber,haystackNum) = m; + infrasoundEvent(eventNumber).secsDiff(chanNumber,haystackNum) = lag(I)/get(haystack,'freq') + pretrigger - precorrtime; + if make_figures + figure; + subplot(3,1,1),plot(haystack,'axeshandle',gca); + subplot(3,1,2),plot(needle,'axeshandle',gca); + subplot(3,1,3),plot(lag,acor); + outfile = sprintf('%s/xcorr_infrasoundEvent%03d_%d_%d.png',figureOutDirectory,eventNumber,chanNumber,haystackNum); + feval('print', '-dpng', outfile); + close + end + end + end + infrasoundEvent(eventNumber).meanCorr = mean(infrasoundEvent(eventNumber).maxCorr([2 3 4 6 7 8])); + infrasoundEvent(eventNumber).stdCorr = std(infrasoundEvent(eventNumber).maxCorr([2 3 4 6 7 8])); + infrasoundEvent(eventNumber).meanSecsDiff = mean(infrasoundEvent(eventNumber).secsDiff([2 3 4 6 7 8])); + infrasoundEvent(eventNumber).stdSecsDiff = std(infrasoundEvent(eventNumber).secsDiff([2 3 4 6 7 8])); + end +end \ No newline at end of file diff --git a/applications/rockets/misc/plot_average_frequency.m b/applications/rockets/misc/plot_average_frequency.m new file mode 100644 index 0000000..91742aa --- /dev/null +++ b/applications/rockets/misc/plot_average_frequency.m @@ -0,0 +1,19 @@ +function plot_average_frequency(w) + figure + sp=spectralobject(1024,924,100,[]); + % Plot a spectrogram with superposed frequency metrics + [Tcell,meanF,peakF]=spectrogram(w,'spectralobject',sp,'plot_metrics',1); + % Plot the frequency metrics on their own + for c=1:numel(w) + subplot(numel(w),1,c) + plot(Tcell{c},smooth(peakF{c}),'g') + hold on + plot(Tcell{c},meanF{c},'k') + datetick('x') + ylabel('Hz') + sta= get(w(c),'station'); + chan= get(w(c),'channel'); + th=text(0.1,0.9, sprintf('%s %s.%s',datestr(Tcell{c}(1),30),sta,chan),'Units','normalized') + end + +end diff --git a/applications/uispecgram.m b/applications/uispecgram.m old mode 100644 new mode 100755 diff --git a/applications/xcorrlocate/Multipletscorrelation.m b/applications/xcorrlocate/Multipletscorrelation.m new file mode 100644 index 0000000..b84ec7f --- /dev/null +++ b/applications/xcorrlocate/Multipletscorrelation.m @@ -0,0 +1,397 @@ +function mitchmadnesswrapper() + + +clear all +close all +clc + +POSTTRIG = 20 + +%% THIS IS THE ONLY SECTION IN THE SCRIPT THAT NEEDS CHANGED +%SIMPLY CHANGE THE WORKING DIRECTORY AND THE FILENAME TO RUN IT +%NOTE: MUST HAVE THE SAME DATA STRUCTURE AND NAMING SCHEME AS +%MEL'S DATA + +MULTIPLETS_TOP_DIR = '~/Desktop/Multiplets'; +PEAKMATCH_OUTPUT_FILE = 'M-2012-05-11-edit2.dat'; + +% Move to the working directory that contains the correct data structure +% for this script, which is... +cd(MULTIPLETS_TOP_DIR); + +mitchmadness(PEAKMATCH_OUTPUT_FILE, POSTTRIG); + + +end + +function mitchmadness(PEAKMATCH_OUTPUT_FILE, POSTTRIG) +% Author: Mitchell Hastings +% Welcome Weary Seismologists! +% This is a .m script made to automate the process of creating a subset +% of waveforms at each station at Telica volcano, Nicaragua. This script +% can work with any kind of waveform data if you are making subsets of +% families of waveforms. This script was written for the purpose of a class +% project for Steve McNutt's Volcano Seismology course. The author is tired +% and wishes to no longer continue modulating this script, but he may come +% back to doing so....eventually... + +% Read in file that has the original list of the Multiplets and all +% member events +fid = fopen(PEAKMATCH_OUTPUT_FILE,'r'); + +%% TBTN-------------------------------------------------------------------- +% loop to read in each line and write a file path relative to the working +% directory + +count = 1; +while 1 + tline = fgetl(fid); + if ~ischar(tline), break, end; + fname = tline; + d = fname(1:4); + full = fullfile(d,fname); + w(count) = waveform(full, 'sac'); + count = count + 1; +end + +% create correlation object of the waveforms to do the correlation +c = correlation(w, get(w, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +% Use the subset_list to find the waveforms that correlate above the +% threshold on other stations + +%% TBMR-------------------------------------------------------------------- + +% this loop makes a cell array for the subset of events on station TBMR +subMR = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBTN', 'TBMR'); + subMR{count} = sub; + count = count + 1; +end + +% this loop creates a waveform object for the subset on TBMR +count = 1; +for count = 1:numel(subMR) + w_subMR(count) = waveform(subMR{count}, 'sac'); + count = count + 1; +end + +% Now this step is to do the correlation with the subset on TBMR +clear c, c2, corr, lag, keep, c3, w3, h, subset_list, h1, h11 + +% create correlation object of the waveforms to do the correlation +c = correlation(w_subMR, get(w_subMR, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +%% TBHY-------------------------------------------------------------------- + +% this loop makes a cell array for the subset of events on station TBMR +subHY = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBMR', 'TBHY'); + subHY{count} = sub; + count = count + 1; +end + +% this loop creates a waveform object for the subset on TBMR +count = 1; +for count = 1:numel(subHY) + w_subHY(count) = waveform(subHY{count}, 'sac'); + count = count + 1; +end + +% Now this step is to do the correlation with the subset on TBHY +clear c, c2, corr, lag, keep, c3, w3, h, subset_list, h1, h11 + +% create correlation object of the waveforms to do the correlation +c = correlation(w_subHY, get(w_subHY, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +%% TBHS-------------------------------------------------------------------- + +% this loop makes a cell array for the subset of events on station TBMR +subHS = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBHS'); + subHS{count} = sub; + count = count + 1; +end + +% this loop creates a waveform object for the subset on TBMR +count = 1; +for count = 1:numel(subHS) + w_subHS(count) = waveform(subHY{count}, 'sac'); + count = count + 1; +end + +% Now this step is to do the correlation with the subset on TBHY +clear c, c2, corr, lag, keep, c3, w3, h, subset_list, h1, h11 + +% create correlation object of the waveforms to do the correlation +c = correlation(w_subHS, get(w_subHS, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +%% CREATE A LIST FOR THE NEW SUBSET FOR EACH STATION + +% this loop makes a cell array for the new subset on TBTN +subset_TBTN = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBTN'); + subset_TBTN{count} = sub; + count = count + 1; +end + +% this loop makes a cell array for the new subset on TBMR +subset_TBMR = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBMR'); + subset_TBMR{count} = sub; + count = count + 1; +end + +% this loop makes a cell array for the new subset on TBHS +subset_TBHS = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBHS'); + subset_TBHS{count} = sub; + count = count + 1; +end + +% this loop makes a cell array for the new subset on TBHY +subset_TBHY = subset_list; + +%% WRITE THE SUBSETS TO .DAT FILES + +% Ensure that the previous file being read is closed +% fclose(fid); + +% loops to create the .dat files +fid = fopen('Subset-TBTN.dat', 'w'); +for row = 1:numel(subset_TBTN) + fprintf(fid, '%s\n', subset_TBTN{row}); +end +fclose(fid); + +fid = fopen('Subset-TBMR.dat', 'w'); +for row = 1:numel(subset_TBMR) + fprintf(fid, '%s\n', subset_TBMR{row}); +end +fclose(fid); + +fid = fopen('Subset-TBHY.dat', 'w'); +for row = 1:numel(subset_TBHY) + fprintf(fid, '%s\n', subset_TBHY{row}); +end +fclose(fid); + +fid = fopen('Subset-TBHS.dat', 'w'); +for row = 1:numel(subset_TBHS) + fprintf(fid, '%s\n', subset_TBHS{row}); +end +fclose(fid); + +%% CORRELATIONS AND STACKS WITH THE SUBSET THAT APPEARS ON ALL STATIONS + +% load final waveform subset in for generating the stacks + +% TBTN +for count = 1:numel(subset_TBTN) + fname = subset_TBTN{count}; + WTN(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CTN = correlation(WTN, get(WTN, 'start')); +CTN2 = xcorr(CTN, [1 POSTTRIG]); +corr = get(CTN2, 'CORR'); % just to check correlation coefficients +CTN3 = adjusttrig(CTN2, 'median'); + +% plot(CTN3); + +CTN4 = stack(CTN3); +% plot(CTN4); +WTN4 = waveform(CTN4); +plot(WTN4(end)); % stacked waveform +% plot(WTN4(1)); % master waveform +plot_panels(WTN4, 'alignWaveform', 1) + +% TBMR +clear fname; +for count = 1:numel(subset_TBMR) + fname = subset_TBMR{count}; + WMR(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CMR = correlation(WMR, get(WMR, 'start')); +CMR2 = xcorr(CMR, [1 POSTTRIG]); +corr = get(CMR2, 'CORR'); % just to check correlation coefficients +CMR3 = adjusttrig(CMR2, 'median'); + +% plot(CMR3); + +CMR4 = stack(CMR3); +% plot(CMR4); +WMR4 = waveform(CMR4); +plot(WMR4(end)); % stacked waveform +%plot(WMR4(1)); % master waveform +plot_panels(WMR4, 'alignWaveform', 1) +% TBHY +clear fname +for count = 1:numel(subset_TBHY) + fname = subset_TBHY{count}; + WHY(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CHY = correlation(WHY, get(WHY, 'start')); +CHY2 = xcorr(CHY, [1 POSTTRIG]); +corr = get(CHY2, 'CORR'); % just to check correlation coefficients +CHY3 = adjusttrig(CHY2, 'median'); + +% plot(CHY3); + +CHY4 = stack(CHY3); +% plot(CHY4); +WHY4 = waveform(CHY4); +plot(WHY4(end)); % stacked waveform +%plot(WHY4(1)); % master waveform +plot_panels(WHY4, 'alignWaveform', 1) + +% TBHS +clear fname +for count = 1:numel(subset_TBHS) + fname = subset_TBHS{count}; + WHS(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CHS = correlation(WHS, get(WHS, 'start')); +CHS2 = xcorr(CHS, [1 POSTTRIG]); +corr = get(CHS2, 'CORR'); % just to check correlation coefficients +CHS3 = adjusttrig(CHS2, 'median'); + +% plot(CHS3); + +CHS4 = stack(CHS3); +% plot(CHS4); +WHS4 = waveform(CHS4); +plot(WHS4(end)); % stacked waveform +% plot(WHS4(1)); % master waveform +plot_panels(WHS4, 'alignWaveform', 1) + +end + + +function glenngadzeeks() + +%% STUFF ADDED BY GLENN +% Maths to use for computing arrival time: +% where: +% wEvent is a real event +% lagSeconds is xcorr(wstack, wEvent) +% onsetDelaySeconds is how far into stack we pick the arrival onset +% arrivalDatenum = wEventDateNum + (lagSeconds + onsetDelaySeconds)/SECONDS_PER_DAY +% +% This test example shows that xcorr(a,b) where b is a delayed by 1 sample produces a lag of -1 +% +% b = randn(1,10) + [0 0 0 0 0 10 0 0 0 0]; +% a = randn(1,10) + [0 0 0 0 10 0 0 0 0 0]; +% [acor,lag] = xcorr(a,b); +% plot(lag,acor) +% +% then we create Arrival objects, add them into Catalog object +% +% write Catalog object to Antelope database +% +% Linux script Antelope relocate or dbgenloc or dblocsat to locate events using those Arrivals time +% Also drive db2kml to plot in Google Earth (or GMT) + +end + + + diff --git a/applications/xcorrlocate/MultipletscorrelationGT.m b/applications/xcorrlocate/MultipletscorrelationGT.m new file mode 100644 index 0000000..9506cc2 --- /dev/null +++ b/applications/xcorrlocate/MultipletscorrelationGT.m @@ -0,0 +1,425 @@ +function Multipletscorrelation() + + +clear all +close all +clc +STATIONS = {'TBTN';'TBMR';'TBHY';'TBHS'}; +POSTTRIG = 20 + +%% THIS IS THE ONLY SECTION IN THE SCRIPT THAT NEEDS CHANGED +%SIMPLY CHANGE THE WORKING DIRECTORY AND THE FILENAME TO RUN IT +%NOTE: MUST HAVE THE SAME DATA STRUCTURE AND NAMING SCHEME AS +%MEL'S DATA + +MULTIPLETS_TOP_DIR = '~/Desktop/Multiplets'; +PEAKMATCH_OUTPUT_FILE = 'M-2012-05-11-edit2.dat'; + +% Move to the working directory that contains the correct data structure +% for this script, which is... +cd(MULTIPLETS_TOP_DIR); + +[stacked_waveform, waveforms_that_went_into_stack] = stackwaveforms(PEAKMATCH_OUTPUT_FILE, POSTTRIG, STATIONS); +stack_onset_time = manually_pick_onset_time(stacked_waveform); +arrivals = find_onset_times(stacked_waveforms); % xcorr +write_arrivals_to_antelope(arrivals, dbname); + +end + +function stacked_waveforms = stackwaveforms(PEAKMATCH_OUTPUT_FILE, POSTTRIG, STATIONS) +% % Author: Mitchell Hastings +% % Welcome Weary Seismologists! +% % This is a .m script made to automate the process of creating a subset +% % of waveforms at each station at Telica volcano, Nicaragua. This script +% % can work with any kind of waveform data if you are making subsets of +% % families of waveforms. This script was written for the purpose of a class +% % project for Steve McNutt's Volcano Seismology course. The author is tired +% % and wishes to no longer continue modulating this script, but he may come +% % back to doing so....eventually... +%% NOW HACKED BY GLENN to +% use functions, structures, loops, meaningful variable names +% remove station names hardwired into variable names + +% Read in file that has the original list of the Multiplets and all +% member events (this is for station TBTN only, that is all PeakMatch was run for) +fid = fopen(PEAKMATCH_OUTPUT_FILE,'r'); + +%% TBTN-------------------------------------------------------------------- +% loop to read in each line and read the corresponding SAC file into the +% next element of the waveform vector, w +count = 1; +while 1 + tline = fgetl(fid); + if ~ischar(tline), break, end; + fname = tline; + d = fname(1:4); + full = fullfile(d,fname); + w(count) = waveform(full, 'sac'); + count = count + 1; +end + +% cross-correlate each element of w against every other +c = correlation(w, get(w, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create correlation object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); + +% create waveform vector for these +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +% Use the subset_list to find the waveforms that correlate above the +% threshold on other stations + +%% TBMR-------------------------------------------------------------------- + +% this loop makes a cell array for the subset of events on station TBMR +subMR = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBTN', 'TBMR'); + subMR{count} = sub; + count = count + 1; +end + +% this loop creates a waveform object for the subset on TBMR +count = 1; +for count = 1:numel(subMR) + w_subMR(count) = waveform(subMR{count}, 'sac'); + count = count + 1; +end + +% Now this step is to do the correlation with the subset on TBMR +clear c, c2, corr, lag, keep, c3, w3, h, subset_list, h1, h11 + +% create correlation object of the waveforms to do the correlation +c = correlation(w_subMR, get(w_subMR, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +%% TBHY-------------------------------------------------------------------- + +% this loop makes a cell array for the subset of events on station TBMR +subHY = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBMR', 'TBHY'); + subHY{count} = sub; + count = count + 1; +end + +% this loop creates a waveform object for the subset on TBMR +count = 1; +for count = 1:numel(subHY) + w_subHY(count) = waveform(subHY{count}, 'sac'); + count = count + 1; +end + +% Now this step is to do the correlation with the subset on TBHY +clear c, c2, corr, lag, keep, c3, w3, h, subset_list, h1, h11 + +% create correlation object of the waveforms to do the correlation +c = correlation(w_subHY, get(w_subHY, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +%% TBHS-------------------------------------------------------------------- + +% this loop makes a cell array for the subset of events on station TBMR +subHS = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBHS'); + subHS{count} = sub; + count = count + 1; +end + +% this loop creates a waveform object for the subset on TBMR +count = 1; +for count = 1:numel(subHS) + w_subHS(count) = waveform(subHY{count}, 'sac'); + count = count + 1; +end + +% Now this step is to do the correlation with the subset on TBHY +clear c, c2, corr, lag, keep, c3, w3, h, subset_list, h1, h11 + +% create correlation object of the waveforms to do the correlation +c = correlation(w_subHS, get(w_subHS, 'start')); +c2 = xcorr(c, [1 POSTTRIG]); +corr = get(c2, 'CORR'); +lag = get(c2, 'LAG'); + +% create object with correlation coefficient above 0.7 +corrthresh = 0.7; +keep = find(corr(:,1) > corrthresh); +c3 = subset(c2, keep); +w3 = waveform(c3); + +% Extract the waveform files that correlate above the threshold +h = get(w3, 'history'); +subset_list = cell(numel(h), 1); +count = 1; +for count = 1:numel(h) + h1 = h{count,1}{2,1}; + h11 = strrep(h1,'Loaded SAC file: ', ''); + subset_list{count} = h11; + count = count + 1; +end + +%% CREATE A LIST FOR THE NEW SUBSET FOR EACH STATION + +% this loop makes a cell array for the new subset on TBTN +subset_TBTN = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBTN'); + subset_TBTN{count} = sub; + count = count + 1; +end + +% this loop makes a cell array for the new subset on TBMR +subset_TBMR = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBMR'); + subset_TBMR{count} = sub; + count = count + 1; +end + +% this loop makes a cell array for the new subset on TBHS +subset_TBHS = cell(numel(subset_list),1); +count = 1; +for count = 1:numel(subset_list) + sub = strrep(subset_list{count}, 'TBHY', 'TBHS'); + subset_TBHS{count} = sub; + count = count + 1; +end + +% this loop makes a cell array for the new subset on TBHY +subset_TBHY = subset_list; + +%% WRITE THE SUBSETS TO .DAT FILES + +% Ensure that the previous file being read is closed +% fclose(fid); + +% loops to create the .dat files +fid = fopen('Subset-TBTN.dat', 'w'); +for row = 1:numel(subset_TBTN) + fprintf(fid, '%s\n', subset_TBTN{row}); +end +fclose(fid); + +fid = fopen('Subset-TBMR.dat', 'w'); +for row = 1:numel(subset_TBMR) + fprintf(fid, '%s\n', subset_TBMR{row}); +end +fclose(fid); + +fid = fopen('Subset-TBHY.dat', 'w'); +for row = 1:numel(subset_TBHY) + fprintf(fid, '%s\n', subset_TBHY{row}); +end +fclose(fid); + +fid = fopen('Subset-TBHS.dat', 'w'); +for row = 1:numel(subset_TBHS) + fprintf(fid, '%s\n', subset_TBHS{row}); +end +fclose(fid); + +%% CORRELATIONS AND STACKS WITH THE SUBSET THAT APPEARS ON ALL STATIONS + +% load final waveform subset in for generating the stacks + +% TBTN +for count = 1:numel(subset_TBTN) + fname = subset_TBTN{count}; + WTN(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CTN = correlation(WTN, get(WTN, 'start')); +CTN2 = xcorr(CTN, [1 POSTTRIG]); +corr = get(CTN2, 'CORR'); % just to check correlation coefficients +CTN3 = adjusttrig(CTN2, 'median'); + +% plot(CTN3); + +CTN4 = stack(CTN3); +% plot(CTN4); +WTN4 = waveform(CTN4); +plot(WTN4(end)); % stacked waveform +% plot(WTN4(1)); % master waveform +plot_panels(WTN4, 'alignWaveform', 1) + +% TBMR +clear fname; +for count = 1:numel(subset_TBMR) + fname = subset_TBMR{count}; + WMR(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CMR = correlation(WMR, get(WMR, 'start')); +CMR2 = xcorr(CMR, [1 POSTTRIG]); +corr = get(CMR2, 'CORR'); % just to check correlation coefficients +CMR3 = adjusttrig(CMR2, 'median'); + +% plot(CMR3); + +CMR4 = stack(CMR3); +% plot(CMR4); +WMR4 = waveform(CMR4); +plot(WMR4(end)); % stacked waveform +%plot(WMR4(1)); % master waveform +plot_panels(WMR4, 'alignWaveform', 1) +% TBHY +clear fname +for count = 1:numel(subset_TBHY) + fname = subset_TBHY{count}; + WHY(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CHY = correlation(WHY, get(WHY, 'start')); +CHY2 = xcorr(CHY, [1 POSTTRIG]); +corr = get(CHY2, 'CORR'); % just to check correlation coefficients +CHY3 = adjusttrig(CHY2, 'median'); + +% plot(CHY3); + +CHY4 = stack(CHY3); +% plot(CHY4); +WHY4 = waveform(CHY4); +plot(WHY4(end)); % stacked waveform +%plot(WHY4(1)); % master waveform +plot_panels(WHY4, 'alignWaveform', 1) + +% TBHS +clear fname +for count = 1:numel(subset_TBHS) + fname = subset_TBHS{count}; + WHS(count) = waveform(fname, 'sac'); + count = count + 1; +end + +CHS = correlation(WHS, get(WHS, 'start')); +CHS2 = xcorr(CHS, [1 POSTTRIG]); +corr = get(CHS2, 'CORR'); % just to check correlation coefficients +CHS3 = adjusttrig(CHS2, 'median'); + +% plot(CHS3); + +CHS4 = stack(CHS3); +% plot(CHS4); +WHS4 = waveform(CHS4); +plot(WHS4(end)); % stacked waveform +% plot(WHS4(1)); % master waveform +plot_panels(WHS4, 'alignWaveform', 1) + +end + +function stack_onset_time = manually_pick_onset_time(stacked_waveform); + plot(stacked_waveform) + [t,y]=ginput(1); + close + stack_onset_time = get(stacked_waveform,'start') + t/86400; +end + +function arrivalobj = find_onset_times(stacked_waveforms); % xcorr +% Maths to use for computing arrival time: +% where: +% wEvent is a real event +% lagSeconds is xcorr(wstack, wEvent) +% onsetDelaySeconds is how far into stack we pick the arrival onset +% arrivalDatenum = wEventDateNum + (lagSeconds + onsetDelaySeconds)/SECONDS_PER_DAY +% +% This test example shows that xcorr(a,b) where b is a delayed by 1 sample produces a lag of -1 +end + +function write_arrivals_to_antelope(arrivals, dbname); +% build a Catalog object +end + + + +function ignore_these_notes +%% STUFF ADDED BY GLENN +% Maths to use for computing arrival time: +% where: +% wEvent is a real event +% lagSeconds is xcorr(wstack, wEvent) +% onsetDelaySeconds is how far into stack we pick the arrival onset +% arrivalDatenum = wEventDateNum + (lagSeconds + onsetDelaySeconds)/SECONDS_PER_DAY +% +% This test example shows that xcorr(a,b) where b is a delayed by 1 sample produces a lag of -1 +% +% b = randn(1,10) + [0 0 0 0 0 10 0 0 0 0]; +% a = randn(1,10) + [0 0 0 0 10 0 0 0 0 0]; +% [acor,lag] = xcorr(a,b); +% plot(lag,acor) +% +% then we create Arrival objects, add them into Catalog object +% +% write Catalog object to Antelope database +% +% Linux script Antelope relocate or dbgenloc or dblocsat to locate events using those Arrivals time +% Also drive db2kml to plot in Google Earth (or GMT) + +end + + + diff --git a/applications/xcorrlocate/testfile.m b/applications/xcorrlocate/testfile.m new file mode 100644 index 0000000..e69de29 diff --git a/contributed/+obspy/incomplete.m b/contributed/+obspy/incomplete.m old mode 100644 new mode 100755 diff --git a/contributed/+obspy/stream2waveform.m b/contributed/+obspy/stream2waveform.m old mode 100644 new mode 100755 diff --git a/contributed/+obspy/stream_matfile2waveform.m b/contributed/+obspy/stream_matfile2waveform.m old mode 100644 new mode 100755 diff --git a/contributed/ReadMSEEDFast/ReadMSEEDFast.m b/contributed/ReadMSEEDFast/ReadMSEEDFast.m old mode 100644 new mode 100755 diff --git a/contributed/correlation_derived/corrderived_plot_swarm_history.m b/contributed/correlation_derived/corrderived_plot_swarm_history.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/demo_plutons.m b/contributed/instrument_response/demo_plutons.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_apply.m b/contributed/instrument_response/response_apply.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_cookbook.m b/contributed/instrument_response/response_cookbook.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_demo_database.m b/contributed/instrument_response/response_demo_database.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_demo_waveforms.m b/contributed/instrument_response/response_demo_waveforms.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_get_from_db.m b/contributed/instrument_response/response_get_from_db.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_get_from_polezero.m b/contributed/instrument_response/response_get_from_polezero.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_plot.m b/contributed/instrument_response/response_plot.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_polezero_demo.m b/contributed/instrument_response/response_polezero_demo.m old mode 100644 new mode 100755 diff --git a/contributed/instrument_response/response_structure_description.m b/contributed/instrument_response/response_structure_description.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/+irisdmc/about.m b/contributed/iris_dmc_tools/+irisdmc/about.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/+irisdmc/cookbook.m b/contributed/iris_dmc_tools/+irisdmc/cookbook.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/+irisdmc/station_meta.m b/contributed/iris_dmc_tools/+irisdmc/station_meta.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/+irisdmc/xml2struct.m b/contributed/iris_dmc_tools/+irisdmc/xml2struct.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/dmc_cookbook.m b/contributed/iris_dmc_tools/dmc_cookbook.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/dmc_station_meta.m b/contributed/iris_dmc_tools/dmc_station_meta.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/dmc_xml2struct.m b/contributed/iris_dmc_tools/dmc_xml2struct.m old mode 100644 new mode 100755 diff --git a/contributed/iris_dmc_tools/irisFetch.m b/contributed/iris_dmc_tools/irisFetch.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/+mastercorr/about.m b/contributed/master_correlation/+mastercorr/about.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/+mastercorr/cookbook.m b/contributed/master_correlation/+mastercorr/cookbook.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/+mastercorr/extract.m b/contributed/master_correlation/+mastercorr/extract.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/+mastercorr/load_cookbook_data.m b/contributed/master_correlation/+mastercorr/load_cookbook_data.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/+mastercorr/plot_stats.m b/contributed/master_correlation/+mastercorr/plot_stats.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/+mastercorr/scan.m b/contributed/master_correlation/+mastercorr/scan.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/mastercorr_cookbook.m b/contributed/master_correlation/mastercorr_cookbook.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/mastercorr_extract.m b/contributed/master_correlation/mastercorr_extract.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/mastercorr_plot_stats.m b/contributed/master_correlation/mastercorr_plot_stats.m old mode 100644 new mode 100755 diff --git a/contributed/master_correlation/mastercorr_scan.m b/contributed/master_correlation/mastercorr_scan.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/attenuation/hankelq.m b/contributed_antelope/attenuation/hankelq.m deleted file mode 100644 index 2e0885d..0000000 --- a/contributed_antelope/attenuation/hankelq.m +++ /dev/null @@ -1,186 +0,0 @@ -function hankelq(dbpath, expr, f1, f2, pretrigger, posttrigger, max_arrivals) -%HANKELQ Compute spectral ratio in two frequency bands -% HANKELQ(dbpath, expr, freq_high, freq_low, pretrigger_seconds, posttrigger_seconds, max_arrivals ) -% Loads arrivals from an arrival table (after subsetting with expr) -% retrieves waveform data corresponding to those arrivals, cleans and -% plots the waveform data. The spectral ratio in bands around freq_high -% and freq_low is computed. When done for multiple earthquakes, a plot of this the -% natural log of this ratio versus travel time has a slope from which Q -% can be measured. -% pretrigger_seconds is the number of seconds before the arrival time to -% get waveform data for. -% posttrigger_seconds is the number of seconds after the arrival time to -% get waveform data for. -% max_arrivals is the maximum number of arrivals to process. -% -% Based on the method of Arthur Hankel, "The Effects of Attenuation and -% Site Response on the Spectra of Microearthquakes in the Northeastern -% Caribbean", BSSA, 72, 4, 1379-1402. -% -% Example: -% hankelq('/raid/data/antelope/databases/PLUTONS/dbmerged', 'sta==''PLWB'' && iphase==''P''', 20, 5, 0.3, 1.28, 10); -% -% History: -% April 2014: Glenn Thompson -% Original version: load arrivals, load waveforms and plot waveforms -% April-November 2014: Heather McFarlin -% Modified to also plot amplitude spectra -% November 20, 2014: Glenn Thompson -% Completely overhauled & modularized. -% Modified method to compute amplitude spectra. Now computes Q too. Now -% also generic - works with input variables so it can be used on different -% databases, for example. - - if ~admin.antelope_exists - warning('Antelope not installed on this computer') - return - end - - if ~exist('max_arrivals','var') - max_arrivals=Inf; - end - - taper_seconds=pretrigger+posttrigger; - - arrivals = antelope.dbgetarrivals(dbpath, expr); - w = antelope.arrivals2waveforms(dbpath, arrivals, pretrigger, posttrigger, taper_seconds, max_arrivals); - %w = waveform_clean(w); - [y, t]=plot_arrival_waveforms(arrivals, w, pretrigger, posttrigger, taper_seconds, max_arrivals, f1, f2); - - % This is the figure we use to derive q from spectral ratios for - % earthquakes of different distances (travel times) - % Q comes from the slope - figure; - plot(t, y, 'o'); - ylabel('ln(A_1/A_2)') - xlabel('travel time (s)') - p = polyfit(t,y,1); - xlim=get(gca,'XLim'); - yline = polyval(p, xlim); - hold on; - plot(xlim, yline) - - slope = (yline(2) - yline(1)) / (xlim(2) - xlim(1)); - q = - pi * (f1 - f2) / slope; - title(sprintf('Q = %.0f', q)); -end - -function [y, t] = plot_arrival_waveforms(arrivals, w, pretrigger, posttrigger, taper_seconds, max_arrivals, f1, f2) - FMIN = 1.0; - close all - - %% open an output file - fid=fopen([mfilename,'.txt'],'w'); - - taper_fraction = (taper_seconds * 2) / (taper_seconds * 2 + pretrigger + posttrigger); - - % get travel time for p-wave - p_time = arrivals.time-arrivals.otime; - - % time conversion - anum = epoch2datenum(arrivals.time); - - for i=1:min([max_arrivals numel(w)]) - signal = get(w(i),'data'); - N = length(signal); - if N>0 - - % taper, filter to remove long period noise, and cut out tapered signal to leave only - % untapered signal - wf = w(i); - - taperwin=tukeywin(N, taper_fraction); - signal=signal.*taperwin; - wf = set(wf, 'data', signal); - fmax = get(wf, 'freq') * 0.4; - fobj = filterobject('b', [FMIN fmax], 2); - wf = filtfilt(fobj, wf); - [snum, enum]=gettimerange(wf); - wf=subtime(wf, snum+taper_seconds/86400, enum-taper_seconds/86400); - - % integrate - dwf = integrate(wf); - - % Plot waveform - figure(i) - subplot(3,1,1); - plot(w(i), 'xunit', 'date', 'color', 'r'); %unfiltered signal - hold on; - plot(wf, 'xunit', 'date', 'color', 'g'); %filtered signal - xlabel(sprintf('Time with arrival at %s',datestr(anum(i), 'yyyy-mm-dd HH:MM:SS.FFF'))); - ylabel('Velocity'); - title(sprintf('%s.%s',arrivals.sta{i},arrivals.chan{i})); - % plot arrival time as grey dotted line - ylim=get(gca,'YLim'); - plot([anum(i) anum(i)],ylim,'Color',[0.5 0.5 0.5], 'linestyle', '--') - hold off - - subplot(3,1,2); - plot(dwf, 'xunit', 'date', 'color', 'b'); %filtered & integrated signal - xlabel(sprintf('Time with arrival at %s',datestr(anum(i), 'yyyy-mm-dd HH:MM:SS.FFF'))); - ylabel('Displacement'); - title(sprintf('%s.%s',arrivals.sta{i},arrivals.chan{i})); - % plot arrival time as grey dotted line - ylim=get(gca,'YLim'); - hold on - plot([anum(i) anum(i)],ylim,'Color',[0.5 0.5 0.5], 'linestyle', '--') - hold off - - % compute and plot amplitude spectrum - [A, phi, f] = amplitude_spectrum(dwf); - subplot(3,1,3) - plot(f,A); - %loglog(f, A) - xlabel('Frequency (Hz)') - ylabel('Displacement Amplitude Spectrum') - - % evaluate the spectral ratio - A1=mean(A(find(f>=f1*0.9 & f<=f1*1.1))); % for 20 Hz, this is 18-22 Hz - A2=mean(A(find(f>=f2*0.8 & f<=f2*1.2 ))); % for 5 Hz this is 4-6 Hz - disp(sprintf('A1 = %6.3f, A2 = %6.3f, A1/A2 = %5.2f',A1, A2, A1/A2)); - - % add title - outstr=sprintf('phase=%s orid=%d seaz=%6.2f delta=%5.3f depth=%5.1f\nA1=%5.3f A2=%5.3f A1/A2=%5.2f\n',arrivals.phase{i}, arrivals.orid(i), arrivals.seaz(i), arrivals.delta(i), arrivals.depth(i), A1, A2, A1/A2); - title(outstr) - - % write out to file & close plot - filename=sprintf('%s-%d',mfilename,i); - print('-dpng', figure(i),filename) - close - - % compute y=ln A1/A2 & t for formula 2 in Hankel (1982) - y(i) = log(A1/A2); - [times, phasenames] = arrtimes(arrivals.delta(i), arrivals.depth(i)); - phase_index = 1; - found = false; - while phase_index <= length(times) & ~found - thisphase = lower(phasenames{phase_index}); - thisphase = thisphase(1); - if strcmp(lower(arrivals.phase{i}), thisphase) - t(i)=times(phase_index); - found=true; - end - phase_index = phase_index + 1; - end - - %% write out to file - fprintf(fid,'%14.2f %8.2f %6.3f %5.1f %4.1f %4.1f %e %e\n', p_time(i), t(i), arrivals.delta(i), arrivals.depth(i), f1, f2, A1, A2); - - end - - end - - %% close output file - fclose(fid); -end - - - - - - - - - - - diff --git a/contributed_antelope/attenuation/singlestation_specrat.m b/contributed_antelope/attenuation/singlestation_specrat.m new file mode 100644 index 0000000..612c0cf --- /dev/null +++ b/contributed_antelope/attenuation/singlestation_specrat.m @@ -0,0 +1,247 @@ +function arrivalobj = singlestation_specrat(dbpath, expr, f1, f2, pretrigger, posttrigger, max_arrivals) +%HANKELQ Compute spectral ratio in two frequency bands +% HANKELQ(dbpath, expr, freq_high, freq_low, pretrigger_seconds, posttrigger_seconds, max_arrivals ) +% Loads arrivals from an arrival table (after subsetting with expr) +% retrieves waveform data corresponding to those arrivals, cleans and +% plots the waveform data. The spectral ratio in bands around freq_high +% and freq_low is computed. When done for multiple earthquakes, a plot of this the +% natural log of this ratio versus travel time has a slope from which Q +% can be measured. +% pretrigger_seconds is the number of seconds before the arrival time to +% get waveform data for. +% posttrigger_seconds is the number of seconds after the arrival time to +% get waveform data for. +% max_arrivals is the maximum number of arrivals to process. +% +% Based on the method of Arthur Hankel, "The Effects of Attenuation and +% Site Response on the Spectra of Microearthquakes in the Northeastern +% Caribbean", BSSA, 72, 4, 1379-1402. +% +% Example: +% +% +% History: +% April 2014: Glenn Thompson +% Original version: load arrivals, load waveforms and plot waveforms +% April-November 2014: Heather McFarlin +% Modified to also plot amplitude spectra +% November 20, 2014: Glenn Thompson +% Completely overhauled & modularized. +% Modified method to compute amplitude spectra. Now computes Q too. Now +% also generic - works with input variables so it can be used on different +% databases, for example. +% November 2017: Glenn Thompson +% Fixed to work with updated GISMO classes +% Heather McFarlin +% Added units to Y-axis on plots +% Added amplitude spectrum of noise before waveform onto amplitude spectrum plot +% Added noise part of waveform to top subplot +% NEED TO FIX TO CALCULATE Q based on seaz- Q is azimuthally dependent +% at Uturuncu stations! + + if ~admin.antelope_exists + warning('Antelope not installed on this computer') + return + end + + if ~exist('max_arrivals','var') + max_arrivals=Inf; + end + + taper_seconds=pretrigger+posttrigger; + + %arrivals = antelope.dbgetarrivals(dbpath, expr); + arrivalobj = Arrival.retrieve('antelope', dbpath, 'subset_expr', expr); + %arrivalobj = arrivalobj.subset(expr) + %arrivalobj = arrivalobj.subset(1:max_arrivals); %when done, comment this line out, it's just for testing + arrivalobj = arrivalobj.addwaveforms(datasource('antelope', dbpath), pretrigger+taper_seconds, posttrigger+taper_seconds); + + +% w = antelope.arrivals2waveforms(dbpath, arrivals, pretrigger, posttrigger, taper_seconds, max_arrivals); + %w = waveform_clean(w); + close all + + %[y, t]=plot_arrival_waveforms(arrivals, w, pretrigger, posttrigger, taper_seconds, max_arrivals, f1, f2); + [y, t]=plot_arrival_waveforms(arrivalobj, pretrigger, posttrigger, taper_seconds, max_arrivals, f1, f2); + % This is the figure we use to derive q from spectral ratios for + % earthquakes of different distances (travel times) + % Q comes from the slope + y + t + figure; + plot(t, y, 'o'); + ylabel('ln(A_1/A_2)') + xlabel('travel time (s)') + p = polyfit(t,y,1); + xlim=get(gca,'XLim'); + yline = polyval(p, xlim); + hold on; + plot(xlim, yline) + + slope = (yline(2) - yline(1)) / (xlim(2) - xlim(1)); + q = - pi * (f1 - f2) / slope; + title(sprintf('Q = %.0f', q)); %% Can we add something that denotes whether the Q value is for Qp or Qs? +end + +%function [y, t] = plot_arrival_waveforms(arrivals, w, pretrigger, posttrigger, taper_seconds, max_arrivals, f1, f2) +function [y, t] = plot_arrival_waveforms(arrivalobj, pretrigger, posttrigger, taper_seconds, max_arrivals, f1, f2) + + FMIN = 1.0; + + %% open an output file + fid=fopen([mfilename,'.txt'],'w'); + + taper_fraction = (taper_seconds * 2) / (taper_seconds * 2 + pretrigger + posttrigger); + + % get travel time for p-wave + p_time = arrivalobj.time - arrivalobj.otime + anum = arrivalobj.time; + + + w = arrivalobj.waveforms + + for i=1:min([max_arrivals numel(w)]) + signal = get(w(i),'data'); + N = length(signal); + if N>0 + + % taper, filter to remove long period noise, and cut out tapered signal to leave only + % untapered signal + wf = w(i); + + taperwin=tukeywin(N, taper_fraction); + signal=signal.*taperwin; + wf = set(wf, 'data', signal); + fmax = get(wf, 'freq') * 0.4; + fobj = filterobject('b', [FMIN fmax], 2); + wf = filtfilt(fobj, wf); + [snum, enum]=gettimerange(wf); + + + + %get noise before event + wf_noise = extract(wf, 'time', snum, anum(i)-pretrigger/86400) + + %wf=subtime(wf, snum+taper_seconds/86400, enum-taper_seconds/86400); + wf=extract(wf, 'time', snum+taper_seconds/86400, enum-taper_seconds/86400) + + % integrate + dwf = integrate(wf); + + % integrate noise + + dwf_noise = integrate(wf_noise); + + % Plot waveform + hf(i)=figure; + ax(i,1)=subplot(3,1,1); + plot(w(i), 'xunit', 'date', 'color', 'r', 'axeshandle', ax(i,1)); %detrended and cleaned waveform -artifacts at beginning of signal are likely due to cleaning and detrending filters + datetick('x','HH:MM:SS', 'keeplimits') + hold on; + plot(wf, 'xunit', 'date', 'color', 'g', 'axeshandle', ax(i,1)); %filtered signal + datetick('x','HH:MM:SS','keeplimits') + hold on + plot(wf_noise, 'xunit', 'date', 'color', 'k', 'axeshandle', ax(i,1)) % noise + datetick('x','HH:MM:SS', 'keeplimits') + xlabel(sprintf('Time with arrival at %s',datestr(anum(i), 'yyyy-mm-dd HH:MM:SS.FFF'))); + ylabel('Velocity (nm/s)'); + title(sprintf('%s',arrivalobj.channelinfo{i})); + % plot arrival time as grey dotted line + ylim=get(gca,'YLim'); + hold on + plot([anum(i) anum(i)],ylim,'Color',[0.5 0.5 0.5], 'linestyle', '--') + hold off + + ax(i,2)=subplot(3,1,2); + plot(dwf, 'xunit', 'date', 'color', 'b', 'axeshandle', ax(i,2)); %filtered & integrated signal + datetick('x','HH:MM:SS:FFF', 'keeplimits', 'keepticks') + xlabel(sprintf('Time with arrival at %s',datestr(anum(i), 'yyyy-mm-dd HH:MM:SS.FFF'))); + ylabel('Displacement (nm)'); + title(sprintf('%s',arrivalobj.channelinfo{i})); + % plot arrival time as grey dotted line + ylim=get(gca,'YLim'); + hold on + plot([anum(i) anum(i)],ylim,'Color',[0.5 0.5 0.5], 'linestyle', '--') + hold off + + + % compute and plot amplitude spectrum + s = amplitude_spectrum(dwf); + A = s.amp; + f = s.f; + phi = s.phi; +% [A, phi, f] = amplitude_spectrum(dwf); + ax(i,3)=subplot(3,1,3); + A=smooth(A); + semilogy(f,A); + size(f) + %loglog(f, A) + xlabel('Frequency (Hz)') + ylabel('Amplitude (nm)') + hold on + % add noise spectrum + s_noise = amplitude_spectrum(dwf_noise); + A_noise = s_noise.amp; + f_noise = s_noise.f; + phi_noise = s_noise.phi; + A_noise = smooth(A_noise); + semilogy(f_noise, A_noise, 'color', 'k'); + size(f_noise) + hold on + % evaluate the spectral ratio + A1=mean(A(find(f>=f1*0.9 & f<=f1*1.1))); % for 20 Hz, this is 18-22 Hz + A2=mean(A(find(f>=f2*0.8 & f<=f2*1.2 ))); % for 5 Hz this is 4-6 Hz + patch([f1*0.9 f1*1.1 f1*1.1 f1*0.9],[0 0 A1 A1],[0.9 0.9 0.9]); + patch([f2*0.8 f2*1.2 f2*1.2 f2*0.8],[0 0 A2 A2],[0.9 0.9 0.9]); + %evaluate spectral ratio of noise + A1_noise = mean(A_noise(find(f_noise>=f1*0.9 & f_noise<=f1*1.1))); % for 20 Hz noise + A2_noise = mean(A_noise(find(f_noise>= f2*0.8 & f_noise<=f2*1.2))); % for 5 Hz noise + snr1 = A1/A1_noise; + snr2 = A2/A2_noise; + disp(sprintf('A1 = %6.3f, A2 = %6.3f, A1/A2 = %5.2f', A1, A2, A1/A2)); + hold on + disp(sprintf('SNR1 = %6.3f, SNR2 = %6.3f', snr1, snr2)); + hold off + + + + + + % add title + %outstr=sprintf('phase=%s orid=%d seaz=%6.2f delta=%5.3f depth=%5.1f\nA1=%5.3f A2=%5.3f A1/A2=%5.2f\n',arrivals.phase{i}, arrivals.orid(i), arrivals.seaz(i), arrivals.delta(i), arrivals.depth(i), A1, A2, A1/A2); + outstr=sprintf('phase=%s orid=%d seaz=%6.2f delta=%5.3f depth=%5.1f\nA1=%5.3f A2=%5.3f A1/A2=%5.2f\n SNR1 = %6.3f SNR2 = %6.3f',arrivalobj.iphase{i}, arrivalobj.orid(i), arrivalobj.seaz(i), arrivalobj.delta(i), arrivalobj.depth(i), A1, A2, A1/A2, snr1, snr2); + title(outstr) + + % write out to file & close plot + filename=sprintf('%s-%d',mfilename,i); + print('-dpng', figure(i),filename) + if numel(w)>100 % I changed this from 10 to 100 because when I am passing more than 10 arrivals, my figures end up blank + close + end + + % compute y=ln A1/A2 & t for formula 2 in Frankel (1982) + y(i) = log(A1/A2); + [times, phasenames] = arrtimes(arrivalobj.delta(i), arrivalobj.depth(i)); + phase_index = 1; + found = false; + while phase_index <= length(times) & ~found + thisphase = lower(phasenames{phase_index}); + thisphase = thisphase(1); + %if strcmp(lower(arrivals.phase{i}), thisphase) + if strcmp(lower(arrivalobj.iphase{i}), thisphase) + t(i)=times(phase_index); + found=true; + end + phase_index = phase_index + 1; + end + + %% write out to file + fprintf(fid,'%14.2f %8.2f %6.3f %5.1f %4.1f %4.1f %e %e\n', p_time(i), t(i), arrivalobj.delta(i), arrivalobj.depth(i), f1, f2, A1, A2); + + end + + end + + %% close output file + fclose(fid); +end \ No newline at end of file diff --git a/contributed_antelope/db_get/db_get_arrival_info.m b/contributed_antelope/db_get/db_get_arrival_info.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/db_get/db_get_net_info.m b/contributed_antelope/db_get/db_get_net_info.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/db_get/db_get_origin_info.m b/contributed_antelope/db_get/db_get_origin_info.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/db_get/db_get_site_info.m b/contributed_antelope/db_get/db_get_site_info.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/db_get/db_get_sitechan_info.m b/contributed_antelope/db_get/db_get_sitechan_info.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/double_difference/dd_collate.m b/contributed_antelope/double_difference/dd_collate.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/double_difference/dd_cookbook.m b/contributed_antelope/double_difference/dd_cookbook.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/double_difference/dd_make_scp.m b/contributed_antelope/double_difference/dd_make_scp.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/double_difference/dd_process_scp.m b/contributed_antelope/double_difference/dd_process_scp.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/import_events/arrivals_rearrange.m b/contributed_antelope/import_events/arrivals_rearrange.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/import_events/ie_readchans.m b/contributed_antelope/import_events/ie_readchans.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/import_events/import_events.m b/contributed_antelope/import_events/import_events.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/import_events/waveform2threecomp.m b/contributed_antelope/import_events/waveform2threecomp.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/reduced_displacement/Contents.m b/contributed_antelope/reduced_displacement/Contents.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/reduced_displacement/reducedisp_calc.m b/contributed_antelope/reduced_displacement/reducedisp_calc.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/reduced_displacement/reducedisp_plot.m b/contributed_antelope/reduced_displacement/reducedisp_plot.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/reduced_displacement/reducedisp_wfmeas.m b/contributed_antelope/reduced_displacement/reducedisp_wfmeas.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/reduced_displacement/reducedisp_write_wfmeas.m b/contributed_antelope/reduced_displacement/reducedisp_write_wfmeas.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/about.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/about.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/arrival_histogram.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/arrival_histogram.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/dbload.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/dbload.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/depth_section.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/depth_section.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/do_all.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/do_all.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/map.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/map.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/tt_curve.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/tt_curve.m old mode 100644 new mode 100755 diff --git a/contributed_antelope/traveltime_and_ray_coverage/+ttimes/write_lotos.m b/contributed_antelope/traveltime_and_ray_coverage/+ttimes/write_lotos.m old mode 100644 new mode 100755 diff --git a/cookbooks/Catalog_cookbook.m b/cookbooks/Catalog_cookbook.m new file mode 100755 index 0000000..d664414 --- /dev/null +++ b/cookbooks/Catalog_cookbook.m @@ -0,0 +1,305 @@ +%% Catalog Cookbook +% GISMO can read events from many different earthquake catalog file formats +% (e.g. Seisan, Antelope) and data sources (e.g. IRIS DMC) using the +% Catalog.retrieve() method. + +%% Reading events from IRIS DMC +% To load events into a Catalog object we use the Catalog.retrieve method. +% The first argument is the data source/format - when this is given as +% 'iris', retrieve uses the irisFetch.m program to retrieve event data +% via the IRIS webservices. To narrow down our data search we can give +% retrieve any name-value parameter pairs supported by irisFetch. +% +% In this example we will use retrieve to retrieve all events at IRIS DMC +% with a magnitude of at least 8.0 from year 2000 to 2014 (inclusive): +greatquakes = Catalog.retrieve('iris', 'minimumMagnitude', 8.0, ... + 'starttime', '2000-01-01', 'endtime', '2015-01-01') + +%% +% To access any particular property we can use dot notation, as if the +% object were a structure, e.g.: +greatquakes.mag + +%% +% greatquakes is a Catalog object, an instance of the Catalog class. To see +% a list of functions ("methods" in object-oriented speak) we can apply to +% a Catalog object, use the methods command: +methods(greatquakes) + +%% +% Save this dataset so you can use it again later: +save('great_earthquakes.mat', 'greatquakes') + +%% +% Now we'll do another example - we will get events within 200 km of the +% great M9.0 Tohoku earthquake that occurred on 2011/03/11. +% The mainshock parameters are: +% +% Date/Time: "2011/03/11 05:46:24" +% Longitude: 142.372 +% Latitude: 38.297 +% Depth: 30 km + +%% +% We will limit our search to 1 day before and after the earthquake: +mainshocktime = datenum('2011/03/11 05:46:24'); +tohoku_events = Catalog.retrieve('iris', ... + 'radialcoordinates', [38.297 142.372 km2deg(200)], ... + 'starttime', mainshocktime - 1, ... + 'endtime', mainshocktime + 1); + +%% +% This returns 1136 earthquakes. Let's get a summary: +tohoku_events.summary() + +%% +% Save this dataset so you can use it again later: +save('tohoku_events.mat', 'tohoku_events') + +%% Readings events from an Antelope database +% To load event data from an Antelope/Datascope CSS3.0 database you will +% need to have Antelope () installed, +% including the Antelope toolbox for MATLAB (ATM). To see if ATM is +% installed, use the admin.antelope_exists() command, e.g. +if admin.antelope_exists() + disp('Antelope Toolbox for MATLAB found') +else + disp('Sorry, Antelope not found') +end + +%% +% If you do not have ATM installed, any attempt to read from an Antelope +% database will result in a warning like: +% +% Warning: Sorry, cannot read event Catalog from Antelope database as Antelope toolbox for MATLAB not found +% +% and an empty Catalog object will be returned. + +% +%% +% For the purpose of this exercise we will be using data from Redoubt +% volcano from 2009/03/20 to 2009/03/23. We will use snippets from two +% catalogs that are provided with GISMO in Antelope format: + +%% +% +% * The real-time catalog (rtdb200903). +% * The analyst-reviewed offical AVO catalog (avodb200903). + +%% +% Both catalog segments are included in the "demo" directory. +% We will now load the official AVO catalog into an Events object: +dbpath = fullfile(TESTDATA, 'css3.0', 'avodb200903') +avocatalog = Catalog.retrieve('antelope', 'dbpath', dbpath); + +%% +% This should load 1441 events. What if we only want events within 20km of +% Redoubt volcano? There are two ways to do this. The first is the use the +% radialcoordinates parameter: +redoubtLon = -152.7431; +redoubtLat = 60.4853; +maxR = km2deg(20.0); +redoubt_events = Catalog.retrieve('antelope', 'dbpath', dbpath, ... + 'radialcoordinates', [redoubtLat redoubtLon maxR]) + +%% +% Anyone familiar with Antelope will know that it subsets databases by +% using a dbeval subset expression, and the command above does this +% internally. You can also specify a subset expression directly. The +% following example is completely equivalent to that above: +expr = sprintf('distance(lat, lon, %f, %f) < %f',redoubtLat, redoubtLon,maxR) +redoubt_events = Catalog.retrieve('antelope', 'dbpath', dbpath, ... + 'subset_expression', expr) + +%% +% Save this dataset so you can use it again later: +save('redoubt_events.mat', 'redoubt_events') + + +%% Reading events from a Seisan database +% Here we load events from a Seisan catalog. A Seisan "Sfile" contains all +% the metadata for 1 event. These Sfiles are stored in a flat-file database +% structure the path to which is: $SEISAN_TOP/REA/databaseName. Sfiles are +% organized in year/month subdirectories under this path. +% +% *SCAFFOLD: INCLUDE DEMO DATASET FROM MVOE* + +%% +% The following will navigate this where in this case +% $SEISAN_TOP = '/raid/data/seisan' and the databaseName is MVOE_ which +% stands for the Montserrat Volcano Observatory Event database. +% (In Seisan, databaseName is limited to exactly 5 characters). +% +% This example will load Sfiles from 4 hours on 1st Nov, 1996. This is a slow +% function to run as MATLAB is slow at parsing text files, and there are +% many events per day in this particular database. +%demodir = Catalog.demo.demo_path(); +demodir = fullfile(TESTDATA, 'seisan', 'REA', 'MVOE_'); +montserrat_events = Catalog.retrieve('seisan', ... + 'dbpath', demodir, ... + 'startTime', '1996/11/01 11:00:00', .... + 'endTime', '1996/11/01 15:00:00') + +%% +% Save this dataset so you can use it again later: +save('montserrat_events.mat', 'montserrat_events') + +%% +% Only a few of these earthquakes have been located and even fewer have +% magnitudes. This is common for volcanic earthquakes. Most of these are of +% type 'h' - a hybrid earthquake. + +%% Converting a Zmap data structure to a Catalog object +% ZMap is a graphical application written by Max Wyss & Stefan Wiemer for +% statistical analysis of catalogs. GISMO can convert a ZMap data structure +% into a Catalog object with: +% +% catalogObject = Catalog.retrieve('zmap', zmapdata) + +%% Plotting hypocenter maps +% Catalog objects have three builtin ways for plotting hypocenters + +%% +% Reload the Tohoku dataset +load tohoku_events.mat + +%% +% *Map view & cross-sections* +tohoku_events.plot() + +%% +% *3D-Hypocenters* +tohoku_events.plot3() + +%% +% *web map* +%% +% +% tohoku_events.webmap() +tohoku_events.webmap() +wmzoom(7) + +%% Plotting time series of events +% *Magnitude-time plot* +tohoku_events.plot_time() + +%% +% *Earthquake event counts (number of events per unit time)* +% A plot of seismic catalog per day is often called an "event counts" plot. +% In GISMO, we call this an "event rate plot" and the first step is to +% generate an EventRate object. Here our binsize is 1/24 days, i.e. 1 hour. + +eventrateObject = tohoku_events.eventrate('binsize', 1/24) + +%% +% Now plot the EventRate object: +eventrateObject.plot() + +%% +% We can do the same thing for another dataset, e.g. redoubt_events +redoubt_events.plot_time() +erobj_red = redoubt_events.eventrate('binsize', 1/24) +erobj_red.plot() + +%% +% To see more of the things we can do with EventRate objects see the +% EventRate cookbook + + + +%% Analysis +% *Peak event rate (PR) and maximum magnitude* +% A common type of analysis is to identify the peak rate in an earthquake +% sequence such as this preshock-mainshock-aftershock sequence or an +% earthquake swarm. This can be done with: + +tohoku_events.plotprmm() + + +%% +% In the command window this returns: +% MM=9.1 occurs at 50.0% of time series +% PR=32 occurs at 53.5% of time series +% +% These are labelled on the plot above with PR and MM. + +%% +% Now with the Redoubt dataset +redoubt_events.plotprmm() + +%% +% *b-value and magnitude of completeness* +% Code from "ZMap" (written by Stefan Wiemer and others) has been added to +% Catalog to compute and plot b-values and the magnitude of completeness. + +%% +% Definitions: +% +% * b-value: the slope of a plot of the logarithm of the cumulative number of events against magnitude. A measure of the number of small earthquakes to larger earthquakes. +% * magnitude of completeness (Mc): all events with magnitude>=Mc are in the catalog. Below Mc, not all events are detected, and below the magnitude detection threshold, no events are captured. + +%% +% Just calling the bvalue method, i.e. +% +% catalogObject.bvalue() +% +% displays a menu of techniques available to compute b-value (b) and +% magnitude of completeness (Mc): +% +% -------------------------------------------------------- +% Usage is: eventsObject.bvalue(mcType) +% -------------------------------------------------------- +% mcType can be: +% 1: Maximum curvature +% 2: Fixed Mc = minimum magnitude (Mmin) +% 3: Mc90 (90% probability) +% 4: Mc95 (95% probability) +% 5: Best combination (Mc95 - Mc90 - maximum curvature) +% +% We will use the first menu option: + +tohoku_events.bvalue(1) + +%% +% In this particular example, the b-value is 0.6 and the magnitude of +% completeness is 4.2. + +%% +% Now for the Redoubt events: + +redoubt_events.bvalue(1) + +%% Saving Catalog objects to disk +% *Writing to a MAT file* +% We've already seen how to do this, the general syntax is: +% save('myfilename.mat', 'myCatalogObject') + +%% +% This can simply be loaded again with: +% load('myfilename.mat') + +%% +% *Writing to an Antelope CSS3.0 database* +% This method requires the Antelope toolbox for MATLAB and writes the +% Catalog as a CSS3.0 flat-file database: + +%% +% First make sure there is no database with this name already - else we +% will be appending to it: +delete greatquakes_db* + +%% +% Now write to the database +greatquakes.write('antelope', 'greatquakes_db', 'css3.0') + +%% +% This database can be reloaded with: +greatquakes2 = Catalog.retrieve('antelope', 'dbpath', 'greatquakes_db') + +%% +% Compare: +greatquakes + +%% +% This concludes the Catalog cookbook/tutorial. + diff --git a/core/@EventRate/cookbook.m b/cookbooks/EventRate_cookbook.m old mode 100644 new mode 100755 similarity index 98% rename from core/@EventRate/cookbook.m rename to cookbooks/EventRate_cookbook.m index 75a633b..ba11bec --- a/core/@EventRate/cookbook.m +++ b/cookbooks/EventRate_cookbook.m @@ -6,7 +6,7 @@ %% Simple EventRate example % First we create a Catalog object from the Redoubt dataset -dbpath = Catalog.demo.demodb('avo'); +dbpath = fullfile(TESTDATA, 'css3.0', 'avodb200903'); redoubtLon = -152.7431; redoubtLat = 60.4853; maxR = km2deg(20.0); diff --git a/core/@correlation/cookbook.m b/cookbooks/correlation_cookbook.m old mode 100644 new mode 100755 similarity index 96% rename from core/@correlation/cookbook.m rename to cookbooks/correlation_cookbook.m index 17390e9..4ffb3c7 --- a/core/@correlation/cookbook.m +++ b/cookbooks/correlation_cookbook.m @@ -213,13 +213,13 @@ plot(c1,'int',1,'lag',.01); % plot the lag value interferogram set(gcf,'Position',[50 50 700 400]); - + %% Occurence plot % Display stacked traces for the 10 largest clusters and plot them against % the time history of the events. c = crop(c,-3,10); - plot(c,'occurence',1,1:10); + plot(c,'occurrence',1,1:10); set(gcf,'Position',[50 50 700 400]); @@ -233,7 +233,7 @@ correlationVariables.c1 = c1; correlationVariables.c2 = c2; - correlationVariables.corr = corr; + %correlationVariables.corr = corr; correlationVariables.corr_matrix = corr_matrix; correlationVariables.index = index; correlationVariables.n = n; diff --git a/core/@drumplot/cookbook.m b/cookbooks/drumplot_cookbook.m old mode 100644 new mode 100755 similarity index 93% rename from core/@drumplot/cookbook.m rename to cookbooks/drumplot_cookbook.m index a43692c..b06b39c --- a/core/@drumplot/cookbook.m +++ b/cookbooks/drumplot_cookbook.m @@ -21,7 +21,7 @@ enum=snum+1; % create the datasource object - in this case it is a SAC file -ds = datasource('sac', 'SACDATA/REF.EHZ.2009-03-22T00:00:00.000000Z.sac'); +ds = datasource('sac', 'SACDATA/REF.EHZ.2009-03-22.sac'); % create the waveform object with this datasource, ChannelTag, starttime % and endtime @@ -77,16 +77,16 @@ post_trigger_seconds = 0; % Do not pad after trigger event_detection_params = [sta_seconds lta_seconds thresh_on thresh_off ... minimum_event_duration_seconds]; - +%% % run the STA/LTA detector. lta_mode = 'frozen' means the LTA stops % updating when trigger is "ON". -[cobj,sta,lta,sta_to_lta] = Detection.sta_lta(w2, 'edp', event_detection_params, ... +[detectionObject,sta,lta,sta_to_lta] = Detection.sta_lta(w2, 'edp', event_detection_params, ... 'lta_mode', 'frozen'); % not sure what this is for set(gca, 'XLim', [44*60 48*60]) %% Plot detected events on top of the continuous drumplot -h3 = drumplot(w2, 'mpl', 5, 'catalog', cobj); +h3 = drumplot(w2, 'mpl', 5, 'detections', detectionObject); plot(h3) diff --git a/cookbooks/iceweb_wrapper_cookbook.m b/cookbooks/iceweb_wrapper_cookbook.m new file mode 100644 index 0000000..45713f1 --- /dev/null +++ b/cookbooks/iceweb_wrapper_cookbook.m @@ -0,0 +1,70 @@ +%% How to run IceWeb on archived data + +%% 1. Introduction +% +% It is useful to generate spectrograms, helicorder plots, RSAM data and +% other products from continuous waveform data. +% +% This is where we use "IceWeb", an application originally written in 1998 +% to process continuous waveform data into a variety of products for +% volcano observatory web pages, to aid rapid recognition of anomalous +% activity. Since we only want RSAM data in this case, we will drive this +% using iceweb.rsam_wrapper. To get help on this, use: +%% +% +% help iceweb.iceweb_wrapper +% + +%% +% +% The following is a fully worked example using a Redoubt 2009 +% dataset for illustration. +PRODUCTS_TOP_DIR = './iceweb'; + + +%% 2. Setup for the Redoubt 2009 example + +%% +% Define datasource - where to get waveform data from +dbpath = fullfile(TESTDATA, 'css3.0', 'demodb') +datasourceObject = datasource('antelope', dbpath) + +%% +% Define the list of channels to get waveform data for +ChannelTagList = ChannelTag.array('AV',{'REF';'RSO'},'','EHZ') + +%% +% Set the start and end times +startTime = datenum(2009,3,20,2,0,0) +endTime = datenum(2009,3,23,12,0,0) + + +%% 3. Call the iceweb_wrapper +iceweb.iceweb_wrapper(PRODUCTS_TOP_DIR, 'Redoubt', datasourceObject, ChannelTagList, ... + startTime, endTime); + +%% +% Note: this may take a long time to run, e.g. 1 week of data for 1 +% channel might take about 10 minutes on a desktop computer, reading data +% from a network-mounted drive. + +%% 4. Results +% Running this command creates an iceweb directory. Beneath this are: +% +%% +% * waveforms_raw: raw waveform objects saved into MAT files +% * waveforms_clean: cleaned waveform objects, saved into MAT files +% * waveforms_clean_plots: plots of cleaned waveform objects +% * rsam_data: binary bob files with the mean amplitude, median frequency, peak frequency, frequency ratio and frequency index for each 60-s time window +% * spectrograms: 10-minute spectrogram plots +% * spectral_data: an amplitude spectrum for each 10-minute time window. Will be used to generate 24-hour spectrograms later. +% +% AUTHOR NOTES: To be added to this tutorial: +% +%% +% * Examples of each of the directory structures above. +% * RSAM plots for the whole time series. +% * 24-h spectrograms. +% * 24-h helicorder plots. +% +% These last 2 will be added into iceweb.iceweb_wrapper() \ No newline at end of file diff --git a/cookbooks/rsam_cookbook.m b/cookbooks/rsam_cookbook.m new file mode 100755 index 0000000..fab1942 --- /dev/null +++ b/cookbooks/rsam_cookbook.m @@ -0,0 +1,204 @@ +%% RSAM Cookbook + +%% Short explanation +% RSAM is essentially just a downsampled version of continuous seismic +% data, making it easier to examine long-term trends in the data, e.g. to +% recognize periods of unrest, episodes of tremor, etc. The properties of a +% GISMO RSAM object are: +% +% +% +% * dnum = date/time of time-windows corresponding to each data value, as a +% MATLAB datenum. +% * data = each data value is the RSAM value for a particular time window +% * measure = records which statistic was used. Default "mean", which +% implies that each data value is the mean absolute value of the +% seismic waveform during the corresponding time window. +% * units = records the units that the data is expressed in. Default +% counts, which implies uncalibrated units straight out of the +% digitizer. +% * ChannelTag = records the ChannelTag object corresponding to the time +% series expressed by dnum and data. A ChannelTag object +% records the network, station, location and channel code. +% * snum = the MATLAB datenum corresponding to the start time/date - equal +% to the lowest value in dnum. +% * enum = the MATLAB datenum corresponding to the end time/date - equal +% to the higest value in dnum. +% * sampling_interval = the length of the time window for each RSAM data +% sample, in seconds. (Default 60 seconds) +% +% +%% Longer explanation +% RSAM stands for "Real-time Seismic Amplitude Measurement". An RSAM object +% is a data type that holds RSAM data. Typically each RSAM value is +% computed from the raw continuous seismic data like this: +% +% # Break the seismic data into non-overlapping time windows (typically +% 60-second windows). +% # Compute the mean. Subtract it from the data. This removes the offset +% due to a seismometer not being centred. This process is called +% 'demeaning'. +% # Compute the absolute values of the 'demeaned' data. +% # Then compute the mean average of these absolute demeaned data. This +% mean value is the RSAM value for that time window. +% +%% History of RSAM +% The RSAM system was originally developed in 1985 by Tom Murray. The +% purpose was to track the average amplitudes of data from different +% station-channels over time intervals of 2.56, 60 and 600 seconds, and +% plot them graphically. Alarms could also be configured to detect events +% and tremor based on these RSAM values. +% +% The RSAM system also saved 60-sec values to binary files that could be +% read by the program "BOB" (also written by Tom Murray). +% Through the USGS Volcano Disaster Assistance Program, RSAM and BOB became +% widely used throughout the world at volcano observatories. So there are +% many of these binary files (which GISMO refers to as "BOB" files) in the +% archives of volcano observatories. It is important that we can read them. +% as the raw continuous seismic data was usually not preserved (storage was +% very expensive until about the year 2000). This can be done with the +% 'read_bob_file' method of the RSAM class. +% +% Earthworm, the seismic data acquisition system developed by USGS, also +% includes modules to compute RSAM values, create RSAM alarms, and save +% RSAM values to "BOB" files. We want to be able to read these too, and +% 'read_bob_file' will do this. +% +%% Additional capabilities of GISMO's RSAM class +% GISMO expands on the traditional definition of RSAM in 3 ways: +% +% +% * Traditional RSAM units are "counts" - the raw digitizer output. GISMO +% RSAM objects can have physically meaningful units, e.g. nm / sec. This +% is recorded in the "units" property. +% * Each RSAM value is traditionally the mean amplitude of the seismic +% data within the corresponding time window. But GISMO RSAM objects may +% other measurements, such as the maximum amplitude within that time +% window, or the root-mean-square value, or the median value. The +% 'measure' property records which statistic was used. +% * The traditional sampling interval (time window length) for recorded +% RSAM data is 60 seconds. GISMO RSAM objects may use other sampling +% intervals. Common choices are 1 sec, 60 sec, 600 sec, 3600 sec. + + +%% Computing RSAM from waveform objects +% +% In GISMO, waveform objects are the data type created to hold continuous +% seismic data. The waveform2rsam method can be used to compute RSAM data +% from a waveform object. +% +% First we need to load a waveform object: +filepath = fullfile(TESTDATA, 'waveform_data', 'REF.EHZ.2009.081'); +ds = datasource('miniseed', filepath); +snum = datenum(2009,3,22,0,0,0); +enum = snum + 6/24; +ctag = ChannelTag('','REF','','EHZ'); +w = waveform(ds, ctag, snum, enum); +plot(w) + +%% +% Now compute RSAM data. The default sampling interval is 1 minute, and +% the default sampling method is "mean". +rsamobj1 = waveform2rsam(w) + +%% +% This is equivalent to: +rsamobj2 = waveform2rsam(w, 'mean', 60.0) + +%% +% For "events" it may be more useful to use a finer sampling interval, and +% record the absolute maximum value (greatest amplitude) of each time +% window. Here we use a 10-s sampling rate. +rsamobj3 = waveform2rsam(w, 'max', 10.0) + +%% +% For "tremor" we want to filter out the "events". If all events are +% shorter than 30 seconds, then a median filter with a time window of at +% least 60 seconds should work well. Here we choose a time window of 10 +% minutes: +rsamobj4 = waveform2rsam(w, 'median', 600.0) + +%% Plotting RSAM objects +% There are two ways to plot RSAM objects. The first is the 'plot' method, +% which puts all graphs within the same axis: +rsamvector = [rsamobj1 rsamobj2 rsamobj3 rsamobj4] +rsamvector.plot(); +legend('1:default','2:default2','3:max-10s','4:median-600s') +%% +% The second method is 'plot_panels' which puts plots each RSAM object in a +% different subplot: +rsamvector.plot_panels(); + + +%% Saving RSAM objects +% RSAM objects can be saved to text files, and to binary (bob) files. +% + +%% +% *TEXT FILE* +rsamobj3.save_to_text_file('REF.EHZ.10s.max.txt'); +%% +% The first few lines of this file will look like: +%% +% +% 733854.00000000 2009-03-22 00:00:00.000 6.988e+03 +% 733854.00011574 2009-03-22 00:00:10.000 3.535e+02 +% 733854.00023148 2009-03-22 00:00:20.000 2.503e+02 +% 733854.00034722 2009-03-22 00:00:30.000 6.211e+02 +% 733854.00046296 2009-03-22 00:00:40.000 1.129e+03 +% 733854.00057870 2009-03-22 00:00:50.000 9.490e+02 +% +%% +% The first column is the dnum, the second column expresses the dnum as a +% human-readable yyyy-mm-dd HH:MM:SS.FFF string, and the third column is +% the RSAM data value. The sampling interval in 10-seconds in this case. +% +%% +% *BINARY BOB FILE* +rsamobj3.save_to_bob_file('REF.EHZ.10s.max.bob'); + +%% +% Generally it will be more useful to use a file pattern, where the +% following substitutions will be made: +% SSSS - station +% CCC - channel +% YYYY - year +% MMMM - measure +% +% This way multiple rsam objects can be saved using a simialr filename +% pattern, e.g. +rsamvector.save_to_bob_file('SSSS.CCC.10s.MMMM.bob'); + + +%% +% ------------------------------------------------------------------- +% +% To proceed further with this tutorial, you must have the GISMO test +% dataset available. +if ~is_testdata_setup() + error('GISMO test data must be accessible to proceed further with this tutorial') +end + +%% Loading data from a binary "BOB" file +% The original RSAM system recorded data in binary files that could be read +% by a program called "BOB". Earthworm also has a module to record RSAM +% data in BOB compatible files. +% Each BOB file holds the RSAM data for one station-channel, per year. + +%% +% For one station we can use an explicit path: +dp = fullfile(TESTDATA,'rsam','MCPZ1996.DAT'); +s = rsam.read_bob_file(dp, 'snum', datenum(1996,12,1), ... + 'enum', datenum(1996,12,31), 'sta', 'MCPZ', 'units', 'Counts') +s.plot(); + +%% +% If we want to read across year boundaries, or read all the files in a +% directory, we can use a file pattern. For example to read all files with +% that begin with a 4-character station code, followed by a 4-character +% year, and an extension of '.DAT', use the following file pattern: +dp = fullfile(TESTDATA,'rsam','SSSSYYYY.DAT'); %SSSS means station, YYYY means year +s = rsam.read_bob_file(dp, 'snum', datenum(1996,12,1), ... + 'enum', datenum(1997,2,1), 'sta', 'MCPZ', 'units', 'Counts') +s.plot(); + diff --git a/cookbooks/rsam_wrapper_cookbook.m b/cookbooks/rsam_wrapper_cookbook.m new file mode 100644 index 0000000..4026a68 --- /dev/null +++ b/cookbooks/rsam_wrapper_cookbook.m @@ -0,0 +1,127 @@ +%% How to compute RSAM data + +%% 1. Introduction +% +% Computing rsam data from waveform objects is easy to do with the +% waveform2rsam method. But waveform objects are generally no more than 1 +% hour long. So what if you want to compute RSAM data for days, weeks or +% months of waveform data? How do we set this up? +% +% This is where we use "IceWeb", an application originally written in 1998 +% to process continuous waveform data into a variety of products for +% volcano observatory web pages, to aid rapid recognition of anomalous +% activity. Since we only want RSAM data in this case, we will drive this +% using iceweb.rsam_wrapper. To get help on this, use: +%% +% +% help iceweb.rsam_wrapper +% + +%% +% RSAM data will be saved to binary "BOB" files in a directory +% "iceweb/rsam_data". So the final step in the tutorial will show how to +% load and plot these data. +% +% The following is a fully worked example using the Sakurajima +% dataset for illustration. + + +%% 2. Setup for the Redoubt 2009 example + +%% +% Set the directory name under which RSAM files will be stored +TOP_DIR = 'rsam'; + +%% +% Define datasource - where to get waveform data from +dbpath = fullfile(TESTDATA, 'css3.0', 'demodb') +datasourceObject = datasource('antelope', dbpath) + +%% +% Define the list of channels to get waveform data for +ChannelTagList = ChannelTag.array('AV',{'REF';'RSO'},'','EHZ') + +%% +% Set the start and end times +startTime = datenum(2009,3,20,2,0,0) +endTime = datenum(2009,3,23,12,0,0) + +%% +% We can't process more than 1 day of waveform data at a time, so we have +% to 'gulp it down' in non-overlapping timewindows of size 'gulpMinutes'. +% Experiments suggest 60 minutes is ideal, minimum 10 minutes, maximum 120 +% minutes. +gulpMinutes = 60; + +%% +% RSAM data is traditionally computed with a sampling interval of 60 sec, +% with each RSAM value being the mean absolute value of the waveform data +% in a 60 sec window starting at that time. +% But with GISMO's implementation of RSAM, we can compute multiple RSAM +% datasets from the same waveform data, using different statistical +% measures and sampling intervals. We will compute: +% 1. traditional rsam: mean absolute value of 60-sec time windows +% 2. max absolute values of 10-sec time windows - great for highlighting +% events +% 3. median absolute values of 600-sec time windows - great for +% highlighting tremor +measures = {'mean';'max';'median'}; +samplingIntervalSeconds = [60 10 600]; + + +%% 3. Call the rsam_wrapper +iceweb.rsam_wrapper(TOP_DIR, 'Redoubt', datasourceObject, ChannelTagList, ... + startTime, endTime, gulpMinutes, ... + samplingIntervalSeconds, measures); + +%% +% Note: this may take a long time to run, e.g. 1 week of data for 1 +% channel might take about 10 minutes on a desktop computer, reading data +% from a network-mounted drive. + +%% 4. Load RSAM data +% The RSAM data computed have been stored in binary BOB files. To load +% these we use can loop through our channels, loading one file per channel, +% creating an array of RSAM objects. +% +% Loading a single RSAM file is trivial. Here is the path to one of our +% files that iceweb.rsam_wrapper just created: +rsamfile = fullfile('iceweb', 'rsam_data', 'REF.EHZ.2009.mean.bob') + +%% +% To load this we use the 'read_bob_file' method: +s = rsam.read_bob_file(rsamfile) + +%% +% But in a more general way, we can load multiple RSAM files using a +% filepattern. In a filepattern SSSS is a placeholder for station, CCC is a +% placeholder for channel, YYYY for year, and MMMM for measure. IceWeb +% creates RSAM files using the 'SSSS.CCC.YYYY.MMMM.bob' filepattern. +% +% If you don't want to load all data from all the files matching that +% pattern, you can also specify an optional start time (snum) and end time +% (enum). +s = []; +for c=1:numel(ChannelTagList) + sta = ChannelTagList(c).station(); + chan = ChannelTagList(c).channel(); + filepattern = fullfile('iceweb', 'rsam_data', 'SSSS.CCC.YYYY.MMMM.bob') + r = rsam.read_bob_file(filepattern, 'sta', sta, 'chan', chan, ... + 'snum', startTime, 'enum', endTime, 'measure', 'mean'); + s = [s r]; +end +s + +%% 5. Plotting RSAM data +% There are two ways to plot RSAM data. The first is to use the plot +% method, which generates one figure per channel: +plot(s); + +%% +% The second is plot_panels, which generates one subplot per channel: +plot_panels(s) + +%% +% This is the end of this tutorial. + + \ No newline at end of file diff --git a/cookbooks/sakurajima_iceweb_cookbook.m b/cookbooks/sakurajima_iceweb_cookbook.m new file mode 100644 index 0000000..f0065e6 --- /dev/null +++ b/cookbooks/sakurajima_iceweb_cookbook.m @@ -0,0 +1,126 @@ +%% How to compute RSAM data + +%% 1. Introduction +% +% Computing rsam data from waveform objects is easy to do with the +% waveform2rsam method. But waveform objects are generally no more than 1 +% hour long. So what if you want to compute RSAM data for days, weeks or +% months of waveform data? How do we set this up? +% +% This is where we use "IceWeb", an application originally written in 1998 +% to process continuous waveform data into a variety of products for +% volcano observatory web pages, to aid rapid recognition of anomalous +% activity. Since we only want RSAM data in this case, we will drive this +% using iceweb.rsam_wrapper. To get help on this, use: +%% +% +% help iceweb.rsam_wrapper +% + +%% +% RSAM data will be saved to binary "BOB" files in a directory +% "iceweb/rsam_data". So the final step in the tutorial will show how to +% load and plot these data. +% +% The following is a fully worked example using the Sakurajima +% dataset for illustration. + + +%% 2. Setup for the Sakurajima example + +%% +% Define datasource - where to get waveform data from +datasourceObject = datasource('antelope', '/raid/data/sakurajima/db'); + +%% +% We can't process more than 1 day of waveform data at a time, so we have +% to 'gulp it down' in non-overlapping timewindows of size 'gulpMinutes'. +% Experiments suggest 60 minutes is ideal, minimum 10 minutes, maximum 120 +% minutes. +gulpMinutes = 60; + +%% +% RSAM data is traditionally computed with a sampling interval of 60 sec, +% with each RSAM value being the mean absolute value of the waveform data +% in a 60 sec window starting at that time. +% But with GISMO's implementation of RSAM, we can compute multiple RSAM +% datasets from the same waveform data, using different statistical +% measures and sampling intervals. We will compute: +% 1. traditional rsam: mean absolute value of 60-sec time windows +% 2. max absolute values of 10-sec time windows - great for highlighting +% events +% 3. median absolute values of 600-sec time windows - great for +% highlighting tremor +measures = {'mean';'max';'median'}; +samplingIntervalSeconds = [60 10 600]; + +%% 3. Days with data from SAKA only: 21-23 May 2015 + +%% +% Set up the list of network/station/location/channel combinations +% +% SAKA was installed on May 20, 2015, but there are lots bad data that day +% from people walking around and moving the seismometer. So we will start +% to process SAKA from May 21. +ChannelTagList = ChannelTag.array('JP','SAKA','',{'BD1';'BD2';'BD3';'HHE';'HHN';'HHZ'}); + +%% +% Set the start and end times +startTime = datenum(2015,5,21); +endTime = datenum(2015,5,24); + +%% +% Call the rsam_wrapper +iceweb.rsam_wrapper('Sakurajima', datasourceObject, ChannelTagList, ... + startTime, endTime, gulpMinutes, ... + samplingIntervalSeconds, measures); + +%% +% Note: this may take a long time to run, e.g. 3 days of data for 6 +% channels might take about 15 minutes, depending on the speed of your +% computer and whether the data is being read across a network. + +%% 4. Days with data from SAKA and SAKB: 24 May - 7 Jun 2015 +% SAKB was installed on May 23, 2015, but there are lots bad data that day +% from people walking around and moving the seismometer. So we will add +% SAKB from May 24. +ChannelTagList = [ChannelTagList ChannelTag.array('JP','SAKB','',{'BD1';'BD2';'BD3';'HHE';'HHN';'HHZ'}) ]; +startTime = datenum(2015,5,24); +endTime = datenum(2015,6,8); +iceweb.rsam_wrapper('Sakurajima', datasourceObject, ChannelTagList, ... + startTime, endTime, gulpMinutes, ... + samplingIntervalSeconds, measures); + +%% +% Note: this may take a long time to run, e.g. 2 weeks of data for 12 +% channels might take about an hour, depending on the speed of your +% computer and whether the data is being read across a network. + +%% 5. Load RSAM data +% The RSAM data computed have been stored in binary BOB files. To load +% these we use can loop through our channels, loading one file per channel, +% creating an array of RSAM objects. +s = []; +year = 2015; +for c=1:numel(ChannelTagList) + sta = ChannelTagList(c).station(); + chan = ChannelTagList(c).channel(); + filepattern = fullfile('iceweb', 'rsam_data', 'SSSS.CCC.YYYY.MMMM.bob'); + r = rsam.read_bob_file(filepattern, 'sta', sta, 'chan', chan, ... + 'snum', datenum(2015,5,28), 'enum', datenum(2015,6,8), 'measure', 'median'); + s = [s r]; +end + +%% 6. Plotting RSAM data +% There are two ways to plot RSAM data. The first is to use the plot +% method, which generates one figure per channel: +plot(s); + +%% +% The second is plot_panels, which generates one subplot per channel: +plot_panels(s) + +%% +% This is the end of this tutorial. + + \ No newline at end of file diff --git a/cookbooks/waveform_cookbook.m b/cookbooks/waveform_cookbook.m new file mode 100755 index 0000000..5e99c94 --- /dev/null +++ b/cookbooks/waveform_cookbook.m @@ -0,0 +1,136 @@ +%% waveform +% A waveform object is the GISMO data type for holding seismic waveform +% data. The basic idea is that in GISMO we don't care whether our waveform +% data come from a SAC, Seisan or Miniseed file, or from an Earthworm or +% Winston waveserver, or from an Antelope database - we just want to work +% with waveform objects. So GISMO contains routines to read these different +% data formats into waveform objects, and then methods for processing +% waveform objects. + +%% Reading data files into GISMO +% To read in a single data file, we just need the path and the file format: + +%% +% *Reading a MiniSEED (or SEED file) into MATLAB* +filepath = fullfile(TESTDATA, 'waveform_data', 'REF.EHZ.2009.081') +w = waveform(filepath, 'seed') + +%% +% *Reading a SAC file into MATLAB* +filepath = fullfile(TESTDATA, 'waveform_data', 'REF.EHZ.2009-03-22.sac') +w = waveform(filepath, 'sac') + +%% +% *Reading a Seisan file into MATLAB* +filepath = fullfile(TESTDATA, 'waveform_data', '2001-02-02-0303-55S.MVO___019') +w = waveform(filepath, 'seisan') + +%% Reading from data sources + +%% +% *Reading waveform data from IRIS DMC webservices* +% When reading from IRIS DMC webservices, you must tell GISMO which +% datasource to use ('irisdmcws'), which network/station/location/channel +% combinations to search for (using scnlobject or ChannelTag), and the +% start and end times of the data window you want to load. All these +% parameters must be passed to the waveform function call. +ds = datasource('irisdmcws'); +ctag = ChannelTag('AV', 'RSO', '--', 'EHZ'); +startTime = '2009/03/22 06:00:00'; +endTime = '2009/03/22 07:00:00'; +w = waveform(ds, ctag, startTime, endTime) + +%% +% *Reading waveform data from an Earthworm or Winston waveserver* +% Reading from an Earthworm or Winston waveserver is exactly the same as +% reading from IRIS DMC webservices, except that the datasource should be +% 'earthworm' or 'winston', and the two arguments are the host-ip-address +% and the port number: +% ds = datasource('winston', 'pubavo1.wr.usgs.gov', 16022); +% ctag = ChannelTag('AV', 'RSO', '--', 'EHZ'); +% startTime = now-1/24; +% endTime = now; +% w = waveform(ds, ctag, startTime, endTime) + +%% +% *Reading waveform data from an CSS3.0 flat-file database (the format used +% by Antelope)* +% Reading from an CSS3.0 flat-flat database is exactly the same as reading from IRIS DMC webservices, except that the datasource should be 'css3.0' (or 'antelope') +dbpath = fullfile(TESTDATA, 'css3.0', 'demodb') +ds = datasource('antelope', dbpath); +ctag = ChannelTag('AV', 'REF', '--', 'EHZ'); +startTime = '2009/03/23 06:00:00'; +endTime = '2009/03/23 07:00:00'; +w = waveform(ds, ctag, startTime, endTime) + +%% Processing / Analyzing waveform data +% Once you have loaded your data into a waveform object (or into an array +% of waveform objects), you can do common tasks like make a time series +% plot, detrend or filter, plot an amplitude spectrum, a spectrogram or a +% helicorder. Here are some simple examples: + +%% +% *Plotting* +% Typically the first analysis step is to look at the time series data. +plot(w) + +%% +% *Interpolate missing values* +% Typically the data will have some missing samples, marked by NaN values. +% Some processing will break down with NaN values. So it may be a good idea +% to remove them with interpolation: +w = fillgaps(w,'interp') + +%% +% *Detrending* +% Seismic data usually have an offset (DC) due to the seismometer not being +% completely level, and there is also often a drift because of temperature +% vairations or air currents. Detrending gets rid of this. Note that +% detrending will fail if NaN values are present. So use the 'fillgaps' +% method first, presented in the previous step. +w = detrend(w) +plot(w) + +%% +% *Filtering* +% To remove non-linear trends in data, a high-pass filter is helpful. For +% example, to apply a 0.5-Hz high pass filter, 2 poles, in both directions +% (acausal): +f = filterobject('h', 0.5, 2) +w = filtfilt(f,w) +plot(w) + +%% +% Here are examples of band-pass filters and low-pass filters: +%% +% * Band-pass 0.5-10 Hz, 3 poles +%% +% +% f = filterobject('b', [0.5 10], 3) +%% +% +% * Low-pass 25 Hz, 4 poles +%% +% +% f = filterobject('l', 25, 4) +% +% If only one-way (causal) filtering is wanted, use 'filter' instead of +% 'filtfilt'. + +%% +% *Spectrum* +% An amplitude spectrum can be generated with: +s=amplitude_spectrum(w) +plot(s.f, s.amp); xlabel('Frequency (Hz)'); ylabel('Amplitude') + + +%% +% *Spectrogram* +% A spectrogram can be generated with: +spectrogram(w) + +%% +% *Helical drum recorder plot* +% A helicorder plot can be generated with: +plot_helicorder(w) + diff --git a/core/+Arrival/+read_arrivals/antelope.m b/core/+Arrival/+read_arrivals/antelope.m old mode 100644 new mode 100755 index eef26b9..ba05520 --- a/core/+Arrival/+read_arrivals/antelope.m +++ b/core/+Arrival/+read_arrivals/antelope.m @@ -1,25 +1,73 @@ -function arrivalObj = read_antelope(dbname, subset_expr) - % LOAD_ARRIVALS Load arrivals from a CSS3.0 database - fprintf(2,'Loading arrivals from %s',dbname); +function arrivalObj = antelope(dbname, subset_expr) +% LOAD_ARRIVALS Load arrivals from a CSS3.0 database +% Modifed by Glenn Thompson 2017-11-28 to include some fields from assoc table +% if present, necessary to support hankelq code +ARRIVAL_TABLE_PRESENT = antelope.dbtable_present(dbname, 'arrival'); +ASSOC_TABLE_PRESENT = antelope.dbtable_present(dbname, 'assoc'); +ORIGIN_TABLE_PRESENT = antelope.dbtable_present(dbname, 'origin'); + if ~ARRIVAL_TABLE_PRESENT + fprintf('No arrival table belonging to %s\n',dbname); + return + end - % Open database - db = dbopen(dbname,'r'); + fprintf('Loading arrivals from %s\n',dbname); - % Apply subset expression - db = dblookup_table(db,'arrival'); - if exist('subset_expr','var') - db = dbsubset(db,subset_expr); - end + % Open database + db = dbopen(dbname,'r'); + disp('- database opened'); - % Sort by arrival time - db = dbsort(db,'time'); + % Apply subset expression + db = dblookup_table(db,'arrival'); + disp('- arrival table opened'); + if exist('subset_expr','var') + db = dbsubset(db,subset_expr); + disp('- subsetted database') + end - % Get the values - [sta,chan,time,amp,signal2noise,iphase] = dbgetv(db,'sta','chan','time','amp','snr','iphase'); + nrows = dbnrecs(db); + if nrows > 0 - % Close database link - dbclose(db); + % Sort by arrival time + db = dbsort(db,'time'); + disp('- sorted arrival table') + + seaz =[]; deltim=[]; delta=[]; orid=[]; evid=[]; timeres=[]; otime=[]; depth=[]; + fprintf('- reading %d rows\n',nrows); + [sta,chan,time,amp,signal2noise,iphase, arid] = dbgetv(db,'sta','chan','time','amp','snr','iphase','arid'); + + % Join with assoc table if present + if ASSOC_TABLE_PRESENT + db2 = dblookup_table( db , 'assoc'); + db = dbjoin(db, db2); + + % Get the values + [sta,chan,time,amp,signal2noise,iphase, arid, seaz, deltim, delta, orid, timeres] = ... + dbgetv(db,'sta','chan','time','amp','snr','iphase','arid', ... + 'seaz', 'deltim', 'delta', 'orid', 'timeres'); - arrivalObj = Arrival(cellstr(sta), cellstr(chan), epoch2datenum(time), cellstr(iphase), 'amp', amp, 'signal2noise', signal2noise); + % Join with origin table if present + if ORIGIN_TABLE_PRESENT + db3 = dblookup_table( db , 'origin'); + db = dbjoin(db, db3); + [evid,otime,depth] = dbgetv(db,'evid','time','depth'); + end + end + + % Create arrival object + disp('- creating arrival object') + arrivalObj = Arrival(cellstr(sta), cellstr(chan), epoch2datenum(time), cellstr(iphase), ... + 'amp', amp, 'signal2noise', signal2noise, 'arid', arid, 'seaz', seaz, ... + 'deltim', deltim, 'delta', delta, 'otime', otime, 'orid', orid, ... + 'evid', evid, 'timeres', timeres, 'depth', depth); + + % Close database link + dbclose(db); + disp('- database closed') + + + disp('- complete!') + else + fprintf('No arrivals found matching request\n'); + end end diff --git a/core/+Catalog/+bvalue_lib/calc_Mc.m b/core/+Catalog/+bvalue_lib/calc_Mc.m index 90dd6de..95b61b5 100755 --- a/core/+Catalog/+bvalue_lib/calc_Mc.m +++ b/core/+Catalog/+bvalue_lib/calc_Mc.m @@ -67,7 +67,8 @@ fMc = Catalog.bvalue_lib.calc_McMaxCurvature(mag); case 2 % Fixed Mc (Mc = Mmin) - fMc = min(mag(:,6)); + %fMc = min(mag(:,6)); + fMc = min(mag); case 3 % Automatic Mc90 [fDummy, fDummy, fMc] = Catalog.bvalue_lib.calc_McBest(mag, fBinning); diff --git a/core/+Catalog/+bvalue_lib/calc_McBest.m b/core/+Catalog/+bvalue_lib/calc_McBest.m index 6470aa8..a80d60e 100755 --- a/core/+Catalog/+bvalue_lib/calc_McBest.m +++ b/core/+Catalog/+bvalue_lib/calc_McBest.m @@ -59,9 +59,9 @@ % Is fMc90 available nSel = min(find(mData(:,2) < 10)); if isempty(nSel) - fMc90 = NaN; + fMc90 = NaN else - fMc90 = mData(nSel,1); + fMc90 = mData(nSel,1) end % Is fMc95 available diff --git a/core/+Catalog/+plotEarthquakes/importPlates.m b/core/+Catalog/+plotEarthquakes/importPlates.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/aef.m b/core/+Catalog/+read_catalog/aef.m old mode 100644 new mode 100755 index 13a1420..fd265d8 --- a/core/+Catalog/+read_catalog/aef.m +++ b/core/+Catalog/+read_catalog/aef.m @@ -54,7 +54,8 @@ fprintf('Directory %s not found. Perhaps you need to generate from S files?\n',dbpath); return; end - + snum=startTime; + enum=endTime; lnum=snum; % loop over all years and months selected @@ -62,9 +63,11 @@ [yyyy, mm] = datevec(lnum); % concatenate catalogs - Object0 = import_aef_file(dbpath,yyyy,mm,snum,enum,p.Results.RUNMODE); + Object0 = import_aef_file(dbpath,yyyy,mm,snum,enum);%,p.Results.RUNMODE); if exist('self', 'var') - self = self + Object0; + %self = self + Object0; + %self = combine(self, Object0); + self = add(self, Object0); else self = Object0; end @@ -76,13 +79,13 @@ self.mag(self.mag<-3)=NaN; - if ~isempty(self.dnum) + if ~isempty(self.otime) % cut data according to threshold mag if ~isempty(minimumMagnitude) disp('Applying minimum magnitude filter') m = find(self.mag > minimumMagnitude); - fprintf('Accepting %d events out of %d\n',length(m),length(self.dnum)); + fprintf('Accepting %d events out of %d\n',length(m),length(self.otime)); self.event_list = self.event_list(m); end end @@ -134,8 +137,7 @@ [yr,mn,dd,hh,mi,ss,etype0,mag0] = textread(datfile,'%d %d %d %d %d %d %s %f'); dnum = datenum(yr,mn,dd,hh,mi,ss)'; mag = mag0'; - etype = char(etype0)'; - self = Catalog(dnum, [], [], [], mag, {}, etype); + self = Catalog(dnum, [], [], [], mag, {}, etype0); else disp([datfile,' not found']); end diff --git a/core/+Catalog/+read_catalog/antelope.m b/core/+Catalog/+read_catalog/antelope.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/ensure_dateformat.m b/core/+Catalog/+read_catalog/ensure_dateformat.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/iris.m b/core/+Catalog/+read_catalog/iris.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/seisan.m b/core/+Catalog/+read_catalog/seisan.m old mode 100644 new mode 100755 index 4bc8c33..fa6fb61 --- a/core/+Catalog/+read_catalog/seisan.m +++ b/core/+Catalog/+read_catalog/seisan.m @@ -83,7 +83,7 @@ return; end - % get dir list of matching sfiles + % get dir list of matching sfilesSfile.list_sfiles sfiles = Sfile.list_sfiles(dbpath, snum, enum); % loop over sfiles diff --git a/core/+Catalog/+read_catalog/sru.m b/core/+Catalog/+read_catalog/sru.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/usgsrealtime.m b/core/+Catalog/+read_catalog/usgsrealtime.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/vdap.m b/core/+Catalog/+read_catalog/vdap.m old mode 100644 new mode 100755 diff --git a/core/+Catalog/+read_catalog/zmap.m b/core/+Catalog/+read_catalog/zmap.m old mode 100644 new mode 100755 index 086fec9..534e0d5 --- a/core/+Catalog/+read_catalog/zmap.m +++ b/core/+Catalog/+read_catalog/zmap.m @@ -1,12 +1,28 @@ -function self = zmap(zmapdata) +function self = zmap(fname_or_data) %readEvents.zmap Translate a ZMAP-format data matrix into a Catalog object % ZMAP format is 10 columns: longitude, latitude, decimal year, month, day, % magnitude, depth, hour, minute, second - lon = zmapdata(:,1); - lat = zmapdata(:,2); - time = datenum( floor(zmapdata(:,3)), zmapdata(:,4), zmapdata(:,5), ... - zmapdata(:,8), zmapdata(:,9), zmapdata(:,10) ); - mag = zmapdata(:,6); - depth = zmapdata(:,7); + + if ischar(fname_or_data(1)) + %% read the data + data = load(fname_or_data) + else + data = fname_or_data; + end + lon = data(:,1); + lat = data(:,2); + yyyy = data(:,3); + mm = data(:,4); + dd = data(:,5); + hh = data(:,8); + mi = data(:,9); + if size(data,2) == 10 + ss = data(:,10); + else + ss = zeros(size(mi)); + end + time = datenum( floor(yyyy), mm, dd, hh, mi, ss ); + mag = data(:,6); + depth = data(:,7); self = Catalog(time, lon, lat, depth, mag, {}, {}); end diff --git a/core/+Catalog/subclass2longname.m b/core/+Catalog/subclass2longname.m old mode 100644 new mode 100755 diff --git a/core/+Detection/sta_lta.m b/core/+Detection/sta_lta.m old mode 100644 new mode 100755 index e681083..7e5e6dd --- a/core/+Detection/sta_lta.m +++ b/core/+Detection/sta_lta.m @@ -1,4 +1,5 @@ -function [cobj,sta,lta,sta_to_lta] = sta_lta(wave,varargin) +function [detObj, sta, lta, sta_to_lta] = sta_lta(wave,varargin) +%function [detectionObject, sta, lta, sta_to_lta] = sta_lta(wave, varargin) %STA_LTA: Short-Time-Average/Long-Time-Average event detector. % @@ -35,13 +36,13 @@ % --> 'continuous' - LTA window continues w/ STA window after trigger % is turned on (Same behavior as before trigger) -%OUTPUTS: events - Catalog object +%OUTPUTS: Detection object -% Author: Glenn Thompson 2016-04-19 inspired by an earlier program by Dane +% Author: Glenn Thompson 2016-04-19 based heavily on an earlier program by Dane % Ketner (Alaska Volcano Observatory). The main differences are: -% * algorithm completely rewritten to improve execution speed, clarity +% * algorithm rewritten to improve execution speed, clarity % * visualization of the sta_lta ratio added -% * triggered events are returned as a GISMO Catalog object, for +% * triggered events are returned as a GISMO Detection object, for % consistency across GISMO % % $Date$ @@ -49,9 +50,27 @@ %% Check waveform variable if isa(wave,'waveform') + if numel(wave)>1 + detObj = []; + for wavnum=1:numel(wave) + [detObj0,sta,lta,sta_to_lta] = Detection.sta_lta(wave(wavnum),varargin{:}); + if strcmp(class(detObj0),'Detection') + if isempty(detObj) + detObj = detObj0; + else + detObj = detObj.append(detObj0); + end + end + + end + return + end + Fs = get(wave,'freq'); % Sampling frequency l_v = get(wave,'data_length'); % Length of time series tv = get(wave, 'timevector'); % Time vector of waveform + staname = get(wave, 'station'); + channame = get(wave, 'channel'); if isempty(wave) disp('Input waveform empty, no events detected') events = []; @@ -149,6 +168,7 @@ EVENT_ON = true; eventstart = t(count); lta_freeze_level = lta(count); + snr_start = sta_to_lta(count); end if EVENT_ON & ((sta_to_lta(count) <= th_off) || count == length(y)) @@ -164,6 +184,10 @@ end trig_array(eventnum, 1) = eventstart; trig_array(eventnum, 2) = eventend; + snr_val(eventnum*2-1) = snr_start; + snr_val(eventnum*2) = sta_to_lta(count); +% detectionArray(eventnum*2-1) = Detection(staname, channame, trig_array(eventnum,1) , 'ON', '', snr_start); +% detectionArray(eventnum*2) = Detection(staname, channame, trig_array(eventnum,2) , 'OFF', '', snr_end); eventstart = 0; eventend = 0; end @@ -174,10 +198,18 @@ t_secs=(t-t(1))*86400; ta_secs = (trig_array-t(1))*86400; - ax(1)=subplot(4,1,1);plot(t_secs,get(wave,'data'),'k'),title('waveform') - ax(2)=subplot(4,1,2);plot(t_secs,sta,'k'),title('STA') - ax(3)=subplot(4,1,3);plot(t_secs,lta,'k'),title('LTA') - ax(4)=subplot(4,1,4);plot(t_secs,sta_to_lta,'k'),title('STA:LTA') + ax(1)=subplot(4,1,1); + plot(t_secs, get(wave,'data'), 'k') + title('waveform') + ax(2)=subplot(4,1,2); + plot(t_secs, sta, 'k') + title('STA') + ax(3)=subplot(4,1,3); + plot(t_secs, lta, 'k') + title('LTA') + ax(4)=subplot(4,1,4); + plot(t_secs, sta_to_lta, 'k') + title('STA:LTA') linkaxes(ax,'x') hold on a=axis(); @@ -187,11 +219,24 @@ for count=1:size(ta_secs,1) plot(ta_secs(count,:),[0 0],'b','LineWidth',5); end - if eventnum==0 - cobj = Catalog(); - else - cobj = Catalog([], [], [], [], [], {}, {}, 'ontime', trig_array(:,1), 'offtime', trig_array(:,2)); - end + %% CHANGED CODE FROM RETURNING A CATALOG TO RETURNING A DETECTION VECTOR +% if eventnum==0 +% cobj = Catalog(); +% else +% cobj = Catalog([], [], [], [], [], {}, {}, 'ontime', trig_array(:,1), 'offtime', trig_array(:,2)); +% end + + if eventnum>0 + detObj = Detection(repmat(cellstr(staname),eventnum*2,1), ... + repmat(cellstr(channame),eventnum*2,1), ... + reshape(trig_array', 1, eventnum*2), ... + repmat({'ON';'OFF'},eventnum,1), ... + repmat(cellstr(''), eventnum*2,1), ... + snr_val); + else + detObj = 0; + end + end diff --git a/core/+admin/about.m b/core/+admin/about.m old mode 100644 new mode 100755 diff --git a/core/+admin/antelope_exists.m b/core/+admin/antelope_exists.m old mode 100644 new mode 100755 diff --git a/core/+admin/deprecated.m b/core/+admin/deprecated.m old mode 100644 new mode 100755 diff --git a/core/+admin/getpath.m b/core/+admin/getpath.m old mode 100644 new mode 100755 diff --git a/core/+admin/refresh.m b/core/+admin/refresh.m old mode 100644 new mode 100755 diff --git a/core/+admin/remove.m b/core/+admin/remove.m old mode 100644 new mode 100755 diff --git a/core/+admin/rmgismo.m b/core/+admin/rmgismo.m old mode 100644 new mode 100755 diff --git a/core/+admin/which.m b/core/+admin/which.m old mode 100644 new mode 100755 diff --git a/core/+antelope/antelope2waveform.m b/core/+antelope/antelope2waveform.m old mode 100644 new mode 100755 diff --git a/core/+antelope/arrivals2waveforms.m b/core/+antelope/arrivals2waveforms.m old mode 100644 new mode 100755 diff --git a/core/+antelope/candidate/db_lookupunits.m b/core/+antelope/candidate/db_lookupunits.m old mode 100644 new mode 100755 diff --git a/core/+antelope/dbcp.m b/core/+antelope/dbcp.m new file mode 100755 index 0000000..afe7745 --- /dev/null +++ b/core/+antelope/dbcp.m @@ -0,0 +1,10 @@ +function dbcp( dbpath1, dbpath2 ) +%DBCP Copy one Antelope database to another +% dbcp(sourcedbpath, targetdbpath) calls the dbcp command + + cmdstr = sprintf('%s/bin/dbcp %s %s', getenv('ANTELOPE'),dbpath1, dbpath2); + fprintf('Copying database:\n\t%s\n',cmdstr); + result = system(cmdstr); + +end + diff --git a/core/+antelope/dbcreate.m b/core/+antelope/dbcreate.m old mode 100644 new mode 100755 diff --git a/core/+antelope/dbget_closest_sites.m b/core/+antelope/dbget_closest_sites.m old mode 100644 new mode 100755 index d60b5d7..447ff55 --- a/core/+antelope/dbget_closest_sites.m +++ b/core/+antelope/dbget_closest_sites.m @@ -54,6 +54,10 @@ end nrecs = dbquery(dbptr_site, 'dbRECORD_COUNT'); debug.print_debug(2,sprintf('After time subset: %d records', nrecs)); +if nrecs==0 +sites=struct(); +return +end dbgetv(dbptr_site, 'sta') % Filter the sitechan table diff --git a/core/+antelope/dbget_site_locations.m b/core/+antelope/dbget_site_locations.m new file mode 100644 index 0000000..3ef52f4 --- /dev/null +++ b/core/+antelope/dbget_site_locations.m @@ -0,0 +1,114 @@ +function sites = dbget_site_locations(sitesdb, channeltags, snum, enum) +%DBGET_SITE_LOCATIONS Get sites from an Antelope database +% sites = DBGET_SITE_LOCATIONS(sitesdb, channeltags) +% sites = DBGET_SITE_LOCATIONS(sitesdb, channeltags, snum, enum) + +% AUTHOR: Glenn Thompson +% $Date: $ +% $Revision: -1 $ + debug.printfunctionstack('>') + + % If channeltags given as strings, convert to ChannelTag objects + if isa(channeltags, 'cell') + for c=1:numel(channeltags) + ctag(c) = ChannelTag(channeltags{c}); + end + channeltags = ctag; + end + + debug.print_debug(1,sprintf('sites db is %s',sitesdb)); + dbptr = dbopen(sitesdb, 'r'); + + % Filter the site table + dbptr_site = dblookup_table(dbptr, 'site'); + nrecs = dbquery(dbptr_site, 'dbRECORD_COUNT'); + debug.print_debug(2,sprintf('Site table has %d records', nrecs)); + + + if ~exist('snum', 'var') + % No start time given, so assume we just want sites that exist today. + % Remove any sites that have been decommissioned + debug.print_debug(2,sprintf('offdate == NULL')); + dbptr_site = dbsubset(dbptr_site, sprintf('offdate == NULL')); + else + % Remove any sites that were decommissioned before the start time + debug.print_debug(2,sprintf('offdate == NULL || offdate > ''%d'' ',datenum2julday(snum))); + dbptr_site = dbsubset(dbptr_site, sprintf('offdate == NULL || offdate > ''%d\'' ',datenum2julday(snum))); + end + nrecs = dbquery(dbptr_site, 'dbRECORD_COUNT'); + debug.print_debug(2,sprintf('After startTime subset: %d records', nrecs)); + + % Remove any sites that were installed after the end time (this may remove + % some sites that exist today) + if exist('enum', 'var') + debug.print_debug(2, sprintf('ondate < ''%d'' ',datenum2julday(enum))); + dbptr_site = dbsubset(dbptr_site, sprintf('ondate < ''%d'' ',datenum2julday(enum))); + end + + nrecs = dbquery(dbptr_site, 'dbRECORD_COUNT'); + debug.print_debug(2,sprintf('After endTime subset: %d records', nrecs)); + if nrecs==0 + sites=struct(); + return + end + + sta = dbgetv(dbptr_site, 'sta'); + %chan = dbgetv(dbptr_site, 'chan'); + lat = dbgetv(dbptr_site, 'lat'); + lon = dbgetv(dbptr_site, 'lon'); + elev = dbgetv(dbptr_site, 'elev'); + ondate = dbgetv(dbptr_site, 'ondate'); + offdate = dbgetv(dbptr_site, 'offdate'); + % calib = dbgetv(dbptr_calibration, 'calibration.calib'); + % units = dbgetv(dbptr_calibration, 'calibration.units'); + dbclose(dbptr); + + sites = struct(); + for c=1:numel(channeltags) + ctag = channeltags(c); + thissta = get(ctag,'station'); + i = find(strcmp(sta, thissta)); + if length(i)>0 + sites(c).channeltag = ctag; + sites(c).lat = lat(i); + sites(c).lon = lon(i); + sites(c).elev = elev(i); + else + sites(c).channeltag = ctag; + sites(c).lat = NaN; + sites(c).lon = NaN; + sites(c).elev = NaN; + end + end + + +% % Reformat data into sites return structure +% numsites = numel(lat); +% for c=1:numsites +% yyyy = floor(ondate(c)/1000); +% if yyyy>1900 +% jjj = ondate(c)-yyyy*1000; +% ondnum(c) = datenum(yyyy,1,jjj); +% else +% ondnum(c)=-Inf; +% end +% end +% +% for c=1:numsites +% yyyy = floor(offdate(c)/1000); +% if yyyy>1900 +% jjj = offdate(c)-yyyy*1000; +% offdnum(c) = datenum(yyyy,1,jjj); +% else +% offdnum(c) = Inf; +% end +% end +% +% +% % remove any duplicate sites +% [~,j]=unique({sites.string}); +% sites = sites(j); + + + debug.printfunctionstack('<') +end \ No newline at end of file diff --git a/core/+antelope/dbgetarrivals.m b/core/+antelope/dbgetarrivals.m old mode 100644 new mode 100755 index b48b86f..6403e72 --- a/core/+antelope/dbgetarrivals.m +++ b/core/+antelope/dbgetarrivals.m @@ -1,8 +1,8 @@ function a = dbgetarrivals(databasePath, subset_expr) %DBGETARRIVALS Load arrivals from an Antelope CSS3.0 database % - % arrivals = DBGETORIGINS(dbpath) opens the arrival table belonging to - % the database specified by dbpath. The arrival table will be joined to + % arrivals = DBGETORIGINS(databasePath) opens the arrival table belonging to + % the database specified by databasePath. The arrival table will be joined to % the assoc, origin and event tables too, if these are present. If all % of these is present, only arrivals corresponding to preferred origins % will be loaded. If assoc is present, but not event or origin, only @@ -29,7 +29,7 @@ % All these fields are vectors of numbers, or cell arrays of strings. % If you want more fields adding, please email Glenn % - % arrivals = DBGETARRIVALS(dbpath, subset_expression) evaluates the + % arrivals = DBGETARRIVALS(databasePath, subset_expression) evaluates the % subset specified before reading the arrivals. subset_expression must % be a valid expression accepted by dbe/dbeval. @@ -51,16 +51,16 @@ return end debug.print_debug(0, sprintf('Loading data from %s',databasePath)); - ARRIVAL_TABLE_PRESENT = dbtable_present(databasePath, 'arrival'); + ARRIVAL_TABLE_PRESENT = antelope.dbtable_present(databasePath, 'arrival'); if (ARRIVAL_TABLE_PRESENT) % Open the arrival table, subset if expr exists db = dbopen(databasePath, 'r'); db = dblookup_table(db, 'arrival'); numarrivals = dbquery(db,'dbRECORD_COUNT'); - debug.print_debug(1,sprintf('Got %d records from %s.arrival',numarrivals,dbpath)); + debug.print_debug(1,sprintf('Got %d records from %s.arrival',numarrivals,databasePath)); if numarrivals > 0 - ASSOC_TABLE_PRESENT = dbtable_present(dbpath, 'assoc'); - ORIGIN_TABLE_PRESENT = dbtable_present(dbpath, 'origin'); - EVENT_TABLE_PRESENT = dbtable_present(dbpath, 'event'); + ASSOC_TABLE_PRESENT = antelope.dbtable_present(databasePath, 'assoc'); + ORIGIN_TABLE_PRESENT = antelope.dbtable_present(databasePath, 'origin'); + EVENT_TABLE_PRESENT = antelope.dbtable_present(databasePath, 'event'); if (ASSOC_TABLE_PRESENT) % open and join the assoc table @@ -75,8 +75,9 @@ if dbnrecs(db) > 0 & (EVENT_TABLE_PRESENT) % open and join the event table and subset for prefor db4=dblookup_table( db, 'event'); - db= dbjoin(db, db4); - db = dbsubset('origin.orid = event.prefor'); + db= dbjoin(db, db4); + %dbunjoin(db, 'dbview2') + db = dbsubset( db, 'origin.orid == event.prefor'); end end end @@ -84,15 +85,18 @@ db = dbsubset(db, subset_expr); end + if dbnrecs(db)>0 % read (some) fields & close db + dbsave_view(db) [a.sta, a.chan, a.time, a.phase, a.arid, a.amp, a.snr, a.deltim] = dbgetv(db, 'sta', 'chan', 'arrival.time', 'iphase', 'arid', 'amp', 'snr', 'deltim'); if (ASSOC_TABLE_PRESENT) [a.delta, a.seaz, a.esaz, a.timeres] = dbgetv(db, 'assoc.delta', 'assoc.seaz', 'assoc.esaz', 'assoc.timeres'); end if (ORIGIN_TABLE_PRESENT) [a.otime, a.orid, a.evid] = dbgetv(db, 'origin.time', 'origin.orid', 'origin.evid'); + a.otime a.traveltime = a.time - a.otime; end @@ -112,6 +116,7 @@ % Display counts fprintf('\n%d arrivals\n',numel(a.time)); end + end end diff --git a/core/+antelope/dbgetorigins.m b/core/+antelope/dbgetorigins.m old mode 100644 new mode 100755 diff --git a/core/+antelope/dbtable_present.m b/core/+antelope/dbtable_present.m old mode 100644 new mode 100755 index 46add2c..60fb7c4 --- a/core/+antelope/dbtable_present.m +++ b/core/+antelope/dbtable_present.m @@ -1,14 +1,21 @@ -function present=dbtable_present(dbpath, table) +function present=dbtable_present(dbpath, tablename) % DBTABLE_PRESENT Check if a Datascope database table exists and has more than 0 rows. % -% PRESENT = DBTABLE_PRESENT(DBPATH, TABLE) +% PRESENT = DBTABLE_PRESENT(DBPATH, TABLENAME) % AUTHOR: Glenn Thompson, UAF-GI % $Date$ % $Revision$ present = 0; +if isa(dbpath,'cell') + dbpath = dbpath{1}; +end +if isa(tablename,'cell') + tablename = tablename{1}; +end +tablepath = sprintf('%s.%s',dbpath,tablename); +if exist(dbpath, 'file') || exist(tablepath, 'file') -if exist(dbpath, 'file') || exist(sprintf('%s.%s',dbpath,table), 'file') try db = dbopen(dbpath, 'r'); catch @@ -16,7 +23,7 @@ return; end try - db = dblookup_table(db, table); + db = dblookup_table(db, tablename); numrows = dbquery(db, 'dbRECORD_COUNT'); if numrows > 0 present = numrows; @@ -35,8 +42,8 @@ end catch - debug.print_debug(0, 'Failure: dblookup_table fails for %s.%s',dbpath,table); + debug.print_debug(0, 'Failure: dblookup_table fails for %s',tablepath); end else - debug.print_debug(0,'Failure: cannot find %s or %s.%s on this computer.',dbpath,dbpath,table); + debug.print_debug(0,'Failure: cannot find %s or %s.%s on this computer.',dbpath,tablepath); end diff --git a/core/+antelope/listMiniseedFiles.m b/core/+antelope/listMiniseedFiles.m old mode 100644 new mode 100755 diff --git a/core/+debug/get_debug.m b/core/+debug/get_debug.m old mode 100644 new mode 100755 index e4834a3..24c3230 --- a/core/+debug/get_debug.m +++ b/core/+debug/get_debug.m @@ -12,4 +12,4 @@ debuglevel = debug.print_debug(); -return +end diff --git a/core/+debug/print_debug.m b/core/+debug/print_debug.m old mode 100644 new mode 100755 index 204290c..b28958a --- a/core/+debug/print_debug.m +++ b/core/+debug/print_debug.m @@ -32,8 +32,8 @@ % to read function. % Glenn Thompson 2015: restored order of verboseness (increasing numbers = % more verbose). Implemented workarounds for set_debug and get_debug. -value = []; -persistent Lev + value = []; + persistent Lev if isempty(Lev) % if Lev not already set Lev = 0; @@ -54,4 +54,5 @@ fprintf('\n'); end end + end diff --git a/core/+debug/printfunctionstack.m b/core/+debug/printfunctionstack.m old mode 100644 new mode 100755 diff --git a/core/+debug/set_debug.m b/core/+debug/set_debug.m old mode 100644 new mode 100755 diff --git a/core/+magnitude/ml_richter.m b/core/+magnitude/ml_richter.m old mode 100644 new mode 100755 diff --git a/core/+scnlobject/cookbook.m b/core/+scnlobject/cookbook.m old mode 100644 new mode 100755 diff --git a/core/@Arrival/Arrival.m b/core/@Arrival/Arrival.m index fe0197a..7f001b9 100755 --- a/core/@Arrival/Arrival.m +++ b/core/@Arrival/Arrival.m @@ -2,14 +2,13 @@ % An Arrival object is a container for phase arrival metadata % See also Catalog classdef Arrival - properties(Dependent) - %properties + properties channelinfo - daynumber - %arid + time + arid %jdate iphase - %deltim + deltim %azimuth %delaz %slow @@ -17,84 +16,116 @@ %ema %rect amp - %per + per %clip %fm signal2noise %qual %auth - end - properties(Hidden) - table + seaz + delta + otime + orid + evid + timeres + traveltime + depth + waveforms end methods - function obj = Arrival(sta, chan, daynumber, iphase, varargin) + function obj = Arrival(sta, chan, time, iphase, varargin) + + % Blank constructor + if ~exist('sta','var') + return + end + % Parse required, optional and param-value pair arguments, - % set default values, and add validation conditions + % set default values, and add validation conditions p = inputParser; p.addRequired('sta', @iscell); p.addRequired('chan', @iscell); %p.addRequired('time', @(t) t>0 & t 0 + if numrows > 0 if ~exist('showall','var') showall = false; end if numel(obj) == 1 - if height(obj.table) <= 50 || showall - disp(obj.table) + if numrows <= 50 || showall + for rownum=1:numrows + summarize_row(obj, rownum); + end else - disp(obj.table([1:50],:)) - + for rownum=1:50 + summarize_row(obj, rownum); + end disp('* Only showing first 50 rows/arrivals - to see all rows/arrivals use:') disp('* arrivalObject.disp(true)') end @@ -103,26 +134,137 @@ function summary(obj, showall) end end - function self = subset(self, columnname, findval) - N = numel(self.daynumber); + function summarize_row(self, rownum) + fprintf('%s\t%s\t%s\t%e\t%e\t%e\n', ... + self.channelinfo(rownum), ... + datestr(self.time(rownum)), ... + self.iphase(rownum), ... + self.amp(rownnum), ... + self.per(rownum), ... + self.signal2noise(rownum)); + end + + function self2 = subset(self, columnname, findval) + self2 = self; + N = numel(self.time); indexes = []; - for c=1:N - gotval = eval(sprintf('self.%s(c);',columnname)); - if isa(gotval,'cell') - gotval = cell2mat(gotval); - end - if isnumeric(gotval) - if gotval==findval - indexes = [indexes c]; + if ~exist('findval','var') + % assume columnname is actually row numbers + indexes = columnname; + else + + for c=1:N + gotval = eval(sprintf('self.%s(c);',columnname)); + if isa(gotval,'cell') + gotval = cell2mat(gotval); end - else - if strcmp(gotval,findval) - indexes = [indexes c]; + if isnumeric(gotval) + if gotval==findval + indexes = [indexes c]; + end + else + if strcmp(gotval,findval) + indexes = [indexes c]; + end end end end - self.table = self.table(indexes,:); - end + self2.channelinfo = self.channelinfo(indexes); + self2.time = self.time(indexes); + self2.iphase = self.iphase(indexes); + if numel(self.amp)==N + self2.amp = self.amp(indexes); + end + if numel(self.per)==N + self2.per = self.per(indexes); + end + if numel(self.signal2noise)==N + self2.signal2noise = self.signal2noise(indexes); + end + if numel(self.traveltime)==N + self2.traveltime = self.traveltime(indexes); + end + if numel(self.arid)==N + self2.arid = self.arid(indexes); + end + if numel(self.seaz)==N + self2.seaz = self.seaz(indexes); + end + if numel(self.deltim)==N + self2.deltim = self.deltim(indexes); + end + if numel(self.delta)==N + self2.delta = self.delta(indexes); + end + if numel(self.otime)==N + self2.otime = self.otime(indexes); + end + if numel(self.orid)==N + self2.orid = self.orid(indexes); + end + if numel(self.evid)==N + self2.evid = self.evid(indexes); + end + if numel(self.waveforms)==N + self2.waveforms = self.waveforms(indexes); + end + if numel(self.timeres)==N + self2.timeres = self.timeres(indexes); + end + if numel(self.depth)==N + self2.depth = self.depth(indexes); + end + end + function plot(obj) + ctaguniq = unique(obj.channelinfo) + N = numel(ctaguniq); + hf1=figure; + suptitle('Cumulative arrivals vs time') + hf2=figure; + suptitle('Percentage of arrivals captured by signal2noise') + disp(sprintf('Arrivals: %d',numel(obj.time))) + for c=1:N + indexes = find(strcmp(obj.channelinfo,ctaguniq{c})==1); +% size(indexes) +% indexes(1:10) + figure(hf1) + %subplot(N,1,c); + hold on + t = obj.time(indexes); + y = cumsum(ones(size(t))); + plot(t,y); + %ylabel(ctaguniq{c}); + ylabel(sprintf('Cumulative #\nArrivals')) + xlabel('Date/Time') + datetick('x') + set(gca,'XLim',[min(obj.time) max(obj.time)]); + disp(sprintf('- %s: %d',ctaguniq{c},length(indexes==1))); + + figure(hf2) + %subplot(N,1,c); + hold on + s = obj.signal2noise(indexes); + s(s>101)=101; + [n x]=hist(s, 1:0.1:100); + plot(x,100-cumsum(n)/sum(n)*100); + xlabel('signal2noise'); + %ylabel(ctaguniq{c}); + ylabel('%age') + set(gca, 'XLim', [2.4 20]); + end + figure(hf1) + legend(ctaguniq) + figure(hf2) + legend(ctaguniq) + end + + + % prototypes + [catalogobj,arrivalobj] = associate(self, maxTimeDiff, sites, source) + %arrivalobj = setminman(self, w, pretrig, posttrig, maxtimediff) + arrivalobj = addmetrics(self, maxtimediff) + arrivalobj = addwaveforms(self, datasourceobj, pretrigsecs, posttrigsecs); + write(arrivalobj, FORMAT, PATH); end methods(Static) function self = retrieve(dataformat, varargin) @@ -153,14 +295,20 @@ function summary(obj, showall) % Add in support for 'get_arrivals' debug.printfunctionstack('>') - + self = []; switch lower(dataformat) case {'css3.0','antelope', 'datascope'} if admin.antelope_exists() - self = Arrival.read_arrivals.antelope(varargin{:}); + switch nargin + case 2 + self = Arrival.read_arrivals.antelope(varargin{1}); + case 4 + if strcmp(varargin{2}, 'subset_expr') + self = Arrival.read_arrivals.antelope(varargin{1}, varargin{3}); + end + end else - warning('Sorry, cannot read event Catalog from Antelope database as Antelope toolbox for MATLAB not found') - self = Catalog(); + warning('Antelope toolbox for MATLAB not found') end case 'hypoellipse' self = read_hypoellipse(varargin{:}); @@ -168,9 +316,6 @@ function summary(obj, showall) self = NaN; fprintf('format %s unknown\n\n',dataformat); end - if isempty(self) - self=Catalog(); - end debug.printfunctionstack('<') end diff --git a/core/@Arrival/Arrival_table.m b/core/@Arrival/Arrival_table.m new file mode 100755 index 0000000..9c905d1 --- /dev/null +++ b/core/@Arrival/Arrival_table.m @@ -0,0 +1,213 @@ +%Arrival the blueprint for Arrival objects in GISMO +% An Arrival object is a container for phase arrival metadata +% See also Catalog +classdef Arrival + properties(Dependent) + %properties + channelinfo + time + %arid + %jdate + iphase + %deltim + %azimuth + %delaz + %slow + %delslo + %ema + %rect + amp + per + %clip + %fm + signal2noise + %qual + %auth + end + properties + waveforms + end + properties(Hidden) + table + end + methods + function obj = Arrival(sta, chan, time, iphase, varargin) + % Parse required, optional and param-value pair arguments, + % set default values, and add validation conditions + p = inputParser; + p.addRequired('sta', @iscell); + p.addRequired('chan', @iscell); + %p.addRequired('time', @(t) t>0 & t 0 + if ~exist('showall','var') + showall = false; + end + if numel(obj) == 1 + if height(obj.table) <= 50 || showall + disp(obj.table) + else + disp(obj.table([1:50],:)) + + disp('* Only showing first 50 rows/arrivals - to see all rows/arrivals use:') + disp('* arrivalObject.disp(true)') + end + end + end + end + end + + function self2 = subset(self, columnname, findval) + self2 = self; + N = numel(self.time); + indexes = []; + if ~exist('findval','var') + % assume columnname is actually row numbers + indexes = columnname; + else + + for c=1:N + gotval = eval(sprintf('self.%s(c);',columnname)); + if isa(gotval,'cell') + gotval = cell2mat(gotval); + end + if isnumeric(gotval) + if gotval==findval + indexes = [indexes c]; + end + else + if strcmp(gotval,findval) + indexes = [indexes c]; + end + end + end + end + self2.table = self.table(indexes,:); + +% % now go into misc_fields and apply same index subset to +% % anything with N elements +% fields = fieldnames(self.misc_fields); +% for fieldnum=1:numel(fields) +% fieldval = getfield(self.misc_fields, fields{fieldnum}); +% if numel(fieldval)==N +% self2.misc_fields = setfield(self2.misc_fields, fields{fieldnum}, fieldval(indexes)); +% end +% end + + self2.waveforms = self.waveforms(indexes); + + end + + % prototypes + catalogobj = associate(self, maxTimeDiff) + %arrivalobj = setminman(self, w, pretrig, posttrig, maxtimediff) + arrivalobj = addmetrics(self, maxtimediff) + arrivalobj = addwaveforms(self, datasourceobj, pretrigsecs, posttrigsecs); + end + methods(Static) + function self = retrieve(dataformat, varargin) + %ARRIVAL.RETRIEVE Read arrivals from common file formats & data sources. + % retrieve can read phase arrivals from different earthquake catalog file + % formats (e.g. Seisan, Antelope) and data sources (e.g. IRIS DMC) into a + % GISMO Catalog object. + % + % Usage: + % arrivalObject = ARRIVAL.RETRIEVE(dataformat, 'param1', _value1_, ... + % 'paramN', _valueN_) + % + % dataformat may be: + % + % * 'iris' (for IRIS DMC, using irisFetch.m), + % * 'antelope' (for a CSS3.0 Antelope/Datascope database) + % * 'seisan' (for a Seisan database with a REA/YYYY/MM/ directory structure) + % * 'zmap' (converts a Zmap data strcture to a Catalog object) + % + % See also CATALOG, IRISFETCH, CATALOG_COOKBOOK + + % Author: Glenn Thompson (glennthompson1971@gmail.com) + + %% To do: + % Implement name-value parameter pairs for all methods + % Test the Antelope method still works after factoring out db_load_origins + % Test the Seisan method more + % Add in support for 'get_arrivals' + + debug.printfunctionstack('>') + self = []; + switch lower(dataformat) + case {'css3.0','antelope', 'datascope'} + if admin.antelope_exists() + try + self = Arrival.read_arrivals.antelope(varargin{:}); + catch + % no arrivals + end + else + warning('Antelope toolbox for MATLAB not found') + end + case 'hypoellipse' + self = read_hypoellipse(varargin{:}); + otherwise + self = NaN; + fprintf('format %s unknown\n\n',dataformat); + end + + debug.printfunctionstack('<') + end + + %cookbook() + end +end \ No newline at end of file diff --git a/core/@Arrival/addmetrics.m b/core/@Arrival/addmetrics.m new file mode 100755 index 0000000..8ed7ed6 --- /dev/null +++ b/core/@Arrival/addmetrics.m @@ -0,0 +1,20 @@ +function arrivalobj = addmetrics(arrivalobj, maxTimeDiff) +%ADDMETRICS add metrics to waveforms in Arrival object +% arrivalobj = addmetrics(arrivalobj, maxTimeDiff) + disp('Computing waveform metrics for arrivals') + w = arrivalobj.waveforms; + N = numel(w); + if N>0 + w = addmetrics(w, maxTimeDiff); + amp=-ones(size(w)); + for c=1:N + m = get(w(c), 'metrics'); + amp(c) = max(abs([m.maxAmp m.minAmp])); + end + arrivalobj.amp = amp; + arrivalobj.waveforms = w; + end + +end + + diff --git a/core/@Arrival/addwaveforms.m b/core/@Arrival/addwaveforms.m new file mode 100755 index 0000000..1786db6 --- /dev/null +++ b/core/@Arrival/addwaveforms.m @@ -0,0 +1,71 @@ +function arrivalobj = addwaveforms(arrivalobj, datasourceobj, pretrigsecs, posttrigsecs); +%addwaveforms Add waveform objects corresponding to arrivals +% addwaveforms will attempt to add a waveform object corresponding to +% each arrival row in an Arrival object. It is added as a field to the +% structure misc_fields. +% +% Usage: +% arrivalobj = arrivalobj.addwaveforms(datasourceobj, pretrigsecs, posttrigsecs) +% or: +% arrivalobj = arrivalobj.addwaveforms(waveformobj, pretrigsecs, posttrigsecs) +% +% Example: +% dbpath = '/raid/data/sakurajima/db'; +% ds = datasource('antelope', dbpath); +% arrivalobj = Arrival.retrieve('antelope', dbpath); +% pretrigsecs = 5; +% posttrigsecs = 5; +% arrivalobj = arrivalobj.addwaveforms(ds, pretrigsecs, posttrigsecs) +% + Na = numel(arrivalobj.time); + disp(sprintf('Adding waveforms to all %d arrivals in Arrival object',Na)); + w = []; + numsuccess=0; + + for c=1:Na + ctag = ChannelTag(arrivalobj.channelinfo(c)); + snum = arrivalobj.time(c) - pretrigsecs/86400; + enum = arrivalobj.time(c) + posttrigsecs/86400; + if strcmp(class(datasourceobj),'waveform') + index = match_channel(ctag, get(datasourceobj,'channelinfo') ); + neww = extract(datasourceobj(index),'time',snum,enum); + else +% try + neww = waveform(datasourceobj, ctag, snum, enum); + end + + if isempty(neww) + error('Blank waveform') + end + w = [w neww]; + fprintf('.'); + numsuccess = numsuccess + 1; +% catch +% w = [w waveform()]; +% fprintf('x'); +% end + if mod(c,30) == 0 + fprintf('\nDone %d out of %d\n',c, Na); + end + + end + arrivalobj.waveforms = clean(w'); + fprintf('\n(added %d of %d waveforms successfully)\n', numsuccess, numel(arrivalobj.time)); + +end + +function index=match_channel(chaninfo, chaninfo_list) + index=0; + for c=1:numel(chaninfo_list) + pattern = chaninfo.string(); + if strcmp(pattern(end-2),'_') % take care of when the location has changed between waveform and arrival + pattern = pattern(1:end-3); + end + result=strfind(chaninfo_list{c},pattern); + if ~isempty(result) + index=result; + break; + end + end +end + diff --git a/core/@Arrival/associate.m b/core/@Arrival/associate.m new file mode 100755 index 0000000..b4a4826 --- /dev/null +++ b/core/@Arrival/associate.m @@ -0,0 +1,138 @@ +function [catalogobj, arrivalobj] = associate(arrivalobj, maxTimeDiff, sites, source) +%ASSOCIATE Associate arrivals into events +% [catalogobj] = associate(arrivals, maxTimeDiff) will scan through an +% arrivals object and look for times where there are at least 2 arrivals on +% within maxTimeDiff seconds of each other, and declare an event. No +% checking is done to see if arrivals are on different channels +% +% [catalogobj, arrivalobj] = associate(arrivals, maxTimeDiff, sites) will +% reduce the arrival times by traveltimes in the sites structure. This +% allows a smaller maxTimeDiff to be used (since all airwaves should now +% line up, and all P waves etc.). These traveltimes are in the returned +% arrivalobj. +% +% Example: Imagine you have an infrasound array and you have marked +% infrasound events manually (or automatically) with an "N" phase, picking +% an "N" on at least two channels. You also notice that the time to cross +% the array is less than 0.1s (e.g. array less than 34m across if sound +% speed is 340m/s). Then the following code will load the arrivals, subset +% for "N" phases, and associate into events wherever 2 channels have an "N" +% pick within 0.1s. The final line returns a Catalog object - the GISMO +% container for multiple events. +% +% dbpath = '/raid/data/sakurajima/db'; +% arrivalobj = Arrival.retrieve('antelope', dbpath); +% arrivalobj = arrivalobj.subset('iphase', 'N'); +% catalogobj = arrivalobj.associate(0.1); + +%% REDUCE BY SUBTRACTING TRAVEL TIME +% If sites exist, let's correct the arrival times first +if exist('sites', 'var') + arrivalobj.traveltime = NaN(size(arrivalobj.time)); + for c=1:numel(sites) + thissite = sites(c); + thischanstr = thissite.channeltag.string(); + if strcmp(thischanstr(end-2),'_') + thischanstr=thischanstr(1,end-3); + end + i = strcmp(arrivalobj.channelinfo, thischanstr)==1; + arrivalobj.traveltime(i) = sites(c).traveltime; + arrivalobj.time(i) = arrivalobj.time(i) - sites(c).traveltime/86400; + end +end +% sort arrivalobj in ascending time order +[arrtimes,indices]=sort(arrivalobj.time); +arrivalobj = arrivalobj.subset(indices); + +%% ASSOCIATION ALGORITHM +% 1. First we begin with each arrival and find how many arrivals occur in the +% maxTimeDiff seconds that follow. This way each arrival is assigned a +% weight equal to that number of arrivals. The point of this is to help us +% identify the beginning of each event. + fprintf('\nFinding how many arrivals within %e sec of each arrival\n',maxTimeDiff) + for c=1:numel(arrtimes) + associated_indices{c} = find(arrtimes>=arrtimes(c) & arrtimes<=arrtimes(c)+maxTimeDiff/86400); + numarrivals(c) = numel(associated_indices{c}); + end + + % 2. Remove decrementing series, e.g. replace sequence + % like 6 5 4 3 2 1 with 6 0 0 0 0 0 + numarrivals = [numarrivals(1) diff(numarrivals)+1]; + numarrivals(numarrivals<=1)=0; + + % 3. Now loop over numarrivals and create events + fprintf('Making events') + eventnum = 0; + duplicatecount = 0; + for c=1:numel(numarrivals) + if numarrivals(c)>0 + fprintf('.') + + try + eventarrivalobj = arrivalobj.subset([c : c + numarrivals(c) - 1]); + catch + numarrivals(c) + c+numarrivals(c)-1 + numel(numarrivals) + rethrow(); + end + + +% % remove the reduced time +% if exist('sites','var') +% for cc=1:numel(eventarrivalobj.time) +% eventarrivalobj.time(cc) = eventarrivalobj.time(cc) + eventarrivalobj.traveltime(cc)/86400; +% end +% end + + + % remove duplicate channels + [uc,ia] = unique(eventarrivalobj.channelinfo, 'stable'); + duplicatecount = duplicatecount + numel(eventarrivalobj.channelinfo) - numel(uc); + if numel(ia)>1 + eventarrivalobj = eventarrivalobj.subset(ia); + eventnum = eventnum + 1; + otime(eventnum) = arrtimes(c); + + % set the arrivalobj for this event + arrivalobj2{eventnum} = eventarrivalobj; + firstArrivalTime(eventnum) = min(eventarrivalobj.time); + lastArrivalTime(eventnum) = max(eventarrivalobj.time); + + end + + end + if mod(c,30) == 0 + fprintf('\nProcessed %d out of %d\n',c, numel(numarrivals)); + end + end + + % refix arrivalobj - but now it has traveltimes filled out too + if exist('sites', 'var') + arrivalobj.time = arrivalobj.time + arrivalobj.traveltime/86400; + end + + + %% CREATE CATALOG + fprintf('\nCreating Catalog\n') + if exist('source','var') + olon = source.lon*ones(size(otime)); + olat = source.lat*ones(size(otime)); + else + olon = NaN(size(otime)); + olat = NaN(size(otime)); + end + + for enum=1:numel(otime) + %disp(sprintf('%d: %s-%s',enum,firstArrivalTime,lastArrivalTime)); + end + + catalogobj = Catalog(otime, olon, olat, [], [], {}, {}, 'ontime', firstArrivalTime, 'offtime', lastArrivalTime); + catalogobj.arrivals = arrivalobj2; + fprintf('%d arrivals were determined to be duplicates using a time window of %.1f seconds\n',duplicatecount, maxTimeDiff); + + + +% function result = alreadyHaveArrivalFromThisChannel(ctaglist, thisctag) +% result = sum(cellfun(@(s) ~isempty(strfind(thisctag, s)), ctaglist)); +%end \ No newline at end of file diff --git a/core/@Arrival/readphafile.m b/core/@Arrival/readphafile.m old mode 100644 new mode 100755 diff --git a/core/@Arrival/setminmax.m b/core/@Arrival/setminmax.m new file mode 100755 index 0000000..c58ead6 --- /dev/null +++ b/core/@Arrival/setminmax.m @@ -0,0 +1,113 @@ +function arrivalobj = setminmax(arrivalobj, w, maxTimeDiff, pretrig, posttrig) +% for each arrival, find the minimum and maximum value that lie within +% the window from arrivaltime-pretrig:arrivaltime+pretrig are in within maxTimeDiff seconds of each other + +if ~exist('pretrig', 'var') + pretrig = Inf; +end +if ~exist('posttrig', 'var') + posttrig = Inf; +end + +disp('Computing waveform metrics for arrivals') +SECONDS_PER_DAY = 86400; +maxTime = -1; +minTime = -1; +maxAmp = -1; +minAmp = -1; +p2p = -1; +stdev = -1; +energy = -1; +amp = -ones(size(arrivalobj.amp)); + +for arrivalnum=1:numel(arrivalobj.amp) + fprintf('.'); + thisA = arrivalobj.subset(arrivalnum); + thisW = detrend(fillgaps(w(arrivalnum),'interp')); % make sure there is no trend or offset + wstart = get(thisW,'start'); % waveform start time + wend = get(thisW,'end'); % waveform end time + wstd = std(thisW); % waveform standard deviation - for noise estimation + + % GET THE DATA + y = get(thisW,'data'); + + % COMPUTING AMPLITUDE METRICS + + % Define time window + time_to_begin_at = max([wstart thisA.time - pretrig/SECONDS_PER_DAY]); + time_to_end_at = min([wend thisA.time + posttrig/SECONDS_PER_DAY]); + fs = get(thisW,'freq'); + numSamples = length(y); + seconds_begin_offset = (time_to_begin_at - wstart) * SECONDS_PER_DAY; + seconds_end_offset = (time_to_end_at - wstart) * SECONDS_PER_DAY; + sample_to_begin_at = max( [round( seconds_begin_offset * fs) 1]); + sample_to_end_at = min( [round( seconds_end_offset * fs) numSamples]); + + % Loop over subwindows + % find p2p amplitude in each, compare to highest p2p found so far + subWindowSize = round(fs * maxTimeDiff); + maxA = 0; + for startSamp = sample_to_begin_at:1:sample_to_end_at - subWindowSize + samples = startSamp:startSamp + subWindowSize-1; + [maxy, maxindex] = nanmax(y(samples)); + [miny, minindex] = nanmin(y(samples)); + if (maxy-miny) > maxA % found the biggest peak-to-peak so far, update + maxSecs = ((maxindex+samples(1)-1)/fs); + minSecs = ((minindex+samples(1)-1)/fs); + maxA = maxy-miny; + + % save max and min amplitudes and corresponding times + maxTime = wstart + maxSecs/SECONDS_PER_DAY; + minTime = wstart + minSecs/SECONDS_PER_DAY; + maxAmp = maxy; + minAmp = miny; + p2p = maxy - miny; + amp(arrivalnum) = nanmax(abs([maxy miny])); + end + + end + + % COMPUTE METRICS OF THE WHOLE WAVEFORM + stdev = wstd; % stdev of whole trace - noise level estimate + energy = sum(y(sample_to_begin_at:sample_to_end_at).^2)/fs; + + % ADD ALL METRICS TO THE WAVEFORM OBJECT + thisW = addfield(thisW, 'maxTimeDiff', maxTimeDiff); + thisW = addfield(thisW, 'timeDiff', (maxTime - minTime) * 86400); + thisW = addfield(thisW, 'minTime', minTime); + thisW = addfield(thisW, 'maxTime', maxTime); + thisW = addfield(thisW, 'minAmp', minAmp); + thisW = addfield(thisW, 'maxAmp', maxAmp); + thisW = addfield(thisW, 'p2p', p2p); + thisW = addfield(thisW, 'stdev', stdev); + thisW = addfield(thisW, 'energy', energy); + w(arrivalnum) = thisW; + + + if amp(arrivalnum)==0 + % plot waveform for arrival + fh=plot_panels(thisW, false, thisA); + ah=get(fh,'Children'); + set(fh, 'Position', [0 0 1600 1000]); + hold on + plot(maxSecs, misc_fields.maxAmp(arrivalnum), 'g*'); + plot(minSecs, misc_fields.minAmp(arrivalnum), 'r*'); + % plot(ah,[pos.Start/fs pos.End/fs], [0 0], 'b-'); + % plot(ah,[neg.Start/fs neg.End/fs], [0 0], 'k-'); + teststr = sprintf('maxTime = %s, minTime = %s, timeDiff = %.3f s\namp = %.2e, maxAmp = %.2e, minAmp = %.2e\n rms = %.2e, energy = %.2e', ... + datestr(maxTime,'HH:MM:SS.FFF'), ... + datestr(minTime,'HH:MM:SS.FFF'), ... + 86400*(maxTime-minTime), ... + amp, ... + maxAmp, ... + minAmp, ... + stdev, ... + energy); + text(0.1, 0.1, teststr, 'units', 'normalized') + dummy=input('Any key to continue'); + close + end +end +fprintf('\n(Complete)\n'); +arrivalobj.amp = amp; + diff --git a/core/@Arrival/write.m b/core/@Arrival/write.m new file mode 100644 index 0000000..e48c5f0 --- /dev/null +++ b/core/@Arrival/write.m @@ -0,0 +1,107 @@ +function write(arrivalObject, outformat, outpath, varargin) + %ARRIVAL.WRITE Write an Arrival object to disk + % + % arrivalObject.write('antelope', 'mydb', 'css3.0') writes the + % arrivalObject to a CSS3.0 database called 'mydb' using + % Antelope. Requires Antelope and Antelope Toolbox. Support for + % aefsam0.1 schema will be added later. + % + % Support for other output formats, e.g. Seisan, will be added + % later. + + % Glenn Thompson, 4 February 2015 + + switch outformat + case {'text';'csv';'xls'} % help table.write for more info + write(catalogObject.table, outpath); + + case 'antelope' + + + if admin.antelope_exists + + dbpath = outpath; + + % create new db + if ~exist('schema','var') + schema='css3.0'; + end + antelope.dbcreate(dbpath, schema); + + % remove the following tables if they exist and mode is + % "overwrite" + if nargin==4 & strcmp(varargin{1},'overwrite') + tableNames = {'arrival'}; + for tablenum = 1 : numel(tableNames) + thisTable = sprintf('%s.%s',dbpath,tableNames{tablenum}); + if exist(thisTable, 'file') + if nargin>=4 + if strcmp(varargin{1},'overwrite') + fprintf('Overwrite mode: Removing %s\n',thisTable); + delete(thisTable); + else + % for 'append' mode, nothing to do + fprintf('Append mode: You will append to %s\n',thisTable); + end + else + % nothing specified, so force user to + % choose, as we never want to mess up + % existing tables or delete them without + % user input + choice = input(sprintf('delete %s (y/n)',thisTable),'s'); + if lower(choice(1)=='y') + fprintf('Overwrite mode: Removing %s\n',thisTable); + delete(thisTable); + end + end + end + end + end +% system(sprintf('touch %s.event',dbpath)); +% system(sprintf('touch %s.origin',dbpath)); + + + disp('Writing new rows...'); + + % open db + db = dbopen(dbpath, 'r+'); + dbar = dblookup_table(db,'arrival'); + + thisA = arrivalObject; + N = numel(thisA.time); + + if N>0 + for arrnum = 1:N + ctag = ChannelTag(thisA.channelinfo{arrnum}); + asta = ctag.station; + atime = datenum2epoch(thisA.time(arrnum)); + aarid = dbnextid(dbar,'arid'); + achan = ctag.channel; + aiphase = thisA.iphase{arrnum}; + try + aamp = thisA.amp(arrnum); + %aper = thisA.per(arrnum); + %asnr = thisA.snr(arrnum); + catch + aamp = -1; + end + + % add arrival row + dbar.record = dbaddnull(dbar); + dbputv(dbar, 'sta', asta, ... + 'time', atime, ... + 'arid', aarid, ... + 'chan', achan, ... + 'iphase', aiphase, ... + 'amp', aamp); + %'per', aper, ... + %'snr', asnr, ... + end + end + dbclose(db); + disp('(Complete)'); + end + otherwise, + warning('format not supported yet') + end % end switch +end % function diff --git a/core/@Calibration/Calibration.m b/core/@Calibration/Calibration.m new file mode 100644 index 0000000..b17727c --- /dev/null +++ b/core/@Calibration/Calibration.m @@ -0,0 +1,58 @@ +%SITE the blueprint for Calibration objects in GISMO +classdef Calibration + + properties + nslc; + calib; + ondate; + offdate; + end + + properties(Dependent) % need get methods + + end + + methods + + function sobj = Calibration(varargin) + %Calibration.Calibration constructor for Calibration object + % sobj = Calibration(ChannelTag, calibration [[, ondate [, offdate]) + + % Blank constructor + if nargin==0 + return + end + + % Parse required, optional and param-value pair arguments, + % set default values, and add validation conditions + p = inputParser; + p.addRequired('nslc', @(c) strcmp(class(c),'ChannelTag') ) % positional + p.addRequired('calib', @isnumeric ) % positional + p.addOptional('ondate', [], @(t1) t1>datenum(1900,1,1) & t1datenum(1900,1,1)) + p.parse(varargin{:}); + fields = fieldnames(p.Results); + for i=1:length(fields) + field=fields{i}; + val = p.Results.(field); + eval(sprintf('sobj.%s = val;',field)); + end + + end + +% % Get methods +% function val = get.duration(obj) +% val = 86400 * (obj.offtime - obj.ontime); +% end + + % Prototypes + + end +%% --------------------------------------------------- + methods (Access=protected, Hidden=true) + end + + methods(Static) + end + +end diff --git a/core/@Catalog/Catalog.m b/core/@Catalog/Catalog.m old mode 100644 new mode 100755 index eedb18d..8310dc9 --- a/core/@Catalog/Catalog.m +++ b/core/@Catalog/Catalog.m @@ -3,10 +3,10 @@ % See also EventRate, readEvents, Catalog/Cookbook classdef Catalog - properties(Dependent) % These all come from table, computed on the fly + properties otime = [];% origin time - date = {}; - time = {}; +% date = {}; +% time = {}; lon = []; lat = []; depth = []; @@ -15,12 +15,6 @@ etype = {}; ontime = []; offtime = []; - - numberOfEvents = 0; - - end - - properties % These are properties of the catalog itself request = struct(); % request.dataformat = ''; % request.minimumLongitude = -Inf; @@ -38,12 +32,15 @@ waveforms = {}; % cell array with one vector waveform objects per event end - properties(Hidden) % internal, external code cannot access them - table = table([], [], [],[], [], [], [], {}, {}, [], [], ... - 'VariableNames', ... - {'otime' 'date' 'time' 'lon' 'lat' 'depth' 'mag' 'magtype' 'etype' 'ontime' 'offtime'}); + properties(Dependent) + numberOfEvents; + duration; + cum_mag; + max_mag; + peakrate; end + methods %function obj = Catalog(otime, lon, lat, depth, mag, magtype, etype, varargin) @@ -56,13 +53,13 @@ return end - % Table constructor - if nargin==1 - if isa(varargin{1},'table') - obj.table = varargin{1}; - end - return - end +% % Table constructor +% if nargin==1 +% if isa(varargin{1},'table') +% obj.table = varargin{1}; +% end +% return +% end % Parse required, optional and param-value pair arguments, % set default values, and add validation conditions @@ -138,213 +135,147 @@ end clear s s1 s2 - dstr = datestr(otime, 'yyyy_mm_dd'); - tstr = datestr(otime, 'HH:MM:SS.fff'); - tstr = tstr(:,1:10); +% dstr = datestr(otime, 'yyyy_mm_dd'); +% tstr = datestr(otime, 'HH:MM:SS.fff'); +% tstr = tstr(:,1:10); + obj.otime = otime; + obj.lat = lat; + obj.lon = lon; + obj.depth = depth; + obj.mag = mag; + obj.magtype = magtype; + obj.etype = etype; + obj.ontime = ontime; + obj.offtime = offtime; - obj.table = table(otime, dstr, tstr, ... - lon, lat, depth, mag, magtype, etype, ontime, offtime, ... - 'VariableNames', {'otime' 'yyyy_mm_dd' 'hh_mm_ss' 'lon' 'lat' 'depth' 'mag' 'magtype' 'etype' 'ontime' 'offtime'}); - - obj.table = sortrows(obj.table, 'otime', 'ascend'); fprintf('Got %d events\n',obj.numberOfEvents); end end - function val = get.otime(obj) - val = obj.table.otime; - end - - function val = get.lon(obj) - val = obj.table.lon; - end - - function val = get.lat(obj) - val = obj.table.lat; - end - - function val = get.depth(obj) - val = obj.table.depth; - end - - function val = get.mag(obj) - val = obj.table.mag; - end - - function val = get.magtype(obj) - val = obj.table.magtype; - end - - function val = get.etype(obj) - val = obj.table.etype; +% function val = get.otime(obj) +% val = obj.table.otime; +% end +% +% function val = get.lon(obj) +% val = obj.table.lon; +% end +% +% function val = get.lat(obj) +% val = obj.table.lat; +% end +% +% function val = get.depth(obj) +% val = obj.table.depth; +% end +% +% function val = get.mag(obj) +% val = obj.table.mag; +% end +% +% function val = get.magtype(obj) +% val = obj.table.magtype; +% end +% +% function val = get.etype(obj) +% val = obj.table.etype; +% end +% +% function val = get.ontime(obj) +% val = obj.table.ontime; +% end +% +% function val = get.offtime(obj) +% val = obj.table.offtime; +% end + + function val = get.duration(obj) + val = 86400 * (obj.offtime - obj.ontime); end - function val = get.ontime(obj) - val = obj.table.ontime; + function val = get.numberOfEvents(obj) + val = max([ numel(obj.otime) numel(obj.ontime)]); end + + function val = get.cum_mag(obj) + val = magnitude.eng2mag( sum(magnitude.mag2eng(obj.mag)) ); + end - function val = get.offtime(obj) - val = obj.table.offtime; + function mm = get.max_mag(obj) + % return max_mag as the real component & percentage through the + % time series as the imaginary component (use real() & imag() + % to separate these) + t=obj.gettimerange(); + days = t(2) - t(1); + [mm, mmi] = max(obj.mag); + mmpercent = 100*(obj.otime(mmi) - t(1))/days; + mm = mm + mmpercent * j; end - function val = get.numberOfEvents(obj) - val = height(obj.table); + function pr = get.peakrate(obj) + t=obj.gettimerange(); + days = t(2) - t(1); + binsize = days/100; + erobj = obj.eventrate('binsize',binsize); + [pr, pri] = max(erobj.counts); + pr = pr + 100*(erobj.time(pri) - erobj.snum)/(erobj.enum-erobj.snum) * j; end + + function t=gettimerange(obj) - snum = nanmin([obj.table.otime; obj.table.ontime]); - enum = nanmax([obj.table.otime; obj.table.offtime]); + snum = nanmin([obj.otime; obj.ontime]); + enum = nanmax([obj.otime; obj.offtime]); t = [snum enum]; end + + function cobj3 = add(cobj1, cobj2) +% combine method already exists, but uses tables - Catalog isn't a table +% anymore + cobj3 = cobj1; + cobj3.otime = [cobj1.otime; cobj2.otime]; + cobj3.lon = [cobj1.lon; cobj2.lon]; + cobj3.lat = [cobj1.lat; cobj2.lat]; + cobj3.depth = [cobj1.depth; cobj2.depth]; + cobj3.mag = [cobj1.mag; cobj2.mag]; + cobj3.magtype = [cobj1.magtype; cobj2.magtype]; + cobj3.etype = [cobj1.etype; cobj2.etype]; + cobj3.ontime = [cobj1.ontime; cobj2.ontime]; + cobj3.offtime = [cobj1.offtime; cobj2.offtime]; + cobj3.arrivals = [cobj1.arrivals; cobj2.arrivals]; + cobj3.waveforms = [cobj1.waveforms; cobj2.waveforms]; + end + % Prototypes - summary(obj) - disp(catalogObject) + gr = bvalue(catalogObject, mcType) + catalogObject = addwaveforms(catalogObject, varargin); catalogObject = combine(catalogObject1, catalogObject2) - webmap(catalogObject) + catalogObject2 = subset(catalogObject, varargin) + catalogObjects=subclassify(catalogObject, subclasses) + disp(catalogObject) + eev(obj, eventnum) + erobj=eventrate(catalogObject, varargin) + hist(catalogObject) + list_waveform_metrics(catalogObject); plot(catalogObject, varargin) plot3(catalogObject, varargin) plot_time(catalogObject) - hist(catalogObject) - bvalue(catalogObject, mcType) - catalogObjects=subclassify(catalogObject, subclasses) - erobj=eventrate(catalogObject, varargin) + plot_waveform_metrics(catalogObject); plotprmm(catalogObject) - eev(obj, eventnum) + summary(catalogObject) + webmap(catalogObject) write(catalogObject, outformat, outpath, schema) - catalogObject2 = subset(catalogObject, indices) - catalogObject = addwaveforms(catalogObject, w); + arrivals_per_event(catalogObject) + end %% --------------------------------------------------- methods (Access=protected, Hidden=true) - - %% AUTOBINSIZE - function binsize = autobinsize(catalogObject) - %autobinsize Compute the best bin size based on start and end times - binsize = binning.autobinsize(catalogObject.enum - catalogObject.snum); - end -%% --------------------------------------------------- - function region = get_region(catalogObject, nsigma) - % region Compute the region to plot based on spread of lon,lat data - medianlat = nanmedian(catalogObject.lat); - medianlon = nanmedian(catalogObject.lon); - cosine = cos(medianlat); - stdevlat = nanstd(catalogObject.lat); - stdevlon = nanstd(catalogObject.lon); - rangeindeg = max([stdevlat stdevlon*cosine]) * nsigma; - region = [(medianlon - rangeindeg/2) (medianlon + rangeindeg/2) (medianlat - rangeindeg/2) (medianlat + rangeindeg/2)]; - end - -%% --------------------------------------------------- - function symsize = get_symsize(catalogObject) - %get_symsize Get symbol marker size based on magnitude of event - % Compute Marker Size - minsymsize = 3; - maxsymsize = 50; - symsize = (catalogObject.mag + 2) * 10; % -2- -> 1, 1 -> 10, 0 -> 20, 1 -> 30, 2-> 40, 3+ -> 50 etc. - symsize(symsizemaxsymsize)=maxsymsize; - % deal with NULL (NaN) values - symsize(isnan(symsize))=minsymsize; - end -%% --------------------------------------------------- - + region = get_region(catalogObject, nsigma) + symsize = get_symsize(catalogObject) end methods(Static) - function self = retrieve(dataformat, varargin) - %CATALOG.RETRIEVE Read seismic events from common file formats & data sources. - % retrieve can read events from many different earthquake catalog file - % formats (e.g. Seisan, Antelope) and data sources (e.g. IRIS DMC) into a - % GISMO Catalog object. - % - % Usage: - % catalogObject = CATALOG.RETRIEVE(dataformat, 'param1', _value1_, ... - % 'paramN', _valueN_) - % - % dataformat may be: - % - % * 'iris' (for IRIS DMC, using irisFetch.m), - % * 'antelope' (for a CSS3.0 Antelope/Datascope database) - % * 'seisan' (for a Seisan database with a REA/YYYY/MM/ directory structure) - % * 'zmap' (converts a Zmap data strcture to a Catalog object) - % - % The name-value parameter pairs supported are the same as those supported - % by irisFetch.Events(). Currently these are: - % - % startTime - % endTime - % eventId - % fetchLimit - % magnitudeType - % minimumLongitude - % maximumLongitude - % minimumLatitude - % maximumLatitude - % minimumMagnitude - % maximumMagnitude - % minimumDepth - % maximumDepth - % - % And the two convenience parameters: - % - % radialcoordinates = [ centerLatitude, centerLongitude, maximumRadius ] - % - % boxcoordinates = [ minimumLatitude maximumLatitude minimumLongitude maximumLongitude ] - % - % For examples, see Catalog_cookbook. Also available at: - % https://geoscience-community-codes.github.io/GISMO/tutorials/html/Catalog_cookbook.html - % - % - % See also CATALOG, IRISFETCH, CATALOG_COOKBOOK - - % Author: Glenn Thompson (glennthompson1971@gmail.com) - - %% To do: - % Implement name-value parameter pairs for all methods - % Test the Antelope method still works after factoring out db_load_origins - % Test the Seisan method more - % Add in support for 'get_arrivals' - - debug.printfunctionstack('>') - - switch lower(dataformat) - case 'iris' - if exist('irisFetch.m','file') - ev = irisFetch.Events(varargin{:}); - self = Catalog.read_catalog.iris(ev); - else - warning('Cannot find irisFetch.m') - end - case {'css3.0','antelope', 'datascope'} - if admin.antelope_exists() - self = Catalog.read_catalog.antelope(varargin{:}); - else - warning('Sorry, cannot read event Catalog from Antelope database as Antelope toolbox for MATLAB not found') - self = Catalog(); - end - case 'seisan' - self = Catalog.read_catalog.seisan(varargin{:}); - case 'aef' - self = Catalog.read_catalog.aef(varargin{:}); - case 'sru' - self = Catalog.read_catalog.sru(varargin{:}); - case 'vdap' - self = Catalog.read_catalog.vdap(varargin{:}); - case 'zmap' - self = Catalog.read_catalog.zmap(varargin{:}); - otherwise - self = NaN; - fprintf('format %s unknown\n\n',data_source); - end - if isempty(self) - self=Catalog(); - end - - debug.printfunctionstack('<') - end - - cookbook() + self = retrieve(dataformat, varargin) end end diff --git a/core/@Catalog/addwaveforms.m b/core/@Catalog/addwaveforms.m old mode 100644 new mode 100755 index cd8762f..5c275c9 --- a/core/@Catalog/addwaveforms.m +++ b/core/@Catalog/addwaveforms.m @@ -1,16 +1,63 @@ -function cobj = addwaveforms(cobj, w_continuous) +function cobj = addwaveforms(cobj, varargin) % Catalog.addwaveforms Add waveform objects corresponding to ontimes and % offtimes in a Catalog object. % % cobj2 = cobj.addwaveforms(w_continuous) will extract event waveform objects from a % continuous waveform object, w_continuous. Each event is defined by its ontime and % offtime, which are recorded in cobj. +% +% cobj2 = cobj.addwaveforms(w_continuous, pretriggerSecs, posttriggerSecs) +% will prepend each waveform by pretriggerSecs Seconds. And append by +% posttriggerSecs. +% +% cobj2 = cobj.addwaveforms(ds, ctag, pretriggerSecs, posttriggerSecs) will create event waveform objects from +% datasource, ChannelTag.array. Each event is defined by its ontime and +% offtime, which are recorded in cobj. +% + disp(sprintf('Adding waveforms for each of %d events in Catalog',cobj.numberOfEvents)) + w_events = {}; + pretriggerSecs = 0; + posttriggerSecs = 0; + if nargin>1 + + if isa(varargin{1},'waveform') + + if nargin>2 + pretriggerSecs = varargin{2}; + posttriggerSecs = varargin{3}; + end + w_cont = clean(varargin{1}); + %w_temp = extract(w_cont, 'time', cobj.ontime, cobj.offtime); + for count = 1:cobj.numberOfEvents + fprintf('.'); + w_temp = extract(w_cont, 'time', cobj.ontime(count)-pretriggerSecs/86400, cobj.offtime(count)+posttriggerSecs/86400); + %w_events{count} = w_temp(:,count); + w_events{count} = w_temp; + + if mod(count,30) == 0 + fprintf('\nDone %d out of %d\n',count, cobj.numberOfEvents); + end + end + + end - w_temp = extract(w_continuous, 'time', cobj.ontime, cobj.offtime); - for count = 1:length(cobj.ontime) - w_events{count} = w_temp(:,count); + if isa(varargin{1},'datasource') + if nargin>2 + pretriggerSecs = varargin{3}; + postriggerSecs = varargin{4}; + end + for count = 1:cobj.numberOfEvents + fprintf('.'); + thisw = waveform(varargin{1}, varargin{2}, cobj.ontime(count)-pretriggerSecs/86400, cobj.offtime(count)+posttriggerSecs/86400); + w_events{count} = clean(thisw); + if mod(count,30) == 0 + fprintf('\nDone %d out of %d\n',count, cobj.numberOfEvents); + end + end + end end cobj.waveforms = w_events; + fprintf('\n(Complete)\n'); end \ No newline at end of file diff --git a/core/@Catalog/arrivals_per_event.m b/core/@Catalog/arrivals_per_event.m new file mode 100644 index 0000000..5199283 --- /dev/null +++ b/core/@Catalog/arrivals_per_event.m @@ -0,0 +1,15 @@ +function arrivals_per_event(cobj) + N = numel(cobj.arrivals); + na = []; + if N>1 + for c=1:N + na = [na numel(cobj.arrivals{c}.time)]; + end + m = max(na); + for v=1:m + disp(sprintf('arrivals = %d, count = %d', v, sum(na==v) )); + end + end + +end + \ No newline at end of file diff --git a/core/@Catalog/bvalue.m b/core/@Catalog/bvalue.m old mode 100644 new mode 100755 index 5039f8b..2c1bece --- a/core/@Catalog/bvalue.m +++ b/core/@Catalog/bvalue.m @@ -1,10 +1,11 @@ -function bvalue(catalogObject, mcType) +function gr=bvalue(catalogObject, mcType, manual_on) %BVALUE evaluate b-value, a-value and magnitude of completeness % of an earthquake catalog stored in a Catalog object. % - % BVALUE(catalogObject, MCTYPE) produces a Gutenberg-Richter type plot + % gr = BVALUE(catalogObject, MCTYPE) produces a Gutenberg-Richter type plot % with the best fit line and display of b-,a-values and Mc - % for catalogObject. MCTYPE is a number from 1-5 + % for catalogObject. These values are also returned in a structure. + % MCTYPE is a number from 1-5 % to select the algorithm used for calculation of the % magnitude of completeness. Options are: % @@ -13,6 +14,14 @@ function bvalue(catalogObject, mcType) % 3: Mc90 (90% probability) % 4: Mc95 (95% probability) % 5: Best combination (Mc95 - Mc90 - maximum curvature) + % + % * Note: it seems only 1 only really works, and 5 is same as 1 *' + % + % gr = BVALUE(catalogObject, MCTYPE, manual_on) where the value of manual_on + % computes as true will give the user the ability to manually pick a + % linear segment on the graph too (which is then plotted with a + % green line). The manual Mc and bvalue are returned in the gr + % structure as gr.Mc_manual and gr.bvalue_manual % Liberally adapted from original code in ZMAP. % Author: Silvio De Angelis, 27/07/2012 00:00:00 @@ -35,16 +44,13 @@ function bvalue(catalogObject, mcType) % 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. if nargin < 2 - disp('--------------------------------------------------------') - disp('Usage is: catalogObject.bvalue(mcType)') - disp('--------------------------------------------------------') - disp('mcType can be:') - disp('1: Maximum curvature') - disp('2: Fixed Mc = minimum magnitude (Mmin)') - disp('3: Mc90 (90% probability)') - disp('4: Mc95 (95% probability)') - disp('5: Best combination (Mc95 - Mc90 - maximum curvature)') - return + disp('* Note: it seems only 1 only really works, and 5 is same as 1 *') + mcType = menu('mcType can be:','Maximum curvature','Fixed Mc = minimum magnitude (Mmin)', ... + 'Mc90 (90% probability)', 'Mc95 (95% probability)', ... + 'Best combination (Mc95 - Mc90 - maximum curvature)') + end + if ~exist('manual_on','var') + manual_on = false; end % form magnitude vector - removing any NaN values with find @@ -163,6 +169,7 @@ function bvalue(catalogObject, mcType) magco = fMc; index_low=find(xt3 < magco+.05 & xt3 > magco-.05); +try mag_hi = xt3(1); index_hi = 1; mz = xt3 <= mag_hi & xt3 >= magco-.0001; @@ -212,4 +219,27 @@ function bvalue(catalogObject, mcType) text(.53,.88, ['b-value = ',tt1,' +/- ',tt2,', a value = ',num2str(aw,3)],'FontSize',12); text(.53,.85,sol_type,'FontSize',12 ); text(.53,.82,['Magnitude of Completeness = ',tmc],'FontSize',12); + + %% Added by Glenn 2018-05-01 to return a structure + gr.bvalue = str2num(tt1); + gr.bvalue_error = str2num(tt2); + gr.avalue = aw; + gr.Mc = str2num(tmc); + + %% Manually fit a line (added by Glenn 2018-05-01) + if manual_on + [xmag, yN] = ginput(2); + slope = (log10(yN(1)) - log10(yN(2) )) / (xmag(2) - xmag(1) ) + plot(xmag, yN, 'g'); + gr.Mc_manual = xmag(1); + gr.bvalue_manual = slope; + text(xmag(1) + (xmag(2)-xmag(1))*0.5, yN(1) + (yN(2)-yN(1))*0.5, sprintf('manual b=%.2f',slope),'Color','g'); + end +catch + gr.bvalue = NaN; + gr.Mc = NaN; + gr.avalue = NaN; + gr.bvalue_error = NaN; +end + end \ No newline at end of file diff --git a/core/@Catalog/combine.m b/core/@Catalog/combine.m old mode 100644 new mode 100755 index 5c26749..d985980 --- a/core/@Catalog/combine.m +++ b/core/@Catalog/combine.m @@ -11,6 +11,6 @@ catalogObject = catalogObject1; - catalogObject.table = union(catalogObject1.table, catalogObject2.table); + catalogObject.table = union(catalogObject1.table(), catalogObject2.table()); end \ No newline at end of file diff --git a/core/@Catalog/cookbook.m b/core/@Catalog/cookbook.m old mode 100644 new mode 100755 index eed7389..d664414 --- a/core/@Catalog/cookbook.m +++ b/core/@Catalog/cookbook.m @@ -12,26 +12,22 @@ % % In this example we will use retrieve to retrieve all events at IRIS DMC % with a magnitude of at least 8.0 from year 2000 to 2014 (inclusive): - greatquakes = Catalog.retrieve('iris', 'minimumMagnitude', 8.0, ... 'starttime', '2000-01-01', 'endtime', '2015-01-01') %% % To access any particular property we can use dot notation, as if the % object were a structure, e.g.: - greatquakes.mag %% % greatquakes is a Catalog object, an instance of the Catalog class. To see % a list of functions ("methods" in object-oriented speak) we can apply to % a Catalog object, use the methods command: - methods(greatquakes) %% % Save this dataset so you can use it again later: - save('great_earthquakes.mat', 'greatquakes') %% @@ -46,7 +42,6 @@ %% % We will limit our search to 1 day before and after the earthquake: - mainshocktime = datenum('2011/03/11 05:46:24'); tohoku_events = Catalog.retrieve('iris', ... 'radialcoordinates', [38.297 142.372 km2deg(200)], ... @@ -55,15 +50,12 @@ %% % This returns 1136 earthquakes. Let's get a summary: - tohoku_events.summary() %% % Save this dataset so you can use it again later: - save('tohoku_events.mat', 'tohoku_events') - %% Readings events from an Antelope database % To load event data from an Antelope/Datascope CSS3.0 database you will % need to have Antelope () installed, @@ -97,14 +89,13 @@ %% % Both catalog segments are included in the "demo" directory. % We will now load the official AVO catalog into an Events object: -dbpath = Catalog.demo.demodb('avo'); +dbpath = fullfile(TESTDATA, 'css3.0', 'avodb200903') avocatalog = Catalog.retrieve('antelope', 'dbpath', dbpath); %% % This should load 1441 events. What if we only want events within 20km of % Redoubt volcano? There are two ways to do this. The first is the use the % radialcoordinates parameter: - redoubtLon = -152.7431; redoubtLat = 60.4853; maxR = km2deg(20.0); @@ -116,7 +107,6 @@ % using a dbeval subset expression, and the command above does this % internally. You can also specify a subset expression directly. The % following example is completely equivalent to that above: - expr = sprintf('distance(lat, lon, %f, %f) < %f',redoubtLat, redoubtLon,maxR) redoubt_events = Catalog.retrieve('antelope', 'dbpath', dbpath, ... 'subset_expression', expr) @@ -143,16 +133,15 @@ % This example will load Sfiles from 4 hours on 1st Nov, 1996. This is a slow % function to run as MATLAB is slow at parsing text files, and there are % many events per day in this particular database. -demodir = Catalog.demo.demo_path(); -dbpath = fullfile(demodir,'seisan'); +%demodir = Catalog.demo.demo_path(); +demodir = fullfile(TESTDATA, 'seisan', 'REA', 'MVOE_'); montserrat_events = Catalog.retrieve('seisan', ... - 'dbpath', dbpath, ... + 'dbpath', demodir, ... 'startTime', '1996/11/01 11:00:00', .... 'endTime', '1996/11/01 15:00:00') %% % Save this dataset so you can use it again later: - save('montserrat_events.mat', 'montserrat_events') %% @@ -305,12 +294,10 @@ %% % This database can be reloaded with: - greatquakes2 = Catalog.retrieve('antelope', 'dbpath', 'greatquakes_db') %% % Compare: - greatquakes %% diff --git a/core/@Catalog/disp.m b/core/@Catalog/disp.m old mode 100644 new mode 100755 index 57a69b1..c443d63 --- a/core/@Catalog/disp.m +++ b/core/@Catalog/disp.m @@ -4,37 +4,114 @@ function disp(obj, showall) % methods(obj) % obj.table % return -for c=1:numel(obj) -% subclass = '*'; -% if numel(obj)>1 -% subclass = obj(c).table.etype{1}; -% end - try - disp(sprintf('%s object: Event type: %s',class(obj(c)),obj(c).request.subclass)); - catch + for c=1:numel(obj) + % subclass = '*'; + % if numel(obj)>1 + % subclass = obj(c).table.etype{1}; + % end + try + disp(sprintf('\n%s object: Event type: %s',class(obj(c)),obj(c).request.subclass)); + catch - end - fprintf('Number of events: %d\n',obj(c).numberOfEvents); - if obj(c).numberOfEvents > 0 - [maxmag, maxmagindex] = nanmax(obj(c).mag); - if ~isnan(maxmag) - fprintf('Biggest event: %f at %s\n',maxmag, datestr(obj(c).otime(maxmagindex))); end - if ~exist('showall','var') - showall = false; + fprintf('Number of events: %d\n',obj(c).numberOfEvents); + try + fprintf('Cumulative magnitude: %.2f\n',obj(c).cum_mag ); end - if numel(obj) == 1 - if height(obj.table) <= 50 || showall - disp(obj.table) - else - disp(obj.table([1:50],:)) + if obj(c).numberOfEvents > 0 + [maxmag, maxmagindex] = nanmax(obj(c).mag); + if ~isnan(maxmag) + fprintf('Biggest event: %.2f at %s\n',maxmag, datestr(obj(c).otime(maxmagindex))); + end + if ~exist('showall','var') + showall = false; + end + if numel(obj) == 1 + disp_title(obj); + if obj(c).numberOfEvents <= 50 || showall + rows_to_show = obj(c).numberOfEvents; + else + rows_to_show = 50; + disp('* Only showing first 50 rows/events - to see all rows/events use:') + disp('* catalogObject.disp(true)') + end + for eventnum=1:rows_to_show + disp_event(obj(c), eventnum); + end - disp('* Only showing first 50 rows/events - to see all rows/events use:') - disp('* catalogObject.disp(true)') end -% else -% disp(sprintf('Event type: %s',obj(c).table.etype{1})); end end end + +function disp_title(obj) + fprintf('Event'); + %if ~isempty(obj.otime) + if sum(~isnan(obj.otime)) + fprintf('\tOriginTime '); + end + if sum(~isnan(obj.ontime)) + fprintf('\tOnTime '); + end + if sum(~isnan(obj.offtime)) + fprintf('\tOffTime '); + end + if sum(~isnan(obj.offtime)) + fprintf('\tDuration'); + end + if sum(~isnan(obj.lat)) + fprintf('\tLat'); + end + if sum(~isnan(obj.lon)) + fprintf('\tLon'); + end + if sum(~isnan(obj.depth)) + fprintf('\tDepth'); + end + if sum(~isnan(obj.mag)) + fprintf('\tMag'); + end + if sum(~strcmp(obj.magtype,'u')) + fprintf('\tMagType'); + end + if sum(~strcmp(obj.etype,'u')) + fprintf('\tClass'); + end + fprintf('\n'); +end + +function disp_event(obj, eventnum) + %l = length(num2str(cobj.numberOfEvents)) + 1; + fprintf('%4d',eventnum); + if sum(~isnan(obj.otime)) + fprintf('\t%s',datestr(obj.otime(eventnum), 'yyyy-mm-dd HH:MM.SS.FFF') ); + end + if sum(~isnan(obj.ontime)) + fprintf('\t%s',datestr(obj.ontime(eventnum), 'yyyy-mm-dd HH:MM.SS.FFF') ); + end + if sum(~isnan(obj.offtime)) + fprintf('\t%s',datestr(obj.offtime(eventnum), 'yyyy-mm-dd HH:MM.SS.FFF') ); + end + if sum(~isnan(obj.offtime)) + fprintf('\t%.4f s', obj.duration(eventnum) ); + end + if sum(~isnan(obj.lat)) + fprintf('\t%.4f', obj.lat(eventnum) ); + end + if sum(~isnan(obj.lon)) + fprintf('\t%.4f', obj.lon(eventnum) ); + end + if sum(~isnan(obj.depth)) + fprintf('\t%.2f', obj.depth(eventnum) ); + end + if sum(~isnan(obj.mag)) + fprintf('\t%.1f', obj.mag(eventnum) ); + end + if sum(~strcmp(obj.magtype,'u')) + fprintf('\t%s', obj.magtype{eventnum} ); + end + if sum(~strcmp(obj.etype,'u')) + fprintf('\t%s', obj.etype{eventnum} ); + end + fprintf('\n'); end diff --git a/core/@Catalog/eev.m b/core/@Catalog/eev.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/eventrate.m b/core/@Catalog/eventrate.m old mode 100644 new mode 100755 index 4d55cb1..e96911e --- a/core/@Catalog/eventrate.m +++ b/core/@Catalog/eventrate.m @@ -13,17 +13,35 @@ p = inputParser; p.addParamValue('binsize', 0, @isnumeric); p.addParamValue('stepsize', 0, @isnumeric); + p.addParamValue('snum', 0, @isnumeric); + p.addParamValue('enum', 0, @isnumeric); p.parse(varargin{:}); binsize = p.Results.binsize; stepsize = p.Results.stepsize; + snum = p.Results.snum; + enum = p.Results.enum; + if snum>0 && enum>0 + for c=1:numel(catalogObject) + indices = find(catalogObject(c).otime >= snum & catalogObject(c).otime <= enum); + catalogObject(c) = catalogObject(c).subset(indices); + end + end + + + for i=1:numel(catalogObject) if catalogObject(i).numberOfEvents > 0 -% timerange = catalogObject(i).gettimerange(); -% snum=timerange(1); -% enum=timerange(2); - snum = catalogObject(i).request.startTime; - enum = catalogObject(i).request.endTime; + + try + snum = catalogObject(i).request.startTime; + enum = catalogObject(i).request.endTime; + catch + timerange = catalogObject(i).gettimerange(); + snum=timerange(1); + enum=timerange(2); + end + if ~(binsize>0) binsize = Catalog.binning.autobinsize(enum-snum); end @@ -53,4 +71,10 @@ snum, enum, etypes, binsize, stepsize, numbins); end end +end + +%% AUTOBINSIZE +function binsize = autobinsize(catalogObject) +%autobinsize Compute the best bin size based on start and end times + binsize = binning.autobinsize(catalogObject.enum - catalogObject.snum); end \ No newline at end of file diff --git a/core/@Catalog/get_region.m b/core/@Catalog/get_region.m new file mode 100755 index 0000000..626310f --- /dev/null +++ b/core/@Catalog/get_region.m @@ -0,0 +1,10 @@ +function region = get_region(catalogObject, nsigma) +% region Compute the region to plot based on spread of lon,lat data + medianlat = nanmedian(catalogObject.lat); + medianlon = nanmedian(catalogObject.lon); + cosine = cos(medianlat); + stdevlat = nanstd(catalogObject.lat); + stdevlon = nanstd(catalogObject.lon); + rangeindeg = max([stdevlat stdevlon*cosine]) * nsigma; + region = [(medianlon - rangeindeg/2) (medianlon + rangeindeg/2) (medianlat - rangeindeg/2) (medianlat + rangeindeg/2)]; +end \ No newline at end of file diff --git a/core/@Catalog/get_symsize.m b/core/@Catalog/get_symsize.m new file mode 100755 index 0000000..e87bf3c --- /dev/null +++ b/core/@Catalog/get_symsize.m @@ -0,0 +1,11 @@ +function symsize = get_symsize(catalogObject) + %get_symsize Get symbol marker size based on magnitude of event + % Compute Marker Size + minsymsize = 3; + maxsymsize = 50; + symsize = (catalogObject.mag + 2) * 10; % -2- -> 1, 1 -> 10, 0 -> 20, 1 -> 30, 2-> 40, 3+ -> 50 etc. + symsize(symsizemaxsymsize)=maxsymsize; + % deal with NULL (NaN) values + symsize(isnan(symsize))=minsymsize; +end \ No newline at end of file diff --git a/core/@Catalog/hist.m b/core/@Catalog/hist.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/html/cookbook.html b/core/@Catalog/html/cookbook.html deleted file mode 100644 index 48435d5..0000000 --- a/core/@Catalog/html/cookbook.html +++ /dev/null @@ -1,950 +0,0 @@ - - - - - Catalog Cookbook

Catalog Cookbook

GISMO can read events from many different earthquake catalog file formats (e.g. Seisan, Antelope) and data sources (e.g. IRIS DMC) using the Catalog.retrieve() method.

Contents

Reading events from IRIS DMC

To load events into a Catalog object we use the Catalog.retrieve method. The first argument is the data source/format - when this is given as 'iris', retrieve uses the irisFetch.m program to retrieve event data via the IRIS webservices. To narrow down our data search we can give retrieve any name-value parameter pairs supported by irisFetch.

In this example we will use retrieve to retrieve all events at IRIS DMC with a magnitude of at least 8.0 from year 2000 to 2014 (inclusive):

greatquakes = Catalog.retrieve('iris', 'minimumMagnitude', 8.0, ...
-    'starttime', '2000-01-01', 'endtime', '2015-01-01')
-
fetching...
-
-
-20 events found *************
-
-parsing into MATLAB structures
-Got 20 events
-
-greatquakes = 
-
-
-ans =
-
-Catalog
-
-Number of events: 20
-Biggest event: 9.100000 at 11-Mar-2011 05:46:23
-      otime       yyyy_mm_dd     hh_mm_ss       lon        lat      depth    mag    magtype       etype        ontime    offtime
-    __________    __________    __________    _______    _______    _____    ___    _______    ____________    ______    _______
-
-    7.3103e+05    2001_06_23    20:33:09.3    -73.561    -16.303      2.2    8.3    'MW'       'earthquake'    NaN       NaN    
-    7.3185e+05    2003_09_25    19:50:07.2     143.87     41.749       33    8.1    'MW'       'earthquake'    NaN       NaN    
-     7.323e+05    2004_12_23    14:59:00.6     161.58     -49.71       10      8    'MW'       'earthquake'    NaN       NaN    
-    7.3231e+05    2004_12_26    00:58:52.0     95.901     3.4125     26.1    8.2    'MW'       'earthquake'    NaN       NaN    
-     7.324e+05    2005_03_28    16:09:35.2     97.113     2.0964       30    8.1    'MW'       'earthquake'    NaN       NaN    
-    7.3283e+05    2006_06_01    18:57:02.9     120.88     19.054       13    8.4    'MS'       'earthquake'    NaN       NaN    
-    7.3283e+05    2006_06_05    00:50:31.5     119.08     17.992      124      8    'MS'       'earthquake'    NaN       NaN    
-      7.33e+05    2006_11_15    11:14:14.5     153.21     46.681     12.2    8.3    'MW'       'earthquake'    NaN       NaN    
-    7.3306e+05    2007_01_13    04:23:23.2      154.5     46.231     22.5    8.1    'MW'       'earthquake'    NaN       NaN    
-    7.3313e+05    2007_04_01    20:39:56.5     157.03    -8.4468      9.5    8.1    'MW'       'earthquake'    NaN       NaN    
-    7.3327e+05    2007_08_15    23:40:58.4    -76.555    -13.384     41.2      8    'MW'       'earthquake'    NaN       NaN    
-     7.333e+05    2007_09_12    11:10:26.8      101.4    -4.4637     35.5    8.5    'MW'       'earthquake'    NaN       NaN    
-    7.3405e+05    2009_09_29    17:48:11.5    -171.94    -15.512     18.5    8.1    'MW'       'earthquake'    NaN       NaN    
-     7.342e+05    2010_02_27    06:34:13.3    -72.933    -36.148     28.1    8.8    'MW'       'earthquake'    NaN       NaN    
-    7.3457e+05    2011_03_11    05:46:23.2      142.5     38.296     19.7    9.1    'MW'       'earthquake'    NaN       NaN    
-    7.3497e+05    2012_04_11    08:38:37.8     93.014     2.2376     26.3    8.6    'MW'       'earthquake'    NaN       NaN    
-    7.3497e+05    2012_04_11    10:43:10.5     92.428     0.7675     21.6    8.2    'MW'       'earthquake'    NaN       NaN    
-    7.3527e+05    2013_02_06    01:12:27.0     165.14    -10.738     28.7      8    'MW'       'earthquake'    NaN       NaN    
-    7.3538e+05    2013_05_24    05:44:49.6     153.28     54.874    608.9    8.3    'MW'       'earthquake'    NaN       NaN    
-    7.3569e+05    2014_04_01    23:46:47.2    -70.769     -19.61       25    8.2    'MWW'      'earthquake'    NaN       NaN    
-
-

To access any particular property we can use dot notation, as if the object were a structure, e.g.:

greatquakes.mag
-
-ans =
-
-    8.3000
-    8.1000
-    8.0000
-    8.2000
-    8.1000
-    8.4000
-    8.0000
-    8.3000
-    8.1000
-    8.1000
-    8.0000
-    8.5000
-    8.1000
-    8.8000
-    9.1000
-    8.6000
-    8.2000
-    8.0000
-    8.3000
-    8.2000
-
-

greatquakes is a Catalog object, an instance of the Catalog class. To see a list of functions ("methods" in object-oriented speak) we can apply to a Catalog object, use the methods command:

methods(greatquakes)
-
-Methods for class Catalog:
-
-Catalog       disp          hist          plot_time     summary       
-addwaveforms  eev           plot          plotprmm      webmap        
-bvalue        eventrate     plot3         subclassify   write         
-combine       gettimerange  plot_counts   subset        
-
-Static methods:
-
-cookbook      retrieve      
-
-

Save this dataset so you can use it again later:

save('great_earthquakes.mat', 'greatquakes')
-

Now we'll do another example - we will get events within 200 km of the great M9.0 Tohoku earthquake that occurred on 2011/03/11. The mainshock parameters are:

   Date/Time:  "2011/03/11 05:46:24"
-   Longitude:  142.372
-   Latitude:   38.297
-   Depth:      30 km

We will limit our search to 1 day before and after the earthquake:

mainshocktime = datenum('2011/03/11 05:46:24');
-tohoku_events = Catalog.retrieve('iris', ...
-            'radialcoordinates', [38.297 142.372 km2deg(200)], ...
-            'starttime', mainshocktime - 1, ...
-            'endtime', mainshocktime + 1);
-
fetching...
-
-
-1136 events found *************
-
-parsing into MATLAB structures
-Got 1136 events
-

This returns 1136 earthquakes. Let's get a summary:

tohoku_events.summary()
-
-Variables:
-
-    otime: 1136x1 double
-        Values:
-
-            min       7.3457e+05
-            median    7.3457e+05
-            max       7.3457e+05
-
-    yyyy_mm_dd: 1136x10 char
-
-    hh_mm_ss: 1136x10 char
-
-    lon: 1136x1 double
-        Values:
-
-            min       140.31
-            median    142.59
-            max       144.63
-
-    lat: 1136x1 double
-        Values:
-
-            min       36.517
-            median     37.88
-            max       40.044
-
-    depth: 1136x1 double
-        Values:
-
-            min           0  
-            median       25  
-            max       149.7  
-
-    mag: 1136x1 double
-        Values:
-
-            min       0.1  
-            median    4.3  
-            max       9.1  
-            NaNs        4  
-
-    magtype: 1136x1 cell string
-
-    etype: 1136x1 cell string
-
-    ontime: 1136x1 double
-        Values:
-
-            min        NaN    
-            median     NaN    
-            max        NaN    
-            NaNs      1136    
-
-    offtime: 1136x1 double
-        Values:
-
-            min        NaN     
-            median     NaN     
-            max        NaN     
-            NaNs      1136     
-
-

Save this dataset so you can use it again later:

save('tohoku_events.mat', 'tohoku_events')
-

Readings events from an Antelope database

To load event data from an Antelope/Datascope CSS3.0 database you will need to have Antelope (http://www.brtt.com/software.html) installed, including the Antelope toolbox for MATLAB (ATM). To see if ATM is installed, use the admin.antelope_exists() command, e.g.

if admin.antelope_exists()
-    disp('Antelope Toolbox for MATLAB found')
-else
-    disp('Sorry, Antelope not found')
-end
-
Antelope Toolbox for MATLAB found
-

If you do not have ATM installed, any attempt to read from an Antelope database will result in a warning like:

     Warning: Sorry, cannot read event Catalog from Antelope database as Antelope toolbox for MATLAB not found

and an empty Catalog object will be returned.

%
-

For the purpose of this exercise we will be using data from Redoubt volcano from 2009/03/20 to 2009/03/23. We will use snippets from two catalogs that are provided with GISMO in Antelope format:

  • The real-time catalog (rtdb200903).
  • The analyst-reviewed offical AVO catalog (avodb200903).

Both catalog segments are included in the "demo" directory. We will now load the official AVO catalog into an Events object:

dbpath = Catalog.demo.demodb('avo');
-avocatalog = Catalog.retrieve('antelope', 'dbpath', dbpath);
-
Loading data from /Users/glennthompson/src/GISMO/core/+Catalog/+demo/css3.0/avodb200903
-Got 1441 events
-

This should load 1441 events. What if we only want events within 20km of Redoubt volcano? There are two ways to do this. The first is the use the radialcoordinates parameter:

redoubtLon = -152.7431;
-redoubtLat = 60.4853;
-maxR = km2deg(20.0);
-redoubt_events = Catalog.retrieve('antelope', 'dbpath', dbpath, ...
-    'radialcoordinates', [redoubtLat redoubtLon maxR])
-
Loading data from /Users/glennthompson/src/GISMO/core/+Catalog/+demo/css3.0/avodb200903
-Got 1397 events
-
-redoubt_events = 
-
-
-ans =
-
-Catalog
-
-Number of events: 1397
-Biggest event: 2.000000 at 22-Mar-2009 20:21:20
-      otime       yyyy_mm_dd     hh_mm_ss       lon       lat      depth    mag     magtype    etype    ontime    offtime
-    __________    __________    __________    _______    ______    _____    ____    _______    _____    ______    _______
-
-    7.3385e+05    2009_03_20    00:24:41.6     -152.8    60.481     3.62     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    07:29:44.8    -152.74    60.494       -3    -0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    07:43:40.6    -152.78    60.484       -3     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    07:48:53.5    -152.75    60.455     0.44     0.7    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    11:09:21.9    -152.77    60.497     3.09     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    11:10:43.2    -152.77    60.488     0.69     0.8    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    12:20:02.7    -152.77     60.48    -0.15     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    13:21:06.0    -152.76    60.488    -2.78     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    14:04:39.6    -152.76     60.48     3.72     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    15:25:39.3    -152.76    60.486     -2.9     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    16:51:47.1    -152.78    60.483       -3     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    16:52:08.1    -152.77    60.501     2.12     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:10:26.7    -152.77    60.484     0.61     0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:17:16.1    -152.73    60.495       -3    -0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:46:27.3    -152.78    60.505     2.59    -0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:46:39.2    -152.76    60.489    -2.46     0.8    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:59:41.8    -152.77    60.487    -2.67     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    19:42:17.3    -152.77    60.484     2.28     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    19:44:11.7    -152.76     60.49     0.04     0.7    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    19:46:08.9    -152.77    60.483       -3    -0.2    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:02:49.4    -152.77    60.484     0.12     0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:23:13.9    -152.74    60.489    -1.39     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:27:29.6    -152.77    60.491    -2.55     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:35:09.7    -152.77    60.462     5.19     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:36:50.5    -152.76    60.487    -2.92     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:39:44.5    -152.77    60.485     3.52     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:52:01.3    -152.78    60.474     2.36     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:00:45.1    -152.76    60.484     0.65       1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:11:41.9    -152.77    60.483    -2.48     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:13:05.4    -152.78    60.461      6.4       1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:45:52.3    -152.76    60.482       -3     0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:45:54.8    -152.81    60.482    -2.74     0.8    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:13:19.7    -152.77    60.488    -2.92     0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:20:14.2    -152.77    60.487      2.8     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:32:48.9    -152.77    60.501       -3    -0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:39:30.5    -152.76    60.491        1     0.6    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:42:14.6    -152.77     60.49     3.57     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:59:34.3    -152.78    60.478       -3     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    23:20:30.0    -152.77    60.491     0.59     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    23:43:54.8    -152.76    60.489     0.48     0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    23:52:53.7    -152.77     60.49     1.14     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:09:20.6    -152.72    60.487       -3    -0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:09:34.6    -152.77    60.491     3.04     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:19:32.5    -152.77    60.489    -2.83       0    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:24:39.7    -152.77    60.487     0.66     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:42:16.9    -152.77    60.497     2.73     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:44:30.9    -152.77    60.487    -2.06     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:47:06.5    -152.77    60.492    -0.14    -0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:47:12.3    -152.77    60.493    -0.49     0.6    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    01:16:51.1    -152.77    60.483     -0.4     0.6    'ml'       'a'      NaN       NaN    
-
-* Only showing first 50 rows/events - to see all rows/events use:
-*      catalogObject.disp(true)
-

Anyone familiar with Antelope will know that it subsets databases by using a dbeval subset expression, and the command above does this internally. You can also specify a subset expression directly. The following example is completely equivalent to that above:

expr = sprintf('distance(lat, lon, %f, %f) < %f',redoubtLat, redoubtLon,maxR)
-redoubt_events = Catalog.retrieve('antelope', 'dbpath', dbpath, ...
-    'subset_expression', expr)
-
-expr =
-
-distance(lat, lon, 60.485300, -152.743100) < 0.179864
-
-Loading data from /Users/glennthompson/src/GISMO/core/+Catalog/+demo/css3.0/avodb200903
-Got 1397 events
-
-redoubt_events = 
-
-
-ans =
-
-Catalog
-
-Number of events: 1397
-Biggest event: 2.000000 at 22-Mar-2009 20:21:20
-      otime       yyyy_mm_dd     hh_mm_ss       lon       lat      depth    mag     magtype    etype    ontime    offtime
-    __________    __________    __________    _______    ______    _____    ____    _______    _____    ______    _______
-
-    7.3385e+05    2009_03_20    00:24:41.6     -152.8    60.481     3.62     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    07:29:44.8    -152.74    60.494       -3    -0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    07:43:40.6    -152.78    60.484       -3     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    07:48:53.5    -152.75    60.455     0.44     0.7    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    11:09:21.9    -152.77    60.497     3.09     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    11:10:43.2    -152.77    60.488     0.69     0.8    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    12:20:02.7    -152.77     60.48    -0.15     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    13:21:06.0    -152.76    60.488    -2.78     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    14:04:39.6    -152.76     60.48     3.72     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    15:25:39.3    -152.76    60.486     -2.9     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    16:51:47.1    -152.78    60.483       -3     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    16:52:08.1    -152.77    60.501     2.12     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:10:26.7    -152.77    60.484     0.61     0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:17:16.1    -152.73    60.495       -3    -0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:46:27.3    -152.78    60.505     2.59    -0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:46:39.2    -152.76    60.489    -2.46     0.8    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    17:59:41.8    -152.77    60.487    -2.67     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    19:42:17.3    -152.77    60.484     2.28     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    19:44:11.7    -152.76     60.49     0.04     0.7    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    19:46:08.9    -152.77    60.483       -3    -0.2    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:02:49.4    -152.77    60.484     0.12     0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:23:13.9    -152.74    60.489    -1.39     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:27:29.6    -152.77    60.491    -2.55     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:35:09.7    -152.77    60.462     5.19     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:36:50.5    -152.76    60.487    -2.92     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:39:44.5    -152.77    60.485     3.52     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    20:52:01.3    -152.78    60.474     2.36     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:00:45.1    -152.76    60.484     0.65       1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:11:41.9    -152.77    60.483    -2.48     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:13:05.4    -152.78    60.461      6.4       1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:45:52.3    -152.76    60.482       -3     0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    21:45:54.8    -152.81    60.482    -2.74     0.8    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:13:19.7    -152.77    60.488    -2.92     0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:20:14.2    -152.77    60.487      2.8     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:32:48.9    -152.77    60.501       -3    -0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:39:30.5    -152.76    60.491        1     0.6    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:42:14.6    -152.77     60.49     3.57     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    22:59:34.3    -152.78    60.478       -3     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    23:20:30.0    -152.77    60.491     0.59     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    23:43:54.8    -152.76    60.489     0.48     0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_20    23:52:53.7    -152.77     60.49     1.14     0.3    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:09:20.6    -152.72    60.487       -3    -0.9    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:09:34.6    -152.77    60.491     3.04     0.5    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:19:32.5    -152.77    60.489    -2.83       0    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:24:39.7    -152.77    60.487     0.66     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:42:16.9    -152.77    60.497     2.73     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:44:30.9    -152.77    60.487    -2.06     0.4    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:47:06.5    -152.77    60.492    -0.14    -0.1    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    00:47:12.3    -152.77    60.493    -0.49     0.6    'ml'       'a'      NaN       NaN    
-    7.3385e+05    2009_03_21    01:16:51.1    -152.77    60.483     -0.4     0.6    'ml'       'a'      NaN       NaN    
-
-* Only showing first 50 rows/events - to see all rows/events use:
-*      catalogObject.disp(true)
-

Save this dataset so you can use it again later:

save('redoubt_events.mat', 'redoubt_events')
-

Reading events from a Seisan database

Here we load events from a Seisan catalog. A Seisan "Sfile" contains all the metadata for 1 event. These Sfiles are stored in a flat-file database structure the path to which is: $SEISAN_TOP/REA/databaseName. Sfiles are organized in year/month subdirectories under this path.

SCAFFOLD: INCLUDE DEMO DATASET FROM MVOE

The following will navigate this where in this case $SEISAN_TOP = '/raid/data/seisan' and the databaseName is MVOE_ which stands for the Montserrat Volcano Observatory Event database. (In Seisan, databaseName is limited to exactly 5 characters).

This example will load Sfiles from 4 hours on 1st Nov, 1996. This is a slow function to run as MATLAB is slow at parsing text files, and there are many events per day in this particular database.

demodir = Catalog.demo.demo_path();
-dbpath = fullfile(demodir,'seisan');
-montserrat_events = Catalog.retrieve('seisan', ...
-    'dbpath', dbpath, ...
-	'startTime', '1996/11/01 11:00:00', ....
-	'endTime', '1996/11/01 15:00:00')
-
There are 29 sfiles matching your request in /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1108-25L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1115-03L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1131-08L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1135-09L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1141-34L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1146-34L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1154-07L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1214-17L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1232-42L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1239-55L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1246-24L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1301-08L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1306-18L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1314-39L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1320-33L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1326-24L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1329-18L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1334-58L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1336-31L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1353-50L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1408-52L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1412-15L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1417-35L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1424-29L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1432-44L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1433-40L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1443-02L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1450-08L.S199611
-Processing /Users/glennthompson/src/GISMO/core/+Catalog/+demo/seisan/1996/11/01-1459-30L.S199611
-Got 29 events
-
-montserrat_events = 
-
-
-ans =
-
-Catalog
-
-Number of events: 29
-Biggest event: NaN at 01-Nov-1996 11:08:25
-      otime       yyyy_mm_dd     hh_mm_ss       lon       lat      depth    mag    magtype    etype    ontime    offtime
-    __________    __________    __________    _______    ______    _____    ___    _______    _____    ______    _______
-
-    7.2933e+05    1996_11_01    11:08:25.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    11:15:03.0        NaN       NaN    NaN      NaN    'u'        't'      NaN       NaN    
-    7.2933e+05    1996_11_01    11:31:08.0        NaN       NaN    NaN      NaN    'u'        't'      NaN       NaN    
-    7.2933e+05    1996_11_01    11:35:20.0    -62.177    16.713    1.7      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    11:41:34.0        NaN       NaN    NaN      NaN    'u'        't'      NaN       NaN    
-    7.2933e+05    1996_11_01    11:46:42.9    -62.156     16.63    3.1      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    11:54:07.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    12:14:17.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    12:32:42.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    12:39:55.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    12:46:24.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:01:08.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:06:18.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:14:39.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:20:43.0    -62.176    16.713      0      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:26:24.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:29:18.0        NaN       NaN    NaN      NaN    'u'        'e'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:34:58.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:36:42.0    -62.177    16.715      0      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    13:53:50.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:08:52.0        NaN       NaN    NaN      NaN    'u'        't'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:12:15.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:17:35.0        NaN       NaN    NaN      NaN    'u'        'u'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:24:40.0    -62.176    16.714    1.3      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:32:44.0        NaN       NaN    NaN      NaN    'u'        'r'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:33:40.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:43:13.0    -62.177    16.711    1.8      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:50:08.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-    7.2933e+05    1996_11_01    14:59:30.0        NaN       NaN    NaN      NaN    'u'        'h'      NaN       NaN    
-
-

Save this dataset so you can use it again later:

save('montserrat_events.mat', 'montserrat_events')
-

Only a few of these earthquakes have been located and even fewer have magnitudes. This is common for volcanic earthquakes. Most of these are of type 'h' - a hybrid earthquake.

Converting a Zmap data structure to a Catalog object

ZMap is a graphical application written by Max Wyss & Stefan Wiemer for statistical analysis of catalogs. GISMO can convert a ZMap data structure into a Catalog object with:

  catalogObject = Catalog.retrieve('zmap', zmapdata)

Plotting hypocenter maps

Catalog objects have three builtin ways for plotting hypocenters

Reload the Tohoku dataset

load tohoku_events.mat
-

Map view & cross-sections

tohoku_events.plot()
-

3D-Hypocenters

tohoku_events.plot3()
-

web map

tohoku_events.webmap()
-
tohoku_events.webmap()
-wmzoom(7)
-

Plotting time series of events

Magnitude-time plot

tohoku_events.plot_time()
-

Earthquake event counts (number of events per unit time) A plot of seismic catalog per day is often called an "event counts" plot. In GISMO, we call this an "event rate plot" and the first step is to generate an EventRate object. Here our binsize is 1/24 days, i.e. 1 hour.

eventrateObject = tohoku_events.eventrate('binsize', 1/24)
-
-eventrateObject = 
-
-  EventRate with properties:
-
-            time: [1x47 double]
-          counts: [1x47 double]
-       mean_rate: [1x47 double]
-     median_rate: [1x47 double]
-         cum_mag: [1x47 double]
-        mean_mag: [1x47 double]
-      median_mag: [1x47 double]
-          energy: [1x47 double]
-    total_counts: 1136
-       total_mag: 9.1024
-         numbins: 47
-         min_mag: [1x47 double]
-         max_mag: [1x47 double]
-           etype: {'earthquake'}
-            snum: 7.3457e+05
-            enum: 7.3457e+05
-         binsize: 0.0417
-        stepsize: 0.0417
-     misc_fields: {}
-     misc_values: {}
-
-

Now plot the EventRate object:

eventrateObject.plot()
-

We can do the same thing for another dataset, e.g. redoubt_events

redoubt_events.plot_time()
-erobj_red = redoubt_events.eventrate('binsize', 1/24)
-erobj_red.plot()
-
-erobj_red = 
-
-  EventRate with properties:
-
-            time: [1x71 double]
-          counts: [1x71 double]
-       mean_rate: [1x71 double]
-     median_rate: [1x71 double]
-         cum_mag: [1x71 double]
-        mean_mag: [1x71 double]
-      median_mag: [1x71 double]
-          energy: [1x71 double]
-    total_counts: 1397
-       total_mag: 2.6140
-         numbins: 71
-         min_mag: [1x71 double]
-         max_mag: [1x71 double]
-           etype: {2x1 cell}
-            snum: 7.3385e+05
-            enum: 7.3385e+05
-         binsize: 0.0417
-        stepsize: 0.0417
-     misc_fields: {}
-     misc_values: {}
-
-

To see more of the things we can do with EventRate objects see the EventRate cookbook EventRate.html

Analysis

Peak event rate (PR) and maximum magnitude A common type of analysis is to identify the peak rate in an earthquake sequence such as this preshock-mainshock-aftershock sequence or an earthquake swarm. This can be done with:

tohoku_events.plotprmm()
-
MM=9.1 occurs at 50.0% of time series
-PR=32 occurs at 53.5% of time series
-

In the command window this returns: MM=9.1 occurs at 50.0% of time series PR=32 occurs at 53.5% of time series

These are labelled on the plot above with PR and MM.

Now with the Redoubt dataset

redoubt_events.plotprmm()
-
MM=2.0 occurs at 94.9% of time series
-PR=76 occurs at 91.5% of time series
-

b-value and magnitude of completeness Code from "ZMap" (written by Stefan Wiemer and others) has been added to Catalog to compute and plot b-values and the magnitude of completeness.

Definitions:

  • b-value: the slope of a plot of the logarithm of the cumulative number of events against magnitude. A measure of the number of small earthquakes to larger earthquakes.
  • magnitude of completeness (Mc): all events with magnitude>=Mc are in the catalog. Below Mc, not all events are detected, and below the magnitude detection threshold, no events are captured.

Just calling the bvalue method, i.e.

catalogObject.bvalue()
-

displays a menu of techniques available to compute b-value (b) and magnitude of completeness (Mc):

   --------------------------------------------------------
-   Usage is: eventsObject.bvalue(mcType)
-   --------------------------------------------------------
-   mcType can be:
-   1: Maximum curvature
-   2: Fixed Mc = minimum magnitude (Mmin)
-   3: Mc90 (90% probability)
-   4: Mc95 (95% probability)
-   5: Best combination (Mc95 - Mc90 - maximum curvature)

We will use the first menu option:

tohoku_events.bvalue(1)
-

In this particular example, the b-value is 0.6 and the magnitude of completeness is 4.2.

Now for the Redoubt events:

redoubt_events.bvalue(1)
-

Saving Catalog objects to disk

Writing to a MAT file We've already seen how to do this, the general syntax is: save('myfilename.mat', 'myCatalogObject')

This can simply be loaded again with: load('myfilename.mat')

Writing to an Antelope CSS3.0 database This method requires the Antelope toolbox for MATLAB and writes the Catalog as a CSS3.0 flat-file database:

First make sure there is no database with this name already - else we will be appending to it:

delete greatquakes_db*
-

Now write to the database

greatquakes.write('antelope', 'greatquakes_db', 'css3.0')
-

This database can be reloaded with:

greatquakes2 = Catalog.retrieve('antelope', 'dbpath', 'greatquakes_db')
-
Loading data from greatquakes_db
-Got 20 events
-
-greatquakes2 = 
-
-
-ans =
-
-Catalog
-
-Number of events: 20
-Biggest event: 9.100000 at 11-Mar-2011 05:46:23
-      otime       yyyy_mm_dd     hh_mm_ss       lon        lat      depth    mag     magtype      etype    ontime    offtime
-    __________    __________    __________    _______    _______    _____    ___    __________    _____    ______    _______
-
-    7.3103e+05    2001_06_23    20:33:09.3    -73.561    -16.303      2.2    8.3    {1x1 cell}    'eq'     NaN       NaN    
-    7.3185e+05    2003_09_25    19:50:07.2     143.87     41.749       33    8.1    {1x1 cell}    'eq'     NaN       NaN    
-     7.323e+05    2004_12_23    14:59:00.6     161.58     -49.71       10      8    {1x1 cell}    'eq'     NaN       NaN    
-    7.3231e+05    2004_12_26    00:58:52.0     95.901     3.4125     26.1    8.2    {1x1 cell}    'eq'     NaN       NaN    
-     7.324e+05    2005_03_28    16:09:35.2     97.113     2.0964       30    8.1    {1x1 cell}    'eq'     NaN       NaN    
-    7.3283e+05    2006_06_01    18:57:02.9     120.88     19.054       13    8.4    {1x1 cell}    'eq'     NaN       NaN    
-    7.3283e+05    2006_06_05    00:50:31.5     119.08     17.992      124      8    {1x1 cell}    'eq'     NaN       NaN    
-      7.33e+05    2006_11_15    11:14:14.5     153.21     46.681     12.2    8.3    {1x1 cell}    'eq'     NaN       NaN    
-    7.3306e+05    2007_01_13    04:23:23.2      154.5     46.231     22.5    8.1    {1x1 cell}    'eq'     NaN       NaN    
-    7.3313e+05    2007_04_01    20:39:56.5     157.03    -8.4468      9.5    8.1    {1x1 cell}    'eq'     NaN       NaN    
-    7.3327e+05    2007_08_15    23:40:58.4    -76.555    -13.384     41.2      8    {1x1 cell}    'eq'     NaN       NaN    
-     7.333e+05    2007_09_12    11:10:26.8      101.4    -4.4637     35.5    8.5    {1x1 cell}    'eq'     NaN       NaN    
-    7.3405e+05    2009_09_29    17:48:11.5    -171.94    -15.512     18.5    8.1    {1x1 cell}    'eq'     NaN       NaN    
-     7.342e+05    2010_02_27    06:34:13.3    -72.933    -36.148     28.1    8.8    {1x1 cell}    'eq'     NaN       NaN    
-    7.3457e+05    2011_03_11    05:46:23.2      142.5     38.296     19.7    9.1    {1x1 cell}    'eq'     NaN       NaN    
-    7.3497e+05    2012_04_11    08:38:37.8     93.014     2.2376     26.3    8.6    {1x1 cell}    'eq'     NaN       NaN    
-    7.3497e+05    2012_04_11    10:43:10.5     92.428     0.7675     21.6    8.2    {1x1 cell}    'eq'     NaN       NaN    
-    7.3527e+05    2013_02_06    01:12:27.0     165.14    -10.738     28.7      8    {1x1 cell}    'eq'     NaN       NaN    
-    7.3538e+05    2013_05_24    05:44:49.6     153.28     54.874    608.9    8.3    {1x1 cell}    'eq'     NaN       NaN    
-    7.3569e+05    2014_04_01    23:46:47.2    -70.769     -19.61       25    8.2    {1x1 cell}    'eq'     NaN       NaN    
-
-

Compare:

greatquakes
-
-greatquakes = 
-
-
-ans =
-
-Catalog
-
-Number of events: 20
-Biggest event: 9.100000 at 11-Mar-2011 05:46:23
-      otime       yyyy_mm_dd     hh_mm_ss       lon        lat      depth    mag    magtype       etype        ontime    offtime
-    __________    __________    __________    _______    _______    _____    ___    _______    ____________    ______    _______
-
-    7.3103e+05    2001_06_23    20:33:09.3    -73.561    -16.303      2.2    8.3    'MW'       'earthquake'    NaN       NaN    
-    7.3185e+05    2003_09_25    19:50:07.2     143.87     41.749       33    8.1    'MW'       'earthquake'    NaN       NaN    
-     7.323e+05    2004_12_23    14:59:00.6     161.58     -49.71       10      8    'MW'       'earthquake'    NaN       NaN    
-    7.3231e+05    2004_12_26    00:58:52.0     95.901     3.4125     26.1    8.2    'MW'       'earthquake'    NaN       NaN    
-     7.324e+05    2005_03_28    16:09:35.2     97.113     2.0964       30    8.1    'MW'       'earthquake'    NaN       NaN    
-    7.3283e+05    2006_06_01    18:57:02.9     120.88     19.054       13    8.4    'MS'       'earthquake'    NaN       NaN    
-    7.3283e+05    2006_06_05    00:50:31.5     119.08     17.992      124      8    'MS'       'earthquake'    NaN       NaN    
-      7.33e+05    2006_11_15    11:14:14.5     153.21     46.681     12.2    8.3    'MW'       'earthquake'    NaN       NaN    
-    7.3306e+05    2007_01_13    04:23:23.2      154.5     46.231     22.5    8.1    'MW'       'earthquake'    NaN       NaN    
-    7.3313e+05    2007_04_01    20:39:56.5     157.03    -8.4468      9.5    8.1    'MW'       'earthquake'    NaN       NaN    
-    7.3327e+05    2007_08_15    23:40:58.4    -76.555    -13.384     41.2      8    'MW'       'earthquake'    NaN       NaN    
-     7.333e+05    2007_09_12    11:10:26.8      101.4    -4.4637     35.5    8.5    'MW'       'earthquake'    NaN       NaN    
-    7.3405e+05    2009_09_29    17:48:11.5    -171.94    -15.512     18.5    8.1    'MW'       'earthquake'    NaN       NaN    
-     7.342e+05    2010_02_27    06:34:13.3    -72.933    -36.148     28.1    8.8    'MW'       'earthquake'    NaN       NaN    
-    7.3457e+05    2011_03_11    05:46:23.2      142.5     38.296     19.7    9.1    'MW'       'earthquake'    NaN       NaN    
-    7.3497e+05    2012_04_11    08:38:37.8     93.014     2.2376     26.3    8.6    'MW'       'earthquake'    NaN       NaN    
-    7.3497e+05    2012_04_11    10:43:10.5     92.428     0.7675     21.6    8.2    'MW'       'earthquake'    NaN       NaN    
-    7.3527e+05    2013_02_06    01:12:27.0     165.14    -10.738     28.7      8    'MW'       'earthquake'    NaN       NaN    
-    7.3538e+05    2013_05_24    05:44:49.6     153.28     54.874    608.9    8.3    'MW'       'earthquake'    NaN       NaN    
-    7.3569e+05    2014_04_01    23:46:47.2    -70.769     -19.61       25    8.2    'MWW'      'earthquake'    NaN       NaN    
-
-

This concludes the Catalog cookbook/tutorial.

\ No newline at end of file diff --git a/core/@Catalog/html/cookbook.png b/core/@Catalog/html/cookbook.png deleted file mode 100644 index ba73b5e..0000000 Binary files a/core/@Catalog/html/cookbook.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_01.png b/core/@Catalog/html/cookbook_01.png deleted file mode 100644 index a8c0bf6..0000000 Binary files a/core/@Catalog/html/cookbook_01.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_02.png b/core/@Catalog/html/cookbook_02.png deleted file mode 100644 index c4a089f..0000000 Binary files a/core/@Catalog/html/cookbook_02.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_03.png b/core/@Catalog/html/cookbook_03.png deleted file mode 100644 index 262629d..0000000 Binary files a/core/@Catalog/html/cookbook_03.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_04.png b/core/@Catalog/html/cookbook_04.png deleted file mode 100644 index 407571a..0000000 Binary files a/core/@Catalog/html/cookbook_04.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_05.png b/core/@Catalog/html/cookbook_05.png deleted file mode 100644 index f19ab5e..0000000 Binary files a/core/@Catalog/html/cookbook_05.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_06.png b/core/@Catalog/html/cookbook_06.png deleted file mode 100644 index 18a3ca0..0000000 Binary files a/core/@Catalog/html/cookbook_06.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_07.png b/core/@Catalog/html/cookbook_07.png deleted file mode 100644 index 2ea13e8..0000000 Binary files a/core/@Catalog/html/cookbook_07.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_08.png b/core/@Catalog/html/cookbook_08.png deleted file mode 100644 index 79d41be..0000000 Binary files a/core/@Catalog/html/cookbook_08.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_09.png b/core/@Catalog/html/cookbook_09.png deleted file mode 100644 index 166ddc7..0000000 Binary files a/core/@Catalog/html/cookbook_09.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_10.png b/core/@Catalog/html/cookbook_10.png deleted file mode 100644 index 772bbac..0000000 Binary files a/core/@Catalog/html/cookbook_10.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_11.png b/core/@Catalog/html/cookbook_11.png deleted file mode 100644 index 8b3493c..0000000 Binary files a/core/@Catalog/html/cookbook_11.png and /dev/null differ diff --git a/core/@Catalog/html/cookbook_12.png b/core/@Catalog/html/cookbook_12.png deleted file mode 100644 index d17a5a5..0000000 Binary files a/core/@Catalog/html/cookbook_12.png and /dev/null differ diff --git a/core/@Catalog/list_waveform_metrics.m b/core/@Catalog/list_waveform_metrics.m new file mode 100644 index 0000000..501de62 --- /dev/null +++ b/core/@Catalog/list_waveform_metrics.m @@ -0,0 +1,111 @@ +function list_waveform_metrics(cobj, list_arrival_metrics_instead) +%LIST_WAVEFORM_METRICS For each event in a Catalog object, list the metrics for each station-channel. +% For this to work, the addwaveforms() and addmetrics() methods must have been run on a Catalog object. +% +% Inputs: +% cobj = a Catalog object to which waveforms, or Arrival waveforms have been added +% list_arrival_metrics_instead = an optional argument. Can be true or false. default: false +% +% list_waveform_metrics(catalogObj) List amplitude for each station-channel for each event. Catalog.addwaveforms() +% and Catalog.addmetrics() must have been run. For example, the metrics for event 1 can be viewed with: +% get(catalogObj.waveforms{1}, 'metrics') +% +% +% list_waveform_metrics(catalogObj, true) List amplitude for each Arrival in each event. Arrival.addwaveforms() +% and Arrival.addmetrics() and Arrival.associate() must have been run. For example, the metrics for event 1 can be viewed with: +% get(catalogObj.arrivals{1}.waveforms, 'metrics') + + + if ~exist('list_arrival_metrics_instead', 'var') + list_arrival_metrics_instead = false; + end + + if list_arrival_metrics_instead + numevents = numel(cobj.arrivals); + if numevents == 0 + disp('No Arrivals found in Catalog. You may need to run Detection.associate() or Arrival.associate()') + end + else + numevents = numel(cobj.waveforms); + if numevents == 0 + disp('No Waveforms found in Catalog. You may need to run Catalog.addwaveforms() and Catalog.addmetrics()') + end + end + + + % get a complete list of channel tags + ctags = []; + numctags = 0; + mintime = Inf; + maxtime = -Inf; + for eventnum=1:numevents + if list_arrival_metrics_instead + w = cobj.arrivals{eventnum}.waveforms; + else + w = cobj.waveforms{eventnum}; + end + [snum enum]=gettimerange(w); + if min(snum)maxtime + maxtime = max(enum); + end + ctags = unique([ctags; get(w,'ChannelTag')]); + end + timediff = maxtime-mintime; + + % titles + disp('Maximum Amplitude____________________') + fprintf('\nEvent\tdd-mmm-yyyy hh:mm:ss\tJulianDay'); + + for ctagnum = 1:numel(ctags) + thischan = get(ctags(ctagnum),'channel'); + %fprintf('\t%s', ctags(ctagnum).string()); + fprintf('\t%s', thischan(1:3)); + end + fprintf('\n.....\t....................\t.........'); + for ctagnum = 1:numel(ctags) + if strfind(get(ctags(ctagnum),'channel'),'D') + fprintf('\t(Pascals)'); + else + fprintf('\t(nm/sec)'); + end + end + fprintf('\n'); + + + % now go through different metrics of interest, and list for each + % event for each channel + for eventnum=1:numevents + fprintf('%5d:', eventnum); + if list_arrival_metrics_instead + w = cobj.arrivals{eventnum}.waveforms; + else + w = cobj.waveforms{eventnum}; + end + wctags = get(w,'ChannelTag'); + a = []; + t = Inf; + for ctagnum = 1:numel(ctags) + a(ctagnum) = -1; + thisctag = ctags(ctagnum); + + idx = find(ismember(wctags.string(), thisctag.string())); + if idx + m = get(w(idx(1)),'metrics'); + a(ctagnum) = max(abs([m.minAmp m.maxAmp])); + t = min([m.minTime m.maxTime t]); + end + end + jday = datenum2julday(t); + fprintf('\t%s\t%3d', datestr(t),mod(jday,1000)); + for ctagnum = 1:numel(ctags) + if a(ctagnum)>=0 + fprintf('\t%15.1f', a(ctagnum)); + else + fprintf('\t%s', ' '); %change whats here but keept o 15 characters if need change in table + end + end + fprintf('\n'); + end diff --git a/core/@Catalog/crap.txt b/core/@Catalog/obsolete/crap.txt similarity index 100% rename from core/@Catalog/crap.txt rename to core/@Catalog/obsolete/crap.txt diff --git a/core/@Catalog/plot.m b/core/@Catalog/plot.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/plot3.m b/core/@Catalog/plot3.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/plot_counts.m b/core/@Catalog/plot_counts.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/plot_time.m b/core/@Catalog/plot_time.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/plot_waveform_metrics.m b/core/@Catalog/plot_waveform_metrics.m new file mode 100755 index 0000000..44e1883 --- /dev/null +++ b/core/@Catalog/plot_waveform_metrics.m @@ -0,0 +1,48 @@ +function plot_waveform_metrics(cobj) + wcell = cobj.waveforms; + numevents = numel(wcell); + if numevents == 0 + return + end + + % get a complete list of channel tags + ctags = []; + numctags = 0; + mintime = Inf; + maxtime = -Inf; + for eventnum=1:numevents + w = wcell{eventnum}; + [snum enum]=gettimerange(w); + if min(snum)maxtime + maxtime = max(enum); + end + ctags = unique([ctags; get(w,'ChannelTag')]); + end + timediff = maxtime-mintime; + + % 1 subplot per channel tag + figure + for eventnum=1:numevents + w = wcell{eventnum}; + for wavnum=1:numel(w) + ctag = get(w(wavnum),'ChannelTag'); + idx = find(ismember(ctags.string(), ctag.string())); + m = get(w(wavnum),'metrics'); + + hold on + subplot(numel(ctags), 1, idx) + + plot(m.maxTime, max(abs([m.maxAmp m.minAmp])), 'b*'); + + u = get(w(wavnum),'units'); + ylabel(sprintf('%s\n%s',ctag.string(),u)); + set(gca,'XLim',[mintime-timediff/10 maxtime+timediff/10]); + datetick('x','keeplimits') + end + end +end + + \ No newline at end of file diff --git a/core/@Catalog/plotprmm.m b/core/@Catalog/plotprmm.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/retrieve.m b/core/@Catalog/retrieve.m new file mode 100755 index 0000000..550fcf1 --- /dev/null +++ b/core/@Catalog/retrieve.m @@ -0,0 +1,91 @@ +function self = retrieve(dataformat, varargin) +%CATALOG.RETRIEVE Read seismic events from common file formats & data sources. +% retrieve can read events from many different earthquake catalog file +% formats (e.g. Seisan, Antelope) and data sources (e.g. IRIS DMC) into a +% GISMO Catalog object. +% +% Usage: +% catalogObject = CATALOG.RETRIEVE(dataformat, 'param1', _value1_, ... +% 'paramN', _valueN_) +% +% dataformat may be: +% +% * 'iris' (for IRIS DMC, using irisFetch.m), +% * 'antelope' (for a CSS3.0 Antelope/Datascope database) +% * 'seisan' (for a Seisan database with a REA/YYYY/MM/ directory structure) +% * 'zmap' (converts a Zmap data strcture to a Catalog object) +% +% The name-value parameter pairs supported are the same as those supported +% by irisFetch.Events(). Currently these are: +% +% startTime +% endTime +% eventId +% fetchLimit +% magnitudeType +% minimumLongitude +% maximumLongitude +% minimumLatitude +% maximumLatitude +% minimumMagnitude +% maximumMagnitude +% minimumDepth +% maximumDepth +% +% And the two convenience parameters: +% +% radialcoordinates = [ centerLatitude, centerLongitude, maximumRadius ] +% +% boxcoordinates = [ minimumLatitude maximumLatitude minimumLongitude maximumLongitude ] +% +% For examples, see Catalog_cookbook. Also available at: +% https://geoscience-community-codes.github.io/GISMO/tutorials/html/Catalog_cookbook.html +% +% +% See also CATALOG, IRISFETCH, CATALOG_COOKBOOK + +% Author: Glenn Thompson (glennthompson1971@gmail.com) + +%% To do: +% Implement name-value parameter pairs for all methods +% Test the Antelope method still works after factoring out db_load_origins +% Test the Seisan method more +% Add in support for 'get_arrivals' + + debug.printfunctionstack('>') + + switch lower(dataformat) + case 'iris' + if exist('irisFetch.m','file') + ev = irisFetch.Events(varargin{:}); + self = Catalog.read_catalog.iris(ev); + else + warning('Cannot find irisFetch.m') + end + case {'css3.0','antelope', 'datascope'} + if admin.antelope_exists() + self = Catalog.read_catalog.antelope(varargin{:}); + else + warning('Sorry, cannot read event Catalog from Antelope database as Antelope toolbox for MATLAB not found') + self = Catalog(); + end + case 'seisan' + self = Catalog.read_catalog.seisan(varargin{:}); + case 'aef' + self = Catalog.read_catalog.aef(varargin{:}); + case 'sru' + self = Catalog.read_catalog.sru(varargin{:}); + case 'vdap' + self = Catalog.read_catalog.vdap(varargin{:}); + case 'zmap' + self = Catalog.read_catalog.zmap(varargin{:}); + otherwise + self = NaN; + fprintf('format %s unknown\n\n',data_source); + end + if isempty(self) + self=Catalog(); + end + + debug.printfunctionstack('<') +end \ No newline at end of file diff --git a/core/@Catalog/subclassify.m b/core/@Catalog/subclassify.m old mode 100644 new mode 100755 index d7deb9a..e01f135 --- a/core/@Catalog/subclassify.m +++ b/core/@Catalog/subclassify.m @@ -9,15 +9,15 @@ if strcmp(subclasses, '*')==0 for i = 1:length(subclasses); c(i) = catalogObject; - subclass = subclasses(i); - index = strfind(char(catalogObject.etype)', subclass); % previously findstr + subclass{i} = subclasses(i); + index = strfind(char(catalogObject.etype)', subclass{i}); % previously findstr if length(index)>0 - c(i) = catalogObject.subset(index); + c(i) = catalogObject.subset('indices',index); else c(i) = Catalog(); end c(i).request = catalogObject.request; - c(i).request.subclass = subclass; + c(i).request.subclass = subclass{i}; end end end diff --git a/core/@Catalog/subset.m b/core/@Catalog/subset.m old mode 100644 new mode 100755 index c36b719..6363dec --- a/core/@Catalog/subset.m +++ b/core/@Catalog/subset.m @@ -1,5 +1,113 @@ -function catalogObject2 = subset(catalogObject, indices) +function cobj2 = subset(cobj, varargin) %CATALOG.SUBSET Create a new catalogObject by subsetting based %on indices. - catalogObject2 = Catalog(catalogObject.table(indices,:)); + % Usage: + % cobj.subset('indices', []) + % or: + % cobj.subset('start_time', [], 'end_time', []) + % can also just give one of start_time or end_time + + %if nargin>0 + % Parse required, optional and param-value pair arguments, + % set default values, and add validation conditions + p = inputParser; + p.addParameter('start_time', [], @isnumeric) % positional + p.addParameter('end_time', [], @isnumeric) + p.addParameter('indices', [], @isnumeric) + p.parse(varargin{:}); + fields = fieldnames(p.Results); + for i=1:length(fields) + field=fields{i}; + val = p.Results.(field); + eval(sprintf('%s = val;',field)); + end + %end + +% datestr(min(cobj.otime)) +% datestr(max(cobj.otime)) +% datestr(start_time) +% datestr(end_time) + + if isempty(indices) + i1 = 1:numel(cobj.otime); + if start_time + i1 = find(cobj.otime >= start_time); + debug.print_debug(1,sprintf('Found %d events after %s',numel(i1), datestr(start_time))); + end + i2 = 1:numel(cobj.otime); + if end_time + i2 = find(cobj.otime <= end_time); + debug.print_debug(1,sprintf('Found %d events before %s',numel(i1), datestr(end_time))); + end + indices = intersect(i1, i2); + end + + + cobj2 = cobj; + N = numel(cobj.otime); + debug.print_debug(1,sprintf('Subsetting from %d events to %d events',N, numel(indices))); + cobj2.otime = cobj.otime(indices); + if numel(cobj.lon)==N + cobj2.lon = cobj.lon(indices); + end + if numel(cobj.lat)==N + cobj2.lat = cobj.lat(indices); + end + if numel(cobj.depth)==N + cobj2.depth = cobj.depth(indices); + end + if numel(cobj.mag)==N + cobj2.mag = cobj.mag(indices); + end + if numel(cobj.magtype)==N + dummy=[]; +% try % there is a limit on subsetting cell array +% cobj2.magtype = cobj.magtype{indices}; +% end + for c=1:numel(indices) + dummy{c} = cobj.magtype{indices(c)}; + end + cobj2.magtype = dummy; + clear dummy + end + if numel(cobj.etype)==N + dummy=[]; + for c=1:numel(indices) + dummy{c} = cobj.etype{indices(c)}; + end + cobj2.etype = dummy; + clear dummy; + end + if numel(cobj.arrivals)==N + cobj2.arrivals = cobj.arrivals(indices); + end + if numel(cobj.waveforms)==N + cobj2.waveforms = cobj.waveforms(indices); +% counter = 1; +% indices +% for thisindex=indices +% cobj2.waveforms{counter} = cobj.waveforms{thisindex}; +% counter = counter + 1; +% end + end + if numel(cobj.ontime)==N + cobj2.ontime = cobj.ontime(indices); + end + if numel(cobj.offtime)==N + cobj2.offtime = cobj.offtime(indices); + end + + %% Added by Glenn Thompson 2018-05-01 to add a request start & end time + if isempty(start_time) + cobj2.request.start_time = min(cobj2.otime); + else + cobj2.request.start_time = start_time; + end + if isempty(end_time) + cobj2.request.end_time = max(cobj2.otime); + else + cobj2.request.end_time = end_time; + end + + end \ No newline at end of file diff --git a/core/@Catalog/summary.m b/core/@Catalog/summary.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/webmap.m b/core/@Catalog/webmap.m old mode 100644 new mode 100755 diff --git a/core/@Catalog/write.m b/core/@Catalog/write.m old mode 100644 new mode 100755 index d80b345..01e4943 --- a/core/@Catalog/write.m +++ b/core/@Catalog/write.m @@ -1,4 +1,4 @@ -function write(catalogObject, outformat, outpath, schema) +function write(catalogObject, outformat, outpath, varargin) %CATALOG.WRITE Write an Catalog object to disk % % catalogObject.write('antelope', 'mydb', 'css3.0') writes the @@ -13,7 +13,7 @@ function write(catalogObject, outformat, outpath, schema) switch outformat case {'text';'csv';'xls'} % help table.write for more info - write(catalogObject.table, outpath); + write(catalogObject.table(), outpath); % add a table method to Catalog after changing Catalog so it no longer uses a table internally case 'antelope' @@ -28,15 +28,51 @@ function write(catalogObject, outformat, outpath, schema) end antelope.dbcreate(dbpath, schema); - system(sprintf('touch %s.event',dbpath)); - system(sprintf('touch %s.origin',dbpath)); + % remove the following tables if they exist and mode is + % "overwrite" + if nargin==4 & strcmp(varargin{1},'overwrite') + tableNames = {'arrival';'assoc';'netmag';'stamag';'origin';'event';'wfmeas'}; + for tablenum = 1 : numel(tableNames) + thisTable = sprintf('%s.%s',dbpath,tableNames{tablenum}); + if exist(thisTable, 'file') + if nargin>=4 + if strcmp(varargin{1},'overwrite') + fprintf('Overwrite mode: Removing %s\n',thisTable); + delete(thisTable); + else + % for 'append' mode, nothing to do + fprintf('Append mode: You will append to %s\n',thisTable); + end + else + % nothing specified, so force user to + % choose, as we never want to mess up + % existing tables or delete them without + % user input + choice = input(sprintf('delete %s (y/n)',thisTable),'s'); + if lower(choice(1)=='y') + fprintf('Overwrite mode: Removing %s\n',thisTable); + delete(thisTable); + end + end + end + end + end +% system(sprintf('touch %s.event',dbpath)); +% system(sprintf('touch %s.origin',dbpath)); + + + disp('Writing new rows...'); % open db db = dbopen(dbpath, 'r+'); dbe = dblookup_table(db,'event'); dbo = dblookup_table(db,'origin'); dbn = dblookup_table(db,'netmag'); - + dbas = dblookup_table(db,'assoc'); + dbar = dblookup_table(db,'arrival'); + dbs = dblookup_table(db,'stamag'); + dbwm = dblookup_table(db,'wfmeas'); + % write event to event and origin tables if numel(catalogObject.otime)>0 for eventidx = 1:numel(catalogObject.otime) @@ -47,6 +83,8 @@ function write(catalogObject, outformat, outpath, schema) origin.lat = catalogObject.lat(eventidx); origin.depth = catalogObject.depth(eventidx); origin.etype = catalogObject.etype{eventidx}; +% if isnan(origin.lat) +% origin.lat = %%% ad dfeault % Antelope etype can only be two characters % Antelope uses 'eq' where IRIS use @@ -70,8 +108,19 @@ function write(catalogObject, outformat, outpath, schema) 'prefor', origin.orid); % Add new record to origin table & write to - % it dbo.record = dbaddnull(dbo); + if isnan(origin.lat) + origin.lat = -999.0; + end + if isnan(origin.lon) + origin.lon = -999.0; + end + if isnan(origin.depth) + origin.depth = -999.0; + end +% if strcmp(origin.etype,'') +% origin.etype = '-'; +% end dbputv(dbo, 'lat', origin.lat, ... 'lon', origin.lon, ... 'depth', origin.depth, ... @@ -82,18 +131,200 @@ function write(catalogObject, outformat, outpath, schema) % Add new record to netmag table & write to % it - dbn.record = dbaddnull(dbn); - dbputv(dbn, 'magid', netmag.magid, ... - 'orid', origin.orid, ... - 'evid', event.evid, ... - 'magtype', netmag.magtype, ... - 'magnitude', netmag.magnitude ); + if isnumeric(netmag.magnitude) & netmag.magnitude > -999 + dbn.record = dbaddnull(dbn); + dbputv(dbn, 'magid', netmag.magid, ... + 'orid', origin.orid, ... + 'evid', event.evid, ... + 'magtype', netmag.magtype, ... + 'magnitude', netmag.magnitude ); + end + + % Add wfmeas rows for each event waveform metric + if numel(catalogObject.waveforms) >= eventidx + ew = catalogObject.waveforms{eventidx}; + N_ew = numel(ew); + for ewavnum=1:N_ew + thisEW = ew(ewavnum); + waveform2wfmeas(dbwm, thisEW); + end + end + + % Add new record to arrival table & write to + % it + if numel(catalogObject.arrivals) >= eventidx + thisA = catalogObject.arrivals{eventidx}; + N = numel(thisA.time); + + % check if we have 1 waveform per arrival + w = thisA.waveforms; + Nw = numel(w); + bool_add_waveform_metrics = (N == Nw); + + if N>0 + for arrnum = 1:N + ctag = ChannelTag(thisA.channelinfo{arrnum}); + asta = ctag.station; + atime = datenum2epoch(thisA.time(arrnum)); + aarid = dbnextid(dbar,'arid'); + achan = ctag.channel; + aiphase = thisA.iphase{arrnum}; + aamp = -1.0; aper = -1.0; asnr = -1; + try + aamp = thisA.amp(arrnum); + end + try + asnr = thisA.signal2noise(arrnum); + end + try + aper = thisA.per(arrnum); + end + + % add arrival row + dbar.record = dbaddnull(dbar); + dbputv(dbar, 'sta', asta, ... + 'time', atime, ... + 'arid', aarid, ... + 'chan', achan, ... + 'iphase', aiphase, ... + 'amp', aamp, ...); + 'per', aper, ... + 'snr', asnr); + + % add assoc row + dbas.record = dbaddnull(dbas); + dbputv(dbas, 'arid', aarid, ... + 'orid', origin.orid, ... + 'sta', asta, ... + 'phase', aiphase); + + + if bool_add_waveform_metrics + % add wfmeas row for each waveform metric + thisW = w(arrnum); + waveform2wfmeas(dbwm, thisW, aarid, asta, achan); + end + end + end + end end end dbclose(db); + disp('(Complete)'); end otherwise, warning('format not supported yet') end % end switch end % function + + +function waveform2wfmeas(dbwm, thisW, aarid, asta, achan) + wsta = get(thisW, 'station'); + wchan = get(thisW, 'channel'); + [wsnum wenum] = gettimerange(thisW); + wstartepoch = datenum2epoch(wsnum); + wendepoch = datenum2epoch(wenum); + u = get(thisW,'units'); + try + m = get(thisW, 'metrics'); % will error if metrics not defined + if ~isnan(m.maxAmp) + % add minTime maxTime minAmp maxAmp + dbwm.record = dbaddnull(dbwm); + + dbputv(dbwm, 'sta', wsta, ... + 'chan', wchan, ... + 'meastype', 'amplitude', ... + 'time', datenum2epoch(m.minTime), ... + 'endtime', datenum2epoch(m.maxTime), ... + 'val1', m.minAmp, ... + 'val2', m.maxAmp, ... + 'units1', u, ... + 'units2', u); + if exist('aarid', 'var') + if strcmp(wsta, asta) & strcmp(wchan, achan) + dbputv(dbwm, 'arid', aarid); + end + end + + % add stdev + dbwm.record = dbaddnull(dbwm); + dbputv(dbwm, 'sta', wsta, ... + 'chan', wchan, ... + 'meastype', 'stdev', ... + 'time', wstartepoch, ... + 'endtime', wendepoch, ... + 'val1', m.stdev, ... + 'units1', u); + if exist('aarid', 'var') + if strcmp(wsta, asta) & strcmp(wchan, achan) + dbputv(dbwm, 'arid', aarid); + end + end + + % add energy + e = m.energy; + eu = u; + eu(eu==' ') = ''; % remove whitespace + if length(eu)>=4 & strcmp(eu(1:4), 'nm/s') + eu = 'nm^2/s'; + end + if strcmp(eu(1:2), 'Pa') + eu = 'Pa^2.s'; + end + if strfind(eu, 'nm') + if e >= 1e12 + e = e / 1e6; + eu = strrep(eu, 'nm', 'um'); + end + end + dbwm.record = dbaddnull(dbwm); + dbputv(dbwm, 'sta', wsta, ... + 'chan', wchan, ... + 'meastype', 'energy', ... + 'time', wstartepoch, ... + 'endtime', wendepoch, ... + 'val1', e, ... + 'units1', eu); + if exist('aarid', 'var') + if strcmp(wsta, asta) & strcmp(wchan, achan) + dbputv(dbwm, 'arid', aarid); + end + end + + % %names = fieldnames(m); + % names = {'stdev';'energy'}; + % for namecount = 1:numel(names) + % thisname = names{namecount}; + % + % % write a wfmeas row for each metric in + % % this waveform + % dbwm.record = dbaddnull(dbwm); + % val1 = getfield(m, thisname); + % if strfind(thisname, 'Time') + % val1=datenum2epoch(val1); + % end + % + % dbputv(dbwm, 'sta', wsta, ... + % 'chan', wchan, ... + % 'meastype', thisname, ... + % 'time', wstartepoch, ... + % 'endtime', wendepoch, ... + % 'val1', val1 ); + % %'filter', filterdesc, ... + % %'tmeas', atime, ... + % %'twin', atwin, ... + % %'val2', , ... + % %'units1', , ... + % %'units2', , ... + % if exist('aarid', 'var') + % if strcmp(wsta, asta) & strcmp(wchan, achan) + % dbputv(dbwm, 'arid', aarid); + % end + % end + % end + end + catch + disp('No metrics for this waveform object') + end +end diff --git a/core/@Catalog/write_waveform_metrics_to_antelope.m b/core/@Catalog/write_waveform_metrics_to_antelope.m new file mode 100755 index 0000000..283ca1d --- /dev/null +++ b/core/@Catalog/write_waveform_metrics_to_antelope.m @@ -0,0 +1,98 @@ +function write_waveform_metrics_to_antelope( cobj, dbpath1, dbpath2 ) +%WRITE_METRICS_TO_ANTELOPE Write metrics to a CSS3.0 database +% Detailed explanation goes here + +% Pseudocode: +% 1. Make a copy of the database dbpath1 to dbpath2 +% 2. For each arrival without an amp in the arrival table, add the amp & per value. +% 3. For each arrival with an amp in the arrival table, compare the amp & +% per values. Are they consistent? +% 4. Create an event row & origin row for each event. +% 5. Associate arrivals in each event to the origin. +% 6. For each waveform metric for each arrival waveform, add a wfmeas +% row. +% 7. For each waveform metric for each event waveform, add a wfmeas +% row. +% Other related code: +% - a. a function that reads an Antelope database into a Catalog object +% including arrival, assoc, wfmeas - recreating the whole saved Catalog +% object. +% - b. a function to plot a Catalog object. this would call a function to +% plot arrival objects, and another function to plot metrics of waveform objects. + +% Pseudocode: +% 1. Make a copy of the database dbpath1 to dbpath2 + cmdstr = sprintf('dbcp %s %s', dbpath1, dbpath2); + result = eval(cmdstr); + +% 2. For each arrival without an amp in the arrival table, add the amp & per value. + cobj.arrivals.write(dbpath2); + +% 3. For each arrival with an amp in the arrival table, compare the amp & +% per values. Are they consistent? + % Probably easier to do this when computing arrival amplitudes! + +% 4. Create an event row & origin row for each event. + cobj.write('antelope', dbpath2); % this also does netmag and stamag + +% 5. Associate arrivals in each event to the origin. + % add this to cobj.write() + +% 6. For each waveform metric for each arrival waveform, add a wfmeas +% row. + cobj.arrivals. +% 7. For each waveform metric for each event waveform, add a wfmeas +% row. +% Other related code: +% - a. a function that reads an Antelope database into a Catalog object +% including arrival, assoc, wfmeas - recreating the whole saved Catalog +% object. +% - b. a function to plot a Catalog object. this would call a function to +% plot arrival objects, and another function to plot metrics of waveform objects. + + wcell = cobj.waveforms; + numevents = numel(wcell); + if numevents == 0 + return + end + + % get a complete list of channel tags + ctags = []; + numctags = 0; + mintime = Inf; + maxtime = -Inf; + for eventnum=1:numevents + w = wcell{eventnum}; + [snum enum]=gettimerange(w); + if min(snum)maxtime + maxtime = max(enum); + end + ctags = unique([ctags; get(w,'ChannelTag')]); + end + timediff = maxtime-mintime; + + % 1 subplot per channel tag + figure + for eventnum=1:numevents + w = wcell{eventnum}; + for wavnum=1:numel(w) + ctag = get(w(wavnum),'ChannelTag'); + idx = find(ismember(ctags.string(), ctag.string())); + m = get(w(wavnum),'metrics'); + + hold on + subplot(numel(ctags), 1, idx) + + plot(m.maxTime, max(abs([m.maxAmp m.minAmp])), 'b*'); + + u = get(w(wavnum),'units'); + ylabel(sprintf('%s\n%s',ctag.string(),u)); + set(gca,'XLim',[mintime-timediff/10 maxtime+timediff/10]); + datetick('x','keeplimits') + end + end +end + diff --git a/core/@ChannelTag/ChannelTag.m b/core/@ChannelTag/ChannelTag.m old mode 100644 new mode 100755 index fcca273..e484e28 --- a/core/@ChannelTag/ChannelTag.m +++ b/core/@ChannelTag/ChannelTag.m @@ -12,6 +12,7 @@ % ChannelTag Methods: % char - Retrieve properties as 'network.station.location.channel' % string - Retrieve properties as 'network.station.location.channel' + % scn - Retrieve properties as 'station_channel_network' % fixedlengthstrings - return strings of a fixed length % eq - == for channeltags, with simple '*' wildcard support % ne - ~= for channeltag (no wilcard support) @@ -345,7 +346,7 @@ function disp(obj) end end - function s = getDelimitedString(obj, delim) + function s = getDelimitedString(obj, delim) %getDelimitedString return a string with delimited fields % getDelimitedString(chtag, delim) returns the fields in % network,station, location, channel order, with the @@ -359,7 +360,44 @@ function disp(obj) % s = [obj.network, delim, obj.station, delim,... obj.location, delim, obj.channel]; + end + end + + function s = scn(obj, delim, option) + %scn returns scn representation of the channelTag(s) + % s = chaTag.scn() will return the string representation + % (1xn char) in the format STA_CHA_NET + % + % s = chaTag.string(DELIM) will use DELIM to separate + % fields. + % ex. chaTag.string('-'); will return NET-STA-LOC-CHA + % + % If chaTag is an array, then results are returned as a + % a cell of strings of same shape as chaTag will be returned. + % + % s = chaTag.string(DELIM,'nocell') to overrides functionality + % to return a padded NxM char array + % if DELIM is empty, then '.' will be used. + if ~exist('delim','var') || isempty(delim) + delim = '_'; end + if numel(obj) == 1 + s = [obj.station, delim, obj.channel, delim, obj.network]; + else + if exist('option','var') && strcmpi(option,'nocell') + s = ''; + for n=1 : numel(obj) + tmp = [obj.station, delim, obj.channel, delim, obj.network]; + s(n,1:numel(tmp)) = tmp; + end + else + s = cell(size(obj)); + for n=1 : numel(obj) + s(n) = {[obj.station, delim, obj.channel, delim, obj.network]}; + end + end + end + end end%methods diff --git a/core/@ChannelTag/eq.m b/core/@ChannelTag/eq.m old mode 100644 new mode 100755 diff --git a/core/@Detection/Detection.m b/core/@Detection/Detection.m new file mode 100644 index 0000000..1e8a461 --- /dev/null +++ b/core/@Detection/Detection.m @@ -0,0 +1,297 @@ +%Detection the blueprint for Detection objects in GISMO +% An Detection object is a container for sta/lta detection metadata +% See also Arrival, Catalog +classdef Detection + properties + channelinfo + time + state + filterString + signal2noise + traveltime + end + properties(Dependent) + numel + end + methods + function obj = Detection(sta, chan, time, state, filterString, signal2noise) + + % Blank constructor + if ~exist('sta','var') + return + end + + % Parse required, optional and param-value pair arguments, + % set default values, and add validation conditions + p = inputParser; + p.addRequired('sta', @iscell); + p.addRequired('chan', @iscell); + %p.addRequired('time', @(t) t>0 & t101)=101; + [n x]=hist(s, 1:0.1:100); +plot(x,100-cumsum(n)/sum(n)*100,'LineWidth',5); + xlabel('signal2noise'); + %ylabel(ctaguniq{c}); + ylabel('%age') + set(gca, 'XLim', [2.4 20]); + end + figure(hf1) + legend(ctaguniq) + figure(hf2) + legend(ctaguniq) + end + + function summary(obj, showall) + % DETECTION.SUMMARY Summarise Detection object + for c=1:numel(obj) + obj(c) + numrows = numel(obj(c).time); + fprintf('Number of detections: %d\n',numrows); + if numrows > 0 + if ~exist('showall','var') + showall = false; + end + if numel(obj) == 1 + if numrows <= 50 || showall + for rownum=1:numrows + summarize_row(obj, rownum); + end + else + for rownum=1:50 + summarize_row(obj, rownum); + end + disp('* Only showing first 50 rows/arrivals - to see all rows/arrivals use:') + disp('* arrivalObject.disp(true)') + end + end + end + end + end + + function summarize_row(self, rownum) + fprintf('%s\t%s\t%s\t%e\n', ... + self.channelinfo(rownum), ... + datestr(self.time(rownum)), ... + self.state(rownum), ... + self.signal2noise(rownum)); + end + + function self2 = subset(self, columnname, findval) + self2 = self; + N = numel(self.time); + indexes = []; + if ~exist('findval','var') + % assume columnname is actually row numbers + indexes = columnname; + else + + for c=1:N + gotval = eval(sprintf('self.%s(c);',columnname)); + if isa(gotval,'cell') + gotval = cell2mat(gotval); + end + if isnumeric(gotval) + if gotval==findval + indexes = [indexes c]; + end + else + if strcmp(gotval,findval) + indexes = [indexes c]; + end + end + end + end +% if isa(self.channelinfo,'cell') +% self2.channelinfo = self.channelinfo{indexes}; +% else + self2.channelinfo = self.channelinfo(indexes); +% end + self2.time = self.time(indexes); + self2.state = self.state(indexes); + if numel(self.filterString)==N + self2.filterString = self.filterString(indexes); + end + if numel(self.signal2noise)==N + self2.signal2noise = self.signal2noise(indexes); + end + if numel(self.traveltime)==N + self2.traveltime = self.traveltime(indexes); + end + end + + function self = append(self1, self2) + disp('Appending...') + [newtime, indices] = sort([self1.time self2.time]); + size(cellstr(self1.channelinfo)) + size(cellstr(self2.channelinfo)) + newchannelinfo = [cellstr(self1.channelinfo); cellstr(self2.channelinfo)]; + newchannelinfo = newchannelinfo(indices); + newstate = [cellstr(self1.state); cellstr(self2.state)]; + newstate = newstate(indices); + newfs = [cellstr(self1.filterString); cellstr(self2.filterString)]; + newfs = newfs(indices); + newsnr = [self1.signal2noise self2.signal2noise]; + newsnr = newsnr(indices); + ctags = ChannelTag(newchannelinfo); + %self = Detection(cellstr([get(ctags,'station')]), cellstr([get(ctags,'channel')]), newtime, cellstr([newstate]), cellstr([newfs]), newsnr) + self = Detection([get(ctags,'station')], ... + [get(ctags,'channel')], ... + newtime, ... + newstate, ... + [newfs], ... + newsnr) + end + + % prototypes + catalogobj = associate(self, maxTimeDiff, sites, source) +% write(detectionobj, FORMAT, PATH); + end + methods(Static) + function self = retrieve(dbname, subset_expr) + %DETECTION.RETRIEVE Read detections from an Antelope CSS3.0 table + % + % Usage: + % detectionObj = DETECTION.RETRIEVE(dbname, subset_expr) + + % Author: Glenn Thompson (glennthompson1971@gmail.com) + + + debug.printfunctionstack('>') + self = []; + if ~(antelope.dbtable_present(dbname, 'detection')) + fprintf('No detection table belonging to %s\n',dbname); + return + end + + fprintf('Loading detections from %s\n',dbname); + + % Open database + db = dbopen(dbname,'r'); + disp('- database opened'); + + % Apply subset expression + db = dblookup_table(db,'detection'); + disp('- detection table opened'); + if exist('subset_expr','var') + db = dbsubset(db,subset_expr); + disp('- subsetted database') + end + + nrows = dbnrecs(db); + if nrows > 0 + + % Sort by arrival time + db = dbsort(db,'time'); + disp('- sorted detection table') + + % Get the values + fprintf('- reading %d rows\n',nrows); + [sta,chan,time,state,filterString,signal2noise] = dbgetv(db,'sta','chan','time','state', 'filter','snr'); + + % Close database link + dbclose(db); + disp('- database closed') + + % Create detection object + disp('- creating detection object') + self = Detection(cellstr(sta), cellstr(chan), epoch2datenum(time), cellstr(state), cellstr(filterString), signal2noise); + + disp('- complete!') + else + fprintf('No detections found matching request\n'); + end + + debug.printfunctionstack('<') + end + + %cookbook() + end +end \ No newline at end of file diff --git a/core/@Detection/associate.m b/core/@Detection/associate.m new file mode 100644 index 0000000..efbb740 --- /dev/null +++ b/core/@Detection/associate.m @@ -0,0 +1,173 @@ +function catalogobj = associate(obj, maxTimeDiff, sites, source) +%ASSOCIATE Associate detections into events +% catalogobj = associate(detectionObj, maxTimeDiff) will scan through an +% Detection object and look +% for times where there are at least 2 detections on +% within maxTimeDiff seconds of each other, and declare an event. No +% checking is done to see if arrivals are on different channels +% +% catalogobj = associate(arrivals, maxTimeDiff, sites) will +% reduce the detection times by traveltimes in the sites structure. This +% allows a smaller maxTimeDiff to be used (since all airwaves should now +% line up, and all P waves etc.). +% +% Example: Imagine you have run dbdetect. You also notice that the time to cross +% the network is less than 30s. Then the following code will load the detections, subset +% for "D" states, and associate into events wherever 2 channels have an "D" +% detection within 30s. The final line returns a Catalog object - the GISMO +% container for multiple events. +% +% dbpath = '/home/t/thompsong/NEWTON/pavlof2007'; +% detobj = Detection.retrieve(dbpath, 'status=~/D/'); +% catalogobj = detobj.associate(30); +% +% A significant problem is that there may be more than 1 event in a +% 30-second window. So a different approach is to apply a differential +% travel time correction to each detection, to reduce it to a known source +% location. Then a wave might propagate across the reduced network in a +% time of 1-second, for example, allowing multiple events to be detected in +% a 30-seconds time window. +% +% dbpath = '/home/t/thompsong/NEWTON/pavlof2007/db'; +% detobj = Detection.retrieve(dbpath, 'status=~/D/'); +% sites = antelope.dbget_site_locations(dbpath, unique(detobj.channelinfo)) +% source.lat = 55.4203; +% source.lon = -161.8931; +% wavespeed = 330; % m/s +% difftt = calc_traveltime(source, sites, wavespeed); +% source.lat = 55.4173; source.lon = -161.8937; elev = 2.518; +% chantags = ChannelTag(unique(detobj.channelinfo)) +% sites = antelope.dbget_site_locations(dbpath, chantags, startTime, endTime); +% seismicspeed = 330; infrasoundspeed = 330; % m/s +% sites = compute_travel_times(source, sites, seismicspeed, infrasoundspeed); +% association_time_window = 2; % max seconds from seismic to infrasound arrival +% catalogobj = detobj.associate(maxtimediff, sites); + + % break up detection types + detections_on = obj.subset('state', 'ON'); + detections_d = obj.subset('state', 'D'); + detections_off = obj.subset('state', 'OFF'); + if detections_d.numel > 0 + obj = detections_d; + elseif detections_on.numel > 0 + obj = detections_on; + end + + %% REDUCE BY SUBTRACTING TRAVEL TIME + % If sites exist, let's correct the arrival times first + if exist('sites', 'var') + disp('Reducing travel times') + obj.traveltime = NaN(size(obj.time)); + for c=1:numel(sites) + thissite = sites(c); + thischanstr = thissite.channeltag.string(); + i = strcmp(obj.channelinfo, thischanstr)==1; + obj.traveltime(i) = sites(c).traveltime; + obj.time(i) = obj.time(i) - sites(c).traveltime/86400; + end + end + % sort obj in ascending time order + disp('Sorting') + [detection_time,indices]=sort(obj.time); + obj = obj.subset(indices); + + % First we begin with each detection and find how many detections occur in the + % maxTimeDiff seconds that follow. This way each arrival is assigned a + % weight equal to that number of detections. The point of this is to help us + % identify the beginning of each event. + fprintf('\nFinding how many detections within %e sec of each detection\n',maxTimeDiff) + for c=1:numel(detection_time) + associated_indices{c} = find(detection_time>=detection_time(c) & detection_time<=detection_time(c)+maxTimeDiff/86400); + numdetections(c) = numel(associated_indices{c}); + end + + % remove decrementing series, e.g. replace sequence + % like 6 5 4 3 2 1 with 6 0 0 0 0 0 + disp('Removing decrementing series') + numdetections = [numdetections(1) diff(numdetections)+1]; + %numdetections(numdetections<=1)=0; + + + % Now loop over numdetections and create events + fprintf('Making events') + eventnum = 0; + duplicatecount = 0; + for c=1:numel(numdetections) + fprintf('.') + if numdetections(c)>0 + fprintf('*') + eventnum = eventnum + 1; + otime(eventnum) = detection_time(c); + % lastDetectionTime(eventnum) = detection_time(c + numdetections(c) - 1); + + try + eventdetobj = obj.subset([c : c + numdetections(c) - 1]); + catch + numdetections(c) + c+numdetections(c)-1 + numel(numdetections) + rethrow(); + end + + % remove the reduced time + if exist('sites','var') + for cc=1:numel(eventdetobj.time) + eventdetobj.time(cc) = eventdetobj.time(cc) + eventdetobj.traveltime(cc)/86400; + end + end + + % remove duplicate channels + [uc,ia] = unique(eventdetobj.channelinfo, 'stable'); + duplicatecount = duplicatecount + numel(eventdetobj.channelinfo) - numel(uc); + eventdetobj = eventdetobj.subset(ia); + + + % set the arrivalobj for this event + arrivalobj{eventnum} = detection2arrival(eventdetobj); + firstDetectionTime(eventnum) = min(eventdetobj.time); + lastDetectionTime(eventnum) = max(eventdetobj.time); + + end + + if mod(c,30) == 0 + fprintf('\nProcessed %d out of %d\n',c, numel(numdetections)); + end + end + + %% CREATE CATALOG + if numel(otime)==0 + % no events + catalogobj = Catalog(); + return + end + + fprintf('\nCreating Catalog\n') + olon=[]; + olat=[]; + if exist('source','var') + olon = source.lon*ones(size(otime)); + olat = source.lat*ones(size(otime)); + end + catalogobj = Catalog(otime, olon, olat, [], [], {}, {}, 'ontime', firstDetectionTime, 'offtime', lastDetectionTime); + catalogobj.arrivals = arrivalobj; + fprintf('%d detections were determined to be duplicates using a time window of %.1f seconds\n',duplicatecount, maxTimeDiff); + + +end + +function arrivalobj = detection2arrival(detectionobj) + if ~isa(detectionobj.channelinfo,'ChannelTag') + ctag = ChannelTag(detectionobj.channelinfo); + else + ctag = detectionobj.channelinfo; + end + sta = get(ctag, 'station'); + chan = get(ctag, 'channel'); + arrivalobj = Arrival(cellstr(sta), cellstr(chan), detectionobj.time, ... + cellstr(detectionobj.state), 'signal2noise', detectionobj.signal2noise); + +end + +% function result = alreadyHaveArrivalFromThisChannel(ctaglist, thisctag) +% result = sum(cellfun(@(s) ~isempty(strfind(this +% end \ No newline at end of file diff --git a/core/@EventRate/EventRate.m b/core/@EventRate/EventRate.m index 8ac803b..a6ce0b9 100755 --- a/core/@EventRate/EventRate.m +++ b/core/@EventRate/EventRate.m @@ -37,23 +37,6 @@ % (3) Create an eventrate object using a binsize of 1 hour but a stepsize of 5 minutes: % erobj = catalogObject.eventrate('binsize', 1/24, 'stepsize', 5/1440); % -% (4) Create a vector of eventrate objects subclassified using event types 'r', 'e', 'l', 'h', 't': -% erobj = eventrate(catalogObject, 1, 'etypes', 'relht'); -% To plot counts on separate figures: -% erobj.plot() -% To plot counts and energy panels, each event type as a separate figure: -% erobj.plot('metric', {'counts';'energy'}); -% To plot counts and energy panels on separate figures, each event type as panels: -% erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'panels'); -% To plot counts and energy panels on separate figures, each event type stacked: -% erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked'); -% -% (5) A full example: -% catalogObject = catalog(fullfile(MVO_DATA, 'mbwh_catalog'), 'seisan', 'snum', datenum(1996,10,1), 'enum', datenum(2004,3,1), 'region', 'Montserrat') -% erobj = eventrate(catalogObject, 365/12, 'stepsize', 1, 'etypes', 'thlr'); -% erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked'); -% -% %% PROPERTIES % % For a list of all properties type properties(EventRate) @@ -101,6 +84,25 @@ % $Date: 2014-05-06 14:52:40 -0800 (Tue, 06 May 2014) $ % $Revision: 404 $ + +% I don't think these parts work anymore +% (4) Create a vector of eventrate objects subclassified using event types 'r', 'e', 'l', 'h', 't': +% erobj = eventrate(catalogObject, 1, 'etypes', 'relht'); +% To plot counts on separate figures: +% erobj.plot() +% To plot counts and energy panels, each event type as a separate figure: +% erobj.plot('metric', {'counts';'energy'}); +% To plot counts and energy panels on separate figures, each event type as panels: +% erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'panels'); +% To plot counts and energy panels on separate figures, each event type stacked: +% erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked'); +% +% (5) A full example: +% catalogObject = catalog(fullfile(MVO_DATA, 'mbwh_catalog'), 'seisan', 'snum', datenum(1996,10,1), 'enum', datenum(2004,3,1), 'region', 'Montserrat') +% erobj = eventrate(catalogObject, 365/12, 'stepsize', 1, 'etypes', 'thlr'); +% erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked'); +% + %% PROPERTIES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% properties(GetAccess = 'public', SetAccess = 'public') @@ -149,6 +151,9 @@ self.enum = enum; self.binsize = binsize; self.stepsize = stepsize; + if (enum-snum) < binsize + error('binsize cannot be bigger than data time range'); + end end %% ---------------------------------------------- @@ -165,821 +170,15 @@ function total_mag = get.total_mag(erobj) total_mag = magnitude.eng2mag(sum(erobj.energy)); end - - function plotold(obj, varargin) - %EventRate/plot - % Plot metrics of an EventRate object - % - % The following metrics are available: - % - % counts % number of events in each bin - % mean_rate % number of events per hour in each bin - % median_rate % reciprocal of the median time interval between events. Represented as an hourly rate. - % energy % total sum of energy in each bin - % cum_mag % total sum of energy in each bin, represented as a magnitude. - % mean_mag % mean magnitude of events in each bin - % median_mag % median magnitude of events in each bin - % min_mag % smallest magnitude in each bin - % - % erobj.plot() or plot(erobj) will produce a plot of event - % counts per unit time. The time unit is given by erobj.binsize - % days. - % - % erobj.plot('metric', list_of_metrics) will plot each metric - % requested in list_of_metrics in a separate panel. - % list_of_metrics should be a cell array of valid metric - % strings. However, it may be a string if only one metric is - % requested. - % - % erobj.plot('metric', 'counts') is equivalent to - % erobj.plot() and erobj.plot('metric', {'counts'}) - % - % erobj.plot('metric', 'mean_rate') is similar, but the - % mean_rate is always events per hour, regardless of the - % binsize. So if erobj.binsize = 1 (day), counts will be - % exactly 24 * mean_rate. - % - % erobj.plot('metric', {'counts';'cum_mag'}) will plot counts - % in one panel and the cumulative magnitude per bin in - % another panel. - % - % In general any number of metrics can be given in - % list_of_metrics. - % - % If erobj is an array of eventrate structures (e.g. one per - % etype), each is plotted on a separate figure. However the - % plotmode variable overrides this: - % - % plot(eventrate_vector, 'plotmode', 'panels') will plot them - % in separate panels on the same figure - % - % plot(eventrate_vector, 'plotmode', 'single') will plot them - % in a single panel - - p = inputParser; - p.addParamValue('metric', {'counts'}, @(c) iscell(c)||isstr(c)); - p.addParamValue('plotmode', 'figures', @isstr); - p.addParamValue('smooth', 1, @isnumeric); - p.parse(varargin{:}); - metric = p.Results.metric; - plotmode = p.Results.plotmode; - smoothbins = p.Results.smooth; % NOT DOING ANYTHING WITH THIS YET BUT COULD SMOOTH COUNTS OVER SEVERAL BINS - if ~iscell(metric) - metric = {metric}; - end - numMetrics = numel(metric); - colors = {[0.7 0.7 0] [0 0 1]}; - - % plot each etype on a separate figure, each metric as a - % subplot - - if strcmp(plotmode, 'figures') || length(obj)==1 - - for c = 1 : numel(obj) - binsize_str = Catalog.binning.binsizelabel(obj(c).binsize); - numsubplots = length(metric); - %figure(get(gcf,'Number')+1) - figure - set(gcf,'Color', [1 1 1]); - for cc = 1: numsubplots % number of metrics to plot - %eval( sprintf('data = obj(c).%s;',metric{cc} ) ); - data = obj(c).(metric{cc}); - if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) - % replace -Inf values as they mess up plots - ydata = data; % ydata is the data we will plot, but we keep data for cumulative energy etc. - if smoothbins > 1 - ydata = smooth(ydata, smoothbins); - end -% ydata(isinf(data))=NaN; -% mindata = nanmin(ydata); -% ydata(isnan(ydata))=mindata; % we replace NaNs and Infs in data with nanmin(data) in ydata - if (obj(c).binsize == obj(c).stepsize) & ( strcmp(metric{cc}, 'counts') | strcmp(metric{cc}, 'energy') | strcmp(metric{cc}, 'cum_mag') ) - if strfind(metric{cc}, 'mag') - cumdata = magnitude.eng2mag(cumsum(magnitude.mag2eng(data))); - subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, ydata, obj(c).time, cumdata, @stairs, @plot ); - set(h1, 'Color', colors{1}); - else - subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, data, obj(c).time + obj(c).binsize/2, cumsum(data), @bar, @stairs ); - %subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, ydata, obj(c).time, cumsum(data), @stairs, @plot ); - set(h1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}) - set(h1, 'BarWidth', 1); - set(h1, 'LineWidth', 0.1); - end - datetick(ax(1), 'x','keeplimits'); - ylabel(ax(1), metric2label(metric{cc}, obj(c).binsize), 'Color', colors{1}, 'FontSize',12) - datetick(ax(2), 'x','keeplimits'); - ylabel(ax(2),'Cumulative', 'Color', colors{2}, 'FontSize',12) - %set(h1, 'Color', colors{1}); - %set(h2, 'Color', colors{2}, 'LineWidth', 2); - ylims = get(ax(1), 'YLim'); - set(ax(1), 'YColor', colors{1}, 'YLim', [0 ylims(2)]); - ylims = get(ax(2), 'YLim'); - set(ax(2), 'YColor', colors{2}, 'YLim', [0 ylims(2)]); - linkaxes(ax, 'x'); - else - - if strfind(metric{cc}, 'mag') - subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); - else - subplot(numsubplots,1,cc), bar(obj(c).time, data, 1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}, 'BarWidth', 1, 'LineWidth', 0.1); - %subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); - end - datetick('x','keeplimits'); - ylabel(metric2label(metric{cc}, obj(c).binsize), 'FontSize',12) - end - end -% axis tight; -% a=axis; -% axis([a(1) a(2) 0 a(4)]) - end - end - - %% FROM HERE ON THE PLOTMODES HAVE NOT BEEN UPDATED - elseif strcmp(plotmode, 'panels') - % Each metric on a separate figure, showing all requested - % subclasses on separate panels - for c = 1 : numel(metric) - %figure(get(gcf,'Number')+c) - figure - numsubplots = numel(obj); - - %for cc = numsubplots: -1: 1 - for cc = 1:numsubplots - ccc = numsubplots-cc+1; - if strcmp(metric{c},'energy') - %data = cumsum(magnitude.mag2eng(obj(cc).cum_mag)); - data = (magnitude.mag2eng(obj(ccc).cum_mag)); - - else - % eval( sprintf('data = obj(cc).%s;',metric{c} ) ); - data = obj(ccc).(metric{c}); - end - if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) - if smoothbins > 1 - data = smooth(data, smoothbins); - end - % where to position the axes - pos(1) = 0.1; - pos(2) = 0.1+(0.95-0.1)*(cc-1)/numsubplots; - pos(3) = 0.8; - pos(4) = (0.8*(0.95-0.1)/numsubplots); - axes('position', pos); - end - - % plot - if numel(data)>0 - bar( obj(ccc).time, data, 1, 'EdgeColor', 'none', 'FaceColor', [0 0 0] ); - % hold on; - % sdata = smooth(data, 30, 'lowess'); - % plot( obj(cc).time, sdata, 'k-', 'linewidth', 2); - - - % range and label - datetick('x','keeplimits'); - set(gca, 'XLim', [obj(ccc).snum obj(ccc).enum]); - ymax = nanmax(catmatrices(1, data)); - % ymax = min([max(sdata)*2 max(data)*1.01]); - set(gca, 'YLim', [0 ymax]); - ylabel(obj(ccc).etype); - end - - - - end - %title(metric{c}); - fprintf('metric for figure %d is %s\n', get(gcf,'Number'), metric{c}); - end - - - elseif strcmp(plotmode, 'single') - % Each metric on a separate figure, showing all requested - % subclasses on the same panel - colour = 'rgbcm'; - for c = 1 : numel(metric) - %figure(get(gcf,'Number')+c) - figure - for cc = 1: length(obj) - if strcmp(metric{c},'energy') - %data = cumsum(magnitude.mag2eng(obj(cc).cum_mag)); - data = (magnitude.mag2eng(obj(cc).cum_mag)); - else - % eval( sprintf('data = obj(cc).%s;',metric{c} ) ); - data = obj(cc).(metric{c}); - end - - if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) - - if smoothbins > 1 - data = smooth(data, smoothbins); - end - - plot( obj(cc).time, data, sprintf('-%c',colour(cc)) ); - hold on; - datetick('x','keeplimits'); - set(gca, 'XLim', [obj(cc).snum obj(cc).enum]); - %ymax = nanmax(catmatrices(1, data)); - %set(gca, 'YLim', [0 ymax]); - %ylabel(obj(cc).etype); - end - end - title(metric{c}); - end - - - elseif strcmp(plotmode, 'stacked') - % Each metric on a separate figure, showing all subclasses - % stacked on the same panel - colour = 'rgbcm'; - for c = 1 : numel(metric) - figure(get(gcf,'Number')+c) - data =[]; - for cc = 1: length(obj) - if strcmp(metric{c},'energy') - data = (magnitude.mag2eng(obj(cc).cum_mag)); - else - %eval( sprintf('data(:,cc) = obj(cc).%s;',metric{c} ) ); - %data(:,cc) = obj(cc).(metric{c}); - data = obj(cc).(metric{c}); - if findstr(metric{c}, 'mag') - disp('Warning: It is meaningless to stack magnitude data'); - data(data<0)=0; - data(isnan(data))=0; - end - end - if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) - if smoothbins > 1 - data = smooth(data, smoothbins); - end - - %bar( obj(cc).time, data, 1, 'stack' ); - area(obj(cc).time, data); - datetick('x','keeplimits'); - set(gca, 'XLim', [obj(cc).snum obj(cc).enum]); - title(metric{c}); - end - end - end - end - end - - function plot(obj, varargin) - %EventRate/plot - % Plot metrics of an EventRate object - % - % The following metrics are available: - % - % counts % number of events in each bin - % mean_rate % number of events per hour in each bin - % median_rate % reciprocal of the median time interval between events. Represented as an hourly rate. - % energy % total sum of energy in each bin - % cum_mag % total sum of energy in each bin, represented as a magnitude. - % mean_mag % mean magnitude of events in each bin - % median_mag % median magnitude of events in each bin - % min_mag % smallest magnitude in each bin - % - % erobj.plot() or plot(erobj) will produce a plot of event - % counts per unit time. The time unit is given by erobj.binsize - % days. - % - % erobj.plot('metric', list_of_metrics) will plot each metric - % requested in list_of_metrics in a separate panel. - % list_of_metrics should be a cell array of valid metric - % strings. However, it may be a string if only one metric is - % requested. - % - % erobj.plot('metric', 'counts') is equivalent to - % erobj.plot() and erobj.plot('metric', {'counts'}) - % - % erobj.plot('metric', 'mean_rate') is similar, but the - % mean_rate is always events per hour, regardless of the - % binsize. So if erobj.binsize = 1 (day), counts will be - % exactly 24 * mean_rate. - % - % erobj.plot('metric', {'counts';'cum_mag'}) will plot counts - % in one panel and the cumulative magnitude per bin in - % another panel. - % - % In general any number of metrics can be given in - % list_of_metrics. - % - % If erobj is an array of eventrate structures (e.g. one per - % etype), each is plotted on a separate figure. However the - % plotmode variable overrides this: - % - % plot(eventrate_vector, 'plotmode', 'panels') will plot them - % in separate panels on the same figure - % - % plot(eventrate_vector, 'plotmode', 'single') will plot them - % in a single panel - - p = inputParser; - p.addParameter('metric', {'counts'}, @(c) iscell(c)||isstr(c)); - p.addParameter('plotmode', 'figures', @isstr); - p.addParameter('smooth', 1, @isnumeric); - p.parse(varargin{:}); - metric = p.Results.metric; - plotmode = p.Results.plotmode; - smoothbins = p.Results.smooth; % NOT DOING ANYTHING WITH THIS YET BUT COULD SMOOTH COUNTS OVER SEVERAL BINS - if ~iscell(metric) - metric = {metric}; - end - numMetrics = numel(metric); - colors = {[0 0.8 0] [0 0 0.8]}; - - % plot each etype on a separate figure, each metric as a - % subplot - - if strcmp(plotmode, 'figures') || length(obj)==1 - fh = []; - for c = 1 : numel(obj) - binsize_str = Catalog.binning.binsizelabel(obj(c).binsize); - numsubplots = length(metric); - fh(c) = figure; - unique_subclasses = unique(char([obj(c).etype{:}])') - if length(unique_subclasses)==1 - longname = Catalog.subclass2longname(unique_subclasses) - else - longname = Catalog.subclass2longname('*') - end - set(fh(c),'Color', [1 1 1], 'Name', sprintf('%s activity beginning %s',longname, datestr(obj(c).time(1),29) ) ); - for cc = 1: numsubplots % number of metrics to plot - data = obj(c).(metric{cc}); - if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) - % replace -Inf values as they mess up plots - y = data; % ydata is the data we will plot, but we keep data for cumulative energy etc. - if smoothbins > 1 - y = smooth(y, smoothbins); - end - y(isinf(y))=NaN; - mindata = nanmin(y); - y(isnan(y))=mindata; % we replace NaNs and Infs in data with nanmin(data) in ydata - if (obj(c).binsize == obj(c).stepsize) & ( strcmp(metric{cc}, 'counts') | strcmp(metric{cc}, 'energy') | strcmp(metric{cc}, 'cum_mag') ) -% if strfind(metric{cc}, 'mag') -% cumy = magnitude.eng2mag(cumsum(magnitude.mag2eng(y))); -% subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, y, obj(c).time, cumy, @stairs, @plot ); -% set(h1, 'Color', colors{1}); -% else - labels = metric2label(metric{cc}, obj(c).binsize); - t = [ obj(c).time - obj(c).binsize/2 ]; t = [t t(end)+obj(c).binsize]; - y = [y y(end)]; - clear ax h1 h2 - if numel(labels)==2 - cumy = cumsum(data); cumy = [cumy cumy(end)]; - subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(t, y, t, cumy, @stairs, @stairs ); - set(h2, 'LineWidth', 3, 'Color', colors{2}); - datetick(ax(2), 'x','keeplimits'); - ylabel(ax(2),labels{2}, 'Color', colors{2}, 'FontSize',12) - ylims = get(ax(2), 'YLim') - set(ax(2), 'YColor', colors{2}, 'YLim', [0 max([ylims(2) 1])], 'XLim', [t(1) t(end)]); - else - - ax(1) = subplot(numsubplots,1,cc) - h1 = stairs(ax(1), t, y); - %[ax, h1, h2] = plotyy(t, y, t, y, @stairs, @stairs ); - end - set(h1, 'LineWidth', 3, 'Color', colors{1}); - - %end - datetick(ax(1), 'x','keeplimits'); - ylabel(ax(1), labels{1}, 'Color', colors{1}, 'FontSize',12) - ylims = get(ax(1), 'YLim'); - set(ax(1), 'YColor', colors{1}, 'YLim', [0 max([ylims(2) 1])], 'XLim', [t(1) t(end)]); - linkaxes(ax, 'x'); - else - - if strfind(metric{cc}, 'mag') - subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); - else - subplot(numsubplots,1,cc), bar(obj(c).time, data, 1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}, 'BarWidth', 1, 'LineWidth', 0.1); - %subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); - end - datetick('x','keeplimits'); - ylabel(metric2label(metric{cc}, obj(c).binsize), 'FontSize',12) - end - end -% axis tight; -% a=axis; -% axis([a(1) a(2) 0 a(4)]) - end - end - - %% FROM HERE ON THE PLOTMODES HAVE NOT BEEN UPDATED - elseif strcmp(plotmode, 'panels') - end - end - - - - - - - %% PYTHONPLOT - function pythonplot(obj) - obj.plot('metric', {'counts';'cum_mag'}); - end - - %% HELENAPLOT - function helenaplot(obj) - for c=1:length(obj) - figure - set(gcf,'Color', [1 1 1]); - cumcummag = magnitude.eng2mag(cumsum(magnitude.mag2eng(obj(c).cum_mag))); - [ax, h1, h2] = plotyy(obj(c).time, cumcummag, obj(c).time, cumsum(obj(c).energy), @plot, @plot); - datetick(ax(1), 'x','keeplimits'); - datetick(ax(2), 'x','keeplimits'); - ylabel(ax(1), 'Cumulative Magnitude', 'FontSize',12); - ylabel(ax(2), 'Cumulative Energy', 'FontSize',12); - end - end - - - function pvalue(obj) - logt = log10(obj.time-obj.time(1)); - logc = log10(obj.counts); - plot(logt, logc); - p = logt \ logc' -% p = polyfit(logt, logc, 1) -% hold on -% logt1 = min(logt); -% logc1 = polyval(p, logt1); -% logt2 = max(logt); -% logc2 = polyval(p, logt2); -% line([logt1 logt2], [logc1 logc2]); - end - - - %% SAUSAGEPLOT - function sausageplot(obj, numBinsToUse, varargin) - %sausageplot - % Under development, this function attempts to replicate the - % capabilities of sausageplot.xpy written by Glenn Thompson - % at AVO. - p = inputParser; - p.addRequired('numBinsToUse', @isnumeric); - p.addOptional('numPoints', 1, @isnumeric); - p.addOptional('radii', [], @isnumeric); - p.parse(numBinsToUse, varargin{:}); - numBinsToUse = p.Results.numBinsToUse; - numPoints = p.Results.numPoints; - radii = p.Results.radii; -% % Page size, dots-per-inch and scale settings -% fh = figure() -% if numBinsToUse > NUMPOINTS -% dpi = 6*(numBinsToUse+1); -% axes_width = 0.8 * (NUMPOINTS + 1) / (numBinsToUse + 1); -% axes_height = 0.8; -% else -% dpi = 6*(NUMPOINTS+1); -% axes_width = 0.8; -% axes_height = 0.8 * (numBinsToUse + 1) / (NUMPOINTS + 1); -% end -% fh.set_dpi(dpi) -% fh.set_size_inches((10.0,10.0),forward=True) -% fig2ax1 = fig2.add_axes([0.1, 0.85-axes_height, axes_width, axes_height]); -% print_pixels(fig2, fig2ax1, numBinsToUse, NUMPOINTS); -% colormapname = 'hot_r'; - MAXMAGCOLORBAR = 5.0; - MINMAGCOLORBAR = 1.0; - % Marker size configuration -% SCALEFACTOR = 84.0 / dpi; -% MAXMARKERSIZE = 43.0 * SCALEFACTOR; -% MINMARKERSIZE = 4.0 * SCALEFACTOR; - PTHRESHOLD = 50; - for c = 1:length(obj) - % set up weekending list for yticklabels - binendstr = {}; - for bin_index=numel(obj.time)-numBinsToUse+1: numel(obj.time) - - dstr = datestr(obj.time(bin_index)) - binendstr{bin_index} = dstr; - end - pointlabels = {}; - for i=1:length(numPoints) - % filter here based on points and radii - j = 1:length(obj(c).counts); % replace this - counts = obj(c).counts(j); - cummag = obj(c).cum_mag(j); - prcntile = percentiles(obj(c).counts); - %pointlabels{i} = sprintf('%s(%d)', point_label{i}, counts(-1)); - pointlabels{i}=''; - for bin_index=numel(obj.time)-numBinsToUse+1: numel(obj.time)%-numBinsToUse-1: 1: 0 - y = obj(c).counts(bin_index); - magnitude = obj(c).cum_mag(bin_index); - p = y2percentile(y,prcntile); - if y>0 - colorVal = scalarMap.to_rgba(magnitude) - msize = MINMARKERSIZE + (p-PTHRESHOLD) * (MAXMARKERSIZE - MINMARKERSIZE) / (100-PTHRESHOLD); - if msize MAXMARKERSIZE * 0.3 - text(i+0.5, bin_index, num2str(y)) -% fig2ax1.text(i+0.5, w, '%d', y, horizontalalignment='center', verticalalignment='center', fontsize = 8 * SCALEFACTOR) - end - end - end - end - - % Adding xticks, yticks, labels, grid -% fig2ax1.set_axisbelow(True) % I think this puts grid and tickmarks below actual data plotted - - % x-axis -% fig2ax1.set_xticks(np.arange(.5,NUMVOLCANOES+.5,1)) - set(gca, 'XTick', 0.5:1:numPoints+0.5); -% fig2ax1.set_xlim([-0.5, NUMVOLCANOES+0.5]) - set(gca, 'XLim', [-0.5 numPoints+0.5]) -% fig2ax1.xaxis.grid(True, linestyle='-', color='gray') - grid on; - set(gca, 'XTickLabel', pointlabels) -% fig2ax1.xaxis.set_ticks_position('top') -% fig2ax1.xaxis.set_label_position('top') - %plt.setp( fig2ax1.get_xticklabels(), rotation=45, horizontalalignment='left', fontsize=10*SCALEFACTOR ) - - % y-axis -% fig2ax1.set_yticks(np.arange(-number_of_weeks_to_plot-0.5, 0, 1)) - set(gca, 'YTick', -numBinsToUse-0.5: 1: 0) -% fig2ax1.set_yticklabels(weekending) - set(gca, 'YTickLabel', binendstr) -% fig2ax1.set_ylim([-number_of_weeks_to_plot - 0.5, -0.5]) - set(gca, 'YLim', -numBinsToUse -0.5 : 1 : -0.5) -% plt.setp( fig2ax1.get_yticklabels(), fontsize=10*SCALEFACTOR ) - end - end - - - %% IMPORTSWARMDB - function obj = importswarmdb(obj, dbname, auth, snum, enum) - % IMPORTSWARMDB - % Load a swarm database metrics table into an EventRate object - % eventrate = importswarmdb(erobj, dbname, auth, snum, enum); - % - % INPUT: - % dbname the path of the database (must have a 'metrics' table) - % auth name of the grid to load swarm tracking metrics for - % snum,enum start and end datenumbers (Matlab time format, see 'help datenum') - % - % OUTPUT: - % obj an eventrate object - % - % Example: - % erobj = importswarmdb('/avort/devrun/dbswarm/swarm_metadata', 'RD_lo', datenum(2010, 7, 1), datenum(2010, 7, 14) ); - - % Glenn Thompson, 20100714 - - % initialize - obj.dbroot = dbname; - obj.snum = snum; - obj.enum = enum; - obj.auth = auth; - - % check that database exists - dbtablename = sprintf('%s.metrics',dbname); - if exist(dbtablename,'file') - % load the data - try - db = dbopen(dbname, 'r'); - catch me - fprintf('Error: Could not open %s for reading',dbname); - return; - end - db = dblookup_table(db, 'metrics'); - if (dbquery(db, 'dbRECORD_COUNT')==0) - fprintf('Error: Could not open %s for reading',dbtablename); - return; - end - db = dbsubset(db, sprintf('auth ~= /.*%s.*/',auth)); - numrows = dbquery(db,'dbRECORD_COUNT'); - debug.print_debug(sprintf('Got %d rows after auth subset',numrows),2); - sepoch = datenum2epoch(snum); - eepoch = datenum2epoch(enum); - db = dbsubset(db, sprintf('timewindow_starttime >= %f && timewindow_endtime <= %f',sepoch,eepoch)); - numrows = dbquery(db,'dbRECORD_COUNT'); - debug.print_debug(sprintf('Got %d rows after time subset',numrows),2); - - if numrows > 0 - % Note that metrics are only saved when mean_rate >= 1. - % Therefore there will be lots of mean_rate==0 timewindows not in - % database. - [tempsepoch, tempeepoch, mean_rate, median_rate, mean_mag, cum_mag] = dbgetv(db,'timewindow_starttime', 'timewindow_endtime', 'mean_rate', 'median_rate', 'mean_ml', 'cum_ml'); - obj.binsize = (tempeepoch(1) - tempsepoch(1))/86400; - obj.stepsize = min(tempsepoch(2:end) - tempsepoch(1:end-1))/86400; - obj.time = snum+obj.stepsize:obj.stepsize:enum; - obj.numbins = length(obj.time); - obj.mean_rate = zeros(obj.numbins, 1); - obj.counts = zeros(obj.numbins, 1); - obj.median_rate = zeros(obj.numbins, 1); - obj.mean_mag = zeros(obj.numbins, 1); - obj.cum_mag = zeros(obj.numbins, 1); - for c=1:length(tempeepoch) - tempenum = epoch2datenum(tempeepoch(c)); - i = find(obj.time == tempenum); - obj.mean_rate(i) = mean_rate(c); - obj.counts(i) = mean_rate(c) * (obj.binsize * 24); - obj.median_rate(i) = median_rate(c); - obj.mean_mag(i) = mean_mag(c); - obj.cum_mag(i) = cum_mag(c); - end - end - dbclose(db); - - else - % error - table does not exist - fprintf('Error: %s does not exist',dbtablename); - return; - end - - obj.total_counts = sum(obj.counts)*obj.stepsize/obj.binsize; - - end - - %% ADDFIELD - function obj = addfield(obj,fieldname,val) - %ADDFIELD add fields and values to object(s) - % obj = addfield(obj, fieldname, value) - % This function creates a new user defined field, and fills it with the - % included value. If fieldname exists, it will overwrite the existing - % value. - % - % Input Arguments - % obj: an EventRate object - % fieldname: a string name - % value: a value to be added for those fields. Value can be anything - % - % EventRate objects can hold user-defined fields. To access the contents, - % use EventRate/get. - % - % Example: - % % add a field called "TESTFIELD", containing the numbers 1-45 - % obj = addfield(obj,'TestField',1:45); - % - % % add a cell field called "MISHMOSH" - % obj = addfield(obj,'mishmosh',{'hello';'world'}); - % - % % see the result - % disp(obj) - % - % See also EventRate/set, EventRate/get - - % AUTHOR: Glenn Thompson - - if ischar(fieldname) - mask = strcmp(fieldname, properties(obj)); - if any(mask) - obj = obj.set(fieldname, val); - else - mask = strcmp(upper(fieldname),obj.misc_fields); - if any(mask) - obj = obj.set(fieldname, val); - else - obj.misc_fields = [obj.misc_fields, upper(fieldname)]; - obj = obj.set(upper(fieldname), val); - end - end - else - error('%s:addfield:invalidFieldname','fieldname must be a string', class(catalogObject)) - end - - end - - %% SET - function obj = set(obj, varargin) - %SET Set properties for EventRate object(s) - % obj = set(obj,'property_name', val, ['property_name2', val2]) - % SET is one of the two gateway functions of an object, such as EventRate. - % Properties that are changed through SET are typechecked and otherwise - % scrutinized before being stored within the EventRate object. This - % ensures that the other EventRate methods are all retrieving valid data, - % thereby increasing the reliability of the code. - % - % Another strong advantage to using SET and GET to change and retrieve - % properties, rather than just assigning them to EventRate object directly, - % is that the underlying data structure can change and grow without - % harming the code that is written based on the EventRate object. - % - % For a list of valid property names, type: - % properties(obj) - % - % If user-defined fields were added to the EventRate object (ie, through - % addField), these fieldnames are also available through set. - % - % Examples: - % (1) Change the description property - % obj = obj.set('description','hello world'); - % - % (2) Add new a field called CLOSEST_STATION with - % % the value 'MBLG' - % obj = obj.addfield('CLOSEST_STATION','MBLG'); - % - % % change the value of the CLOSEST_STATION field - % obj = obj.set('CLOSEST_STATION','MBWH'); - % - % See also EventRate/get, EventRate/addfield - - Vidx = 1 : numel(varargin); - - while numel(Vidx) >= 2 - prop_name = upper(varargin{Vidx(1)}); - val = varargin{Vidx(2)}; - mask = strcmp(upper(prop_name),upper(properties(obj))); - if any(mask) - mc = metaclass(obj); - i = find(mask); - prop_name = mc.PropertyList(i).Name; - if isempty(mc.PropertyList(i).GetMethod) - %eval(sprintf('obj.%s=val;',prop_name)); - obj.(prop_name) = val; - else - warning('Property %s is a derived property and cannot be set',prop_name); - end - else - switch prop_name - case obj.misc_fields - mask = strcmp(prop_name,obj.misc_fields); - obj.misc_values(mask) = {val}; - otherwise - error('%s:set:unknownProperty',... - 'can''t understand property name : %s', mfilename,prop_name); - end - end - Vidx(1:2) = []; %done with those parameters, move to the next ones... - end - end - - %% GET - function val = get(obj,prop_name) - %GET Get EventRate properties - % val = get(EventRate_object,'property_name') - % - % To see valid property names, type: - % properties(EventRate_object) - % - % If additional fields were added to EventRate using ADDFIELD, then - % values from these can be retrieved using the fieldname - % - % See also EventRate/SET, EventRate/ADDFIELD, Catalog/GET - - mask = strcmp(prop_name, properties(obj)); - if any(mask) - % eval(sprintf('val=obj.%s;',prop_name)); - val = obj.(prop_name); - else - mask = strcmp(upper(prop_name),obj.misc_fields); - if any(mask) - val = obj.misc_values{mask}; - else - warning('%s:get:unrecognizedProperty',... - 'Unrecognized property name : %s', class(obj), prop_name); - end - end - end - - end % methods - methods(Static) cookbook() end end -%% PERCENTILES -function p=percentiles(vals) - lenVals = length(vals); - for i=1:100 - p(i) = vals(floor(i/100 * (lenVals-1))+1); - end -end -%% PLOT_PERCENTILES -function plot_percentiles(p) - figure(get(gcf,'Number')+1, 'Color', [1 1 1]) - plot(1:100, p); -end -function labels = metric2label(metric, binsize) - % label = metric2label(metric, binsize) - labels={}; - blabel = Catalog.binning.binsizelabel(binsize); - time_unit = blabel(4:end); - if strcmp(metric, 'counts') - labels{1} = sprintf('# Events %s',blabel); - labels{2} = 'Cumulative # events'; - elseif strcmp(metric, 'energy') - labels{1} = sprintf('Energy %s (J)',blabel); - labels{2} = 'Cumulative energy (J)'; - elseif strcmp(metric, 'mean_rate') - labels{1} = sprintf('Mean # events per hour (binsize %s)', time_unit); - elseif strcmp(metric, 'median_rate') - labels{1} = sprintf('Median # events per hour (binsize %s)', time_unit); - elseif strcmp(metric, 'cum_mag') - labels{1} = sprintf('Cumulative Magnitude per hour (binsize %s)', time_unit);; - elseif strcmp(metric, 'mean_mag') - labels{1} = sprintf('Mean Magnitude per hour (binsize %s)', time_unit); - elseif strcmp(metric, 'median_mag') - labels{1} = sprintf('Median Magnitude per hour (binsize %s)', time_unit); - end -end + diff --git a/core/@EventRate/extensions/import_swarmdb.m b/core/@EventRate/extensions/import_swarmdb.m new file mode 100755 index 0000000..b26fc9b --- /dev/null +++ b/core/@EventRate/extensions/import_swarmdb.m @@ -0,0 +1,84 @@ +%% IMPORTSWARMDB +function obj = importswarmdb(obj, dbname, auth, snum, enum) + % IMPORTSWARMDB + % Load a swarm database metrics table into an EventRate object + % eventrate = importswarmdb(erobj, dbname, auth, snum, enum); + % + % INPUT: + % dbname the path of the database (must have a 'metrics' table) + % auth name of the grid to load swarm tracking metrics for + % snum,enum start and end datenumbers (Matlab time format, see 'help datenum') + % + % OUTPUT: + % obj an eventrate object + % + % Example: + % erobj = importswarmdb('/avort/devrun/dbswarm/swarm_metadata', 'RD_lo', datenum(2010, 7, 1), datenum(2010, 7, 14) ); + + % Glenn Thompson, 20100714 + + % initialize + obj.dbroot = dbname; + obj.snum = snum; + obj.enum = enum; + obj.auth = auth; + + % check that database exists + dbtablename = sprintf('%s.metrics',dbname); + if exist(dbtablename,'file') + % load the data + try + db = dbopen(dbname, 'r'); + catch me + fprintf('Error: Could not open %s for reading',dbname); + return; + end + db = dblookup_table(db, 'metrics'); + if (dbquery(db, 'dbRECORD_COUNT')==0) + fprintf('Error: Could not open %s for reading',dbtablename); + return; + end + db = dbsubset(db, sprintf('auth ~= /.*%s.*/',auth)); + numrows = dbquery(db,'dbRECORD_COUNT'); + debug.print_debug(sprintf('Got %d rows after auth subset',numrows),2); + sepoch = datenum2epoch(snum); + eepoch = datenum2epoch(enum); + db = dbsubset(db, sprintf('timewindow_starttime >= %f && timewindow_endtime <= %f',sepoch,eepoch)); + numrows = dbquery(db,'dbRECORD_COUNT'); + debug.print_debug(sprintf('Got %d rows after time subset',numrows),2); + + if numrows > 0 + % Note that metrics are only saved when mean_rate >= 1. + % Therefore there will be lots of mean_rate==0 timewindows not in + % database. + [tempsepoch, tempeepoch, mean_rate, median_rate, mean_mag, cum_mag] = dbgetv(db,'timewindow_starttime', 'timewindow_endtime', 'mean_rate', 'median_rate', 'mean_ml', 'cum_ml'); + obj.binsize = (tempeepoch(1) - tempsepoch(1))/86400; + obj.stepsize = min(tempsepoch(2:end) - tempsepoch(1:end-1))/86400; + obj.time = snum+obj.stepsize:obj.stepsize:enum; + obj.numbins = length(obj.time); + obj.mean_rate = zeros(obj.numbins, 1); + obj.counts = zeros(obj.numbins, 1); + obj.median_rate = zeros(obj.numbins, 1); + obj.mean_mag = zeros(obj.numbins, 1); + obj.cum_mag = zeros(obj.numbins, 1); + for c=1:length(tempeepoch) + tempenum = epoch2datenum(tempeepoch(c)); + i = find(obj.time == tempenum); + obj.mean_rate(i) = mean_rate(c); + obj.counts(i) = mean_rate(c) * (obj.binsize * 24); + obj.median_rate(i) = median_rate(c); + obj.mean_mag(i) = mean_mag(c); + obj.cum_mag(i) = cum_mag(c); + end + end + dbclose(db); + + else + % error - table does not exist + fprintf('Error: %s does not exist',dbtablename); + return; + end + + obj.total_counts = sum(obj.counts)*obj.stepsize/obj.binsize; + +end \ No newline at end of file diff --git a/core/@EventRate/extensions/sausageplot.m b/core/@EventRate/extensions/sausageplot.m new file mode 100755 index 0000000..6d6257c --- /dev/null +++ b/core/@EventRate/extensions/sausageplot.m @@ -0,0 +1,128 @@ +%% SAUSAGEPLOT +function sausageplot(obj, numBinsToUse, varargin) + %sausageplot + % Under development, this function attempts to replicate the + % capabilities of sausageplot.xpy written by Glenn Thompson + % at AVO. + p = inputParser; + p.addRequired('numBinsToUse', @isnumeric); + p.addOptional('numPoints', 1, @isnumeric); + p.addOptional('radii', [], @isnumeric); + p.parse(numBinsToUse, varargin{:}); + numBinsToUse = p.Results.numBinsToUse; + numPoints = p.Results.numPoints; + radii = p.Results.radii; +% % Page size, dots-per-inch and scale settings +% fh = figure() +% if numBinsToUse > NUMPOINTS +% dpi = 6*(numBinsToUse+1); +% axes_width = 0.8 * (NUMPOINTS + 1) / (numBinsToUse + 1); +% axes_height = 0.8; +% else +% dpi = 6*(NUMPOINTS+1); +% axes_width = 0.8; +% axes_height = 0.8 * (numBinsToUse + 1) / (NUMPOINTS + 1); +% end +% fh.set_dpi(dpi) +% fh.set_size_inches((10.0,10.0),forward=True) +% fig2ax1 = fig2.add_axes([0.1, 0.85-axes_height, axes_width, axes_height]); +% print_pixels(fig2, fig2ax1, numBinsToUse, NUMPOINTS); +% colormapname = 'hot_r'; + MAXMAGCOLORBAR = 5.0; + MINMAGCOLORBAR = 1.0; + % Marker size configuration +% SCALEFACTOR = 84.0 / dpi; +% MAXMARKERSIZE = 43.0 * SCALEFACTOR; +% MINMARKERSIZE = 4.0 * SCALEFACTOR; + PTHRESHOLD = 50; + for c = 1:length(obj) + % set up weekending list for yticklabels + binendstr = {}; + for bin_index=numel(obj.time)-numBinsToUse+1: numel(obj.time) + + dstr = datestr(obj.time(bin_index)) + binendstr{bin_index} = dstr; + end + pointlabels = {}; + for i=1:length(numPoints) + % filter here based on points and radii + j = 1:length(obj(c).counts); % replace this + counts = obj(c).counts(j); + cummag = obj(c).cum_mag(j); + prcntile = percentiles(obj(c).counts); + %pointlabels{i} = sprintf('%s(%d)', point_label{i}, counts(-1)); + pointlabels{i}=''; + for bin_index=numel(obj.time)-numBinsToUse+1: numel(obj.time)%-numBinsToUse-1: 1: 0 + y = obj(c).counts(bin_index); + magnitude = obj(c).cum_mag(bin_index); + p = y2percentile(y,prcntile); + if y>0 + colorVal = scalarMap.to_rgba(magnitude) + msize = MINMARKERSIZE + (p-PTHRESHOLD) * (MAXMARKERSIZE - MINMARKERSIZE) / (100-PTHRESHOLD); + if msize MAXMARKERSIZE * 0.3 + text(i+0.5, bin_index, num2str(y)) +% fig2ax1.text(i+0.5, w, '%d', y, horizontalalignment='center', verticalalignment='center', fontsize = 8 * SCALEFACTOR) + end + end + end + end + + % Adding xticks, yticks, labels, grid +% fig2ax1.set_axisbelow(True) % I think this puts grid and tickmarks below actual data plotted + + % x-axis +% fig2ax1.set_xticks(np.arange(.5,NUMVOLCANOES+.5,1)) + set(gca, 'XTick', 0.5:1:numPoints+0.5); +% fig2ax1.set_xlim([-0.5, NUMVOLCANOES+0.5]) + set(gca, 'XLim', [-0.5 numPoints+0.5]) +% fig2ax1.xaxis.grid(True, linestyle='-', color='gray') + grid on; + set(gca, 'XTickLabel', pointlabels) +% fig2ax1.xaxis.set_ticks_position('top') +% fig2ax1.xaxis.set_label_position('top') + %plt.setp( fig2ax1.get_xticklabels(), rotation=45, horizontalalignment='left', fontsize=10*SCALEFACTOR ) + + % y-axis +% fig2ax1.set_yticks(np.arange(-number_of_weeks_to_plot-0.5, 0, 1)) + set(gca, 'YTick', -numBinsToUse-0.5: 1: 0) +% fig2ax1.set_yticklabels(weekending) + set(gca, 'YTickLabel', binendstr) +% fig2ax1.set_ylim([-number_of_weeks_to_plot - 0.5, -0.5]) + set(gca, 'YLim', -numBinsToUse -0.5 : 1 : -0.5) +% plt.setp( fig2ax1.get_yticklabels(), fontsize=10*SCALEFACTOR ) + end +end + +function pvalue(obj) + logt = log10(obj.time-obj.time(1)); + logc = log10(obj.counts); + plot(logt, logc); + p = logt \ logc' +% p = polyfit(logt, logc, 1) +% hold on +% logt1 = min(logt); +% logc1 = polyval(p, logt1); +% logt2 = max(logt); +% logc2 = polyval(p, logt2); +% line([logt1 logt2], [logc1 logc2]); +end + + +%% PERCENTILES +function p=percentiles(vals) + lenVals = length(vals); + for i=1:100 + p(i) = vals(floor(i/100 * (lenVals-1))+1); + end +end + +%% PLOT_PERCENTILES +function plot_percentiles(p) + figure(get(gcf,'Number')+1, 'Color', [1 1 1]) + plot(1:100, p); +end diff --git a/core/@EventRate/helenaplot.m b/core/@EventRate/helenaplot.m new file mode 100755 index 0000000..f01c01b --- /dev/null +++ b/core/@EventRate/helenaplot.m @@ -0,0 +1,13 @@ +%% HELENAPLOT +function helenaplot(obj) + for c=1:length(obj) + figure + set(gcf,'Color', [1 1 1]); + cumcummag = magnitude.eng2mag(cumsum(magnitude.mag2eng(obj(c).cum_mag))); + [ax, h1, h2] = plotyy(obj(c).time, cumcummag, obj(c).time, cumsum(obj(c).energy), @plot, @plot); + datetick(ax(1), 'x','keeplimits'); + datetick(ax(2), 'x','keeplimits'); + ylabel(ax(1), 'Cumulative Magnitude'); + ylabel(ax(2), 'Cumulative Energy'); + end +end \ No newline at end of file diff --git a/core/@EventRate/html/EventRate.html b/core/@EventRate/html/EventRate.html deleted file mode 100644 index 81e5be9..0000000 --- a/core/@EventRate/html/EventRate.html +++ /dev/null @@ -1,1628 +0,0 @@ - - - - - EventRate

Contents

classdef EventRate
-
%EventRate Event Rate class constructor.
-%
-%    EventRate is a class that has been developed around plotting earthquake
-%    counts - i.e. the rate of events per unit time. It has evolved to compute
-%    other metrics such the hourly mean event rate, median event rate, mean
-%    magnitude and cumulative magnitude, which are important metrics for an AVO
-%    swarm tracking system.
-%
-%    EventRate can import information from:
-%    (1) a Catalog object.
-%    (2) a Datascope database written in the "swarms1.0" schema, defined at AVO.
-%        This is the format used by the swarm tracking system (Thompson &
-%        West, 2010).
-%
-%    ER = EventRate(Catalog_OBJECT, 'binsize', BINSIZE) creates an eventrate object
-%    from a Catalog object using non-overlapping bins of BINSIZE days.
-%
-%    ER = EventRate(Catalog_OBJECT, 'binsize', BINSIZE, 'stepsize', STEPSIZE) creates an eventrate object
-%    using overlapping bins. If omitted STEPSIZE==BINSIZE.
-%
-

EXAMPLES:

     First create a catalog object from the demo database:
-         dbpath = demodb('avo')
-         catalogObject = readEvents('datascope', 'dbpath', dbpath, ...
-                'dbeval', ...
-                'deg2km(distance(lat, lon, 60.4853, -152.7431))<15.0' ...
-                );
     (1) Create an eventrate object using a binsize of 1 day:
-         erobj = catalogObject.eventrate('binsize', 1);
     (2) Create an eventrate object using a binsize of 1 hour:
-         erobj = catalogObject.eventrate('binsize', 1/24);
     (3) Create an eventrate object using a binsize of 1 hour but a stepsize of 5 minutes:
-         erobj = catalogObject.eventrate('binsize', 1/24, 'stepsize', 5/1440);
     (4) Create a vector of eventrate objects subclassified using event types 'r', 'e', 'l', 'h', 't':
-             erobj = eventrate(catalogObject, 1, 'etypes', 'relht');
-         To plot counts on separate figures:
-             erobj.plot()
-         To plot counts and energy panels, each event type as a separate figure:
-             erobj.plot('metric', {'counts';'energy'});
-         To plot counts and energy panels on separate figures, each event type as panels:
-             erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'panels');
-         To plot counts and energy panels on separate figures, each event type stacked:
-             erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked');
     (5) A full example:
-             catalogObject = catalog(fullfile(MVO_DATA, 'mbwh_catalog'), 'seisan', 'snum', datenum(1996,10,1), 'enum', datenum(2004,3,1), 'region', 'Montserrat')
-             erobj = eventrate(catalogObject, 365/12, 'stepsize', 1, 'etypes', 'thlr');
-             erobj.plot('metric', {'counts';'energy'}, 'plotmode', 'stacked');

PROPERTIES

  For a list of all properties type properties(EventRate)
  time                % (array) time of the center of each bin as a DATENUM
  METRICS:
-      counts 		     % (array) number of events in each bin
-      mean_rate           % (array) number of events per hour in each bin
-      median_rate	     % (array) reciprocal of the median time interval between events. Represented as an hourly rate.
-      cum_mag		     % (array) total sum of energy in each bin, represented as a magnitude.
-      mean_mag		     % (array) mean magnitude of events in each bin
-      median_mag          % (array) median magnitude of events in each bin
-      min_mag             % (array) smallest magnitude in each bin
-      max_mag             % (array) largest magnitude in each bin
  SUMMARY DATA:
-      numbins             % (scalar) number of bins used for grouping
-                              events
-      total_counts        % (scalar) sum of counts
-      total_mag           % (scalar) total sum of energy of all catalogObjects, represented as a magnitude
  METADATA:
-      etype               % event type/classification.
-      snum                % (scalar) start date/time in DATENUM format
-      enum                % (scalar) end date/time in DATENUM format
-      binsize             % (scalar) bin size in days
-      stepsize            % (scalar) step size in days
-      region              % (4-element vector) [minlon maxlon minlat maxlat]
-      minmag              % (scalar) magnitudes smaller than this were eliminated
-      dbroot              % path to the original data on disk
-      archiveformat       % indicates if the source is a flat file, or
-                            'daily' or 'monthly' volumes
-      auth                % auth of the events

METHODS

  For a list of all methods type methods EventRate

See also Catalog, Catalog_lite

AUTHOR: Glenn Thompson

PROPERTIES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    properties(GetAccess = 'public', SetAccess = 'public')
-        time = [];          % (array) in datenum format
-        counts = []; 		% (array) number of events in each bin
-		mean_rate = [];      % (array) number of events per hour in each bin
-		median_rate = [];	% (array) reciprocal of the median time interval between events. Represented as an hourly rate.
-		cum_mag = [];		% (array) total sum of energy in each bin, represented as a magnitude.
-		mean_mag = [];		% (array)
-        median_mag = [];     % (array)
-        energy = [];
-        total_counts = [];   % (scalar) sum of counts
-		total_mag = [];      % (scalar) total sum of energy of all catalogObjects, represented as a magnitude
-        numbins = [];        % (scalar)
-        min_mag = [];
-        max_mag = [];
-        etype = '*';
-        snum = 0;
-        enum = now;
-        binsize = 1;
-        stepsize = 1;
-        misc_fields = {};
-        misc_values = {};
-    end
-

PUBLIC METHODS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	methods
-

CONSTRUCTOR

        function self = EventRate(time, counts, energy, median_energy, ...
-                smallest_energy, biggest_energy, median_time_interval, total_counts, ...
-                snum, enum, etypes, binsize, stepsize, numbins)
-            self.time = time;
-            self.counts = counts;
-            self.median_rate = 1 ./ (median_time_interval * 24);
-            self.median_rate(counts<10) = 0;
-            self.median_rate = max([self.counts / (24 * binsize); self.median_rate]);
-            self.median_mag = magnitude.eng2mag(median_energy);
-            self.energy = energy;
-            self.total_counts = total_counts;
-            self.numbins = numbins;
-            self.min_mag = magnitude.eng2mag(smallest_energy);
-            self.max_mag = magnitude.eng2mag(biggest_energy);
-            self.etype = etypes;
-            self.snum = snum;
-            self.enum = enum;
-            self.binsize = binsize;
-            self.stepsize = stepsize;
-        end
-
Error using EventRate (line 136)
-Not enough input arguments.
-

----------------------------------------------

GETTERS

        function cum_mag = get.cum_mag(erobj)
-            cum_mag = magnitude.eng2mag(erobj.energy);
-        end
-        function mean_mag = get.mean_mag(erobj)
-            mean_mag = magnitude.eng2mag(erobj.energy./erobj.counts);
-        end
-        function mean_rate = get.mean_rate(erobj)
-            mean_rate = erobj.counts / (24 * erobj.binsize);
-        end
-        function total_mag = get.total_mag(erobj)
-            total_mag = magnitude.eng2mag(sum(erobj.energy));
-        end
-
-        function plot(obj, varargin)
-            %EventRate/plot
-            %   Plot metrics of an EventRate object
-            %
-            %   The following metrics are available:
-            %
-            %        counts 		     % number of events in each bin
-            %        mean_rate           % number of events per hour in each bin
-            %        median_rate	     % reciprocal of the median time interval between events. Represented as an hourly rate.
-            %        energy              % total sum of energy in each bin
-            %        cum_mag		     % total sum of energy in each bin, represented as a magnitude.
-            %        mean_mag		     % mean magnitude of events in each bin
-            %        median_mag          % median magnitude of events in each bin
-            %        min_mag             % smallest magnitude in each bin
-            %
-            %   erobj.plot() or plot(erobj) will produce a plot of event
-            %   counts per unit time. The time unit is given by erobj.binsize
-            %   days.
-            %
-            %   erobj.plot('metric', list_of_metrics) will plot each metric
-            %   requested in list_of_metrics in a separate panel.
-            %   list_of_metrics should be a cell array of valid metric
-            %   strings. However, it may be a string if only one metric is
-            %   requested.
-            %
-            %   erobj.plot('metric', 'counts') is equivalent to
-            %   erobj.plot() and erobj.plot('metric', {'counts'})
-            %
-            %   erobj.plot('metric', 'mean_rate') is similar, but the
-            %   mean_rate is always events per hour, regardless of the
-            %   binsize. So if erobj.binsize = 1 (day), counts will be
-            %   exactly 24 * mean_rate.
-            %
-            %   erobj.plot('metric', {'counts';'cum_mag'}) will plot counts
-            %   in one panel and the cumulative magnitude per bin in
-            %   another panel.
-            %
-            %   In general any number of metrics can be given in
-            %   list_of_metrics.
-            %
-            %   If erobj is an array of eventrate structures (e.g. one per
-            %   etype), each is plotted on a separate figure. However the
-            %   plotmode variable overrides this:
-            %
-            %     plot(eventrate_vector, 'plotmode', 'panels') will plot them
-            %     in separate panels on the same figure
-            %
-            %     plot(eventrate_vector, 'plotmode', 'single') will plot them
-            %     in a single panel
-
-            p = inputParser;
-            p.addParamValue('metric', {'counts'}, @(c) iscell(c)||isstr(c));
-            p.addParamValue('plotmode', 'figures', @isstr);
-            p.addParamValue('smooth', 1, @isnumeric);
-            p.parse(varargin{:});
-            metric = p.Results.metric;
-            plotmode = p.Results.plotmode;
-            smoothbins = p.Results.smooth; % NOT DOING ANYTHING WITH THIS YET BUT COULD SMOOTH COUNTS OVER SEVERAL BINS
-            if ~iscell(metric)
-                metric = {metric};
-            end
-            numMetrics = numel(metric);
-            colors = {[0 0.5 0] [0 0 1]};
-
-            % plot each etype on a separate figure, each metric as a
-            % subplot
-
-            if strcmp(plotmode, 'figures') || length(obj)==1
-
                for c = 1 : numel(obj)
-                    binsize_str = Catalog.binning.binsizelabel(obj(c).binsize);
-                    numsubplots = length(metric);
-                    %figure(gcf+1)
-                    figure
-                    set(gcf,'Color', [1 1 1]);
-                    for cc = 1: numsubplots % number of metrics to plot
-                        %eval(  sprintf('data = obj(c).%s;',metric{cc} ) );
-                        data = obj(c).(metric{cc});
-                        % replace -Inf values as they mess up plots
-                        ydata = data; % ydata is the data we will plot, but we keep data for cumulative energy etc.
-                        if smoothbins > 1
-                            ydata = smooth(ydata, smoothbins);
-                        end
-                        ydata(isinf(data))=NaN;
-                        mindata = nanmin(ydata);
-                        ydata(isnan(ydata))=mindata; % we replace NaNs and Infs in data with nanmin(data) in ydata
-                        if (obj(c).binsize == obj(c).stepsize) & ( strcmp(metric{cc}, 'counts') | strcmp(metric{cc}, 'energy') | strcmp(metric{cc}, 'cum_mag') )
-                            if strfind(metric{cc}, 'mag')
-                                cumdata = magnitude.eng2mag(cumsum(magnitude.mag2eng(data)));
-                                subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, ydata, obj(c).time, cumdata, @stairs, @plot );
-                                set(h1, 'Color', colors{1});
-                            else
-                                %subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, data, obj(c).time, cumsum(data), @bar, @plot );
-                                subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, ydata, obj(c).time, cumsum(data), @stairs, @plot );
-                                %set(h1, 'FaceColor', colors{1}, 'EdgeColor', colors{1})
-                                %set(h1, 'BarWidth', 1);
-                                %set(h1, 'LineWidth', 0.1);
-                            end
-                            datetick(ax(1), 'x','keeplimits');
-                            title(ax(1), metric2label(metric{cc}, obj(c).binsize), 'Color', colors{1}, 'FontSize',12)
-                            datetick(ax(2), 'x','keeplimits');
-                            ylabel(ax(2),'Cumulative', 'Color', colors{2}, 'FontSize',12)
-                            set(h1, 'Color', colors{1});
-                            set(h2, 'Color', colors{2}, 'LineWidth', 2);
-                            set(ax(1), 'YColor', colors{1});
-                            set(ax(2), 'YColor', colors{2});
-                            linkaxes(ax, 'x');
-                        else
-
-                            if strfind(metric{cc}, 'mag')
-                                subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1});
-                            else
-                                %subplot(numsubplots,1,cc), bar(obj(c).time, data, 'FaceColor', colors{1}, 'EdgeColor', colors{1}, 'BarWidth', 1, 'LineWidth', 0.1);
-                                subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1});
-                            end
-                            datetick('x','keeplimits');
-                            title(metric2label(metric{cc}, obj(c).binsize), 'FontSize',12)
-                        end
-                        axis tight;
-                    end
-                end
-

FROM HERE ON THE PLOTMODES HAVE NOT BEEN UPDATED

            elseif strcmp(plotmode, 'panels')
-                % Each metric on a separate figure, showing all requested
-                % subclasses on separate panels
-                  for c = 1 : numel(metric)
-                    figure(gcf+c)
-
-                    numsubplots = length(obj);
-
-                    for cc = numsubplots: -1: 1
-                        if strcmp(metric{c},'energy')
-                            %data = cumsum(magnitude.mag2eng(obj(cc).cum_mag));
-                            data = (magnitude.mag2eng(obj(cc).cum_mag));
-
-                        else
-                            % eval(  sprintf('data = obj(cc).%s;',metric{c} ) );
-                            data = obj(cc).(metric{c});
-                        end
-                        if smoothbins > 1
-                            data = smooth(data, smoothbins);
-                        end
-                        % where to position the axes
-                        pos(1) = 0.1;
-                        pos(2) = 0.1+(0.95-0.1)*(cc-1)/numsubplots;
-                        pos(3) = 0.8;
-                        pos(4) = (0.8*(0.95-0.1)/numsubplots);
-                        axes('position', pos);
-
-                        % plot
-                        bar( obj(cc).time, data, 'EdgeColor', 'none', 'FaceColor', [0 0 0] );
-%                         hold on;
-%                         sdata = smooth(data, 30, 'lowess');
-%                         plot( obj(cc).time, sdata, 'k-', 'linewidth', 2);
-
-
-                        % range and label
-                        datetick('x','keeplimits');
-                        set(gca, 'XLim', [obj(cc).snum obj(cc).enum]);
-                        ymax = nanmax(matlab_extensions.catmatrices(1, data));
-%                         ymax = min([max(sdata)*2 max(data)*1.01]);
-                        set(gca, 'YLim', [0 ymax]);
-                        ylabel(obj(cc).etype);
-
-
-
-                    end
-                    %suptitle(metric{c});
-                    fprintf('metric for figure %d is %s\n', gcf, metric{c});
-                  end
-
-
-            elseif strcmp(plotmode, 'single')
-                  % Each metric on a separate figure, showing all requested
-                  % subclasses on the same panel
-                  colour = 'rgbcm';
-                  for c = 1 : numel(metric)
-                    figure(gcf+c)
-
-                    for cc = 1: length(obj)
-                        if strcmp(metric{c},'energy')
-                            %data = cumsum(magnitude.mag2eng(obj(cc).cum_mag));
-                            data = (magnitude.mag2eng(obj(cc).cum_mag));
-                        else
-                            % eval(  sprintf('data = obj(cc).%s;',metric{c} ) );
-                            data = obj(cc).(metric{c});
-                        end
-
-                        if smoothbins > 1
-                            data = smooth(data, smoothbins);
-                        end
-
-                        plot( obj(cc).time, data, sprintf('-%c',colour(cc)) );
-                        hold on;
-                        datetick('x','keeplimits');
-                        set(gca, 'XLim', [obj(cc).snum obj(cc).enum]);
-                        %ymax = nanmax(matlab_extensions.catmatrices(1, data));
-                        %set(gca, 'YLim', [0 ymax]);
-                        %ylabel(obj(cc).etype);
-                    end
-                    suptitle(metric{c});
-                  end
-
-
-             elseif strcmp(plotmode, 'stacked')
-                 % Each metric on a separate figure, showing all subclasses
-                 % stacked on the same panel
-                  colour = 'rgbcm';
-                  for c = 1 : numel(metric)
-                    figure(gcf+c)
-                    data =[];
-                    for cc = 1: length(obj)
-                        if strcmp(metric{c},'energy')
-                            data = (magnitude.mag2eng(obj(cc).cum_mag));
-                        else
-                            %eval(  sprintf('data(:,cc) = obj(cc).%s;',metric{c} ) );
-                            data(:,cc) = obj(cc).(metric{c});
-                            if findstr(metric{c}, 'mag')
-                                disp('Warning: It is meaningless to stack magnitude data');
-                                data(data<0)=0;
-                                data(isnan(data))=0;
-                            end
-                        end
-                        if smoothbins > 1
-                            data = smooth(data, smoothbins);
-                        end
-
-                        %bar( obj(cc).time, data, 1, 'stack' );
-                        area(obj(cc).time, data);
-                        datetick('x','keeplimits');
-                        set(gca, 'XLim', [obj(cc).snum obj(cc).enum]);
-                        suptitle(metric{c});
-                    end
-                end
-            end
-        end
-

PYTHONPLOT

        function pythonplot(obj)
-            obj.plot('metric', {'counts';'cum_mag'});
-        end
-

HELENAPLOT

        function helenaplot(obj)
-            for c=1:length(obj)
-                figure
-                set(gcf,'Color', [1 1 1]);
-                cumcummag = magnitude.eng2mag(cumsum(magnitude.mag2eng(obj(c).cum_mag)));
-                [ax, h1, h2] = plotyy(obj(c).time, cumcummag, obj(c).time, cumsum(obj(c).energy), @plot, @plot);
-                datetick(ax(1), 'x','keeplimits');
-                datetick(ax(2), 'x','keeplimits');
-                ylabel(ax(1), 'Cumulative Magnitude', 'FontSize',12);
-                ylabel(ax(2), 'Cumulative Energy', 'FontSize',12);
-            end
-        end
-
-
-        function pvalue(obj)
-            logt = log10(obj.time-obj.time(1));
-            logc = log10(obj.counts);
-            plot(logt, logc);
-            p = logt \ logc'
-%             p = polyfit(logt, logc, 1)
-%             hold on
-%             logt1 = min(logt);
-%             logc1 = polyval(p, logt1);
-%             logt2 = max(logt);
-%             logc2 = polyval(p, logt2);
-%             line([logt1 logt2], [logc1 logc2]);
-        end
-

SAUSAGEPLOT

        function sausageplot(obj, numBinsToUse, varargin)
-            %sausageplot
-            %    Under development, this function attempts to replicate the
-            %    capabilities of sausageplot.xpy written by Glenn Thompson
-            %    at AVO.
-            p = inputParser;
-            p.addRequired('numBinsToUse', @isnumeric);
-            p.addOptional('numPoints', 1, @isnumeric);
-            p.addOptional('radii', [], @isnumeric);
-            p.parse(numBinsToUse, varargin{:});
-            numBinsToUse = p.Results.numBinsToUse;
-            numPoints = p.Results.numPoints;
-            radii = p.Results.radii;
-%             % Page size, dots-per-inch and scale settings
-%             fh = figure()
-%             if numBinsToUse > NUMPOINTS
-%                 dpi = 6*(numBinsToUse+1);
-%                 axes_width = 0.8 * (NUMPOINTS + 1) / (numBinsToUse + 1);
-%                 axes_height = 0.8;
-%             else
-%                 dpi = 6*(NUMPOINTS+1);
-%                 axes_width = 0.8;
-%                 axes_height = 0.8 * (numBinsToUse + 1) / (NUMPOINTS + 1);
-%             end
-%             fh.set_dpi(dpi)
-%             fh.set_size_inches((10.0,10.0),forward=True)
-%             fig2ax1 = fig2.add_axes([0.1, 0.85-axes_height, axes_width, axes_height]);
-%             print_pixels(fig2, fig2ax1, numBinsToUse, NUMPOINTS);
-%             colormapname = 'hot_r';
-            MAXMAGCOLORBAR = 5.0;
-            MINMAGCOLORBAR = 1.0;
-            % Marker size configuration
-%             SCALEFACTOR = 84.0 / dpi;
-%             MAXMARKERSIZE = 43.0 * SCALEFACTOR;
-%             MINMARKERSIZE = 4.0 * SCALEFACTOR;
-            PTHRESHOLD = 50;
-            for c = 1:length(obj)
-                % set up weekending list for yticklabels
-                binendstr = {};
-                for bin_index=numel(obj.time)-numBinsToUse+1: numel(obj.time)
-                    bin_index
-                    dstr = datestr(obj.time(bin_index))
-                    binendstr{bin_index} = dstr;
-                end
-                pointlabels = {};
-                for i=1:length(numPoints)
-                    % filter here based on points and radii
-                    j = 1:length(obj(c).counts); % replace this
-                    counts = obj(c).counts(j);
-                    cummag = obj(c).cum_mag(j);
-                    prcntile = percentiles(obj(c).counts);
-                    %pointlabels{i} = sprintf('%s(%d)', point_label{i}, counts(-1));
-                    pointlabels{i}='';
-                    for bin_index=numel(obj.time)-numBinsToUse+1: numel(obj.time)%-numBinsToUse-1: 1: 0
-                        y = obj(c).counts(bin_index);
-                        magnitude = obj(c).cum_mag(bin_index);
-                        p = y2percentile(y,prcntile);
-                        if y>0
-                            colorVal = scalarMap.to_rgba(magnitude)
-                            msize = MINMARKERSIZE + (p-PTHRESHOLD) * (MAXMARKERSIZE - MINMARKERSIZE) / (100-PTHRESHOLD);
-                            if msize<MINMARKERSIZE
-                                msize=MINMARKERSIZE;
-                            end
-%                             fig2ax1.plot(i+0.5, w, 's', color=colorVal, markersize=msize, linewidth=0 );
-                            scatter(i+0.5, bin_index, msize, y, 's', 'filled')
-                            if msize > MAXMARKERSIZE * 0.3
-                                text(i+0.5, bin_index, num2str(y))
-%                                fig2ax1.text(i+0.5, w, '%d', y, horizontalalignment='center', verticalalignment='center', fontsize = 8 * SCALEFACTOR)
-                            end
-                        end
-                    end
-                end
-
-                % Adding xticks, yticks, labels, grid
-%                 fig2ax1.set_axisbelow(True) % I think this puts grid and tickmarks below actual data plotted
-
-                % x-axis
-%                 fig2ax1.set_xticks(np.arange(.5,NUMVOLCANOES+.5,1))
-                set(gca, 'XTick', 0.5:1:numPoints+0.5);
-%                 fig2ax1.set_xlim([-0.5, NUMVOLCANOES+0.5])
-                set(gca, 'XLim', [-0.5 numPoints+0.5])
-%                 fig2ax1.xaxis.grid(True, linestyle='-', color='gray')
-                grid on;
-                set(gca, 'XTickLabel', pointlabels)
-%                 fig2ax1.xaxis.set_ticks_position('top')
-%                 fig2ax1.xaxis.set_label_position('top')
-                %plt.setp( fig2ax1.get_xticklabels(), rotation=45, horizontalalignment='left', fontsize=10*SCALEFACTOR )
-
-                % y-axis
-%                 fig2ax1.set_yticks(np.arange(-number_of_weeks_to_plot-0.5, 0, 1))
-                set(gca, 'YTick', -numBinsToUse-0.5: 1: 0)
-%                 fig2ax1.set_yticklabels(weekending)
-                set(gca, 'YTickLabel', binendstr)
-%                 fig2ax1.set_ylim([-number_of_weeks_to_plot - 0.5, -0.5])
-                set(gca, 'YLim', -numBinsToUse -0.5 : 1 : -0.5)
-%                 plt.setp( fig2ax1.get_yticklabels(), fontsize=10*SCALEFACTOR )
-            end
-        end
-

IMPORTSWARMDB

        function obj = importswarmdb(obj, dbname, auth, snum, enum)
-            % IMPORTSWARMDB
-            % Load a swarm database metrics table into an EventRate object
-            % eventrate = importswarmdb(erobj, dbname, auth, snum, enum);
-            %
-            % INPUT:
-            %	dbname		the path of the database (must have a 'metrics' table)
-            %	auth		name of the grid to load swarm tracking metrics for
-            %	snum,enum	start and end datenumbers (Matlab time format, see 'help datenum')
-            %
-            % OUTPUT:
-            %	obj		an eventrate object
-            %
-            % Example:
-            %	erobj = importswarmdb('/avort/devrun/dbswarm/swarm_metadata', 'RD_lo', datenum(2010, 7, 1), datenum(2010, 7, 14) );
-
-            % Glenn Thompson, 20100714
-
-            % initialize
-            obj.dbroot = dbname;
-            obj.snum = snum;
-            obj.enum = enum;
-            obj.auth = auth;
-
-            % check that database exists
-            dbtablename = sprintf('%s.metrics',dbname);
-            if exist(dbtablename,'file')
-                % load the data
-                try
-                    db = dbopen(dbname, 'r');
-                catch me
-                    fprintf('Error: Could not open %s for reading',dbname);
-                        return;
-                end
-                db = dblookup_table(db, 'metrics');
-                if (dbquery(db, 'dbRECORD_COUNT')==0)
-                    fprintf('Error: Could not open %s for reading',dbtablename);
-                    return;
-                end
-                db = dbsubset(db, sprintf('auth ~= /.*%s.*/',auth));
-                numrows = dbquery(db,'dbRECORD_COUNT');
-                debug.print_debug(sprintf('Got %d rows after auth subset',numrows),2);
-                sepoch = datenum2epoch(snum);
-                eepoch = datenum2epoch(enum);
-                db = dbsubset(db, sprintf('timewindow_starttime >= %f && timewindow_endtime <= %f',sepoch,eepoch));
-                numrows = dbquery(db,'dbRECORD_COUNT');
-                debug.print_debug(sprintf('Got %d rows after time subset',numrows),2);
-
-                if numrows > 0
-                    % Note that metrics are only saved when mean_rate >= 1.
-                    % Therefore there will be lots of mean_rate==0 timewindows not in
-                    % database.
-                    [tempsepoch, tempeepoch, mean_rate, median_rate, mean_mag, cum_mag] = dbgetv(db,'timewindow_starttime', 'timewindow_endtime', 'mean_rate', 'median_rate', 'mean_ml', 'cum_ml');
-                    obj.binsize = (tempeepoch(1) - tempsepoch(1))/86400;
-                    obj.stepsize = min(tempsepoch(2:end) - tempsepoch(1:end-1))/86400;
-                    obj.time = snum+obj.stepsize:obj.stepsize:enum;
-                    obj.numbins = length(obj.time);
-                    obj.mean_rate = zeros(obj.numbins, 1);
-                    obj.counts = zeros(obj.numbins, 1);
-                    obj.median_rate = zeros(obj.numbins, 1);
-                    obj.mean_mag = zeros(obj.numbins, 1);
-                    obj.cum_mag = zeros(obj.numbins, 1);
-                    for c=1:length(tempeepoch)
-                        tempenum = epoch2datenum(tempeepoch(c));
-                        i = find(obj.time == tempenum);
-                        obj.mean_rate(i) = mean_rate(c);
-                        obj.counts(i) = mean_rate(c) * (obj.binsize * 24);
-                        obj.median_rate(i) = median_rate(c);
-                        obj.mean_mag(i) = mean_mag(c);
-                        obj.cum_mag(i) = cum_mag(c);
-                    end
-                end
-                dbclose(db);
-
-            else
-                % error - table does not exist
-                fprintf('Error: %s does not exist',dbtablename);
-                return;
-            end
-
-            obj.total_counts = sum(obj.counts)*obj.stepsize/obj.binsize;
-
-        end
-

ADDFIELD

        function obj = addfield(obj,fieldname,val)
-            %ADDFIELD add fields and values to object(s)
-            %   obj = addfield(obj, fieldname, value)
-            %   This function creates a new user defined field, and fills it with the
-            %   included value.  If fieldname exists, it will overwrite the existing
-            %   value.
-            %
-            %   Input Arguments
-            %       obj: an EventRate object
-            %       fieldname: a string name
-            %       value: a value to be added for those fields.  Value can be anything
-            %
-            %   EventRate objects can hold user-defined fields.  To access the contents,
-            %   use EventRate/get.
-            %
-            %   Example:
-            %       % add a field called "TESTFIELD", containing the numbers 1-45
-            %       obj = addfield(obj,'TestField',1:45);
-            %
-            %       % add a cell field called "MISHMOSH"
-            %       obj = addfield(obj,'mishmosh',{'hello';'world'});
-            %
-            %       % see the result
-            %       disp(obj)
-            %
-            % See also EventRate/set, EventRate/get
-
-            % AUTHOR: Glenn Thompson
-
-            if ischar(fieldname)
-                mask = strcmp(fieldname, properties(obj));
-                if any(mask)
-                    obj = obj.set(fieldname, val);
-                else
-                    mask = strcmp(upper(fieldname),obj.misc_fields);
-                    if any(mask)
-                        obj = obj.set(fieldname, val);
-                    else
-                        obj.misc_fields = [obj.misc_fields, upper(fieldname)];
-                        obj = obj.set(upper(fieldname), val);
-                    end
-                end
-            else
-                error('%s:addfield:invalidFieldname','fieldname must be a string', class(catalogObject))
-            end
-
-        end
-

SET

        function obj = set(obj, varargin)
-            %SET Set properties for EventRate object(s)
-            %   obj = set(obj,'property_name', val, ['property_name2', val2])
-            %   SET is one of the two gateway functions of an object, such as EventRate.
-            %   Properties that are changed through SET are typechecked and otherwise
-            %   scrutinized before being stored within the EventRate object.  This
-            %   ensures that the other EventRate methods are all retrieving valid data,
-            %   thereby increasing the reliability of the code.
-            %
-            %   Another strong advantage to using SET and GET to change and retrieve
-            %   properties, rather than just assigning them to EventRate object directly,
-            %   is that the underlying data structure can change and grow without
-            %   harming the code that is written based on the EventRate object.
-            %
-            %   For a list of valid property names, type:
-            %       properties(obj)
-            %
-            %   If user-defined fields were added to the EventRate object (ie, through
-            %   addField), these fieldnames are also available through set.
-            %
-            %   Examples:
-            %       (1) Change the description property
-            %           obj = obj.set('description','hello world');
-            %
-            %       (2) Add new a field called CLOSEST_STATION with
-            %           % the value 'MBLG'
-            %           obj = obj.addfield('CLOSEST_STATION','MBLG');
-            %
-            %           % change the value of the CLOSEST_STATION field
-            %           obj = obj.set('CLOSEST_STATION','MBWH');
-            %
-            %  See also EventRate/get, EventRate/addfield
-
-            Vidx = 1 : numel(varargin);
-
-            while numel(Vidx) >= 2
-                prop_name = upper(varargin{Vidx(1)});
-                val = varargin{Vidx(2)};
-                mask = strcmp(upper(prop_name),upper(properties(obj)));
-                if any(mask)
-                    mc = metaclass(obj);
-                    i = find(mask);
-                    prop_name = mc.PropertyList(i).Name;
-                    if isempty(mc.PropertyList(i).GetMethod)
-                        %eval(sprintf('obj.%s=val;',prop_name));
-                        obj.(prop_name) = val;
-                    else
-                        warning('Property %s is a derived property and cannot be set',prop_name);
-                    end
-                else
-                    switch prop_name
-                        case obj.misc_fields
-                            mask = strcmp(prop_name,obj.misc_fields);
-                            obj.misc_values(mask) = {val};
-                        otherwise
-                            error('%s:set:unknownProperty',...
-                                'can''t understand property name : %s', mfilename,prop_name);
-                    end
-                end
-                Vidx(1:2) = []; %done with those parameters, move to the next ones...
-            end
-        end
-

GET

        function val = get(obj,prop_name)
-            %GET Get EventRate properties
-            %   val = get(EventRate_object,'property_name')
-            %
-            %   To see valid property names, type:
-            %       properties(EventRate_object)
-            %
-            %       If additional fields were added to EventRate using ADDFIELD, then
-            %       values from these can be retrieved using the fieldname
-            %
-            %   See also EventRate/SET, EventRate/ADDFIELD, Catalog/GET
-
-            mask = strcmp(prop_name, properties(obj));
-            if any(mask)
-                % eval(sprintf('val=obj.%s;',prop_name));
-                val = obj.(prop_name);
-            else
-                mask = strcmp(upper(prop_name),obj.misc_fields);
-                if any(mask)
-                    val = obj.misc_values{mask};
-                else
-                    warning('%s:get:unrecognizedProperty',...
-                        'Unrecognized property name : %s',  class(obj), prop_name);
-                end
-            end
-        end
-
    end % methods
-
-
-    methods(Static)
-        cookbook()
-    end
-
end
-

PERCENTILES

function p=percentiles(vals)
-    lenVals = length(vals);
-    for i=1:100
-        p(i) = vals(floor(i/100 * (lenVals-1))+1);
-    end
-end
-

PLOT_PERCENTILES

function plot_percentiles(p)
-    figure(gcf+1, 'Color', [1 1 1])
-    plot(1:100, p);
-end
-
-function label = metric2label(metric, binsize)
-    % label = metric2label(metric, binsize)
-    label=metric;
-    blabel = Catalog.binning.binsizelabel(binsize);
-    time_unit = blabel(4:end);
-    if strcmp(metric, 'counts')
-        label = sprintf('# Events %s',blabel);
-    elseif strcmp(metric, 'energy')
-        label = sprintf('Energy %s',blabel);
-    elseif strcmp(metric, 'mean_rate')
-        label = sprintf('Mean # events per hour (binsize %s)', time_unit);
-    elseif strcmp(metric, 'median_rate')
-        label = sprintf('Median # events per hour (binsize %s)', time_unit);
-    elseif strcmp(metric, 'cum_mag')
-        label = sprintf('Cumulative Magnitude per hour (binsize %s)', time_unit);;
-    elseif strcmp(metric, 'mean_mag')
-        label = sprintf('Mean Magnitude per hour (binsize %s)', time_unit);
-    elseif strcmp(metric, 'median_mag')
-        label = sprintf('Median Magnitude per hour (binsize %s)', time_unit);
-    end
-end
-
\ No newline at end of file diff --git a/core/@EventRate/html/cookbook.html b/core/@EventRate/html/cookbook.html deleted file mode 100644 index 42e1ebb..0000000 --- a/core/@EventRate/html/cookbook.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - EventRate Cookbook

EventRate Cookbook

The EventRate class is for making plots of:

  • number of events per unit time (often called "counts")
  • energy release rate / cumulative magnitude per unit time

Contents

Simple EventRate example

First we create a Catalog object from the Redoubt dataset

dbpath = Catalog.demo.demodb('avo');
-redoubtLon = -152.7431;
-redoubtLat = 60.4853;
-maxR = km2deg(20.0);
-catalogObject = Catalog.retrieve('antelope', 'dbpath', dbpath, ...
-	'radialcoordinates', [redoubtLat redoubtLon maxR]);
-
Loading data from /Users/glennthompson/src/GISMO/core/+Catalog/+demo/css3.0/avodb200903
-Got 1397 events
-

For a quick plot of earthquakes per hour, we create an eventrate object and then plot it. Here our binsize is 1/24 days, i.e. 1 hour.

eventrateObject = catalogObject.eventrate('binsize', 1/24);
-eventrateObject.plot()
-

Change to a smaller bin size of 20 minutes:

eventrateObject = catalogObject.eventrate('binsize', 20/1440);
-plot(eventrateObject);
-

Note that

eventrateObject.plot()
-

and

plot(eventrateObject)
-

do exactly the same thing. The first is the object-oriented style OBJECT.METHOD(INPUT PARAMETERS), the second is the functional style FUNCTION(OBJECT, INPUT PARAMETERS). The object oriented style is preferable

Event rates for overlapping time windows

Sometimes it is desirable to compute event rate metrics for sliding - i.e. overlapping - time windows. This is easily done with the 'stepsize' parameter. If omitted, stepsize defaults to the binsize - which is the length of the time window. So in the previous example, both binsize and stepsize were 1.0 hours. But we can just as easily compute an eventrate object for the same Events object with a binsize of 1 hour, and stepsize of 1 minute. This effectively converts a Catalog into a set of continuous metrics, measured every minute, directly comparable to 1-minute RSAM.

eventrateObject = catalogObject.eventrate('binsize', 1/24,  'stepsize', 1/1440);
-eventrateObject.plot()
-

Plots of other event rate metrics

eventrateObject.plot()
-

is equivalent to typing:

eventrateObject.plot('metric', 'counts');
-

The full list of metrics that can be plotted are:

  • counts
  • mean_rate
  • median_rate
  • cum_mag
  • mean_mag
  • median_mag
  • energy

All of these are properties of an eventrate object except for energy, which is computed from cum_mag on-the-fly. Several can be plotted at once in subplots of the same figure using a cell array:

eventrateObject.plot('metric', {'mean_rate'; 'median_rate'; 'mean_mag'; 'cum_mag'});
-

They can of course be plotted in separate figure windows:

eventrateObject.plot('metric', 'mean_rate')
-eventrateObject.plot('metric', 'median_rate')
-eventrateObject.plot('metric', 'mean_mag')
-eventrateObject.plot('metric', 'cum_mag')
-

These are the same metrics, binsize and stepsize used by the [AVO swarm tracking system] (http://www.aeic.alaska.edu/input/west/papers/2009_srl_thompson_redoubtSwarms.pdf).

Other plot types

Two more simple plot types are:

eventrateObject.helenaplot()
-

and:

eventrateObject.pythonplot()
-

End of tutorial

\ No newline at end of file diff --git a/core/@EventRate/html/cookbook.png b/core/@EventRate/html/cookbook.png deleted file mode 100644 index 5d749f4..0000000 Binary files a/core/@EventRate/html/cookbook.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_01.png b/core/@EventRate/html/cookbook_01.png deleted file mode 100644 index 7444327..0000000 Binary files a/core/@EventRate/html/cookbook_01.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_02.png b/core/@EventRate/html/cookbook_02.png deleted file mode 100644 index 6c8b59e..0000000 Binary files a/core/@EventRate/html/cookbook_02.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_03.png b/core/@EventRate/html/cookbook_03.png deleted file mode 100644 index f252182..0000000 Binary files a/core/@EventRate/html/cookbook_03.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_04.png b/core/@EventRate/html/cookbook_04.png deleted file mode 100644 index a3004c5..0000000 Binary files a/core/@EventRate/html/cookbook_04.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_05.png b/core/@EventRate/html/cookbook_05.png deleted file mode 100644 index 2761864..0000000 Binary files a/core/@EventRate/html/cookbook_05.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_06.png b/core/@EventRate/html/cookbook_06.png deleted file mode 100644 index 71e4fd3..0000000 Binary files a/core/@EventRate/html/cookbook_06.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_07.png b/core/@EventRate/html/cookbook_07.png deleted file mode 100644 index a6bb86d..0000000 Binary files a/core/@EventRate/html/cookbook_07.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_08.png b/core/@EventRate/html/cookbook_08.png deleted file mode 100644 index 89061cb..0000000 Binary files a/core/@EventRate/html/cookbook_08.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_09.png b/core/@EventRate/html/cookbook_09.png deleted file mode 100644 index edece87..0000000 Binary files a/core/@EventRate/html/cookbook_09.png and /dev/null differ diff --git a/core/@EventRate/html/cookbook_10.png b/core/@EventRate/html/cookbook_10.png deleted file mode 100644 index 88a9da4..0000000 Binary files a/core/@EventRate/html/cookbook_10.png and /dev/null differ diff --git a/core/@EventRate/obsolete/addfield.m b/core/@EventRate/obsolete/addfield.m new file mode 100755 index 0000000..22b442e --- /dev/null +++ b/core/@EventRate/obsolete/addfield.m @@ -0,0 +1,48 @@ +%% ADDFIELD +function obj = addfield(obj,fieldname,val) + %ADDFIELD add fields and values to object(s) + % obj = addfield(obj, fieldname, value) + % This function creates a new user defined field, and fills it with the + % included value. If fieldname exists, it will overwrite the existing + % value. + % + % Input Arguments + % obj: an EventRate object + % fieldname: a string name + % value: a value to be added for those fields. Value can be anything + % + % EventRate objects can hold user-defined fields. To access the contents, + % use EventRate/get. + % + % Example: + % % add a field called "TESTFIELD", containing the numbers 1-45 + % obj = addfield(obj,'TestField',1:45); + % + % % add a cell field called "MISHMOSH" + % obj = addfield(obj,'mishmosh',{'hello';'world'}); + % + % % see the result + % disp(obj) + % + % See also EventRate/set, EventRate/get + + % AUTHOR: Glenn Thompson + + if ischar(fieldname) + mask = strcmp(fieldname, properties(obj)); + if any(mask) + obj = obj.set(fieldname, val); + else + mask = strcmp(upper(fieldname),obj.misc_fields); + if any(mask) + obj = obj.set(fieldname, val); + else + obj.misc_fields = [obj.misc_fields, upper(fieldname)]; + obj = obj.set(upper(fieldname), val); + end + end + else + error('%s:addfield:invalidFieldname','fieldname must be a string', class(catalogObject)) + end + +end \ No newline at end of file diff --git a/core/@EventRate/obsolete/get.m b/core/@EventRate/obsolete/get.m new file mode 100755 index 0000000..b374aa0 --- /dev/null +++ b/core/@EventRate/obsolete/get.m @@ -0,0 +1,27 @@ +%% GET +function val = get(obj,prop_name) + %GET Get EventRate properties + % val = get(EventRate_object,'property_name') + % + % To see valid property names, type: + % properties(EventRate_object) + % + % If additional fields were added to EventRate using ADDFIELD, then + % values from these can be retrieved using the fieldname + % + % See also EventRate/SET, EventRate/ADDFIELD, Catalog/GET + + mask = strcmp(prop_name, properties(obj)); + if any(mask) + % eval(sprintf('val=obj.%s;',prop_name)); + val = obj.(prop_name); + else + mask = strcmp(upper(prop_name),obj.misc_fields); + if any(mask) + val = obj.misc_values{mask}; + else + warning('%s:get:unrecognizedProperty',... + 'Unrecognized property name : %s', class(obj), prop_name); + end + end +end \ No newline at end of file diff --git a/core/@EventRate/obsolete/plotold.m b/core/@EventRate/obsolete/plotold.m new file mode 100755 index 0000000..ae4490f --- /dev/null +++ b/core/@EventRate/obsolete/plotold.m @@ -0,0 +1,256 @@ + function plotold(obj, varargin) + %EventRate/plot + % Plot metrics of an EventRate object + % + % The following metrics are available: + % + % counts % number of events in each bin + % mean_rate % number of events per hour in each bin + % median_rate % reciprocal of the median time interval between events. Represented as an hourly rate. + % energy % total sum of energy in each bin + % cum_mag % total sum of energy in each bin, represented as a magnitude. + % mean_mag % mean magnitude of events in each bin + % median_mag % median magnitude of events in each bin + % min_mag % smallest magnitude in each bin + % + % erobj.plot() or plot(erobj) will produce a plot of event + % counts per unit time. The time unit is given by erobj.binsize + % days. + % + % erobj.plot('metric', list_of_metrics) will plot each metric + % requested in list_of_metrics in a separate panel. + % list_of_metrics should be a cell array of valid metric + % strings. However, it may be a string if only one metric is + % requested. + % + % erobj.plot('metric', 'counts') is equivalent to + % erobj.plot() and erobj.plot('metric', {'counts'}) + % + % erobj.plot('metric', 'mean_rate') is similar, but the + % mean_rate is always events per hour, regardless of the + % binsize. So if erobj.binsize = 1 (day), counts will be + % exactly 24 * mean_rate. + % + % erobj.plot('metric', {'counts';'cum_mag'}) will plot counts + % in one panel and the cumulative magnitude per bin in + % another panel. + % + % In general any number of metrics can be given in + % list_of_metrics. + % + % If erobj is an array of eventrate structures (e.g. one per + % etype), each is plotted on a separate figure. However the + % plotmode variable overrides this: + % + % plot(eventrate_vector, 'plotmode', 'panels') will plot them + % in separate panels on the same figure + % + % plot(eventrate_vector, 'plotmode', 'single') will plot them + % in a single panel + + p = inputParser; + p.addParamValue('metric', {'counts'}, @(c) iscell(c)||isstr(c)); + p.addParamValue('plotmode', 'figures', @isstr); + p.addParamValue('smooth', 1, @isnumeric); + p.parse(varargin{:}); + metric = p.Results.metric; + plotmode = p.Results.plotmode; + smoothbins = p.Results.smooth; % NOT DOING ANYTHING WITH THIS YET BUT COULD SMOOTH COUNTS OVER SEVERAL BINS + if ~iscell(metric) + metric = {metric}; + end + numMetrics = numel(metric); + colors = {[0.7 0.7 0] [0 0 1]}; + + % plot each etype on a separate figure, each metric as a + % subplot + + if strcmp(plotmode, 'figures') || length(obj)==1 + + for c = 1 : numel(obj) + binsize_str = Catalog.binning.binsizelabel(obj(c).binsize); + numsubplots = length(metric); + %figure(get(gcf,'Number')+1) + figure + set(gcf,'Color', [1 1 1]); + for cc = 1: numsubplots % number of metrics to plot + %eval( sprintf('data = obj(c).%s;',metric{cc} ) ); + data = obj(c).(metric{cc}); + if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) + % replace -Inf values as they mess up plots + ydata = data; % ydata is the data we will plot, but we keep data for cumulative energy etc. + if smoothbins > 1 + ydata = smooth(ydata, smoothbins); + end +% ydata(isinf(data))=NaN; +% mindata = nanmin(ydata); +% ydata(isnan(ydata))=mindata; % we replace NaNs and Infs in data with nanmin(data) in ydata + if (obj(c).binsize == obj(c).stepsize) & ( strcmp(metric{cc}, 'counts') | strcmp(metric{cc}, 'energy') | strcmp(metric{cc}, 'cum_mag') ) + if strfind(metric{cc}, 'mag') + cumdata = magnitude.eng2mag(cumsum(magnitude.mag2eng(data))); + subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, ydata, obj(c).time, cumdata, @stairs, @plot ); + set(h1, 'Color', colors{1}); + else + subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, data, obj(c).time + obj(c).binsize/2, cumsum(data), @bar, @stairs ); + %subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(obj(c).time, ydata, obj(c).time, cumsum(data), @stairs, @plot ); + set(h1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}) + set(h1, 'BarWidth', 1); + set(h1, 'LineWidth', 0.1); + end + datetick(ax(1), 'x','keeplimits'); + ylabel(ax(1), metric2label(metric{cc}, obj(c).binsize), 'Color', colors{1}) + datetick(ax(2), 'x','keeplimits'); + ylabel(ax(2),'Cumulative', 'Color', colors{2}) + %set(h1, 'Color', colors{1}); + %set(h2, 'Color', colors{2}, 'LineWidth', 2); + ylims = get(ax(1), 'YLim'); + set(ax(1), 'YColor', colors{1}, 'YLim', [0 ylims(2)]); + ylims = get(ax(2), 'YLim'); + set(ax(2), 'YColor', colors{2}, 'YLim', [0 ylims(2)]); + linkaxes(ax, 'x'); + else + + if strfind(metric{cc}, 'mag') + subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); + else + subplot(numsubplots,1,cc), bar(obj(c).time, data, 1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}, 'BarWidth', 1, 'LineWidth', 0.1); + %subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); + end + datetick('x','keeplimits'); + ylabel(metric2label(metric{cc}, obj(c).binsize)) + end + end +% axis tight; +% a=axis; +% axis([a(1) a(2) 0 a(4)]) + end + end + + %% FROM HERE ON THE PLOTMODES HAVE NOT BEEN UPDATED + elseif strcmp(plotmode, 'panels') + % Each metric on a separate figure, showing all requested + % subclasses on separate panels + for c = 1 : numel(metric) + %figure(get(gcf,'Number')+c) + figure + numsubplots = numel(obj); + + %for cc = numsubplots: -1: 1 + for cc = 1:numsubplots + ccc = numsubplots-cc+1; + if strcmp(metric{c},'energy') + %data = cumsum(magnitude.mag2eng(obj(cc).cum_mag)); + data = (magnitude.mag2eng(obj(ccc).cum_mag)); + + else + % eval( sprintf('data = obj(cc).%s;',metric{c} ) ); + data = obj(ccc).(metric{c}); + end + if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) + if smoothbins > 1 + data = smooth(data, smoothbins); + end + % where to position the axes + pos(1) = 0.1; + pos(2) = 0.1+(0.95-0.1)*(cc-1)/numsubplots; + pos(3) = 0.8; + pos(4) = (0.8*(0.95-0.1)/numsubplots); + axes('position', pos); + end + + % plot + if numel(data)>0 + bar( obj(ccc).time, data, 1, 'EdgeColor', 'none', 'FaceColor', [0 0 0] ); + % hold on; + % sdata = smooth(data, 30, 'lowess'); + % plot( obj(cc).time, sdata, 'k-', 'linewidth', 2); + + + % range and label + datetick('x','keeplimits'); + set(gca, 'XLim', [obj(ccc).snum obj(ccc).enum]); + ymax = nanmax(catmatrices(1, data)); + % ymax = min([max(sdata)*2 max(data)*1.01]); + set(gca, 'YLim', [0 ymax]); + ylabel(obj(ccc).etype); + end + + + + end + %title(metric{c}); + fprintf('metric for figure %d is %s\n', get(gcf,'Number'), metric{c}); + end + + + elseif strcmp(plotmode, 'single') + % Each metric on a separate figure, showing all requested + % subclasses on the same panel + colour = 'rgbcm'; + for c = 1 : numel(metric) + %figure(get(gcf,'Number')+c) + figure + for cc = 1: length(obj) + if strcmp(metric{c},'energy') + %data = cumsum(magnitude.mag2eng(obj(cc).cum_mag)); + data = (magnitude.mag2eng(obj(cc).cum_mag)); + else + % eval( sprintf('data = obj(cc).%s;',metric{c} ) ); + data = obj(cc).(metric{c}); + end + + if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) + + if smoothbins > 1 + data = smooth(data, smoothbins); + end + + plot( obj(cc).time, data, sprintf('-%c',colour(cc)) ); + hold on; + datetick('x','keeplimits'); + set(gca, 'XLim', [obj(cc).snum obj(cc).enum]); + %ymax = nanmax(catmatrices(1, data)); + %set(gca, 'YLim', [0 ymax]); + %ylabel(obj(cc).etype); + end + end + title(metric{c}); + end + + + elseif strcmp(plotmode, 'stacked') + % Each metric on a separate figure, showing all subclasses + % stacked on the same panel + colour = 'rgbcm'; + for c = 1 : numel(metric) + figure(get(gcf,'Number')+c) + data =[]; + for cc = 1: length(obj) + if strcmp(metric{c},'energy') + data = (magnitude.mag2eng(obj(cc).cum_mag)); + else + %eval( sprintf('data(:,cc) = obj(cc).%s;',metric{c} ) ); + %data(:,cc) = obj(cc).(metric{c}); + data = obj(cc).(metric{c}); + if findstr(metric{c}, 'mag') + disp('Warning: It is meaningless to stack magnitude data'); + data(data<0)=0; + data(isnan(data))=0; + end + end + if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) + if smoothbins > 1 + data = smooth(data, smoothbins); + end + + %bar( obj(cc).time, data, 1, 'stack' ); + area(obj(cc).time, data); + datetick('x','keeplimits'); + set(gca, 'XLim', [obj(cc).snum obj(cc).enum]); + title(metric{c}); + end + end + end + end + end + diff --git a/core/@EventRate/obsolete/set.m b/core/@EventRate/obsolete/set.m new file mode 100755 index 0000000..b6e2edc --- /dev/null +++ b/core/@EventRate/obsolete/set.m @@ -0,0 +1,63 @@ +%% SET +function obj = set(obj, varargin) + %SET Set properties for EventRate object(s) + % obj = set(obj,'property_name', val, ['property_name2', val2]) + % SET is one of the two gateway functions of an object, such as EventRate. + % Properties that are changed through SET are typechecked and otherwise + % scrutinized before being stored within the EventRate object. This + % ensures that the other EventRate methods are all retrieving valid data, + % thereby increasing the reliability of the code. + % + % Another strong advantage to using SET and GET to change and retrieve + % properties, rather than just assigning them to EventRate object directly, + % is that the underlying data structure can change and grow without + % harming the code that is written based on the EventRate object. + % + % For a list of valid property names, type: + % properties(obj) + % + % If user-defined fields were added to the EventRate object (ie, through + % addField), these fieldnames are also available through set. + % + % Examples: + % (1) Change the description property + % obj = obj.set('description','hello world'); + % + % (2) Add new a field called CLOSEST_STATION with + % % the value 'MBLG' + % obj = obj.addfield('CLOSEST_STATION','MBLG'); + % + % % change the value of the CLOSEST_STATION field + % obj = obj.set('CLOSEST_STATION','MBWH'); + % + % See also EventRate/get, EventRate/addfield + + Vidx = 1 : numel(varargin); + + while numel(Vidx) >= 2 + prop_name = upper(varargin{Vidx(1)}); + val = varargin{Vidx(2)}; + mask = strcmp(upper(prop_name),upper(properties(obj))); + if any(mask) + mc = metaclass(obj); + i = find(mask); + prop_name = mc.PropertyList(i).Name; + if isempty(mc.PropertyList(i).GetMethod) + %eval(sprintf('obj.%s=val;',prop_name)); + obj.(prop_name) = val; + else + warning('Property %s is a derived property and cannot be set',prop_name); + end + else + switch prop_name + case obj.misc_fields + mask = strcmp(prop_name,obj.misc_fields); + obj.misc_values(mask) = {val}; + otherwise + error('%s:set:unknownProperty',... + 'can''t understand property name : %s', mfilename,prop_name); + end + end + Vidx(1:2) = []; %done with those parameters, move to the next ones... + end +end \ No newline at end of file diff --git a/core/@EventRate/plot.m b/core/@EventRate/plot.m new file mode 100755 index 0000000..9f02fc2 --- /dev/null +++ b/core/@EventRate/plot.m @@ -0,0 +1,211 @@ +function plot(obj, varargin) + %EventRate/plot + % Plot metrics of an EventRate object + % + % The following metrics are available: + % + % counts % number of events in each bin + % mean_rate % number of events per hour in each bin + % median_rate % reciprocal of the median time interval between events. Represented as an hourly rate. + % energy % total sum of energy in each bin + % cum_mag % total sum of energy in each bin, represented as a magnitude. + % mean_mag % mean magnitude of events in each bin + % median_mag % median magnitude of events in each bin + % min_mag % smallest magnitude in each bin + % + % erobj.plot() or plot(erobj) will produce a plot of event + % counts per unit time. The time unit is given by erobj.binsize + % days. + % + % erobj.plot('metric', list_of_metrics) will plot each metric + % requested in list_of_metrics in a separate panel. + % list_of_metrics should be a cell array of valid metric + % strings. However, it may be a string if only one metric is + % requested. + % + % erobj.plot('metric', 'counts') is equivalent to + % erobj.plot() and erobj.plot('metric', {'counts'}) + % + % erobj.plot('metric', 'mean_rate') is similar, but the + % mean_rate is always events per hour, regardless of the + % binsize. So if erobj.binsize = 1 (day), counts will be + % exactly 24 * mean_rate. + % + % erobj.plot('metric', {'counts';'cum_mag'}) will plot counts + % in one panel and the cumulative magnitude per bin in + % another panel. + % + % In general any number of metrics can be given in + % list_of_metrics. + % + % If erobj is an array of eventrate structures (e.g. one per + % etype), each is plotted on a separate figure. However the + % plotmode variable overrides this: + % + % plot(eventrate_vector, 'plotmode', 'panels') will plot them + % in separate panels on the same figure + % + % plot(eventrate_vector, 'plotmode', 'single') will plot them + % in a single panel + + p = inputParser; + p.addParameter('metric', {'counts'}, @(c) iscell(c)||isstr(c)); + p.addParameter('plotmode', 'figures', @isstr); + p.addParameter('smooth', 1, @isnumeric); + p.parse(varargin{:}); + metric = p.Results.metric; + plotmode = p.Results.plotmode; + smoothbins = p.Results.smooth; % NOT DOING ANYTHING WITH THIS YET BUT COULD SMOOTH COUNTS OVER SEVERAL BINS + if ~iscell(metric) + metric = {metric}; + end + numMetrics = numel(metric); + colors = {[0 0.8 0] [0 0 0.8]}; + + % plot each etype on a separate figure, each metric as a + % subplot + + fh = []; + for c = 1 : numel(obj) + binsize_str = Catalog.binning.binsizelabel(obj(c).binsize); + numsubplots = numMetrics; + + switch numsubplots + case 1, fontsize = 12; + case 2, fontsize = 8; + otherwise, fontsize = 6; + end + set(0, 'defaultTextFontSize',fontsize); + + fh(c) = figure; + unique_subclasses = unique(char([obj(c).etype{:}])'); + if length(unique_subclasses)==1 + longname = Catalog.subclass2longname(unique_subclasses); + else + longname = Catalog.subclass2longname('*'); + end + set(fh(c),'Color', [1 1 1], 'Name', sprintf('%s activity beginning %s',longname, datestr(obj(c).time(1),29) ) ); + for cc = 1: numsubplots % number of metrics to plot + data = obj(c).(metric{cc}); + if numel(data)>0 & ~all(isinf(data)) & ~all(isnan(data)) + % replace -Inf values as they mess up plots + y = data; % ydata is the data we will plot, but we keep data for cumulative energy etc. + if smoothbins > 1 + y = smooth(y, smoothbins); + end + y(isinf(y))=NaN; + mindata = nanmin(y); + y(isnan(y))=mindata; % we replace NaNs and Infs in data with nanmin(data) in ydata + + labels = metric2label(metric{cc}, obj(c).binsize); + t = [ obj(c).time - obj(c).binsize/2 ]; t = [t t(end)+obj(c).binsize]; + y = [y y(end)]; + clear ax h1 h2 + + % We will only use plotyy to plot 2 axis when we have a metric that can be cumulated + % So the metric must be counts, energy or cumulative magnitude + % and the bins must be non-overlapping + if (obj(c).binsize == obj(c).stepsize) & ( strcmp(metric{cc}, 'counts') | strcmp(metric{cc}, 'energy') | strcmp(metric{cc}, 'cum_mag') ) + + cumy = cumsum(data); cumy = [cumy cumy(end)]; + subplot(numsubplots,1,cc), [ax, h1, h2] = plotyy(t, y, t, cumy, @stairs, @stairs ); + + % with plotyy the right hand label + % was off the page, so fix this + apos=get(ax(1),'Position'); + apos(3)=0.75; + set(ax(1),'Position',apos); + set(ax(2),'Position',apos); + + %% graph 1 + set(h1, 'LineWidth', 3, 'Color', colors{1}); + datetick(ax(1), 'x','keeplimits'); + ylabel(ax(1), labels{1}, 'Color', colors{1}, 'FontSize', fontsize) + ylims = get(ax(1), 'YLim'); + set(ax(1), 'YColor', colors{1}, 'YLim', [0 max([ylims(2) 1])], 'XLim', [t(1) t(end)]); + xticklabels = get(ax(1), 'XTickLabel'); + set(ax(1), 'XTickLabel', xticklabels, 'FontSize', fontsize); + yticklabels = get(ax(1), 'YTickLabel'); + set(ax(1), 'YTickLabel', yticklabels, 'FontSize', fontsize); + + %% graph 2 + set(h2, 'LineWidth', 3, 'Color', colors{2}); + datetick(ax(2), 'x','keeplimits'); + ylabel(ax(2),labels{2}, 'Color', colors{2}, 'FontSize', fontsize) + ylims = get(ax(2), 'YLim'); + set(ax(2), 'YColor', colors{2}, 'YLim', [0 max([ylims(2) 1])], 'XLim', [t(1) t(end)]); + xticklabels = get(ax(2), 'XTickLabel'); + set(ax(2), 'XTickLabel', xticklabels, 'FontSize', fontsize); + yticklabels = get(ax(2), 'YTickLabel'); + set(ax(2), 'YTickLabel', yticklabels, 'FontSize', fontsize); + + linkaxes(ax, 'x'); + + else + + % So here we either have overlapping bins, or a metric + % that is not counts, energy or cum_mag + % In all these cases, we only have 1 graph per subplot + + + %% graph 1 (there is only 1 in this case) + if strfind(metric{cc}, 'mag') + %subplot(numsubplots,1,cc), stairs(obj(c).time, data, 'Color', colors{1}); + subplot(numsubplots,1,cc), ax(1)=stairs(t, y, 'Color', colors{1}); + else + %subplot(numsubplots,1,cc), bar(obj(c).time, data, 1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}, 'BarWidth', 1, 'LineWidth', 0.1); + subplot(numsubplots,1,cc), ax(1)=bar(t, y, 1, 'FaceColor', colors{1}, 'EdgeColor', colors{1}, 'BarWidth', 1, 'LineWidth', 0.1); + %subplot(numsubplots,1,cc), stairs(obj(c).time, ydata, 'Color', colors{1}); + end + + %ylabel(metric2label(metric{cc}, obj(c).binsize)) + datetick('x','keeplimits'); + title(labels{1}, 'FontSize', fontsize) + ylims = get(gca, 'YLim'); + set(gca, 'YLim', [0 max([ylims(2) 1])], 'XLim', [t(1) t(end)]); + xticklabels = get(gca, 'XTickLabels'); + set(gca,'XTickLabels', xticklabels, 'FontSize', fontsize); + yticklabels = get(gca, 'YTickLabels'); + set(gca,'YTickLabels', yticklabels, 'FontSize', fontsize); + end + end + + end + + % fontsize for labels - this does not appear to work + set(findall(gcf,'-property','FontSize'),'FontSize',fontsize) + + % fix yticklabels + axish = get(gcf,'Children'); + for axisnum=1:numel(axish) + if strcmp('matlab.graphics.axis.Axes',class(axish(1))) + set(axish(axisnum),'YTickLabel',get(axish(axisnum),'YTick')); + end + end + + end +end + +function labels = metric2label(metric, binsize) + % label = metric2label(metric, binsize) + labels={}; + blabel = Catalog.binning.binsizelabel(binsize); + time_unit = blabel(4:end); + if strcmp(metric, 'counts') + labels{1} = sprintf('# Events %s',blabel); + labels{2} = 'Cumulative # events'; + elseif strcmp(metric, 'energy') + labels{1} = sprintf('Energy %s (J)',blabel); + labels{2} = 'Cumulative energy (J)'; + elseif strcmp(metric, 'mean_rate') + labels{1} = sprintf('Mean # events per hour\n(binsize %s)', time_unit); + elseif strcmp(metric, 'median_rate') + labels{1} = sprintf('Median # events per hour\n(binsize %s)', time_unit); + elseif strcmp(metric, 'cum_mag') + labels{1} = sprintf('Cumulative Magnitude per hour\n(binsize %s)', time_unit);; + elseif strcmp(metric, 'mean_mag') + labels{1} = sprintf('Mean Magnitude per hour\n(binsize %s)', time_unit); + elseif strcmp(metric, 'median_mag') + labels{1} = sprintf('Median Magnitude per hour\n(binsize %s)', time_unit); + end +end \ No newline at end of file diff --git a/core/@EventRate/pythonplot.m b/core/@EventRate/pythonplot.m new file mode 100755 index 0000000..3f20c5a --- /dev/null +++ b/core/@EventRate/pythonplot.m @@ -0,0 +1,4 @@ +%% PYTHONPLOT +function pythonplot(obj) + obj.plot('metric', {'counts';'cum_mag'}); +end \ No newline at end of file diff --git a/core/@Position/Position.m b/core/@Position/Position.m new file mode 100644 index 0000000..26d7ee7 --- /dev/null +++ b/core/@Position/Position.m @@ -0,0 +1,56 @@ +%POSIITON the blueprint for Position objects in GISMO +classdef Location + + properties + latitude; + longitude; + elevation; + end + + properties(Dependent) % need get methods + + end + + methods + + function lobj = Position(varargin) + %Position.Position constructor for Position object + % lobj = Position(latitude, longitude, elevation) + + % Blank constructor + if nargin==0 + return + end + + % Parse required, optional and param-value pair arguments, + % set default values, and add validation conditions + p = inputParser; + p.addOptional('latitude', [], @isnumeric) % positional + p.addOptional('longitude', [], @isnumeric) + p.addOptional('elevation', [], @isnumeric) % positional + p.parse(varargin{:}); + fields = fieldnames(p.Results); + for i=1:length(fields) + field=fields{i}; + val = p.Results.(field); + eval(sprintf('lobj.%s = val;',field)); + end + + end + +% % Get methods +% function val = get.duration(obj) +% val = 86400 * (obj.offtime - obj.ontime); +% end + + % Prototypes + + end +%% --------------------------------------------------- + methods (Access=protected, Hidden=true) + end + + methods(Static) + end + +end diff --git a/core/@Sfile/Sfile.m b/core/@Sfile/Sfile.m old mode 100644 new mode 100755 diff --git a/core/@Site/Site.m b/core/@Site/Site.m new file mode 100644 index 0000000..641cc0f --- /dev/null +++ b/core/@Site/Site.m @@ -0,0 +1,63 @@ +%SITE the blueprint for Site objects in GISMO +classdef Site + + properties + network; + station; + position; + ondate; + offdate; + end + + properties(Dependent) % need get methods + + end + + methods + + function sobj = Site(varargin) + %Site.Site constructor for Site object + % sobj = Site(ChannelTag, Location) + + % Blank constructor + if nargin==0 + return + end + + % Parse required, optional and param-value pair arguments, + % set default values, and add validation conditions + p = inputParser; +% p.addRequired('nscl', @(c) strcmp(class(c),'ChannelTag') ) % positional + p.addRequired('network', @ischar ) % positional + p.addRequired('station', @ischar ) % positional + p.addRequired('position', @(p) strcmp(class(p),'Position')); +% p.addOptional('ondate', [], @isnumeric) +% p.addOptional('offdate', [], @isnumeric) + p.addOptional('ondate', [], @(t1) t1>datenum(1900,1,1) & t1datenum(1900,1,1)) + p.parse(varargin{:}); + fields = fieldnames(p.Results); + for i=1:length(fields) + field=fields{i}; + val = p.Results.(field); + eval(sprintf('sobj.%s = val;',field)); + end + + end + +% % Get methods +% function val = get.duration(obj) +% val = 86400 * (obj.offtime - obj.ontime); +% end + + % Prototypes + + end +%% --------------------------------------------------- + methods (Access=protected, Hidden=true) + end + + methods(Static) + end + +end diff --git a/core/@correlation/DEP_xcorr.m b/core/@correlation/DEP_xcorr.m old mode 100644 new mode 100755 diff --git a/core/@correlation/adjusttrig.m b/core/@correlation/adjusttrig.m old mode 100644 new mode 100755 diff --git a/core/@correlation/agc.m b/core/@correlation/agc.m old mode 100644 new mode 100755 diff --git a/core/@correlation/align.m b/core/@correlation/align.m old mode 100644 new mode 100755 diff --git a/core/@correlation/butter.m b/core/@correlation/butter.m old mode 100644 new mode 100755 diff --git a/core/@correlation/cat.m b/core/@correlation/cat.m old mode 100644 new mode 100755 diff --git a/core/@correlation/check.m b/core/@correlation/check.m old mode 100644 new mode 100755 diff --git a/core/@correlation/cluster.m b/core/@correlation/cluster.m old mode 100644 new mode 100755 diff --git a/core/@correlation/colormap.m b/core/@correlation/colormap.m old mode 100644 new mode 100755 diff --git a/core/@correlation/conv.m b/core/@correlation/conv.m old mode 100644 new mode 100755 diff --git a/core/@correlation/correlation.m b/core/@correlation/correlation.m old mode 100644 new mode 100755 diff --git a/core/@correlation/crop.m b/core/@correlation/crop.m old mode 100644 new mode 100755 diff --git a/core/@correlation/deconv.m b/core/@correlation/deconv.m old mode 100644 new mode 100755 diff --git a/core/@correlation/demean.m b/core/@correlation/demean.m old mode 100644 new mode 100755 diff --git a/core/@correlation/detrend.m b/core/@correlation/detrend.m old mode 100644 new mode 100755 diff --git a/core/@correlation/diff.m b/core/@correlation/diff.m old mode 100644 new mode 100755 diff --git a/core/@correlation/display.m b/core/@correlation/display.m old mode 100644 new mode 100755 diff --git a/core/@correlation/find.m b/core/@correlation/find.m old mode 100644 new mode 100755 diff --git a/core/@correlation/get.m b/core/@correlation/get.m old mode 100644 new mode 100755 diff --git a/core/@correlation/getclusterstat.m b/core/@correlation/getclusterstat.m old mode 100644 new mode 100755 diff --git a/core/@correlation/getstat.m b/core/@correlation/getstat.m old mode 100644 new mode 100755 diff --git a/core/@correlation/hilbert.m b/core/@correlation/hilbert.m old mode 100644 new mode 100755 diff --git a/core/@correlation/integrate.m b/core/@correlation/integrate.m old mode 100644 new mode 100755 diff --git a/core/@correlation/interferogram.m b/core/@correlation/interferogram.m old mode 100644 new mode 100755 diff --git a/core/@correlation/linkage.m b/core/@correlation/linkage.m old mode 100644 new mode 100755 diff --git a/core/@correlation/match.m b/core/@correlation/match.m old mode 100644 new mode 100755 diff --git a/core/@correlation/minus.m b/core/@correlation/minus.m old mode 100644 new mode 100755 diff --git a/core/@correlation/norm.m b/core/@correlation/norm.m old mode 100644 new mode 100755 diff --git a/core/@correlation/plot.m b/core/@correlation/plot.m old mode 100644 new mode 100755 index 71dc257..6f7df3b --- a/core/@correlation/plot.m +++ b/core/@correlation/plot.m @@ -162,7 +162,7 @@ function plot(c,varargin) elseif strncmpi(plottype,'DEN',3) dendrogramplot(c); elseif strncmpi(plottype,'EVE',3) % ord field has been co-opted - admin.deprecated('event plot','occurence plot') + admin.deprecated('event plot','occurrence plot') if length(ord) > 1 ord = 4; end diff --git a/core/@correlation/private/DEPRICATED_xcorr1xr.m b/core/@correlation/private/DEPRICATED_xcorr1xr.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/DEPRICATED_xcorrcxc.m b/core/@correlation/private/DEPRICATED_xcorrcxc.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/corrplot.m b/core/@correlation/private/corrplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/dendrogramplot.m b/core/@correlation/private/dendrogramplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/eventplot.m b/core/@correlation/private/eventplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/getval.m b/core/@correlation/private/getval.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/lagplot.m b/core/@correlation/private/lagplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/makesynthwaves.m b/core/@correlation/private/makesynthwaves.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/occurrenceplot.m b/core/@correlation/private/occurrenceplot.m old mode 100644 new mode 100755 index f5758f2..9286034 --- a/core/@correlation/private/occurrenceplot.m +++ b/core/@correlation/private/occurrenceplot.m @@ -117,7 +117,7 @@ function doplotrow(c1,n,nmax,bins,nclust) % DO STACK PLOT -subplot('Position',[.5 .99-.094*n .47 .09]); +thisax = subplot('Position',[.5 .99-.094*n .47 .09]); c1 = subset(c1,include); Ts = 86400*(get(c1.W,'START')-c1.trig); Te = 86400*(get(c1.W,'END')-c1.trig); @@ -126,9 +126,9 @@ function doplotrow(c1,n,nmax,bins,nclust) c1 = crop(c1,mean(Ts),mean(Te)); w = get(c1,'WAVEFORMS'); xlim([0 get(w(end),'DURATION_EPOCH')]); -plot(w,'Color',[.7 .7 .7],'LineWidth',.5); +plot(w,'Color',[.7 .7 .7],'LineWidth',.5,'axeshandle',thisax); hold on; -plot(w(end),'Color','k','LineWidth',1); +plot(w(end),'Color','k','LineWidth',1,'axeshandle',thisax); xlim([0 get(w(end),'DURATION_EPOCH')]); ylabel(' '); set(gca,'YTickLabel',[]); title(''); diff --git a/core/@correlation/private/overlayplot.m b/core/@correlation/private/overlayplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/sampleplot.m b/core/@correlation/private/sampleplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/shadedplot.m b/core/@correlation/private/shadedplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/statplot.m b/core/@correlation/private/statplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/wiggleinterferogram.m b/core/@correlation/private/wiggleinterferogram.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/wiggleplot.m b/core/@correlation/private/wiggleplot.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/xcorr1x1.m b/core/@correlation/private/xcorr1x1.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/xcorr1xr.m b/core/@correlation/private/xcorr1xr.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/xcorr1xr_orig.m b/core/@correlation/private/xcorr1xr_orig.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/xcorrdec.m b/core/@correlation/private/xcorrdec.m old mode 100644 new mode 100755 diff --git a/core/@correlation/private/xcorrrow.m b/core/@correlation/private/xcorrrow.m old mode 100644 new mode 100755 diff --git a/core/@correlation/set.m b/core/@correlation/set.m old mode 100644 new mode 100755 diff --git a/core/@correlation/sign.m b/core/@correlation/sign.m old mode 100644 new mode 100755 diff --git a/core/@correlation/sort.m b/core/@correlation/sort.m old mode 100644 new mode 100755 diff --git a/core/@correlation/stack.m b/core/@correlation/stack.m old mode 100644 new mode 100755 diff --git a/core/@correlation/strip.m b/core/@correlation/strip.m old mode 100644 new mode 100755 diff --git a/core/@correlation/subset.m b/core/@correlation/subset.m old mode 100644 new mode 100755 diff --git a/core/@correlation/taper.m b/core/@correlation/taper.m old mode 100644 new mode 100755 diff --git a/core/@correlation/verify.m b/core/@correlation/verify.m old mode 100644 new mode 100755 diff --git a/core/@correlation/waveform.m b/core/@correlation/waveform.m old mode 100644 new mode 100755 diff --git a/core/@correlation/writedb.m b/core/@correlation/writedb.m old mode 100644 new mode 100755 diff --git a/core/@correlation/xcorr.m b/core/@correlation/xcorr.m old mode 100644 new mode 100755 diff --git a/core/@datasource/datasource.m b/core/@datasource/datasource.m old mode 100644 new mode 100755 diff --git a/core/@datasource/disp.m b/core/@datasource/disp.m old mode 100644 new mode 100755 diff --git a/core/@datasource/display.m b/core/@datasource/display.m old mode 100644 new mode 100755 diff --git a/core/@datasource/get.m b/core/@datasource/get.m old mode 100644 new mode 100755 diff --git a/core/@datasource/getfilename.m b/core/@datasource/getfilename.m old mode 100644 new mode 100755 diff --git a/core/@datasource/isunassigned.m b/core/@datasource/isunassigned.m old mode 100644 new mode 100755 diff --git a/core/@datasource/load_file.m b/core/@datasource/load_file.m old mode 100644 new mode 100755 diff --git a/core/@datasource/load_objects_from_file.m b/core/@datasource/load_objects_from_file.m old mode 100644 new mode 100755 diff --git a/core/@datasource/loadobj.m b/core/@datasource/loadobj.m old mode 100644 new mode 100755 diff --git a/core/@datasource/private/void_interpreter.m b/core/@datasource/private/void_interpreter.m old mode 100644 new mode 100755 diff --git a/core/@datasource/query_file.m b/core/@datasource/query_file.m old mode 100644 new mode 100755 diff --git a/core/@datasource/requires_file.m b/core/@datasource/requires_file.m old mode 100644 new mode 100755 diff --git a/core/@datasource/setfile.m b/core/@datasource/setfile.m old mode 100644 new mode 100755 diff --git a/core/@datasource/setinterpreter.m b/core/@datasource/setinterpreter.m old mode 100644 new mode 100755 diff --git a/core/@datasource/subdivide_files_by_date.m b/core/@datasource/subdivide_files_by_date.m old mode 100644 new mode 100755 diff --git a/core/@drumplot/drumplot.m b/core/@drumplot/drumplot.m old mode 100644 new mode 100755 index 0329c8c..13f5702 --- a/core/@drumplot/drumplot.m +++ b/core/@drumplot/drumplot.m @@ -6,6 +6,8 @@ properties wave = waveform(); catalog = Catalog(); + detections = Detection(); + arrivals = Arrival(); mpl = 10; % 10 Minutes per line trace_color = [0 0 0]; % black event_color = [1 0 0]; % red @@ -79,6 +81,8 @@ p.addOptional('wave', obj.wave) p.addParameter('mpl', obj.mpl, @(i) floor(i)==i) p.addParameter('catalog', obj.catalog) + p.addParameter('detections', obj.detections) + p.addParameter('arrivals', obj.arrivals) p.addParameter('trace_color', obj.trace_color); p.addParameter('event_color', obj.event_color); p.addParameter('ytick', obj.ytick, @isnumeric); @@ -100,6 +104,14 @@ if ~isa(obj.catalog, 'Catalog') | numel(obj.catalog)~=1 error('Input catalog must be a single Catalog object') end + + if ~isa(obj.detections, 'Detection') | numel(obj.detections)~=1 + error('Input detections must be a single Detection object') + end + + if ~isa(obj.arrivals, 'Arrival') | numel(obj.arrivals)~=1 + error('Input arrivals must be a single Arrival object') + end end % Prototypes diff --git a/core/@drumplot/get.m b/core/@drumplot/get.m old mode 100644 new mode 100755 diff --git a/core/@drumplot/html/cookbook.html b/core/@drumplot/html/cookbook.html deleted file mode 100644 index e24db29..0000000 --- a/core/@drumplot/html/cookbook.html +++ /dev/null @@ -1,270 +0,0 @@ - - - - - drumplot cookbook

drumplot cookbook

In this example you will learn how to create a waveform object by loading a SAC file, extract 1 hour of data from that waveform object, and make a helical drum recorder plot using the drumplot class.

Another feature of drumplot is that it allows detected events, from a Catalog object, to be superimposed on the helicorder plot. In the example below this Catalog is generated by running an STA/LTA detection algorithm on the extracted 1-hour waveform object.

Contents

Load 1 day of data for REF.EHZ on 2009.03.22

% here we can either use scnlobject or ChannelTag to describe the
-% net-sta-loc-chan we want. waveform understands both.
-ctag = ChannelTag('AV.REF.-.EHZ')
-
-% starttime in MATLAB datenum format
-snum = datenum(2009,3,22);
-
-% endtime (1 day after start)
-enum=snum+1;
-
-% create the datasource object - in this case it is a SAC file
-ds = datasource('sac', 'SACDATA/REF.EHZ.2009-03-22T00:00:00.000000Z.sac');
-
-% create the waveform object with this datasource, ChannelTag, starttime
-% and endtime
-w=waveform(ds, ctag, snum, enum);
-
-ctag = 
-
-<a href="matlab:help ChannelTag">ChannelTag</a> with network.station.location.channel:
-   network: 'AV'
-   station: 'REF'
-  location: '-'
-   channel: 'EHZ'
-

fill gaps, detrend, band pass filter

% in case there are gaps in the time series (marked by NaN) we can interpolate a
-% meaningful value
-w = fillgaps(w, 'interp');
-
-% detrend the time series - this removes linear drift
-w = detrend(w);
-
-% create a Butterworth bandpass filter from 0.5 to 15 Hz, 2 poles
-fobj = filterobject('b', [0.5 15], 2);
-
-% apply the filter in both directions (acausal) - this is a zero phase
-% filter which is helpful because it doesn't disperse different frequency
-% components. the caveat is that it can spread energy so arrivals may appear
-% slighter earlier than they actually are
-w = filtfilt(fobj, w);
-

plot the waveform object

figure
-plot(w)
-

extract the first 1 hour of data and plot with 5 minutes per line

starttime = get(w,'start');
-w2=extract(w, 'time', starttime, starttime + 1/24 )
-
-% make a drumplot object. mpl means minutes per line and is set here to 5.
-h2 = drumplot(w2, 'mpl', 5);
-
-% plot the drumplot object - many events are visible, this is an earthquake
-% swarm
-plot(h2)
-
 
-w2 =
- 
- ChannelTag: AV.REF..EHZ       [network.station.location.channel]
-      start: 2009-03-22 00:00:00.000
-             duration(01:00:00.000)
-       data: 360000 samples
-       freq: 100.0000   Hz
-      units: Counts
-    history: [3 items], last modification: 18-May-2016 15:25:36
-    With misc fields...
-    * SCALE: 1
-    * NZYEAR: 2009
-    * NZJDAY: 81
-    * NZHOUR: 0
-    * NZMIN: 0
-    * NZSEC: 0
-    * NZMSEC: 0
-    * IFTYPE: 1
-    * IZTYPE: 9
-    * LPSPOL: 0
-    * LOVROK: 1
-    * LCALDA: 1
-    * KEVNM: -12345  -12345
-

Run an STA/LTA detector on the waveform object to detect events

Compute STA/LTA ratio. In this example, trigger "ON" when STA/LTA exceeds 3, trigger "OFF" when this drops back below 1.5. If the trigger was on for at least 2 seconds, declare it as an event. All the events get saved into a Catalog object.

% set the STA/LTA detector
-sta_seconds = 0.7; % STA time window 0.7 seconds
-lta_seconds = 7.0; % LTA time window 7 seconds
-thresh_on = 3; % Event triggers "ON" with STA/LTA ratio exceeds 3
-thresh_off = 1.5; % Event triggers "OFF" when STA/LTA ratio drops below 1.5
-minimum_event_duration_seconds = 2.0; % Trigger must be on at least 2 secs
-pre_trigger_seconds = 0; % Do not pad before trigger
-post_trigger_seconds = 0; % Do not pad after trigger
-event_detection_params = [sta_seconds lta_seconds thresh_on thresh_off ...
-    minimum_event_duration_seconds];
-
-% run the STA/LTA detector. lta_mode = 'frozen' means the LTA stops
-% updating when trigger is "ON".
-[cobj,sta,lta,sta_to_lta] = Detection.sta_lta(w2, 'edp', event_detection_params, ...
-    'lta_mode', 'frozen');
-
-% not sure what this is for
-set(gca, 'XLim', [44*60 48*60])
-
Event 1: 22-Mar-2009 00:00:47 to 22-Mar-2009 00:00:58
-Event 2: 22-Mar-2009 00:07:05 to 22-Mar-2009 00:07:08
-Event 3: 22-Mar-2009 00:08:06 to 22-Mar-2009 00:08:08
-Event 4: 22-Mar-2009 00:08:23 to 22-Mar-2009 00:08:28
-Event 5: 22-Mar-2009 00:09:11 to 22-Mar-2009 00:09:23
-Event 6: 22-Mar-2009 00:09:32 to 22-Mar-2009 00:09:38
-Event 7: 22-Mar-2009 00:09:47 to 22-Mar-2009 00:09:57
-Event 8: 22-Mar-2009 00:13:46 to 22-Mar-2009 00:13:51
-Event 9: 22-Mar-2009 00:15:34 to 22-Mar-2009 00:15:44
-Event 10: 22-Mar-2009 00:17:15 to 22-Mar-2009 00:17:24
-Got 46 events
-

Plot detected events on top of the continuous drumplot

h3 = drumplot(w2, 'mpl', 5, 'catalog', cobj);
-plot(h3)
-
\ No newline at end of file diff --git a/core/@drumplot/html/cookbook.png b/core/@drumplot/html/cookbook.png deleted file mode 100644 index 12e52ee..0000000 Binary files a/core/@drumplot/html/cookbook.png and /dev/null differ diff --git a/core/@drumplot/html/cookbook_01.png b/core/@drumplot/html/cookbook_01.png deleted file mode 100644 index 797d766..0000000 Binary files a/core/@drumplot/html/cookbook_01.png and /dev/null differ diff --git a/core/@drumplot/html/cookbook_02.png b/core/@drumplot/html/cookbook_02.png deleted file mode 100644 index cb501b5..0000000 Binary files a/core/@drumplot/html/cookbook_02.png and /dev/null differ diff --git a/core/@drumplot/html/cookbook_03.png b/core/@drumplot/html/cookbook_03.png deleted file mode 100644 index 3d6d01d..0000000 Binary files a/core/@drumplot/html/cookbook_03.png and /dev/null differ diff --git a/core/@drumplot/html/cookbook_04.png b/core/@drumplot/html/cookbook_04.png deleted file mode 100644 index fa4dfe6..0000000 Binary files a/core/@drumplot/html/cookbook_04.png and /dev/null differ diff --git a/core/@drumplot/plot.m b/core/@drumplot/plot.m old mode 100644 new mode 100755 index c9ecc34..d95a5d5 --- a/core/@drumplot/plot.m +++ b/core/@drumplot/plot.m @@ -1,18 +1,17 @@ -function varargout = plot(h) - -%PLOT: Drumplot plotting function. A drumplot object (h) contains no +function fh = plot(drumplotobj) +%PLOT: Drumplot plotting function. A drumplot object contains no % graphical elements, but is instead a blueprint for how the drumplot -% figure should be assembled. Information in h includes waveform data, +% figure should be assembled. Information in drumplotobj includes waveform data, % number of minutes per line, color scheme, multi-waveform display type, -% and a Catalog object to highlight over the drumplot. Calling plot(h) will -% assemble the drumplot figure based the information in h. Plot returns +% and a Catalog object to highlight over the drumplot. Calling plot(drumplotobj) will +% assemble the drumplot figure based the information in drumplotobj. Plot returns % the figure handle if an output argument is provided, otherwise, there is % no output. % -%USAGE: plot(h) --> Create drumplot figure from drumplot object -% fh = plot(h) --> Also return drumplot figure handle +%USAGE: plot(drumplotobj) --> Create drumplot figure from drumplot object +% fh = plot(drumplotobj) --> Also return drumplot figure handle % -%INPUTS: h - drumplot object +%INPUTS: drumplotobj - drumplot object % %OUTPUTS: fh - figure handle of drumplot % @@ -34,9 +33,9 @@ % near the middle of line 14 (2 lines above 16). Note that for any % waveform in a drumplot, B.scale is consistant throughout (no different % scaling on different lines for the same waveform). B.scale is multiplied -% by h.scale, which by default is equal to 1. B.scale cannot be accessed +% by drumplotobj.scale, which by default is equal to 1. B.scale cannot be accessed % directly, but it can be increased or decreased indirectly by setting -% h.scale to a different value: h = set(h,'scale',2); will double the +% drumplotobj.scale to a different value: h = set(h,'scale',2); will double the % displayed amplitude assuming there is only one waveform in drumplot. % B.lines_per_waveform --> Number of drumplot lines occupied by a single waveform % B.number_of_lines --> Total number of drumplot lines @@ -45,36 +44,73 @@ % B.line_waveform_events --> 1 waveform object per line set to NaN between % events from Catalog %% INITIALIZE DRUMPLOT FIGURE - B.fh = figure('Name','Drumplot'); % New drumplot figure handle + B.fh = figure('Name','Drumplot');% New drumplot figure handle + fh = B.fh; B.ax = axes('position',[.10 .07 .83 .86]); % New drumplot axes handle title(B.ax,'Plotting Drumplot...','FontSize',12) - %refresh(B.fh) - refresh(gcf) - pause(0.1) +% %refresh(B.fh) +% refresh(gcf) +drawnow +% pause(0.1) %% INITIALIZE BUILDER STRUCTURE FIELDS % pad waveform object - [snum enum] = gettimerange(h.wave); - snum = floorminute(snum+1/86400, h.mpl); - enum = ceilminute(enum-1/86400, h.mpl); + [snum enum] = gettimerange(drumplotobj.wave); + snum = floorminute(snum+1/86400, drumplotobj.mpl); + enum = ceilminute(enum-1/86400, drumplotobj.mpl); % find start and end times for each line - B.number_of_lines = 1440*(enum-snum)/h.mpl; + B.number_of_lines = 1440*(enum-snum)/drumplotobj.mpl; days_per_line = (enum-snum)/B.number_of_lines; B.starttimes=snum:days_per_line:enum-days_per_line; B.endtimes=snum+days_per_line:days_per_line:enum; - % split h.wave into one waveform per line for continuous data - B.line_waveform_continuous = extract(h.wave, 'time', B.starttimes, B.endtimes); + % split drumplotobj.wave into one waveform per line for continuous data + B.line_waveform_continuous = extract(drumplotobj.wave, 'time', B.starttimes, B.endtimes); % if there are catalog events, add waveform objects, return one waveform per line for event data B.line_waveform_events = []; - if h.catalog.numberOfEvents > 0 - if isempty(h.catalog.waveforms) - h.catalog = h.catalog.addwaveforms(h.wave); + w = []; + if drumplotobj.catalog.numberOfEvents > 0 + + if isempty(drumplotobj.catalog.waveforms) + drumplotobj.catalog = drumplotobj.catalog.addwaveforms(drumplotobj.wave); + end + w = [drumplotobj.catalog.waveforms{1,:}]; + end + + if numel(drumplotobj.arrivals.time) > 0 + pretrigsecs = 1; posttrigsecs = 1; + if isempty(drumplotobj.arrivals.waveforms) + drumplotobj.arrivals = drumplotobj.arrivals.addwaveforms(drumplotobj.wave, pretrigsecs, posttrigsecs); + end + w = [drumplotobj.arrivals.waveforms{1,:}]; + end + + if drumplotobj.detections.numel() > 0 + temp_cobj = associate(drumplotobj.detections, 0.01); + + + if isempty(temp_cobj.waveforms) + pretrigsecs = 1; posttrigsecs = 1; + temp_cobj = temp_cobj.addwaveforms(drumplotobj.wave, pretrigsecs, posttrigsecs); + end + w = [temp_cobj.waveforms{1,:}]; + end +w(1) +w(2) + + if ~isempty(w) + % go through w and check the channeltag is same as drumplotobj.wave + mainctag = get(drumplotobj.wave,'ChannelTag'); + w2=[]; + for wavnum=1:numel(w) + thisctag = get(w(wavnum),'ChannelTag'); + if strcmp(thisctag.string(), mainctag.string()) + w2 = [w2 w(wavnum)]; + end end - w = [h.catalog.waveforms{1,:}]; - w = combine(w); + w = combine(w2); w = pad(w, snum, enum, NaN); B.line_waveform_events = extract(w, 'time', B.starttimes, B.endtimes); clear w @@ -83,47 +119,47 @@ % now we just need to plot each line with an appropriate offset % scaling - y = get(h.wave, 'data'); + y = get(drumplotobj.wave, 'data'); max_amplitude = (nanmax(y)-nanmin(y))/2; - B.scale=h.scale/(B.number_of_lines*max_amplitude); + B.scale=drumplotobj.scale/(B.number_of_lines*max_amplitude); %% PLOT DRUMPLOT TRACES WITH EVENT OVERLAY for n = 1:B.number_of_lines % Glenn 20160513: For cookbook examples I had to add 1 here. Not sure why. Doesn't work in other cases B.line_offset(n) = (B.number_of_lines-n+0.5)/B.number_of_lines; % Trace offset from bottom B.line_waveform_continuous(n) = B.line_waveform_continuous(n) * B.scale + B.line_offset(n); - plot(B.line_waveform_continuous(n), 'color', h.trace_color, 'xunit', 'minutes', 'axeshandle', B.ax); + plot(B.line_waveform_continuous(n), 'color', drumplotobj.trace_color, 'xunit', 'minutes', 'axeshandle', B.ax); hold on; if ~isempty(B.line_waveform_events) B.line_waveform_events(n) = B.line_waveform_events(n) * B.scale + B.line_offset(n); - plot(B.line_waveform_events(n), 'color', h.event_color, 'xunit', 'minutes', 'axeshandle', B.ax); + plot(B.line_waveform_events(n), 'color', drumplotobj.event_color, 'xunit', 'minutes', 'axeshandle', B.ax); end end % Finished plotting all drumplot trace data %% ADD FINISHING TOUCHES TO DRUMPLOT FIGURE - add_title(h,B); - add_y_ticks(h,B); + add_title(drumplotobj,B); + add_y_ticks(drumplotobj,B); set(B.ax,'XGrid','on') - xlim([0 h.mpl]) + xlim([0 drumplotobj.mpl]) set(B.ax, 'YLim', [0 1]); - %% OUTPUT - if nargout == 0 - elseif nargout == 1 - varargout(1) = {B.fh}; - else - warning('DRUMPLOT/PLOT Too many outputs') - end +% %% OUTPUT +% if nargout == 0 +% elseif nargout == 1 +% B(1) = {B.fh}; +% else +% warning('DRUMPLOT/PLOT Too many outputs') +% end end %% ADD TITLE TO DRUMPLOT FIGURE -function add_title(h,B) - if isempty(h.wave) +function add_title(drumplotobj,B) + if isempty(drumplotobj.wave) title(B.ax,'No Data Selected','FontSize',12); else scn_str = []; - sta = get(h.wave,'station'); - cha = get(h.wave,'channel'); - net = get(h.wave,'network'); + sta = get(drumplotobj.wave,'station'); + cha = get(drumplotobj.wave,'channel'); + net = get(drumplotobj.wave,'network'); scn_str = [net,'.',sta,'.',cha]; h_title = sprintf('%s:\n%s to %s',scn_str, datestr(B.starttimes(1),31), datestr(B.endtimes(end),31) ); title(B.ax,h_title); @@ -131,9 +167,9 @@ function add_title(h,B) end %% ADD Y-AXES TICKS AND LABELS -function add_y_ticks(h,B) +function add_y_ticks(drumplotobj,B) - if ~isempty(h.wave) + if ~isempty(drumplotobj.wave) ylabel(B.ax,'') tick_positions = B.line_offset; tick_labels = datestr(B.starttimes,15); diff --git a/core/@drumplot/set.m b/core/@drumplot/set.m old mode 100644 new mode 100755 diff --git a/core/@filterobject/disp.m b/core/@filterobject/disp.m old mode 100644 new mode 100755 diff --git a/core/@filterobject/display.m b/core/@filterobject/display.m old mode 100644 new mode 100755 diff --git a/core/@filterobject/filt.m b/core/@filterobject/filt.m old mode 100644 new mode 100755 diff --git a/core/@filterobject/filterobject.m b/core/@filterobject/filterobject.m old mode 100644 new mode 100755 diff --git a/core/@filterobject/filtfilt.m b/core/@filterobject/filtfilt.m old mode 100644 new mode 100755 index e89d871..a1bd535 --- a/core/@filterobject/filtfilt.m +++ b/core/@filterobject/filtfilt.m @@ -39,13 +39,20 @@ 'will cause filtfilt to return an answer of all NaN. To prevent '... 'this, use waveform/fillgaps to replace NaN with an appropriate'... ' value. \nSee help waveform/fillgaps.']); + return end for n = 1 : numel(w); - WN = f.cutoff / get(w(n),'NYQ'); %only one filter is assumed! - [b, a] = getButter(f,WN); - w(n) = set(w(n),'data', filtfilt(b, a, double(w(n))) ); + fnyq = get(w(n),'NYQ'); + if fnyq>0 + WN = f.cutoff / fnyq; %only one filter is assumed! + [b, a] = getButter(f,WN); + w(n) = set(w(n),'data', filtfilt(b, a, double(w(n))) ); + else + warning('Sampling rate is not set on this waveform. Use w = set(w, ''freq'', 100) to set to 100 Hz, for example. Cannot design a Butterworth filter') + return + end end end @@ -55,8 +62,7 @@ %- - - - helper function - - - - % function [b, a] = getButter(f, WN) -f -WN + switch f.type case 'H'; [b,a] = butter(f.poles, WN, 'high'); @@ -64,4 +70,4 @@ [b,a] = butter(f.poles, WN); case 'L'; [b,a] = butter(f.poles, WN); -end; \ No newline at end of file +end diff --git a/core/@filterobject/get.m b/core/@filterobject/get.m old mode 100644 new mode 100755 diff --git a/core/@filterobject/set.m b/core/@filterobject/set.m old mode 100644 new mode 100755 diff --git a/core/@rsam/Fs.m b/core/@rsam/Fs.m deleted file mode 100644 index 3b93ffe..0000000 --- a/core/@rsam/Fs.m +++ /dev/null @@ -1,5 +0,0 @@ -function fs = Fs(self) - l = length(self.dnum); - s = self.dnum(2:l) - self.dnum(1:l-1); - fs = 1.0/(median(s)*86400); -end \ No newline at end of file diff --git a/core/@rsam/cookbook.m b/core/@rsam/cookbook.m deleted file mode 100644 index fade07e..0000000 --- a/core/@rsam/cookbook.m +++ /dev/null @@ -1,22 +0,0 @@ -%% RSAM Cookbook -% GISMO can read and plot RSAM data from BOB binary files. GISMO can also -% create RSAM data from waveform objects and save RSAM data to BOB binary -% files, or to text files. - -%% Create an RSAM object by hand -t = [0:60:1440]/1440; -y = randn(size(t)) + rand(size(t)); -s = rsam(t, y); - -%% Load an RSAM binary file: - -% For one station we can use an explicit path -dp = 'INETER_DATA/RSAM/MOMN2015.DAT'; - -% But if we want to load several files, we can use a file pattern -dp = 'INETER_DATA/RSAM/SSSSYYYY.DAT'; %SSSS means station, YYYY means year -s = rsam.read_bob_file('file', dp, 'snum', datenum(2015,1,1), ... - 'enum', datenum(2015,2,1), 'sta', 'MOMN', 'units', 'Counts') - - - diff --git a/core/@rsam/bvalue.m b/core/@rsam/extensions/bvalue.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/bvalue.m rename to core/@rsam/extensions/bvalue.m diff --git a/core/@rsam/extensions/correct.m b/core/@rsam/extensions/correct.m new file mode 100755 index 0000000..aaf903a --- /dev/null +++ b/core/@rsam/extensions/correct.m @@ -0,0 +1,14 @@ +function self = correct(self) + ref = 0.707; % note that median, rms and std all give same value on x=sin(0:pi/1000:2*pi) + for c=1:numel(self) + if strcmp(self(c).measure, 'max') + self(c).data = self(c).data * ref; + end + if strcmp(self(c).measure, '68') + self(c).data = self(c).data/0.8761 * ref; + end + if strcmp(self(c).measure, 'mean') + self(c).data = self(c).data/0.6363 * ref; + end + end +end \ No newline at end of file diff --git a/core/@rsam/despike.m b/core/@rsam/extensions/despike.m old mode 100644 new mode 100755 similarity index 87% rename from core/@rsam/despike.m rename to core/@rsam/extensions/despike.m index da9f960..0e5067e --- a/core/@rsam/despike.m +++ b/core/@rsam/extensions/despike.m @@ -18,6 +18,17 @@ % Outputs: % s = rsam object with spikes removed + % NOTE: THIS METHOD REQUIRES EXTRA PROPERTIES FOR rsam CLASS + %spikes = []; % a vector of rsam objects that describe large spikes + % in the data. Populated after running 'despike' method. These are + % removed simultaneously from the data vector. + %transientEvents = []; % a vector of rsam objects that describe + % transient events in the data that might correspond to vt, rf, lp + % etc. Populated after running 'despike' method with the + % 'transientEvents' argument. These are not removed from the data + % vector, but are instead returned in the continuousData vector. + + % find spikes lasting 1 sample only y= self.data; spikeNumber = 0; diff --git a/core/@rsam/detectTremorEvents.m b/core/@rsam/extensions/detectTremorEvents.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/detectTremorEvents.m rename to core/@rsam/extensions/detectTremorEvents.m diff --git a/core/@rsam/duration_amplitude.m b/core/@rsam/extensions/duration_amplitude.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/duration_amplitude.m rename to core/@rsam/extensions/duration_amplitude.m diff --git a/core/@rsam/getwaveform.m b/core/@rsam/extensions/getwaveform.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/getwaveform.m rename to core/@rsam/extensions/getwaveform.m diff --git a/core/@rsam/loadwfmeastable.m b/core/@rsam/extensions/loadwfmeastable.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/loadwfmeastable.m rename to core/@rsam/extensions/loadwfmeastable.m diff --git a/core/@rsam/reduce.m b/core/@rsam/extensions/reduce.m old mode 100644 new mode 100755 similarity index 63% rename from core/@rsam/reduce.m rename to core/@rsam/extensions/reduce.m index 0a6a828..3691c01 --- a/core/@rsam/reduce.m +++ b/core/@rsam/extensions/reduce.m @@ -3,6 +3,28 @@ % s.distance and waveSpeed assumed to be in metres (m) % (INPUT) s.data assumed to be in nm or Pa % (OUTPUT) s.data in cm^2 or Pa.m + + % REQUIRES EXTRA PARAMETERS FOR RSAM OBJECTS + %reduced = struct('Q', Inf, 'sourcelat', NaN, 'sourcelon', NaN, 'distance', NaN, 'waveType', '', 'isReduced', false, 'f', NaN, 'waveSpeed', NaN, 'stationlat', NaN, 'stationlon', NaN); + %use = true; +% REDUCED: a structure that is set is data are "reduced", i.e. corrected +% for geometric spreading (and possibly attenuation) +% Has 4 fields: +% REDUCED.Q = the value of Q used to reduce the data +% (Inf by default, which indicates no attenuation) +% REDUCED.SOURCELAT = the latitude used for reducing the data +% REDUCED.SOURCELON = the longitude used for reducing the data +% REDUCED.STATIONLAT = the station latitude +% REDUCED.STATIONLON = the station longitude +% REDUCED.DISTANCE = the distance between source and +% station in km +% REDUCED.WAVETYPE = the wave type (body or surface) +% assumed +% REDUCED.F = the frequency used for surface waves +% REDUCED.WAVESPEED = the S wave speed +% REDUCED.ISREDUCED = True if the data are reduced +% UNITS: the units of the data, e.g. nm / sec. +% USE: use this rsam object in plots? p = inputParser; p.addParameter('waveSpeed', 2000); p.addParamter('f', 2.0); diff --git a/core/@rsam/remove_calibration_pulses.m b/core/@rsam/extensions/remove_calibration_pulses.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/remove_calibration_pulses.m rename to core/@rsam/extensions/remove_calibration_pulses.m diff --git a/core/@rsam/extensions/remove_calibs.m b/core/@rsam/extensions/remove_calibs.m new file mode 100755 index 0000000..5822c94 --- /dev/null +++ b/core/@rsam/extensions/remove_calibs.m @@ -0,0 +1,7 @@ +function self = remove_calibs(self) + for c=1:numel(self) + % run twice since there may be two pulses per day + self(c).data = remove_calibration_pulses(self(c).dnum, self(c).data); + self(c).data = remove_calibration_pulses(self(c).dnum, self(c).data); + end +end \ No newline at end of file diff --git a/core/@rsam/resample.m b/core/@rsam/extensions/resample.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/resample.m rename to core/@rsam/extensions/resample.m diff --git a/core/@rsam/extensions/rsam2energy.m b/core/@rsam/extensions/rsam2energy.m new file mode 100755 index 0000000..8c51bdc --- /dev/null +++ b/core/@rsam/extensions/rsam2energy.m @@ -0,0 +1,5 @@ +function self=rsam2energy(self, r) + % should i detrend first? + e = energy(self.data, r, get(self.scnl, 'channel'), self.Fs(), self.units); + self = set(self, 'energy', e); +end \ No newline at end of file diff --git a/core/@rsam/extensions/rsam2waveform.m b/core/@rsam/extensions/rsam2waveform.m new file mode 100755 index 0000000..ca6a8ba --- /dev/null +++ b/core/@rsam/extensions/rsam2waveform.m @@ -0,0 +1,12 @@ +function w=rsam2waveform(self) + w = waveform; + w = set(w, 'station', self.sta); + w = set(w, 'channel', self.chan); + w = set(w, 'units', self.units); + w = set(w, 'data', self.data); + w = set(w, 'start', self.snum); + %w = set(w, 'end', self.enum); + w = set(w, 'freq', 1/ (86400 * (self.dnum(2) - self.dnum(1)))); + w = addfield(w, 'reduced', self.reduced); + w = addfield(w, 'measure', self.measure); +end \ No newline at end of file diff --git a/core/@rsam/extensions/save2wfmeastable.m b/core/@rsam/extensions/save2wfmeastable.m new file mode 100755 index 0000000..f488b75 --- /dev/null +++ b/core/@rsam/extensions/save2wfmeastable.m @@ -0,0 +1,3 @@ +function save2wfmeastable(self, dbname) + datascopegt.save2wfmeas(self.scnl, self.dnum, self.data, self.measure, self.units, dbname); +end \ No newline at end of file diff --git a/core/@rsam/tremorstalta.m b/core/@rsam/extensions/tremorstalta.m old mode 100644 new mode 100755 similarity index 97% rename from core/@rsam/tremorstalta.m rename to core/@rsam/extensions/tremorstalta.m index 3fe6160..da8231d --- a/core/@rsam/tremorstalta.m +++ b/core/@rsam/extensions/tremorstalta.m @@ -32,6 +32,9 @@ % ratio - sta:lta ratio of each timeWindow % rsamobject - the input rsamobject but with the % continuousEvents property populated + % NOTE: REQUIRES EXTRA PROPERTIES FOR RSAM OBJECTS + %continuousData = []; % + %continuousEvents = []; % a vector of rsam objects that describe tremor % Process input variables p = inputParser; diff --git a/core/@rsam/extract.m b/core/@rsam/extract.m old mode 100644 new mode 100755 index cc0772b..d63c123 --- a/core/@rsam/extract.m +++ b/core/@rsam/extract.m @@ -1,8 +1,8 @@ function s=extract(self, snum, enum) s = self; - i = find(self.dnum>=snum & self.dnum <= enum); - s.dnum = self.dnum(i); - s.data = self.data(i); - s.snum = snum; - s.enum = enum; -end \ No newline at end of file + for c=1:numel(self) + i = find(self(c).dnum>=snum & self(c).dnum <= enum); + s(c).dnum = self(c).dnum(i); + s(c).data = self(c).data(i); + end +end diff --git a/core/@rsam/findfiles.m b/core/@rsam/findfiles.m old mode 100644 new mode 100755 diff --git a/core/@rsam/load.m b/core/@rsam/load.m old mode 100644 new mode 100755 index db55ec5..2e8136d --- a/core/@rsam/load.m +++ b/core/@rsam/load.m @@ -1,4 +1,4 @@ -function self = load(self) +function self = load(self, f) % Purpose: % Loads derived data from a binary file in the BOB RSAM format % The pointer position at which to reading from the binary file is determined from f.snum @@ -9,36 +9,64 @@ % self.f - a structure which contains 'file', 'snum', 'enum' and 'found' parameters % Author: % Glenn Thompson, MVO, 2000 - - % initialise return variables - - f = self.files; - + debug.printfunctionstack('>'); + % initialise return variables [yyyy, ~]=datevec(f.snum); days=365; if mod(yyyy,4)==0 days=366; end - datapointsperday = 1440; + % work out how many days, and how many samples per day + ddir = dir(f.file); + if numel(ddir)==1 + if (ddir.bytes/365) == round(ddir.bytes/365) + day_recs = 365; + elseif (ddir.bytes/366) == round(ddir.bytes/366) + day_recs = 366; + elseif (ddir.bytes/367) == round(ddir.bytes/367) + day_recs = 367; + else + error('cannot work out how many day records in RSAM file'); % no idea what this file is + end + else + day_recs = NaN; + end + if days == day_recs + % no header day rec + header_rec = false; + elseif days == day_recs - 1 + % no header day rec + header_rec = true; + else + error('cannot work out if there is a header in RSAM file'); + end + + % How many samples per day? + SAMPLES_PER_DAY = (ddir.bytes / day_recs) / 4; + headersamples = 0; tz=0; if strfind(f.file,'RSAM') - headersamples=datapointsperday;% for PC-SEIS RSAM data there is a 1 day header + headersamples=SAMPLES_PER_DAY;% for PC-SEIS RSAM data there is a 1 day header tz=-4;% for Montserrat RSAM data time zone is off by 4 hours end - startsample = ceil( (f.snum-datenum(yyyy,1,1))*datapointsperday)+headersamples; - endsample = (f.enum-datenum(yyyy,1,1)) *datapointsperday + headersamples; + if header_rec + headersamples=SAMPLES_PER_DAY; + end + + startsample = ceil( (f.snum-datenum(yyyy,1,1))*SAMPLES_PER_DAY)+headersamples; + endsample = (f.enum-datenum(yyyy,1,1)) *SAMPLES_PER_DAY + headersamples; nsamples = endsample - startsample + 1; % create dnum & blank data vector - dnum_ = ceilminute(f.snum)+(0:nsamples-1)/datapointsperday - tz/24; + dnum_ = ceilminute(f.snum)+(0:nsamples-1)/SAMPLES_PER_DAY - tz/24; data_ = nan(1,length(dnum_)); if f.found % file found debug.print_debug(0, sprintf( 'Loading data from %s, position %d to %d of %d', ... - f.file, startsample,(startsample+nsamples-1),(datapointsperday*days) )); + f.file, startsample,(startsample+nsamples-1),(SAMPLES_PER_DAY*days) )); fid=fopen(f.file,'r', 'l'); % big-endian for Sun, little-endian for PC @@ -76,5 +104,6 @@ % Fill NULL values with NaN self.data(self.data == -998 | self.data == 0) = NaN; + debug.printfunctionstack('<'); +end -end \ No newline at end of file diff --git a/core/@rsam/makebobfile.m b/core/@rsam/make_bob_file.m old mode 100644 new mode 100755 similarity index 57% rename from core/@rsam/makebobfile.m rename to core/@rsam/make_bob_file.m index b57f3b5..c994c55 --- a/core/@rsam/makebobfile.m +++ b/core/@rsam/make_bob_file.m @@ -1,7 +1,6 @@ -function makebobfile(outfile, days) - % makebobfile(outfile, days); - datapointsperday = 1440; - samplesperyear = days*datapointsperday; +function make_bob_file(outfile, days, SAMPLES_PER_DAY) + % make_bob_file(outfile, days); + samplesperyear = round(days)*round(SAMPLES_PER_DAY); a = zeros(samplesperyear,1); % ensure host directory exists mkdir(fileparts(outfile)); diff --git a/core/@rsam/medfilt1.m b/core/@rsam/medfilt1.m old mode 100644 new mode 100755 diff --git a/core/@rsam/obsolete/load.m b/core/@rsam/obsolete/load.m new file mode 100755 index 0000000..a9dd885 --- /dev/null +++ b/core/@rsam/obsolete/load.m @@ -0,0 +1,77 @@ +function self = load(self, f) +% Purpose: +% Loads derived data from a binary file in the BOB RSAM format +% The pointer position at which to reading from the binary file is determined from f.snum +% Load all the data from f.snum to f.enum. So if timewindow is 12:34:56 to 12:44:56, +% it is the samples at 12:35, ..., 12:44 - i.e. 10 of them. +% +% Input: +% self.f - a structure which contains 'file', 'snum', 'enum' and 'found' parameters +% Author: +% Glenn Thompson, MVO, 2000 + + % initialise return variables + [yyyy, ~]=datevec(f.snum); + days=365; + if mod(yyyy,4)==0 + days=366; + end + + datapointsperday = 1440; + headersamples = 0; + tz=0; + if strfind(f.file,'RSAM') + headersamples=datapointsperday;% for PC-SEIS RSAM data there is a 1 day header + tz=-4;% for Montserrat RSAM data time zone is off by 4 hours + end + startsample = ceil( (f.snum-datenum(yyyy,1,1))*datapointsperday)+headersamples; + endsample = (f.enum-datenum(yyyy,1,1)) *datapointsperday + headersamples; + nsamples = endsample - startsample + 1; + + % create dnum & blank data vector + dnum_ = ceilminute(f.snum)+(0:nsamples-1)/datapointsperday - tz/24; + data_ = nan(1,length(dnum_)); + + if f.found + % file found + debug.print_debug(0, sprintf( 'Loading data from %s, position %d to %d of %d', ... + f.file, startsample,(startsample+nsamples-1),(datapointsperday*days) )); + + fid=fopen(f.file,'r', 'l'); % big-endian for Sun, little-endian for PC + + % Position the pointer + offset=(startsample)*4; + fseek(fid,offset,'bof'); + + % Read the data + [data_, ~] = fread(fid, nsamples, 'float32'); + fclose(fid); + debug.print_debug(0, sprintf('mean of data loaded is %e',nanmean(data_))); + + % Transpose to give same dimensions as dnum + data_=data_'; + + % Test for Nulls + datafound = any(data_ > 0); + else + datafound = false; + debug.print_debug(0, sprintf('File %s not found', f.file)); + end + + % Now paste together the matrices + self.dnum = catmatrices(dnum_, self.dnum); + self.data = catmatrices(data_, self.data); + + if ~datafound + debug.print_debug(0, sprintf('%s: No data loaded from file %s',mfilename,f.file)); + end + + % eliminate any data outside range asked for + myRange = self.dnum >= self.snum & self.dnum <= self.enum; + self.dnum = self.dnum(myRange); + self.data = self.data(myRange); + + % Fill NULL values with NaN + self.data(self.data == -998 | self.data == 0) = NaN; + +end \ No newline at end of file diff --git a/core/@rsam/oneMinuteData.m b/core/@rsam/obsolete/oneMinuteData.m similarity index 100% rename from core/@rsam/oneMinuteData.m rename to core/@rsam/obsolete/oneMinuteData.m diff --git a/core/@rsam/plotrsam.m b/core/@rsam/obsolete/plotrsam.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/plotrsam.m rename to core/@rsam/obsolete/plotrsam.m diff --git a/core/@rsam/plotyy.m b/core/@rsam/obsolete/plotyy.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/plotyy.m rename to core/@rsam/obsolete/plotyy.m diff --git a/core/@rsam/plot.m b/core/@rsam/plot.m old mode 100644 new mode 100755 index 0ed5dc1..ca94309 --- a/core/@rsam/plot.m +++ b/core/@rsam/plot.m @@ -1,12 +1,12 @@ -function handlePlot = plot(rsam_vector, varargin) +function plot(rsam_vector, varargin) % RSAM/PLOT plot rsam data % handle = plot(rsam_vector, varargin) % Properties include: - % yaxisType, h, addgrid, addlegend, fillbelow, plotspikes, plottransients, plottremor + % yaxistype, h, addgrid, addlegend, fillbelow, plotspikes, plottransients, plottremor % to change where the legend plots set the global variable legend_ypos % a positive value will be within the axes, a negative value will be below % default is -0.2. For within the axes, log(20) is a reasonable value. - % yaxisType is like 'logarithmic' or 'linear' + % yaxistype is like 'logarithmic' or 'linear' % h is an axes handle (or an array of axes handles) % use h = generatePanelHandles(numgraphs) @@ -18,97 +18,133 @@ % parse variable input arguments p = inputParser; p.addParameter('addgrid',false); - p.addParameter('addlegend', false); + p.addParameter('addlegend', true); p.addParameter('fillbelow', false); - - %apparently unused options - p.addParameter('yaxisType','logarithmic'); % or linear + p.addParameter('yaxistype','linear'); % or log + p.addParameter('symbol','-'); % or log p.addParameter('h', []); - p.parse(varargin{:}); - + addgrid = p.Results.addgrid; + addlegend = p.Results.addlegend; + fillbelow = p.Results.fillbelow; + yaxistype = p.Results.yaxistype; + symbol = p.Results.symbol; + h = p.Results.h; legend_ypos = -0.2; % colours to plot each station lineColour={[0 0 0]; [0 0 1]; [1 0 0]; [0 1 0]; [.4 .4 0]; [0 .4 0 ]; [.4 0 0]; [0 0 .4]; [0.5 0.5 0.5]; [0.25 .25 .25]}; + + % units - so that we put different ones on different figures + units = {rsam_vector.units}; + unique_units = unique(units); + %previousfignum = get(gcf,'Number'); + previousfignum = get_highest_figure_number(); % Plot the data graphs - figure for c = 1:length(rsam_vector) self = rsam_vector(c); +% thisfignum = find(ismember(unique_units, self.units)) + previousfignum; +% figure(thisfignum); + try + figure(previousfignum + c); + catch + figure; + end + hold on; t = self.dnum; y = self.data; + + debug.print_debug(10,sprintf('Data length: %d',length(y))); - %figure - %if strcmp(rsam_vector(c).units, 'Hz') - if strcmp('yaxisType','linear') - - % plot on a linear axis, with station name as a y label - % datetick too, add measure as title, fiddle with the YTick's and add max(y) in top left corner - if ~p.Results.fillbelow - handlePlot = plot(t, y, '-', 'Color', lineColour{c}); + + if length(y)>0 + %figure + %if strcmp(rsam_vector(c).units, 'Hz') + + if strcmp(yaxistype,'linear') + + % plot on a linear axis, with station name as a y label + % datetick too, add measure as title, fiddle with the YTick's and add max(y) in top left corner + if ~p.Results.fillbelow + %handlePlot = plot(t, y, symbol, 'Color', lineColour{c}); + handlePlot = plot(t, y, symbol, 'Color', lineColour{1}); + else + %handlePlot = fill([min(t) t max(t)], [min([y 0]) y min([y 0])], lineColour{c}); + handlePlot = fill([min(t) t max(t)], [min([y 0]) y min([y 0])], lineColour{1}); + end + + % if c ~= numel(rsam_vector) + % set(gca,'XTickLabel',''); + % end + + % yt=get(gca,'YTick'); + % ytinterval = (yt(2)-yt(1))/2; + % yt = yt(1) + ytinterval: ytinterval: yt(end); + % ytl = yt'; + % ylim = get(gca, 'YLim'); + % set(gca, 'YLim', [0 ylim(2)],'YTick',yt); + % %ylabelstr = sprintf('%s.%s %s (%s)', self.sta, self.chan, self.measure, self.units); + % ylabelstr = sprintf('%s', self.sta); + % ylabel(ylabelstr); + ylabel(sprintf('%s',self.units)) + % datetick('x','keeplimits'); + a = axis; + datetick('x') + set(gca,'XLim',[a(1) a(2)]); + xlabel('Date/Time'); + if addlegend + legend(); + end + else - handlePlot = fill([min(t) t max(t)], [min([y 0]) y min([y 0])], lineColour{c}); - end - % if c ~= numel(rsam_vector) - % set(gca,'XTickLabel',''); - % end - - % yt=get(gca,'YTick'); - % ytinterval = (yt(2)-yt(1))/2; - % yt = yt(1) + ytinterval: ytinterval: yt(end); - % ytl = yt'; - % ylim = get(gca, 'YLim'); - % set(gca, 'YLim', [0 ylim(2)],'YTick',yt); - % %ylabelstr = sprintf('%s.%s %s (%s)', self.sta, self.chan, self.measure, self.units); - ylabelstr = sprintf('%s', self.sta); - ylabel(ylabelstr); -% datetick('x','keeplimits'); - a = axis; - datetick('x') - set(gca,'XLim',[a(1) a(2)]); - xlabel('Date/Time'); - - else - - % make a logarithmic plot, with a marker size and add the station name below the x-axis like a legend - y = log10(y); % use log plots - - handlePlot = plot(t, y, '-', 'Color', lineColour{c},... - 'MarkerSize', 1.0); - - if strfind(self.measure, 'dr') - %ylabel(sprintf('%s (cm^2)',self(c).measure)); - %ylabel(sprintf('D_R (cm^2)',self(c).measure)); - Yticks = [0.01 0.02 0.05 0.1 0.2 0.5 1 2 5 10 20 50 ]; - Ytickmarks = log10(Yticks); - for count = 1:length(Yticks) - Yticklabels{count}=num2str(Yticks(count),3); + % make a logarithmic plot, with a marker size and add the station name below the x-axis like a legend + y = log10(y); % use log plots + + % handlePlot = plot(t, y, symbol, 'Color', lineColour{c},... + % 'MarkerSize', 1.0); + handlePlot = plot(t, y, symbol, 'Color', lineColour{1},... + 'MarkerSize', 1.0); + if strfind(self.measure, 'dr') + %ylabel(sprintf('%s (cm^2)',self(c).measure)); + %ylabel(sprintf('D_R (cm^2)',self(c).measure)); + Yticks = [0.01 0.02 0.05 0.1 0.2 0.5 1 2 5 10 20 50 ]; + Ytickmarks = log10(Yticks); + for count = 1:length(Yticks) + Yticklabels{count}=num2str(Yticks(count),3); + end + set(gca, 'YLim', [min(Ytickmarks) max(Ytickmarks)],... + 'YTick',Ytickmarks,'YTickLabel',Yticklabels); end - set(gca, 'YLim', [min(Ytickmarks) max(Ytickmarks)],... - 'YTick',Ytickmarks,'YTickLabel',Yticklabels); + axis tight + a = axis; + datetick('x') + set(gca,'XLim',[a(1) a(2)]); + % + xlabel(sprintf('Date/Time starting at %s',datestr(self.snum))) + ylabel(sprintf('log(%s)',self.units)) + end - axis tight - a = axis; - datetick('x') - set(gca,'XLim',[a(1) a(2)]); -% - xlabel(sprintf('Date/Time starting at %s',datestr(self.snum))) - ylabel(sprintf('log(%s)',self.units)) - - end -% if p.Results.addgrid -% grid on; -% end -% if p.Results.addlegend && ~isempty(y) -% xlim = get(gca, 'XLim'); -% legend_ypos = 0.9; -% legend_xpos = c/10; -% end + if p.Results.addgrid + grid on; + end + if p.Results.addlegend && ~isempty(y) + xlim = get(gca, 'XLim'); + legend_ypos = 0.9; + legend_xpos = c/10; + end + datetick('x','keeplimits') + if ~strcmp(self.ChannelTag.string(), '...') + tstr = sprintf('%s %s\n%s %.0f s',self.ChannelTag.string(), datestr(t(1)), self.measure, round(self.sampling_interval) ); + else + tstr = sprintf('%s\n%s %.0f s',self.files.file, datestr(a(1)), round(self.sampling_interval) ); + end + title(tstr) + end end end \ No newline at end of file diff --git a/core/@rsam/plot_panels.m b/core/@rsam/plot_panels.m new file mode 100755 index 0000000..3c23e97 --- /dev/null +++ b/core/@rsam/plot_panels.m @@ -0,0 +1,61 @@ +function handlePlot = plot(rsam_vector, varargin) + % RSAM/PLOT plot rsam data + % handle = plot(rsam_vector, varargin) + % Properties include: + % yaxistype, h, addgrid, addlegend, fillbelow, plotspikes, plottransients, plottremor + % to change where the legend plots set the global variable legend_ypos + % a positive value will be within the axes, a negative value will be below + % default is -0.2. For within the axes, log(20) is a reasonable value. + % yaxistype is like 'logarithmic' or 'linear' + % h is an axes handle (or an array of axes handles) + % use h = generatePanelHandles(numgraphs) + + % Glenn Thompson 1998-2009 + % + % % GTHO 2009/10/26 Changed marker size from 5.0 to 1.0 + % % GTHO 2009/10/26 Changed legend position to -0.2 + + w = rsam2waveform(rsam_vector); + plot_panels(w); + + % xticks currently in seconds + xtick_interval_seconds = 1; + xticks=get(gca,'XTick'); + xlims = get(gca,'XLim'); + hfc = get(gcf,'Children'); + if xlims(2) >= 60 * 10 && xlims(2) < 60 * 100 + % change to minutes + xticks = 0:60:xlims(2); + xtick_interval_seconds = 60; + xlabel('Time (minutes)'); + end + if xlims(2) >= 60 * 100 && xlims(2) < 60 * 60 * 100 + % change to hours + xticks = 0:60*15:xlims(2); %15 minute intervals + xtick_interval_seconds = 3600; + xlabel('Time (hours)'); + end + if xlims(2) >= 60 * 60 * 100 && xlims(2) < 60 * 60 * 24 * 100 + % change to days + xticks = 0:60*60*6:xlims(2); %6 hour intervals + xtick_interval_seconds = 3600 * 24; + xlabel('Time (days)'); + end + if xlims(2) >= 60 * 60 * 24 * 100 + % change to weeks + xticks = 0:60*60*24*3.5:xlims(2); %0.5 week intervals + xtick_interval_seconds = 3600 * 24 * 7; + xlabel('Time (weeks)'); + end + if xtick_interval_seconds > 1 + r = stepsize(xticks); + xticks = xticks(1:r:end); + xticklabels = xticks/xtick_interval_seconds; + set(hfc(2:end),'XTick',xticks,'XTickLabels',xticklabels); + end +end + + +function r=stepsize(xticks) + r = ceil(numel(xticks)/14); +end diff --git a/core/@rsam/read_bob_file.m b/core/@rsam/read_bob_file.m index df34272..a84743f 100755 --- a/core/@rsam/read_bob_file.m +++ b/core/@rsam/read_bob_file.m @@ -1,4 +1,4 @@ -function self=read_bob_file(varargin) +function self=read_bob_file(filepattern, varargin) %READ_BOB_FILE Load RSAM-like data from a BOB file into a SAM object. % SAM is a generic term used here to represent any continuous data % sampled at a regular time interval (usually 1 minute). This is a @@ -14,9 +14,9 @@ % of a 4 byte floating number for each minute of the year, for a % single station-channel. % -% s = read_bob_file('file', file, 'snum', snum, 'enum', enum, 'sta', sta, 'chan', chan, 'measure', measure, 'seismogram_type', seismogram_type, 'units', units) +% s = read_bob_file(filepattern, 'snum', snum, 'enum', enum, 'sta', sta, 'chan', chan, 'measure', measure, 'seismogram_type', seismogram_type, 'units', units) % -% file % the path to the file. Substitutions enabled +% filepattern % the path to the file. Substitutions enabled % 'SSSS' replaced with sta % 'CCC' replaced with chan % 'MMMM' replaced with measure @@ -30,77 +30,83 @@ % seismogram_type % e.g. 'velocity' or 'displacement', default is 'raw' % units % units to label y-axis, e.g. 'nm/s' or 'nm' or 'cm2', default is 'counts' % -% See also: sam, oneMinuteData +% See also: sam, oneMinuteData + debug.printfunctionstack('>'); self = rsam(); % Create a blank sam object - classFields = {'sta','chan','measure','seismogram_type','units','snum','enum', 'dnum', 'data'}; + p = inputParser; - p.addParameter('file', ''); - for n=1:numel(classFields) - p.addParameter(classFields{n}, self.(classFields{n})); - end + p.addParameter('snum', []); + p.addParameter('enum', []); + p.addParameter('sta', ''); + p.addParameter('chan', ''); + p.addParameter('measure', ''); + p.addParameter('units', ''); p.parse(varargin{:}); + % modify class values based on user-provided values - for n = 1:numel(classFields) - self.(classFields{n}) = p.Results.(classFields{n}); - end - file = p.Results.file; - - + self.measure = p.Results.measure; + self.units = p.Results.units; + self.ChannelTag = ChannelTag('',p.Results.sta,'',p.Results.chan); %%%% CREATING SAM OBJECT FROM A BOB FILE % check if filename has a year in it, if it does % make sure snum doesn't start before this year % and enum doesn't end after this year - if ~isempty(file) - dummy = regexp(file, '(\d+)', 'match'); + snum = p.Results.snum; + enum = p.Results.enum; + if ~isempty(filepattern) + dummy = regexp(filepattern, '(\d+)', 'match'); if ~isempty(dummy) yyyy = str2num(dummy{end}); d=datevec(now);yearnow=d(1);clear d if yyyy>=1980 & yyyy<=yearnow - self.snum = max([self.snum datenum(yyyy,1,1)]); - self.enum = min([self.enum datenum(yyyy,12,31,23,59,59)]); + snum = max([p.Results.snum datenum(yyyy,1,1)]); + enum = min([p.Results.enum datenum(yyyy,12,31,23,59,59)]); end end % Generate a list of files - self = findfiles(self, file); + self = findfiles(self, filepattern, snum, enum); % Load the data - for f = self.files + for filenum = 1:numel(self.files) + f = self.files(filenum); if f.found - self = self.load(); + self = self.load(f); end end end + debug.printfunctionstack('<'); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function self = findfiles(self, file) +function self = findfiles(self, file, snum, enum) + debug.printfunctionstack('>'); % Generate a list of files corresponding to the file pattern, % snum and enum given. filenum = 0; % substitute for station - file = regexprep(file, 'SSSS', self.sta); + file = regexprep(file, 'SSSS', get(self.ChannelTag,'station')); % substitute for channel - file = regexprep(file, 'CCC', self.chan); + file = regexprep(file, 'CCC', get(self.ChannelTag,'channel')); % substitute for measure file = regexprep(file, 'MMMM', self.measure); % set start year and month, and end year and month - [syyy sm]=datevec(self.snum); - [eyyy em]=datevec(self.enum); + [syyy sm]=datevec(snum); + [eyyy em]=datevec(enum); for yyyy=syyy:eyyy filenum = filenum + 1; - files(filenum) = struct('file', file, 'snum', self.snum, 'enum', self.enum, 'found', false); + files(filenum) = struct('file', file, 'snum', snum, 'enum', enum, 'found', false); % Check year against start year if yyyy~=syyy @@ -126,91 +132,93 @@ end end self.files = files; + debug.printfunctionstack('<'); end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -function self = load(self, f) -% Purpose: -% Loads derived data from a binary file in the BOB RSAM format -% The pointer position at which to reading from the binary file is determined from f.snum -% Load all the data from f.snum to f.enum. So if timewindow is 12:34:56 to 12:44:56, -% it is the samples at 12:35, ..., 12:44 - i.e. 10 of them. -% -% Input: -% f - a structure which contains 'file', 'snum', 'enum' and 'found' parameters -% Author: -% Glenn Thompson, MVO, 2000 - - % initialise return variables - datafound=false; - dnum=[]; - data=[]; - - [yyyy mm]=datevec(f.snum); - days=365; - if mod(yyyy,4)==0 - days=366; - end +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% function self = load(self, f) +% % Purpose: +% % Loads derived data from a binary file in the BOB RSAM format +% % The pointer position at which to reading from the binary file is determined from f.snum +% % Load all the data from f.snum to f.enum. So if timewindow is 12:34:56 to 12:44:56, +% % it is the samples at 12:35, ..., 12:44 - i.e. 10 of them. +% % +% % Input: +% % f - a structure which contains 'file', 'snum', 'enum' and 'found' parameters +% % Author: +% % Glenn Thompson, MVO, 2000 +% +% % initialise return variables +% datafound=false; +% dnum=[]; +% data=[]; +% +% [yyyy mm]=datevec(f.snum); +% days=365; +% if mod(yyyy,4)==0 +% days=366; +% end +% +% datapointsperday = 1440; +% headersamples = 0; +% tz=0; +% if strfind(f.file,'RSAM') +% headersamples=datapointsperday; +% tz=-4; +% end +% startsample = ceil( (f.snum-datenum(yyyy,1,1))*datapointsperday)+headersamples; +% endsample = (f.enum-datenum(yyyy,1,1)) *datapointsperday + headersamples; +% %endsample = floor( max([ datenum(yyyy,12,31,23,59,59) f.enum-datenum(yyyy,1,1) ]) *datapointsperday); +% nsamples = endsample - startsample + 1; +% +% % create dnum & blank data vector +% dnum = ceilminute(f.snum)+(0:nsamples-1)/datapointsperday - tz/24; +% data(1:length(dnum))=NaN; +% +% if f.found +% % file found +% debug.print_debug(sprintf( 'Loading data from %s, position %d to %d of %d', ... +% f.file, startsample,(startsample+nsamples-1),(datapointsperday*days) ),3); +% +% fid=fopen(f.file,'r', 'l'); % big-endian for Sun, little-endian for PC +% +% % Position the pointer +% offset=(startsample)*4; +% fseek(fid,offset,'bof'); +% +% % Read the data +% [data,numlines] = fread(fid, nsamples, 'float32'); +% fclose(fid); +% debug.print_debug(sprintf('mean of data loaded is %e',nanmean(data)),1); +% +% % Transpose to give same dimensions as dnum +% data=data'; +% +% % Test for Nulls +% if length(find(data>0)) > 0 +% datafound=true; +% end +% end +% +% % Now paste together the matrices +% self.dnum = catmatrices(dnum, self.dnum); +% self.data = catmatrices(data, self.data); +% +% if ~datafound +% debug.print_debug(sprintf('%s: No data loaded from file %s',mfilename,f.file),1); +% end +% +% % eliminate any data outside range asked for - MAKE THIS A +% % SEPARATE FN IF AT ALL +% i = find(self.dnum >= self.snum & self.dnum <= self.enum); +% self.dnum = self.dnum(i); +% self.data = self.data(i); +% +% % Fill NULL values with NaN +% i = find(self.data == -998); +% self.data(i) = NaN; +% i = find(self.data == 0); +% self.data(i) = NaN; +% +% end - datapointsperday = 1440; - headersamples = 0; - tz=0; - if strfind(f.file,'RSAM') - headersamples=datapointsperday; - tz=-4; - end - startsample = ceil( (f.snum-datenum(yyyy,1,1))*datapointsperday)+headersamples; - endsample = (f.enum-datenum(yyyy,1,1)) *datapointsperday + headersamples; - %endsample = floor( max([ datenum(yyyy,12,31,23,59,59) f.enum-datenum(yyyy,1,1) ]) *datapointsperday); - nsamples = endsample - startsample + 1; - - % create dnum & blank data vector - dnum = ceilminute(f.snum)+(0:nsamples-1)/datapointsperday - tz/24; - data(1:length(dnum))=NaN; - - if f.found - % file found - debug.print_debug(sprintf( 'Loading data from %s, position %d to %d of %d', ... - f.file, startsample,(startsample+nsamples-1),(datapointsperday*days) ),3); - - fid=fopen(f.file,'r', 'l'); % big-endian for Sun, little-endian for PC - - % Position the pointer - offset=(startsample)*4; - fseek(fid,offset,'bof'); - - % Read the data - [data,numlines] = fread(fid, nsamples, 'float32'); - fclose(fid); - debug.print_debug(sprintf('mean of data loaded is %e',nanmean(data)),1); - - % Transpose to give same dimensions as dnum - data=data'; - - % Test for Nulls - if length(find(data>0)) > 0 - datafound=true; - end - end - - % Now paste together the matrices - self.dnum = catmatrices(dnum, self.dnum); - self.data = catmatrices(data, self.data); - - if ~datafound - debug.print_debug(sprintf('%s: No data loaded from file %s',mfilename,f.file),1); - end - - % eliminate any data outside range asked for - MAKE THIS A - % SEPARATE FN IF AT ALL - i = find(self.dnum >= self.snum & self.dnum <= self.enum); - self.dnum = self.dnum(i); - self.data = self.data(i); - - % Fill NULL values with NaN - i = find(self.data == -998); - self.data(i) = NaN; - i = find(self.data == 0); - self.data(i) = NaN; - -end diff --git a/core/@rsam/rsam.m b/core/@rsam/rsam.m index 93dfc49..bed3f6b 100755 --- a/core/@rsam/rsam.m +++ b/core/@rsam/rsam.m @@ -22,8 +22,7 @@ % dnum % the dates/times (as datenum) corresponding to the start % of each time window % data % the value at each dnum -% sta % station -% chan % channel +% ctag % ChannelTag % measure % statistical measure, default is 'mean' % seismogram_type % e.g. 'velocity' or 'displacement', default is 'raw' % units % units to label y-axis, e.g. 'nm/s' or 'nm' or 'cm2', default is 'counts' @@ -55,24 +54,7 @@ % "energy" % SEISMOGRAM_TYPE: a string describing whether the RSAM data were computed % from "raw" seismogram, "velocity", "displacement" -% REDUCED: a structure that is set is data are "reduced", i.e. corrected -% for geometric spreading (and possibly attenuation) -% Has 4 fields: -% REDUCED.Q = the value of Q used to reduce the data -% (Inf by default, which indicates no attenuation) -% REDUCED.SOURCELAT = the latitude used for reducing the data -% REDUCED.SOURCELON = the longitude used for reducing the data -% REDUCED.STATIONLAT = the station latitude -% REDUCED.STATIONLON = the station longitude -% REDUCED.DISTANCE = the distance between source and -% station in km -% REDUCED.WAVETYPE = the wave type (body or surface) -% assumed -% REDUCED.F = the frequency used for surface waves -% REDUCED.WAVESPEED = the S wave speed -% REDUCED.ISREDUCED = True if the data are reduced % UNITS: the units of the data, e.g. nm / sec. -% USE: use this rsam object in plots? % FILES: structure of files data is loaded from % AUTHOR: Glenn Thompson, Montserrat Volcano Observatory @@ -81,32 +63,27 @@ properties(Access = public) dnum = []; - data = []; % + data = []; measure = 'mean'; - seismogram_type = 'raw'; - %reduced = struct('Q', Inf, 'sourcelat', NaN, 'sourcelon', NaN, 'distance', NaN, 'waveType', '', 'isReduced', false, 'f', NaN, 'waveSpeed', NaN, 'stationlat', NaN, 'stationlon', NaN); + seismogram_type = ''; units = 'counts'; - %use = true; files = ''; - sta = ''; - chan = ''; - snum = -Inf; - enum = Inf; - %spikes = []; % a vector of rsam objects that describe large spikes - % in the data. Populated after running 'despike' method. These are - % removed simultaneously from the data vector. - %transientEvents = []; % a vector of rsam objects that describe - % transient events in the data that might correspond to vt, rf, lp - % etc. Populated after running 'despike' method with the - % 'transientEvents' argument. These are not removed from the data - % vector, but are instead returned in the continuousData vector. - %continuousData = []; % - %continuousEvents = []; % a vector of rsam objects that describe tremor - + ChannelTag = ChannelTag(); + request = struct(); + end + + properties(Dependent) + snum + enum + sta + chan + sampling_interval + stats end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - methods(Access = public) - + %methods(Access = public) + methods function self=rsam(dnum, data, varargin) if nargin==0 return; @@ -116,7 +93,7 @@ self.dnum = dnum; self.data = data; if nargin>2 - classFields = {'sta','chan','measure','seismogram_type','units','snum','enum'}; + classFields = {'ChannelTag','measure','seismogram_type','units'}; p = inputParser; for n=1:numel(classFields) p.addParameter(classFields{n}, self.(classFields{n})); @@ -131,92 +108,76 @@ end end -% % % function result=snum(self) -% % % result = nanmin(self.dnum); -% % % end -% % % function result=enum(self) -% % % result = nanmax(self.dnum); -% % % end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + function r = get.snum(self) + r = []; + for c=1:numel(self) + r(c) = nanmin(self(c).dnum); + end + end + + function r = get.enum(self) + r = []; + for c=1:numel(self) + r(c) = nanmax(self(c).dnum); + end + end + + function r = get.sta(self) + r = {}; + for c=1:numel(self) + r{c} = self(c).ChannelTag.station; + end + end + + function r = get.chan(self) + r = {}; + for c=1:numel(self) + r{c} = self(c).ChannelTag.channel; + end + end + + function r = get.sampling_interval(self) + r = []; + for c = 1:length(self) + l = numel(self(c).dnum); + s = self(c).dnum(2:l) - self(c).dnum(1:l-1); + r(c) = (median(s)*86400); + end + end + + function stats = get.stats(self) + for c=1:numel(self) + stats(c) = struct; + stats(c).min = nanmin(self(c).data); + stats(c).max = nanmax(self(c).data); + stats(c).mean = nanmean(self(c).data); + stats(c).median = nanmedian(self(c).data); + stats(c).rms = rms(self(c).data); + stats(c).std = nanstd(self(c).data); + end + end + + % Prototypes - self = findfiles(self, file) - self = load(self) - handlePlot = plot(rsam_vector, varargin) - save(self, filepattern) - toTextFile(self, filepath) - [aw,tt1, tt2, tmc, mag_zone]=bvalue(this, mcType, method) - [lambda, r2] = duration_amplitude(self, law, min_amplitude, mag_zone) + self = findfiles(self, filepattern) + self = load(self, file) s=extract(self, snum, enum) - fs = Fs(self) self = medfilt1(self, nsamples_to_average_over) - w=getwaveform(self, datapath) - %scrollplot(s) - %plotyy(obj1, obj2, varargin) - %self = reduce(self, waveType, sourcelat, sourcelon, stationlat, stationlon, varargin) - %[self, timeWindow] = tremorstalta(self, varargin) - %self = resample(self, varargin) - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% function save2wfmeastable(self, dbname) -% datascopegt.save2wfmeas(self.scnl, self.dnum, self.data, self.measure, self.units, dbname); -% end -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% function self = remove_calibs(self) -% for c=1:numel(self) -% % run twice since there may be two pulses per day -% self(c).data = remove_calibration_pulses(self(c).dnum, self(c).data); -% self(c).data = remove_calibration_pulses(self(c).dnum, self(c).data); -% end -% end -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% function self = correct(self) -% ref = 0.707; % note that median, rms and std all give same value on x=sin(0:pi/1000:2*pi) -% for c=1:numel(self) -% if strcmp(self(c).measure, 'max') -% self(c).data = self(c).data * ref; -% end -% if strcmp(self(c).measure, '68') -% self(c).data = self(c).data/0.8761 * ref; -% end -% if strcmp(self(c).measure, 'mean') -% self(c).data = self(c).data/0.6363 * ref; -% end -% end -% end -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% function self=rsam2energy(self, r) -% % should i detrend first? -% e = energy(self.data, r, get(self.scnl, 'channel'), self.Fs(), self.units); -% self = set(self, 'energy', e); -% end -% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% function w=rsam2waveform(self) -% w = waveform; -% w = set(w, 'station', self.sta); -% w = set(w, 'channel', self.chan); -% w = set(w, 'units', self.units); -% w = set(w, 'data', self.data); -% w = set(w, 'start', self.snum); -% %w = set(w, 'end', self.enum); -% w = set(w, 'freq', 1/ (86400 * (self.dnum(2) - self.dnum(1)))); -% w = addfield(w, 'reduced', self.reduced); -% w = addfield(w, 'measure', self.measure); -% end - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - + handlePlot = plot(rsam_vector, varargin) + plot_panels(self); + w = rsam2waveform(self); + save_to_bob_file(self, filepattern) + save_to_text_file(self, filepath) + result = isempty(self); end % end of dynamic methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% methods(Access = public, Static) self = read_bob_file(varargin) - makebobfile(outfile, days) - self = loadbobfile(infile, snum, enum) - %self = loadwfmeastable(sta, chan, snum, enum, measure, dbname) - %[data]=remove_calibration_pulses(dnum, data) - %[rsamobjects, ah]=plotrsam(sta, chan, snum, enum, DATAPATH) - %rsamobj = detectTremorEvents(stationName, chan, DP, snum, enum, ... - % spikeRatio, transientEventRatio, STA_minutes, LTA_minutes, ... - % stepsize, ratio_on, ratio_off, plotResults) + make_bob_file(outfile, days, samples_per_day) end end % classdef diff --git a/core/@rsam/rsam2waveform.m b/core/@rsam/rsam2waveform.m new file mode 100755 index 0000000..c3af2b4 --- /dev/null +++ b/core/@rsam/rsam2waveform.m @@ -0,0 +1,11 @@ +function w= rsam2waveform(s) + w = []; + for c=1:numel(s) + if numel(s(c).data) > 0 + wc = waveform(s(c).ChannelTag, 1.0/s(c).sampling_interval, s(c).snum, s(c).data, s(c).units); + else + wc = waveform(s(c).ChannelTag, 1.0/s(c).sampling_interval, 0, [], s(c).units); + end + w = [w wc]; + end +end \ No newline at end of file diff --git a/core/@rsam/save.m b/core/@rsam/save_to_bob_file.m old mode 100644 new mode 100755 similarity index 72% rename from core/@rsam/save.m rename to core/@rsam/save_to_bob_file.m index 799bbb3..487064e --- a/core/@rsam/save.m +++ b/core/@rsam/save_to_bob_file.m @@ -1,11 +1,11 @@ -function save(self, filepattern) - % RSAM/SAVE - save an rsam-like object to an RSAM/BOB binary +function save_to_bob_file(self, filepattern) + % RSAM/SAVE_TO_BOB_FILE - save an rsam-like object to an RSAM/BOB binary % file % % % Examples: % 1. save data to myfile.bob - % r.save('myfile.bob') + % r.save_to_bob_file('myfile.bob') % % 2. save to file like YEAR_STATION_CHANNEL_MEASURE.bob % r.save('YYYY_SSSS_CCC_MMMM.bob') @@ -29,29 +29,34 @@ function save(self, filepattern) % should write data based on dnum only if length(dnum)~=length(data) - debug.print_debug(0,sprintf('%s: Cannot save to %s because data and time vectors are different lengths',mfilename,filename)); + debug.print_debug(1,sprintf('%s: Cannot save to %s because data and time vectors are different lengths',mfilename,filename)); return; end if length(data)<1 - debug.print_debug(0,'No data. Aborting'); + debug.print_debug(1,'No data. Aborting'); return; end % filename % set start year and month, and end year and month + [syyy sm]=datevec(self(c).snum); [eyyy em]=datevec(self(c).enum); - + if syyy~=eyyy if ~strfind(filepattern, 'YYYY') - error('can only save RSAM data to BOB file if all data within 1 year (or you can add YYYY in your file pattern)'); + error('can only save RSAM data to BOB file if all data within 1 year (or you can add YYYY in your file pattern)'); end end + + SECONDS_PER_DAY = 60 * 60 * 24; for yyyy=syyy:eyyy + SAMPLES_PER_DAY = SECONDS_PER_DAY / self(c).sampling_interval; + % how many days in this year? daysperyear = 365; if (mod(yyyy,4)==0) @@ -63,14 +68,14 @@ function save(self, filepattern) debug.print_debug(2,sprintf('Looking for file: %s\n',fname)); if ~exist(fname,'file') - debug.print_debug(2, ['Creating ',fname]) - rsam.makebobfile(fname, daysperyear); + debug.print_debug(2, ['Creating ',fname]); + rsam.make_bob_file(fname, daysperyear, SAMPLES_PER_DAY); end - datapointsperday = 1440; + SAMPLES_PER_DAY = SECONDS_PER_DAY / self(c).sampling_interval; % round times to minute - dnum = round((dnum-1/86400) * 1440) / 1440; + dnum = round((dnum-1/SECONDS_PER_DAY) * SAMPLES_PER_DAY) / SAMPLES_PER_DAY; % subset for current year dnumy = dnum(dnum < datenum(yyyy + 1, 1, 1)); @@ -78,21 +83,21 @@ function save(self, filepattern) % find the next contiguous block of data diff=dnumy(2:end) - dnumy(1:end-1); - i = find(diff > 1.5/1440 | diff < 0.5/1440); + i = find(diff > 1.5/SAMPLES_PER_DAY | diff < 0.5/SAMPLES_PER_DAY); - disp(sprintf('Saving to %s',fname)); - + debug.print_debug(1,sprintf('Saving to %s',fname)); + if length(i)>0 % slow mode for c=1:length(dnumy) % write the data - startsample = round((dnumy(c) - datenum(yyyy,1,1)) * datapointsperday); + startsample = round((dnumy(c) - datenum(yyyy,1,1)) * SAMPLES_PER_DAY); offset = startsample*4; fid = fopen(fname,'r+'); fseek(fid,offset,'bof'); - debug.print_debug(2, sprintf('saving data with mean of %e from to file %s, starting at position %d',nanmean(datay),fname,startsample,(datapointsperday*daysperyear))) + debug.print_debug(2, sprintf('saving data with mean of %e from to file %s, starting at position %d',nanmean(datay),fname,startsample,(datapointsperday*daysperyear))); fwrite(fid,datay(c),'float32'); fclose(fid); end @@ -100,14 +105,14 @@ function save(self, filepattern) % fast mode % write the data - startsample = round((dnumy(1) - datenum(yyyy,1,1)) * datapointsperday); + startsample = round((dnumy(1) - datenum(yyyy,1,1)) * SAMPLES_PER_DAY); offset = startsample*4; fid = fopen(fname,'r+','l'); % little-endian. Anything written on a PC is little-endian by default. Sun is big-endian. fseek(fid,offset,'bof'); - debug.print_debug(2, sprintf('saving data with mean of %e from to file %s, starting at position %d/%d',nanmean(datay),fname,startsample,(datapointsperday*daysperyear))) + debug.print_debug(2, sprintf('saving data with mean of %e from to file %s, starting at position %d/%d',nanmean(datay),fname,startsample,(SAMPLES_PER_DAY*daysperyear))); fwrite(fid,datay,'float32'); fclose(fid); end end end -end \ No newline at end of file +end diff --git a/core/@rsam/toTextFile.m b/core/@rsam/save_to_text_file.m old mode 100644 new mode 100755 similarity index 54% rename from core/@rsam/toTextFile.m rename to core/@rsam/save_to_text_file.m index 0892679..db79b07 --- a/core/@rsam/toTextFile.m +++ b/core/@rsam/save_to_text_file.m @@ -1,9 +1,12 @@ - function toTextFile(self, filepath) - % toTextFile(filepath); + function save_to_text_file(self, filepath) + % save_to_text_file(filepath); % + if numel(self)>1 + error('Cannot write multiple rsam objects to the same text file'); + end fout=fopen(filepath, 'w'); for c=1:length(self.dnum) fprintf(fout, '%15.8f\t%s\t%5.3e\n',self.dnum(c),datestr(self.dnum(c),'yyyy-mm-dd HH:MM:SS.FFF'),self.data(c)); end fclose(fout); -end \ No newline at end of file +end diff --git a/core/@rsam/loadbobfile.m b/core/@rsam/trash/loadbobfile.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/loadbobfile.m rename to core/@rsam/trash/loadbobfile.m diff --git a/core/@rsam/scrollplot_whatisthis.m b/core/@rsam/trash/scrollplot_whatisthis.m old mode 100644 new mode 100755 similarity index 100% rename from core/@rsam/scrollplot_whatisthis.m rename to core/@rsam/trash/scrollplot_whatisthis.m diff --git a/core/@sacpz/sacpz.m b/core/@sacpz/sacpz.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/ChannelTag.m b/core/@scnlobject/ChannelTag.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/disp.m b/core/@scnlobject/disp.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/display.m b/core/@scnlobject/display.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/eq.m b/core/@scnlobject/eq.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/get.m b/core/@scnlobject/get.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/intersect.m b/core/@scnlobject/intersect.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/ismember.m b/core/@scnlobject/ismember.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/scnlobject.m b/core/@scnlobject/scnlobject.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/set.m b/core/@scnlobject/set.m old mode 100644 new mode 100755 diff --git a/core/@scnlobject/unique.m b/core/@scnlobject/unique.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/colorbar_axis.m b/core/@spectralobject/colorbar_axis.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/disp.m b/core/@spectralobject/disp.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/display.m b/core/@spectralobject/display.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/fft.m b/core/@spectralobject/fft.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/get.m b/core/@spectralobject/get.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/getmap.m b/core/@spectralobject/getmap.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/ifft.m b/core/@spectralobject/ifft.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/loadobj.m b/core/@spectralobject/loadobj.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/private/parse_xunit.m b/core/@spectralobject/private/parse_xunit.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/private/subdivide_axes.m b/core/@spectralobject/private/subdivide_axes.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/pwelch.m b/core/@spectralobject/pwelch.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/set.m b/core/@spectralobject/set.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/setmap.m b/core/@spectralobject/setmap.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/specgram.m b/core/@spectralobject/specgram.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/specgram2.m b/core/@spectralobject/specgram2.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/spectralobject.m b/core/@spectralobject/spectralobject.m old mode 100644 new mode 100755 diff --git a/core/@spectralobject/spwelch.m b/core/@spectralobject/spwelch.m old mode 100644 new mode 100755 diff --git a/core/@threecomp/cookbook_map.m b/core/@threecomp/cookbook_map.m old mode 100644 new mode 100755 diff --git a/core/@threecomp/particlemotion.m b/core/@threecomp/particlemotion.m index fc35a19..2e49f2c 100755 --- a/core/@threecomp/particlemotion.m +++ b/core/@threecomp/particlemotion.m @@ -25,12 +25,12 @@ % For an overview of this methodology see: % Montalbetti, J. F. and Kanasewich, E. R. (1970), Enhancement % of Teleseismic Body Phases with a Polarization Filter. Geophys. -% J. Royal Astro. Soc., 21, 119–129. +% J. Royal Astro. Soc., 21, 119???129. % doi: 10.1111/j.1365-246X.1970.tb01771.x % % Ereditato, D. and G. Luongo (1994) Volcanic tremor wave field % during quiescent and eruptive activity at Mt. Etna (Sicily), -% J. Volcanol. Geotherm. Res., 61, 239–25. +% J. Volcanol. Geotherm. Res., 61, 239???25. % % see also threecomp/describe @@ -51,13 +51,13 @@ if length(varargin) >= 1 dt = varargin{1}; else - dt = 86400 * get(TC(1).traces(1),'DURATION' ) / 100; + dt = 86400 * get(TC(1).traces(1),'DURATION' ) / 200; %100 end if length(varargin) >= 2 width = varargin{2}; else - width = 86400 * get(TC(1).traces(1),'DURATION' ) / 10; + width = 86400 * get(TC(1).traces(1),'DURATION' ) / 10; % 10 end disp(['Time step: ' num2str(dt,'%4.3f') ' Window width: ' num2str(width,'%4.3f') ]); diff --git a/core/@threecomp/plot3.m b/core/@threecomp/plot3.m index 9c0bff6..ba4c67e 100755 --- a/core/@threecomp/plot3.m +++ b/core/@threecomp/plot3.m @@ -1,6 +1,6 @@ function plot3(TC) %PLOT3 Interactively make 3D plot of particle motion vector evolution - +disp('got here') %% Extract data for whole threecomp object w = get(TC,'waveform'); fs = get(w(1), 'freq'); diff --git a/core/@threecomp/plotpm.m b/core/@threecomp/plotpm.m index 8fb5fd9..c70fa3d 100755 --- a/core/@threecomp/plotpm.m +++ b/core/@threecomp/plotpm.m @@ -1,4 +1,4 @@ -function plotpm(TC); +function plotpm(TC, bool_inc_az); %PLOTPM Plot particle motion coefficients. % PLOTPM(TC) Plot the particple motion coefficients from threecompcomp @@ -19,8 +19,17 @@ figure('Position',[50 0 900 1200],'Color','w'); set(gcf,'DefaultLineLineWidth',1); +if ~exist('bool_inc_az','var') + bool_inc_az = false; +end +if bool_inc_az + numax = 6; +else + numax = 4; +end + % plot Z, N, E -subplot(3,1,1); +subplot(numax/2,1,1); scale = 0.75; D = double(TC.traces(1:3)); normval = max(max(abs(D))); @@ -32,7 +41,7 @@ plot(gca,t,D(:,2)+0,'-','Color',[0 0.3 0]); plot(gca,t,D(:,3)-1,'-','Color',[0.3 0 0]); ylim(gca,[-1-scale 1+scale]); -xlabel(gca,''); +xlabel(gca,'Seconds'); title(gca,['Station: ' get(TC.traces(1),'station') ' Starting at ' datestr(get(TC.traces(1),'start'),'yyyy/mm/dd HH:MM:SS.FFF') ]); set(gca,'YGrid','on'); set(gca,'XGrid','on'); set( gca , 'YTick' , [-1:1] ); @@ -45,55 +54,59 @@ % titlestr = [titlestr ' orid: ' orid]; % end title(gca,titlestr,'FontSize',14); - +xlim=get(gca,'XLim'); % plot energy -subplot(6,1,3); +subplot(numax,1,3); plot(TC.energy,'k-','axeshandle',gca); set(gca,'YScale','linear'); d = get(TC.energy,'Data'); ylim(gca,[0 1.1*max(d)]); -legend(gca,'Energy',1); +legend(gca,'Energy'); title(gca,''); -set(gca,'XTickLabel',[]); +%set(gca,'XTickLabel',[]); set(gca,'XGrid','on') - +set(gca,'XLim',xlim) % plot rectilinearity and planarity -subplot(6,1,4); +subplot(numax,1,4); plot(TC.rectilinearity,'kx','axeshandle',gca); hold on; plot(TC.planarity,'ko','MarkerFaceColor','y','axeshandle',gca); -xlabel(gca,'') +xlabel(gca,'Seconds'); ylim(gca,[0 1]); -legend(gca,'Rectilinearity','Planarity',1); +legend(gca,'Rectilinearity','Planarity'); title(gca,''); -set(gca,'XTickLabel',[]); +%set(gca,'XTickLabel',[]); set(gca,'XGrid','on') +set(gca,'XLim',xlim) +if bool_inc_az -% plot azimuth -subplot(6,1,5); -plot(TC.azimuth,'ko','MarkerFaceColor',[1 .7 0],'axeshandle',gca); -set(gca,'YTick',[-360:90:360]); -ylim(gca,[-1 361]); -xlabel(gca,'') -legend('Azimuth',1); -title(gca,''); -set(gca,'XTickLabel',[]); -set(gca,'XGrid','on') + % plot azimuth + subplot(6,1,5); + plot(TC.azimuth,'ko','MarkerFaceColor',[1 .7 0],'axeshandle',gca); + set(gca,'YTick',[-360:90:360]); + ylim(gca,[-1 361]); + xlabel(gca,'') + legend('Azimuth'); + title(gca,''); + set(gca,'XTickLabel',[]); + set(gca,'XGrid','on') + set(gca,'XLim',xlim) + % plot inclination + subplot(6,1,6); + plot(TC.inclination,'ko','MarkerFaceColor',[1 .7 0],'axeshandle',gca); + set(gca,'YTick',[-90:30:90]); + ylim(gca,[-1 91]); + xlabel(gca,'') + legend(gca,'Inclination'); + title(gca,''); + set(gca,'XGrid','on') + set(gca,'XLim',xlim) -% plot inclination -subplot(6,1,6); -plot(TC.inclination,'ko','MarkerFaceColor',[1 .7 0],'axeshandle',gca); -set(gca,'YTick',[-90:30:90]); -ylim(gca,[-1 91]); -xlabel(gca,'') -legend(gca,'Inclination',1); -title(gca,''); -set(gca,'XGrid','on') - +end set(gcf, 'paperorientation', 'portrait'); set(gcf, 'paperposition', [.25 .5 8 10] ); diff --git a/core/@threecomp/private/create_demo_data.m b/core/@threecomp/private/create_demo_data.m old mode 100644 new mode 100755 diff --git a/core/@threecomp/private/getproperty.m b/core/@threecomp/private/getproperty.m old mode 100644 new mode 100755 diff --git a/core/@threecomp/private/parseargs.m b/core/@threecomp/private/parseargs.m old mode 100644 new mode 100755 diff --git a/core/@threecomp/private/property2varargin.m b/core/@threecomp/private/property2varargin.m old mode 100644 new mode 100755 diff --git a/core/@waveform/abs.m b/core/@waveform/abs.m old mode 100644 new mode 100755 diff --git a/core/@waveform/addfield.m b/core/@waveform/addfield.m old mode 100644 new mode 100755 diff --git a/core/@waveform/addhistory.m b/core/@waveform/addhistory.m old mode 100644 new mode 100755 diff --git a/core/@waveform/addmetrics.m b/core/@waveform/addmetrics.m new file mode 100755 index 0000000..dd99e35 --- /dev/null +++ b/core/@waveform/addmetrics.m @@ -0,0 +1,146 @@ +function w = addmetrics(w, maxTimeDiff) +%ADDMETRICS +% addmetrics(w) will compute some amplitude, energy and frequency metrics +% for each waveform in waveform vector w, and add them to the 'metrics' +% structure for that waveform +% +% addmetrics(w, maxTimeDiff) is a slight variation where the max and min +% found will correspond to the maximum peak to peak variation found within +% a maxTimeDiff timewindow. (If not given, the max and min are for the whole +% waveform). Note that maxTimeDiff should be about 1 to a few periods of the +% signal you are interested in. +% +% TO DO: a more efficient 1 period algorithm could use findpeaks, see +% bottom of the m-file + + SECONDS_PER_DAY = 86400; + maxTime = -1; + minTime = -1; + maxAmp = -1; + minAmp = -1; + p2p = -1; + stdev = -1; + energy = -1; + amp = -ones(size(w)); + Nw = numel(w); + + for wavnum=1:Nw + fprintf('.'); + clear metrics + + %thisW = detrend(fillgaps(w(wavnum),'interp')); % make sure there is no trend or offset + thisW = clean(w(wavnum)); + wstart = get(thisW,'start'); % waveform start time + wend = get(thisW,'end'); % waveform end time + wstd = std(thisW); % waveform standard deviation - for noise estimation + fs = get(thisW,'freq'); + y = get(thisW,'data'); + u = get(thisW,'units'); + +% %% THIS IS THE START OF AN ATTEMPT TO FIND THE EVENT START AND END TIME BY RUNNING AN STA/LTA +% close all +% %plot(thisW) +% % set the STA/LTA detector +% sta_seconds = 0.7; % STA time window 0.7 seconds +% lta_seconds = 10.0; % LTA time window 7 seconds +% thresh_on = 2.0; % Event triggers "ON" with STA/LTA ratio exceeds 3 +% thresh_off = 1.0; % Event triggers "OFF" when STA/LTA ratio drops below 1.5 +% minimum_event_duration_seconds = 1.0; % Trigger must be on at least 2 secs +% pre_trigger_seconds = 0; % Do not pad before trigger +% post_trigger_seconds = 0; % Do not pad after trigger +% event_detection_params = [sta_seconds lta_seconds thresh_on thresh_off ... +% minimum_event_duration_seconds]; +% [cobj,sta,lta,sta_to_lta] = Detection.sta_lta(thisW, 'edp', event_detection_params, ... +% 'lta_mode', 'frozen'); +% % h3 = drumplot(thisW, 'mpl', 1, 'catalog', cobj); +% % plot(h3) +% input('any key') +% % Several events may be detected. Need to pick the one at the +% % expected time, considering the travel time + + + % WHAT TYPE OF MAXIMUM & MINIMUM DO WE WANT? + if exist('maxTimeDiff', 'var') + % compute largest peak to peak amplitude + % (Note a different algorithm could be added here, using + % findpeaks) + metrics.maxTimeDiff = maxTimeDiff; + + % Define time window + numSamples = length(y); + seconds_end_offset = (wend - wstart) * SECONDS_PER_DAY; + sample_to_end_at = min( [round( seconds_end_offset * fs) numSamples]); + + % Loop over subwindows + % find p2p amplitude in each, compare to highest p2p found so far + N = round(fs * maxTimeDiff); + + % COMPUTING AMPLITUDE METRICS + try + [vamin, vamax] = running_min_max(y, N); + vap2p = vamax-vamin; % biggest peak to peak in each timewindow of length N + [maxap2p, maxap2pindex] = max(vap2p); + amin = vamin(maxap2pindex); + amax = vamax(maxap2pindex); + amaxindex = find(y==amax); + aminindex = find(y==amin); + catch + [amax, amaxindex] = max(thisW); + [amin, aminindex] = min(thisW); + end + + else + [amax, amaxindex] = max(thisW); + [amin, aminindex] = min(thisW); + end + + maxSecs = amaxindex/fs; + minSecs = aminindex/fs; + maxTime = wstart + maxSecs/SECONDS_PER_DAY; + minTime = wstart + minSecs/SECONDS_PER_DAY; + p2p = amax - amin; + amp(wavnum) = round(nanmax(abs([amax amin])), 4, 'significant'); + stdev = round(wstd, 4, 'significant'); % stdev of whole trace - noise level estimate + energy = round(sum(y.^2)/fs, 4, 'significant'); % round to 4 sigfigs + + % ADD ALL METRICS TO THE WAVEFORM OBJECT + metrics.minTime = minTime; + metrics.maxTime = maxTime; + metrics.minAmp = round(amin, 4, 'significant'); + metrics.maxAmp = round(amax, 4, 'significant'); + metrics.stdev = round(stdev, 4, 'significant'); + metrics.energy = round(energy, 4, 'significant'); + metrics.units = u; + thisW = addfield(thisW, 'metrics', metrics); + w(wavnum) = thisW; + + if mod(wavnum,30) == 0 + fprintf('\nDone %d out of %d\n',wavnum, Nw); + end + + end + %fprintf('\n(Complete)\n'); +end + + +function [amin,amax]=running_min_max(y, N) + startsamp=1; + for endsamp=1:N + amax(endsamp) = max(y(startsamp:endsamp)); + amin(endsamp) = min(y(startsamp:endsamp)); + end + for startsamp=2:length(y)-N+1 + endsamp=startsamp+N-1; + amax(endsamp) = max(y(startsamp:endsamp)); + amin(endsamp) = min(y(startsamp:endsamp)); + end +end + +function [amin, aminindex, amax, amaxindex] = find_biggest_peak2peak(y, N) +% SKELETON: NOT USED +% there are options in findpeaks to ignore small adjacent peaks i should be +% using here but don't + [pos_pks, pos_locs] = findpeaks(y); + [neg_pks, neg_locs] = findpeaks(-y); + % now we just need to look for neg_pks which occur within +end diff --git a/core/@waveform/align.m b/core/@waveform/align.m old mode 100644 new mode 100755 diff --git a/core/@waveform/plot_spectrum.m b/core/@waveform/amplitude_spectrum.m old mode 100644 new mode 100755 similarity index 72% rename from core/@waveform/plot_spectrum.m rename to core/@waveform/amplitude_spectrum.m index c26e66d..f9d14ec --- a/core/@waveform/plot_spectrum.m +++ b/core/@waveform/amplitude_spectrum.m @@ -1,7 +1,7 @@ -function s = plot_spectrum(w) - % add_spectral_data Simple method to compute frequency +function s = amplitude_spectrum(w) + % amplitde_spectrum Simple method to compute amplitude % spectrum for a vector waveform object. Uses the MATLAB fft function. - % w = add_spectral_data(w) + % s = amplitude_spectrum(w) % % Inputs: % w - a waveform object (or vector of waveform objects) @@ -17,13 +17,13 @@ % freqratio - frequency ratio (Rodgers) % % Example 1 (for a single waveform object, w): - % s = add_spectral_values(w) + % s = amplitude_spectrum(w) % semilogx(s.f, s.amp) % xlabel('Frequency (Hz)') % ylabel('Amplitude'); % % Example 2 (for a waveform vector, w) - % s = add_spectral_values(w); + % s = amplitude_spectrum(w) % t = get(w, 'start') % peakf = [s.peakf]; % plot(t, peakf,'o') @@ -37,10 +37,9 @@ return end - colours = 'rgbmcyk'; - figure for count = 1:numel(w) - colour = colours(mod(count, 7) + 1); + + Fsamp= get(w(count), 'freq'); signal=get(w(count), 'data'); Nsignal=length(signal); @@ -48,15 +47,11 @@ Y=fft(signal,NFFT); % X will have same length as signal, and will be complex with a magnitude and phase NumUniquePts = NFFT/2 + 1; % Mike uses ceil((N+1)/2) in wf_fft A=2*abs(Y(1:NumUniquePts))/Nsignal; + %A=smooth(A,100); phi = angle(Y(1:NumUniquePts)); f = Fsamp/2*linspace(0,1,NumUniquePts); - %ax(count)=subplot(numel(w), 1, count); - loglog(f, A, colour); hold on; - axis tight; - ctag = get(w(count),'ChannelTag'); - %title(ctag.string()); - xlabel('f (Hz)') - ylabel('Amp') + + % add spectrum vectors to a structure s(count).f = f; % frequencies @@ -80,7 +75,29 @@ s(count).freqratio= log2(Ah/Al); end - ctags = get(w,'ChannelTag'); - legend(ctags.string(),'location', 'south') - linkaxes(ax,'x'); -end \ No newline at end of file + + + if nargout==999 + colours = 'rgbmcyk'; + figure + ax= []; % axis array + for count = 1:numel(w) + colour = colours(mod(count-1, 7) + 1); + %ax(count)=subplot(numel(w), 1, count); + %ax(count) = loglog(f, A, colour); hold on; + ax(count) = plot(f, A, colour); hold on; + axis tight; + ctag = get(w(count),'ChannelTag'); + %title(ctag.string()); + xlabel('f (Hz)') + ylabel('Amplitude') + end + + ctags = get(w,'ChannelTag'); + legend(ctags.string(),'location', 'south') + try + linkaxes(ax,'x'); + end + end + +end diff --git a/core/@waveform/bin_stack.m b/core/@waveform/bin_stack.m old mode 100644 new mode 100755 diff --git a/core/@waveform/clean.m b/core/@waveform/clean.m new file mode 100755 index 0000000..1d329ec --- /dev/null +++ b/core/@waveform/clean.m @@ -0,0 +1,32 @@ +function w = clean(w) +% CLEAN Clean up waveform object(s) +% w = clean(w) will detrend waveforms (in a smart way, aware of NaN +% values marking missing values), then use fillgaps to mark bounded NaNs +% with linear-interpolated values, and also mark bounded NaNs with zeroes +% (now okay, since trend has been removed, so no weird startup effects). +% It then removes non-linear trends using a 20-s (0.05 Hz) highpass +% filter. + +% Glenn Thompson March 28, 2017 + + for c=1:numel(w) + + if ~isempty(w(c)) + + % remove spikes of length 1 + w(c) = medfilt1(w(c), 3); + + % smart detrend + w(c) = detrend(w(c)); + + % % fill gaps to get rid of NaNs marking missing values, so we can filter + w(c) = fillgaps(w(c), 'interp'); + + % highpass data at 20s - to remove non-linear trends + f = filterobject('h',0.05,2); + w(c) = filtfilt(f,w(c)); + + end + end + +end \ No newline at end of file diff --git a/core/@waveform/clearhistory.m b/core/@waveform/clearhistory.m old mode 100644 new mode 100755 diff --git a/core/@waveform/clip.m b/core/@waveform/clip.m old mode 100644 new mode 100755 diff --git a/core/@waveform/combine.m b/core/@waveform/combine.m old mode 100644 new mode 100755 index 67a2be8..2dc4355 --- a/core/@waveform/combine.m +++ b/core/@waveform/combine.m @@ -7,6 +7,8 @@ % AUTHOR: Celso Reyes, Geophysical Institute, Univ. of Alaska Fairbanks % $Date$ % $Revision$ + % Glenn Thompson 2018/01/09: replaced splice_waveform routine as it was + % failing as part of drumplot.plot if numel(waveformlist) < 2 %nothing to do combined_waveforms = waveformlist; @@ -67,16 +69,49 @@ w = set(w1,'data',[w1.data; toAdd; w2.data]); end +% function w = spliceWaveform(w1, w2) +% +% % NOTE * Function uses direct field access +% timesToGrab = sum(get(w1,'timevector') < get(w2,'start')); +% +% samplesRemoved = numel(w1.data) - timesToGrab; +% +% w = set(w1,'data',[double(extract(w1,'index',1,timesToGrab)); w2.data]); +% +% w= addhistory(w,'SPLICEPOINT: %s, removed %d points (overlap)',... +% datestr(get(w2,'start')),samplesRemoved); +% end + function w = spliceWaveform(w1, w2) - % NOTE * Function uses direct field access - timesToGrab = sum(get(w1,'timevector') < get(w2,'start')); - - samplesRemoved = numel(w1.data) - timesToGrab; - - w = set(w1,'data',[double(extract(w1,'index',1,timesToGrab)); w2.data]); - - w= addhistory(w,'SPLICEPOINT: %s, removed %d points (overlap)',... - datestr(get(w2,'start')),samplesRemoved); +% +% % NOTE * Function uses direct field access + snum1 = get(w1,'start'); + snum2 = get(w2,'start'); + enum1 = get(w1,'end'); + enum2 = get(w2,'end'); + [snummin, snuminindex] = min([snum1 snum2]); + [enummax, enumaxindex] = max([enum1 enum2]); + dnum1 = get(w1,'timevector'); + dnum2 = get(w2,'timevector'); + data1 = get(w1,'data'); + data2 = get(w2,'data'); + w = w1; + dnum=dnum1; + data=data1; + ind = find(dnum20 + dnum = [dnum2(ind); dnum]; + data = [data2(ind); data]; + end + ind = find(dnum2>enum1); + if numel(ind)>0 + dnum = [dnum; dnum2(ind)]; + data = [data; data2(ind)]; + end + snum = dnum(1); + w = set(w, 'start', snum, 'data', data); + + end function result = overlaps(dt, sampleInterval) diff --git a/core/@waveform/delfield.m b/core/@waveform/delfield.m old mode 100644 new mode 100755 diff --git a/core/@waveform/demean.m b/core/@waveform/demean.m old mode 100644 new mode 100755 diff --git a/core/@waveform/detrend.m b/core/@waveform/detrend.m old mode 100644 new mode 100755 index 64a1481..0a93c31 --- a/core/@waveform/detrend.m +++ b/core/@waveform/detrend.m @@ -7,13 +7,21 @@ % WAVEFORM: a waveform object N-DIMENSIONAL % OPTIONS: optional parameters as described in matlab's DETREND % - % WARNING: Detrending a waveform with NAN values will return a waveform - % with nothing but NAN values. Replace NaN values before detrending. - % "demean", however, works fine but only removes constant offsets. + % Missing values in GISMO should be marked with NaN. waveform/detrend + % attempts to handle these smartly. Any leading or trailing NaNs are + % temporarily removed, leaving data from the first real value to the + % last real value. Any NaNs within this sequence are temporarily + % replaced via linear interpolation, then the sequence is detrended. + % Then the removed NaNs are all put back in. + % + % If you wish to handle missing values (NaNs) in a different way, do so + % before calling waveform/detrend. % % See also DETREND for list of options % AUTHOR: Celso Reyes, Geophysical Institute, Univ. of Alaska Fairbanks + % Modified by Glenn Thompson 20170328 to handle missing values (marked + % by NaN) smartly. % $Date$ % $Revision$ @@ -27,13 +35,30 @@ if ~warnedAboutNAN && any(isnan(d)) warnedAboutNAN = true; warning('Waveform:detrend:NaNwarning',... - ['NAN values exist in one or more waveforms.',... - ' Detrend will return values of NAN\n',... - 'One possible way to correct this problem is to use FILLGAPS ',... - 'to replace NAN values prior to detrending\n',... - ' "demean", however, should work fine.']); + ['NaN values exist in one or more waveforms.',... + ' Attempting to deal with these in a smart way']); end - w(I).data = detrend(d,varargin{:}); + % 20170328 New code added by Glenn Thompson to deal with leading or + % trailing NaN. It finds the first & last non-NaN value, and only + % tries to detrend that section of the data. + % Between the first and last non-NaN value, it temporarily fills any + % intervening NaNs via linear interpolation. Then puts them back in + % after detrending. + % The overall result is that NaNs are preserved, but the good data + % should also get detrended. A more sophisticated method might define + % breakpoints for detrending. + good_data_indices = find(~isnan(d)); + first_good_index = good_data_indices(1); + last_good_index = good_data_indices(end); + d2 = d(first_good_index:last_good_index); + still_missing_data_indices = find(isnan(d2)); + d2(isnan(d2)) = interp1(find(~isnan(d2)), d2(~isnan(d2)), find(isnan(d2)),'linear'); + d2 = detrend(d2,varargin{:}); + d2(still_missing_data_indices) = NaN; + d(first_good_index:last_good_index) = d2; + + %w(I).data = detrend(d,varargin{:}); + w(I).data = d; end end diff --git a/core/@waveform/diff.m b/core/@waveform/diff.m old mode 100644 new mode 100755 diff --git a/core/@waveform/disp.m b/core/@waveform/disp.m old mode 100644 new mode 100755 diff --git a/core/@waveform/display.m b/core/@waveform/display.m old mode 100644 new mode 100755 diff --git a/core/@waveform/double.m b/core/@waveform/double.m old mode 100644 new mode 100755 diff --git a/core/@waveform/eq.m b/core/@waveform/eq.m old mode 100644 new mode 100755 diff --git a/core/@waveform/extract.m b/core/@waveform/extract.m old mode 100644 new mode 100755 diff --git a/core/@waveform/fillgaps.m b/core/@waveform/fillgaps.m old mode 100644 new mode 100755 index 3e4bfe9..411d430 --- a/core/@waveform/fillgaps.m +++ b/core/@waveform/fillgaps.m @@ -1,7 +1,8 @@ function w = fillgaps(w,value, gapvalue) % FILLGAPS - fill missing data with values of your choice % W = fillgaps(W,number) fills data with the number of your choice - % "number" can also be nan or inf or -inf + % "number" can also be NaN or Inf or -Inf (GISMO is designed to mark + % missing values with NaN). % % W = fillgaps(W,[]) removes missing data from waveform. Warning, the % resulting timing issues are NOT corrected for! @@ -16,7 +17,8 @@ % choice (can also be Inf, -Inf, NaN) % % 'interp' - assuming missing values are marked by NaN, this will use - % cubic interpolation to estimate the missing values + % linear interpolation to estimate the missing values. No values are + % extrapolated. % % FILLGAPS is designed to replace NaN values. However, if if you use % W = fillgaps(W,number, gapvalue), then ALL data points with the value @@ -55,9 +57,14 @@ w(N).data(getgaps(w(N))) = value; end case 'interp' % blame Glenn, inspired by http://www.mathworks.com/matlabcentral/fileexchange/8225-naninterp by E Rodriguez + % 20170328: changed spline interpolation - which blows up because it will extrapolate too - into + % linear interpolation. But with linear interpolation, since it + % does not extrapolate, if there are NaNs from beginning to some + % sample, these will not be replaced. So replace these by zeros. for N = 1:numel(w); X = get(w(N),'data'); - X(isnan(X)) = interp1(find(~isnan(X)), X(~isnan(X)), find(isnan(X)),'spline'); + X(isnan(X)) = interp1(find(~isnan(X)), X(~isnan(X)), find(isnan(X)),'linear'); + X(isnan(X)) = 0; w(N) = set(w(N),'data',X); end otherwise diff --git a/core/@waveform/fix_data_length.m b/core/@waveform/fix_data_length.m old mode 100644 new mode 100755 diff --git a/core/@waveform/get.m b/core/@waveform/get.m old mode 100644 new mode 100755 diff --git a/core/@waveform/getm.m b/core/@waveform/getm.m old mode 100644 new mode 100755 diff --git a/core/@waveform/getpeaks.m b/core/@waveform/getpeaks.m old mode 100644 new mode 100755 diff --git a/core/@waveform/getsamples.m b/core/@waveform/getsamples.m old mode 100644 new mode 100755 diff --git a/core/@waveform/gettimerange.m b/core/@waveform/gettimerange.m old mode 100644 new mode 100755 diff --git a/core/@waveform/hilbert.m b/core/@waveform/hilbert.m old mode 100644 new mode 100755 diff --git a/core/@waveform/history.m b/core/@waveform/history.m old mode 100644 new mode 100755 diff --git a/core/@waveform/integrate.m b/core/@waveform/integrate.m old mode 100644 new mode 100755 diff --git a/core/@waveform/isempty.m b/core/@waveform/isempty.m old mode 100644 new mode 100755 diff --git a/core/@waveform/isfield.m b/core/@waveform/isfield.m old mode 100644 new mode 100755 diff --git a/core/@waveform/ismember.m b/core/@waveform/ismember.m old mode 100644 new mode 100755 diff --git a/core/@waveform/isvertical.m b/core/@waveform/isvertical.m old mode 100644 new mode 100755 diff --git a/core/@waveform/legend.m b/core/@waveform/legend.m old mode 100644 new mode 100755 diff --git a/core/@waveform/loadobj.m b/core/@waveform/loadobj.m old mode 100644 new mode 100755 diff --git a/core/@waveform/loadsac.m b/core/@waveform/loadsac.m old mode 100644 new mode 100755 diff --git a/core/@waveform/max.m b/core/@waveform/max.m old mode 100644 new mode 100755 diff --git a/core/@waveform/mean.m b/core/@waveform/mean.m old mode 100644 new mode 100755 diff --git a/core/@waveform/medfilt1.m b/core/@waveform/medfilt1.m old mode 100644 new mode 100755 diff --git a/core/@waveform/median.m b/core/@waveform/median.m old mode 100644 new mode 100755 diff --git a/core/@waveform/min.m b/core/@waveform/min.m old mode 100644 new mode 100755 diff --git a/core/@waveform/minus.m b/core/@waveform/minus.m old mode 100644 new mode 100755 diff --git a/core/@waveform/mrdivide.m b/core/@waveform/mrdivide.m old mode 100644 new mode 100755 diff --git a/core/@waveform/mtimes.m b/core/@waveform/mtimes.m old mode 100644 new mode 100755 diff --git a/core/@waveform/normalize.m b/core/@waveform/normalize.m old mode 100644 new mode 100755 diff --git a/core/@waveform/pad.m b/core/@waveform/pad.m old mode 100644 new mode 100755 diff --git a/core/@waveform/plot.m b/core/@waveform/plot.m old mode 100644 new mode 100755 index a6cb0e3..aa09869 --- a/core/@waveform/plot.m +++ b/core/@waveform/plot.m @@ -27,6 +27,14 @@ % % the default XUNIT is seconds % + % plot(waveform, 'axeshandle', axh, ...) plots the waveform in the axes + % defined by axh, rather than starting with a new figure. + % + % plot(waveform, 'starttime', datenum(2000,1,1,1,0,0), 'endtime', datenum(2001,1,1,1,10,0), ...) + % will force the plot to start and end at those times, regardless of + % the time data in the waveform. + % + % % For the following examples: % % W is a waveform, and W2 is a smaller waveform (from within W) % W = waveform('SSLN','SHZ','04/02/2005 01:00:00', '04/02/2005 01:10:00'); diff --git a/core/@waveform/plot_helicorder.m b/core/@waveform/plot_helicorder.m old mode 100644 new mode 100755 index afd0277..99398af --- a/core/@waveform/plot_helicorder.m +++ b/core/@waveform/plot_helicorder.m @@ -1,12 +1,14 @@ -function [ output_args ] = plot_helicorder( w, varargin ) +function plot_helicorder( w, varargin ) %WAVEFORM/PLOT_HELICORDER Plot waveform objects as helicorders % Each waveform object is plotted as a separate helicorder plot using the % drumplot class - +close all % Glenn Thompson 20160513 for c=1:numel(w) - h = drumplot(demean(w(c)), varargin{:}); - plot(h); + %figure + drumobj0 = drumplot(demean(w(c)), varargin{:}) + plot(drumobj0); + pause(2) end end diff --git a/core/@waveform/plot_panels.m b/core/@waveform/plot_panels.m old mode 100644 new mode 100755 index 36cd3d4..cd41cc4 --- a/core/@waveform/plot_panels.m +++ b/core/@waveform/plot_panels.m @@ -1,11 +1,15 @@ -function fh=plot_panels(w, alignWaveforms) +function plot_panels(w, varargin) %PLOT_PANELS Plot multiple waveform objects as separate linked panels -% PLOT_PANELS(w, alignWaveforms) +% PLOT_PANELS(w, varargin) % where: % w = a vector of waveform objects % alignWaveforms is either true or false (default) % PLOT_PANELS(w) will plot each waveform is plotted against absolute time. -% PLOT_PANELS(w, true) will align the waveforms on their start times. +% PLOT_PANELS(w, 'alignWaveforms', true) will align the waveforms on their start times. +% PLOT_PANELS(w, 'arrivals', ArrivalObject) will superimpose arrival +% times on the waveforms. +% PLOT_PANELS(w, 'visible', 'off') will prevent the plot showing on the +% screen. % % Along the top of each trace are displayed 3 numbers in different colors: % blue - the mean offset of the trace @@ -15,13 +19,22 @@ % Glenn Thompson 2014/11/05, generalized after a function written in 2000 % to operate on Seisan files only + w = iceweb.waveform_remove_empty(w); + if numel(w)==0 || isempty(w) warning('no waveforms to plot') return end - if ~exist('alignWaveforms', 'var') - alignWaveforms = false; + p = inputParser; + p.addParameter('alignWaveforms', false, @isnumeric); % optional name-param pairs + p.addParameter('visible', 'on', @isstr) + p.addParameter('arrivals', []) + p.parse(varargin{:}); + alignWaveforms = p.Results.alignWaveforms; + visibility = p.Results.visible; + if isa(p.Results.arrivals, 'Arrival') + arrivalobj = p.Results.arrivals; end % get the first start time and last end time @@ -35,10 +48,12 @@ SECSPERDAY = 60 * 60 * 24; nwaveforms = numel(w); - fh=figure; - trace_height=0.9/nwaveforms; - left=0.1; - width=0.8; + previousfignum = get_highest_figure_number(); + fh=figure(previousfignum+1); + set(fh,'visible',visibility); + trace_height=0.87/nwaveforms; + left=0.12; + width=0.78; for wavnum = 1:nwaveforms data=get(w(wavnum),'data'); dnum=get(w(wavnum),'timevector'); @@ -49,9 +64,51 @@ sta=''; chan=''; end - offset = nanmean(data); + % if sampling rate > 1 Hz, assume this is raw data, if < 1 Hz + % assume RSAM data + if get(w(wavnum),'freq')>1 + offset = nanmean(data); % raw waveform data, 2 sided + else + offset = 0; % RSAM data, absolute, 1 sided + end y=data-offset; + y(isnan(y))=0; ax(wavnum)=axes('Position',[left 0.98-wavnum*trace_height width trace_height]); + + % arrivals + if exist('arrivalobj','var') + %disp('- adding arrivals to panel plot') + hold on + thisctag = strrep(string(get(w(wavnum),'ChannelTag')),'-',''); + Index = find(ismember(arrivalobj.channelinfo, thisctag)); + for arrnum=1:numel(Index) + arrtime = arrivalobj.time(Index(arrnum)); + if alignWaveforms + relarrtime = (arrtime-min(dnum))*SECSPERDAY; + else + relarrtime = (arrtime-snum)*SECSPERDAY; + end + plot([relarrtime relarrtime], [min(y) max(y)],'r', 'LineWidth',3); + end + %hold off + end + + % metrics + try + m = get(w(wavnum),'metrics'); + hold on + if alignWaveforms + relmintime = (m.minTime-min(dnum))*SECSPERDAY; + relmaxtime = (m.maxTime-min(dnum))*SECSPERDAY; + else + relmintime = (m.minTime-snum)*SECSPERDAY; + relmaxtime = (m.maxTime-snum)*SECSPERDAY; + end + plot([relmintime relmaxtime], [m.minAmp m.maxAmp],'g','LineWidth',3); + %hold off + end + + % waveform data if alignWaveforms plot((dnum-min(dnum))*SECSPERDAY, y,'-k'); set(gca, 'XLim', [0 maxduration*SECSPERDAY]); @@ -61,6 +118,14 @@ end % xlim = get(gca, 'XLim'); % set(gca,'XTick', linspace(xlim(1), xlim(2), 11)); + + if all((get(w(wavnum),'data'))>=0) % added for RSAM data + ylims = get(gca, 'YLim'); + set(gca, 'YLim', [0 ylims(2)]); + end + if length(chan)>3 + chan = chan(1:3); + end ylabel(sprintf('%s\n%s ',sta,chan),'FontSize',10,'Rotation',90); set(gca,'YTick',[],'YTickLabel',['']); if wavnum1 - w = reshape(w, numel(w), 1); -end -if ~exist('s','var') - s = spectralobject(1024, 924, 10, [60 120]); +figure +nfft = 1024; +overlap = 924; +fmax = 10; +dbLims = [60 120]; +p = inputParser; +p.addRequired('w'); +p.addParameter('spectralobject', spectralobject(nfft, overlap, fmax, dbLims)); +p.addParameter('plot_metrics', 0, @isnumeric); +p.parse(w, varargin{:}); +w=p.Results.w; +s=p.Results.spectralobject; + +% if ~class(s, 'spectralobject') +% disp('Oops, you did not give a valid spectralobject. Using default') + if numel(w)>1 + w = reshape(w, numel(w), 1); + end + + if p.Results.plot_metrics + [result,Tcell,Fcell,Ycell, meanf, peakf] = iceweb.spectrogram_iceweb(s, w, 'plot_metrics',1); + else + [result,Tcell,Fcell,Ycell, meanf, peakf] = iceweb.spectrogram_iceweb(s, w); + end + + end -iceweb.spectrogram_iceweb(s, w, 0.75); + diff --git a/core/@waveform/stack.m b/core/@waveform/stack.m old mode 100644 new mode 100755 diff --git a/core/@waveform/std.m b/core/@waveform/std.m old mode 100644 new mode 100755 diff --git a/core/@waveform/taper.m b/core/@waveform/taper.m old mode 100644 new mode 100755 diff --git a/core/@waveform/times.m b/core/@waveform/times.m old mode 100644 new mode 100755 diff --git a/core/@waveform/uminus.m b/core/@waveform/uminus.m old mode 100644 new mode 100755 diff --git a/core/@waveform/var.m b/core/@waveform/var.m old mode 100644 new mode 100755 diff --git a/core/@waveform/waveform.m b/core/@waveform/waveform.m old mode 100644 new mode 100755 index f91e9dd..a53503f --- a/core/@waveform/waveform.m +++ b/core/@waveform/waveform.m @@ -41,7 +41,7 @@ % a waveform from consituent parts. % % CHANNELTAG - either 'net.sta.loc.cha' or a ChannelTag -% SAMPLERATE - Sampling frequency, in Hz (default nan) +% SAMPLERATE - Sampling frequency, in Hz (default NaN) % DATA - a vector of seismic amplitudes (default []) % STARTTIME - Start time, in most any format (default '1/1/1970') % UNITS - a string describing the data units @@ -227,7 +227,7 @@ %create a fresh waveform. All calls to the waveform object, aside %from the "copy" call (case nargin==1) will be initated HERE. blankW.cha_tag = ChannelTag(); - blankW.Fs = nan; + blankW.Fs = NaN; blankW.start = 719529; % datenum for 1970-01-01 blankW.data = double([]); blankW.units = 'Counts'; diff --git a/core/@waveform/waveform2rsam.m b/core/@waveform/waveform2rsam.m old mode 100644 new mode 100755 index 9e9830f..460f5ef --- a/core/@waveform/waveform2rsam.m +++ b/core/@waveform/waveform2rsam.m @@ -1,16 +1,16 @@ -function s = waveform2rsam(w, method, samplingPeriod) +function s = waveform2rsam(w, measure, samplingIntervalSeconds) %WAVEFORM2RSAM create an RSAM-like object from a waveform object % RSAM data are typically 1 sample per minute, where each sample is the % average amplitude of that minute of data. They are used extensively used % in volcano monitoring. % % Usage: -% s = rsam(waveform, method, samplingPeriod) +% s = rsam(waveform, method, samplingIntervalSeconds) % % Input Arguments % WAVEFORM: waveform object N-dimensional % -% METHOD: which method of sampling to perform within each sample +% MEASURE: which method of sampling to perform within each sample % window % 'max' : maximum value % 'min' : minimum value @@ -18,7 +18,7 @@ % 'median' : mean value % 'rms' : rms value (added 2011/06/01) % -% SAMPLINGPERIOD : the number of seconds between samples (Default: +% samplingIntervalSeconds : the number of seconds between samples (Default: % 60s) % % @@ -35,23 +35,32 @@ % taken, and the time window is 1s rather than 60s. % % Glenn Thompson 2014/10/28 -if ~exist('method', 'var') - method = 'mean'; +if ~exist('measure', 'var') + measure = 'mean'; end -if ~exist('samplingPeriod', 'var') - samplingPeriod = 60; +if ~exist('samplingIntervalSeconds', 'var') + samplingIntervalSeconds = 60; end -% detrend data after fill gaps to get rid of NaNs marking missing values -w = fillgaps(w, 'interp'); -w = detrend(w); +% clean waveform objects - to remove linear and non-linear trends +w = clean(w); for i = 1:numel(w) - Wsamplingperiod = 1.0 / get(w(i), 'freq'); - % either set to whatever samplingPeriod seconds of data are, or the - % length of data if less - crunchfactor = min([round(samplingPeriod / Wsamplingperiod) numel(get(w(i),'data'))]); - wabs = set(w(i), 'data', abs(get(w(i),'data')) ); - wresamp = resample(wabs, method, crunchfactor); - s(i) = rsam(get(wresamp,'timevector')', get(wresamp,'data')', 'sta', get(wresamp, 'station'), 'chan', get(wresamp, 'channel'), 'units', get(wresamp, 'units'), 'snum', get(wresamp, 'start'), 'enum', get(wresamp, 'end')); + if ~isempty(w(i)) + WsamplingIntervalSeconds = 1.0 / get(w(i), 'freq'); + % either set to whatever samplingIntervalSeconds seconds of data are, or the + % length of data if less + crunchfactor = min([round(samplingIntervalSeconds / WsamplingIntervalSeconds) numel(get(w(i),'data'))]); + wabs = set(w(i), 'data', abs(get(w(i),'data')) ); + wresamp = resample(wabs, measure, crunchfactor); + s(i) = rsam(get(wresamp,'timevector')', get(wresamp,'data')', ... + 'ChannelTag', get(wresamp, 'ChannelTag'), ... + 'measure', measure, ... + 'units', get(wresamp, 'units')); + else + s(i) = rsam([], [], ... + 'ChannelTag', get(w(i), 'ChannelTag'), ... + 'measure', measure, ... + 'units', get(w(i), 'units')); + end end diff --git a/core/@waveform/waveform2sound.m b/core/@waveform/waveform2sound.m old mode 100644 new mode 100755 diff --git a/core/@waveform/zero2nan.m b/core/@waveform/zero2nan.m old mode 100644 new mode 100755 diff --git a/core/compute_travel_times.m b/core/compute_travel_times.m new file mode 100644 index 0000000..b8ba368 --- /dev/null +++ b/core/compute_travel_times.m @@ -0,0 +1,16 @@ +function sites = compute_travel_times(source, sites, seismicspeed, infrasoundspeed) +for c=1:numel(sites) + thissite = sites(c); + + + sites(c).distance = distance(source.lat, source.lon, thissite.lat, thissite.lon) * 111000; %m + thischan = get(thissite.channeltag, 'channel'); + if thischan(2)=='D' % infrasound + sites(c).traveltime = sites(c).distance / infrasoundspeed; + elseif thischan(2)=='H' % seismic + sites(c).traveltime = sites(c).distance / seismicspeed; + else + sites(c).traveltime = 0; + end + +end \ No newline at end of file diff --git a/core/dev/+dataretrieval/@antelopesource/antelopesource.m b/core/dev/+dataretrieval/@antelopesource/antelopesource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/@antelopesource/get_antelope_traces.m b/core/dev/+dataretrieval/@antelopesource/get_antelope_traces.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/@antelopesource/load_antelope.m b/core/dev/+dataretrieval/@antelopesource/load_antelope.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/@antelopesource/segtype2units.m b/core/dev/+dataretrieval/@antelopesource/segtype2units.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/@antelopesource/traceToWaveform.m b/core/dev/+dataretrieval/@antelopesource/traceToWaveform.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/iriswebservicesource.m b/core/dev/+dataretrieval/iriswebservicesource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/matsource.m b/core/dev/+dataretrieval/matsource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/obspysource.m b/core/dev/+dataretrieval/obspysource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/sacsource.m b/core/dev/+dataretrieval/sacsource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/seisansource.m b/core/dev/+dataretrieval/seisansource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/spatiotemporal_database.m b/core/dev/+dataretrieval/spatiotemporal_database.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/spatiotemporal_datasource.m b/core/dev/+dataretrieval/spatiotemporal_datasource.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/spatiotemporal_file.m b/core/dev/+dataretrieval/spatiotemporal_file.m old mode 100644 new mode 100755 diff --git a/core/dev/+dataretrieval/winstonsource.m b/core/dev/+dataretrieval/winstonsource.m old mode 100644 new mode 100755 diff --git a/core/dev/@ChannelDetails/ChannelDetails.m b/core/dev/@ChannelDetails/ChannelDetails.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/NewCorrelation.m b/core/dev/@NewCorrelation/NewCorrelation.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/adjusttrig.m b/core/dev/@NewCorrelation/adjusttrig.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/agc.m b/core/dev/@NewCorrelation/agc.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/align.m b/core/dev/@NewCorrelation/align.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/butter.m b/core/dev/@NewCorrelation/butter.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/cat.m b/core/dev/@NewCorrelation/cat.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/check.m b/core/dev/@NewCorrelation/check.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/cluster.m b/core/dev/@NewCorrelation/cluster.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/colormap.m b/core/dev/@NewCorrelation/colormap.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/conv.m b/core/dev/@NewCorrelation/conv.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/cookbook.m b/core/dev/@NewCorrelation/cookbook.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/corrplot.m b/core/dev/@NewCorrelation/corrplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/crop.m b/core/dev/@NewCorrelation/crop.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/deconv.m b/core/dev/@NewCorrelation/deconv.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/demean.m b/core/dev/@NewCorrelation/demean.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/dendrogramplot.m b/core/dev/@NewCorrelation/dendrogramplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/detrend.m b/core/dev/@NewCorrelation/detrend.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/diff.m b/core/dev/@NewCorrelation/diff.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/disp.m b/core/dev/@NewCorrelation/disp.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/eventplot.m b/core/dev/@NewCorrelation/eventplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/find.m b/core/dev/@NewCorrelation/find.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/get.m b/core/dev/@NewCorrelation/get.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/getclusterstat.m b/core/dev/@NewCorrelation/getclusterstat.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/getstat.m b/core/dev/@NewCorrelation/getstat.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/getval.m b/core/dev/@NewCorrelation/getval.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/hilbert.m b/core/dev/@NewCorrelation/hilbert.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/integrate.m b/core/dev/@NewCorrelation/integrate.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/interferogram.m b/core/dev/@NewCorrelation/interferogram.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/lagplot.m b/core/dev/@NewCorrelation/lagplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/linkage.m b/core/dev/@NewCorrelation/linkage.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/makesynthwaves.m b/core/dev/@NewCorrelation/makesynthwaves.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/match.m b/core/dev/@NewCorrelation/match.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/minus.m b/core/dev/@NewCorrelation/minus.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/norm.m b/core/dev/@NewCorrelation/norm.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/occurrenceplot.m b/core/dev/@NewCorrelation/occurrenceplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/overlayplot.m b/core/dev/@NewCorrelation/overlayplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/plot.m b/core/dev/@NewCorrelation/plot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/sampleplot.m b/core/dev/@NewCorrelation/sampleplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/set.m b/core/dev/@NewCorrelation/set.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/shadedplot.m b/core/dev/@NewCorrelation/shadedplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/sign.m b/core/dev/@NewCorrelation/sign.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/sort.m b/core/dev/@NewCorrelation/sort.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/stack.m b/core/dev/@NewCorrelation/stack.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/statplot.m b/core/dev/@NewCorrelation/statplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/strip.m b/core/dev/@NewCorrelation/strip.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/subset.m b/core/dev/@NewCorrelation/subset.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/taper.m b/core/dev/@NewCorrelation/taper.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/unifytracelengths.m b/core/dev/@NewCorrelation/unifytracelengths.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/waveform.m b/core/dev/@NewCorrelation/waveform.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/wiggleinterferogram.m b/core/dev/@NewCorrelation/wiggleinterferogram.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/wiggleplot.m b/core/dev/@NewCorrelation/wiggleplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/writedb.m b/core/dev/@NewCorrelation/writedb.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/xcorr.m b/core/dev/@NewCorrelation/xcorr.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/xcorr1x1.m b/core/dev/@NewCorrelation/xcorr1x1.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/xcorr1xr.m b/core/dev/@NewCorrelation/xcorr1xr.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/xcorr1xr_orig.m b/core/dev/@NewCorrelation/xcorr1xr_orig.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/xcorrdec.m b/core/dev/@NewCorrelation/xcorrdec.m old mode 100644 new mode 100755 diff --git a/core/dev/@NewCorrelation/xcorrrow.m b/core/dev/@NewCorrelation/xcorrrow.m old mode 100644 new mode 100755 diff --git a/core/dev/@SeismicTrace/SeismicTrace.m b/core/dev/@SeismicTrace/SeismicTrace.m old mode 100644 new mode 100755 diff --git a/core/dev/@SeismicTrace/disp.m b/core/dev/@SeismicTrace/disp.m old mode 100644 new mode 100755 diff --git a/core/dev/@SeismicTrace/legend.m b/core/dev/@SeismicTrace/legend.m old mode 100644 new mode 100755 diff --git a/core/dev/@SeismicTrace/linkedplot.m b/core/dev/@SeismicTrace/linkedplot.m old mode 100644 new mode 100755 diff --git a/core/dev/@SeismicTrace/plot.m b/core/dev/@SeismicTrace/plot.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceData/TraceData.m b/core/dev/@TraceData/TraceData.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceData/amplitude_spectrum.m b/core/dev/@TraceData/amplitude_spectrum.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceData/autoscale.m b/core/dev/@TraceData/autoscale.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceFilter/TraceFilter.m b/core/dev/@TraceFilter/TraceFilter.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceFilter/cookbook.m b/core/dev/@TraceFilter/cookbook.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceSpectra/TraceSpectra.m b/core/dev/@TraceSpectra/TraceSpectra.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceSpectra/cookbook.m b/core/dev/@TraceSpectra/cookbook.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceSpectra/parseSpecgramInputs.m b/core/dev/@TraceSpectra/parseSpecgramInputs.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceSpectra/specgram.m b/core/dev/@TraceSpectra/specgram.m old mode 100644 new mode 100755 diff --git a/core/dev/@TraceSpectra/specgram2.m b/core/dev/@TraceSpectra/specgram2.m old mode 100644 new mode 100755 diff --git a/deprecated/+waveform_extensions/sacload.m b/deprecated/+waveform_extensions/sacload.m old mode 100644 new mode 100755 diff --git a/deprecated/+waveform_extensions/sta_lta.m b/deprecated/+waveform_extensions/sta_lta.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/build.m b/deprecated/@helicorder/build.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/disp.m b/deprecated/@helicorder/disp.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/display.m b/deprecated/@helicorder/display.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/get.m b/deprecated/@helicorder/get.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/helicorder.m b/deprecated/@helicorder/helicorder.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/nan2sst.m b/deprecated/@helicorder/private/Event_Conversion/nan2sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/nan2wfa.m b/deprecated/@helicorder/private/Event_Conversion/nan2wfa.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/ssd2sst.m b/deprecated/@helicorder/private/Event_Conversion/ssd2sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/sst2nan.m b/deprecated/@helicorder/private/Event_Conversion/sst2nan.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/sst2ssd.m b/deprecated/@helicorder/private/Event_Conversion/sst2ssd.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/sst2val.m b/deprecated/@helicorder/private/Event_Conversion/sst2val.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/sst2wfa.m b/deprecated/@helicorder/private/Event_Conversion/sst2wfa.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/wfa2nan.m b/deprecated/@helicorder/private/Event_Conversion/wfa2nan.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/Event_Conversion/wfa2sst.m b/deprecated/@helicorder/private/Event_Conversion/wfa2sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/add_sst.m b/deprecated/@helicorder/private/SST/add_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/chk_t.m b/deprecated/@helicorder/private/SST/chk_t.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/compare_sst.m b/deprecated/@helicorder/private/SST/compare_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/delete_sst.m b/deprecated/@helicorder/private/SST/delete_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/extract_sst.m b/deprecated/@helicorder/private/SST/extract_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/is_sst.m b/deprecated/@helicorder/private/SST/is_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/istequal.m b/deprecated/@helicorder/private/SST/istequal.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/merge_sst.m b/deprecated/@helicorder/private/SST/merge_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/search_sst.m b/deprecated/@helicorder/private/SST/search_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/private/SST/sort_sst.m b/deprecated/@helicorder/private/SST/sort_sst.m old mode 100644 new mode 100755 diff --git a/deprecated/@helicorder/set.m b/deprecated/@helicorder/set.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/+wf_fft/about.m b/deprecated/fft_tools/+wf_fft/about.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/+wf_fft/compute.m b/deprecated/fft_tools/+wf_fft/compute.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/+wf_fft/freqindex.m b/deprecated/fft_tools/+wf_fft/freqindex.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/+wf_fft/plot.m b/deprecated/fft_tools/+wf_fft/plot.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/amplitude_spectrum.m b/deprecated/fft_tools/amplitude_spectrum.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/wf_fft.m b/deprecated/fft_tools/wf_fft.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/wf_fftplot.m b/deprecated/fft_tools/wf_fftplot.m old mode 100644 new mode 100755 diff --git a/deprecated/fft_tools/wf_fi.m b/deprecated/fft_tools/wf_fi.m old mode 100644 new mode 100755 diff --git a/is_testdata_setup.m b/is_testdata_setup.m new file mode 100755 index 0000000..25820a8 --- /dev/null +++ b/is_testdata_setup.m @@ -0,0 +1,11 @@ +function result = is_testdata_setup() +% check the TESTDATA variable points to the testdata directory + global TESTDATA + result = false; + if exist('TESTDATA','var') + [dirname, dfile] = fileparts(TESTDATA); + if strcmp(dfile, 'testdata') + result = true; + end + end +end \ No newline at end of file diff --git a/libgismo/cleanup.m b/libgismo/cleanup.m new file mode 100755 index 0000000..c881021 --- /dev/null +++ b/libgismo/cleanup.m @@ -0,0 +1,6 @@ +function cleanup() + close all + clear all + clc + startup +end \ No newline at end of file diff --git a/libgismo/contiguous.m b/libgismo/contiguous.m old mode 100644 new mode 100755 diff --git a/libgismo/datenum2epoch.m b/libgismo/datenum2epoch.m old mode 100644 new mode 100755 diff --git a/libgismo/datenum2julday.m b/libgismo/datenum2julday.m new file mode 100755 index 0000000..c4b814d --- /dev/null +++ b/libgismo/datenum2julday.m @@ -0,0 +1,14 @@ +function jday = datenum2julday(time) +% JDAY = DATENUM2JULDAY(TIME) translates Matlab numeric +% date format into Unix epoch date format. + +% Author: Glenn Thompson. +% $Date$ +% $Revision$ +SECS_PER_DAY = 60 * 60 * 24; +jday = zeros(size(time)); +for n = 1:numel(time) + dv = datevec(time(n)); + jday(n) = dv(1)*1000 + ceil((datenum2epoch(time(n)) - datenum2epoch(datenum(dv(1),1,1)))/SECS_PER_DAY ); +end + diff --git a/libgismo/epoch2datenum.m b/libgismo/epoch2datenum.m old mode 100644 new mode 100755 diff --git a/libgismo/get_highest_figure_number.m b/libgismo/get_highest_figure_number.m new file mode 100755 index 0000000..a70fe53 --- /dev/null +++ b/libgismo/get_highest_figure_number.m @@ -0,0 +1,7 @@ +function m = get_highest_figure_number() +figs = get(0,'Children'); +if numel(figs)>0 + m = max([figs.Number]); +else + m = 0; +end \ No newline at end of file diff --git a/libgismo/utnow.m b/libgismo/utnow.m index 8fb98d9..f5a07bc 100755 --- a/libgismo/utnow.m +++ b/libgismo/utnow.m @@ -21,8 +21,6 @@ unixnow = datenum(unixnowstr); hour_adjust = round(24 * (unixnow - atomtime)); end - - hour_adjust if isunix [status, unixnowstr] = system('date +"%Y-%m-%d %H:%M:%S"'); diff --git a/startup_GISMO.m b/startup_GISMO.m old mode 100644 new mode 100755 index 1a7a272..0b0bf18 --- a/startup_GISMO.m +++ b/startup_GISMO.m @@ -27,7 +27,7 @@ function startup_GISMO(gismopath) addpath(fullfile(gismopath,'core')); % ADD PATH TO PREVIOUS CORE -addpath(fullfile(gismopath,'core','deprecated')); +%addpath(fullfile(gismopath,'core','deprecated')); % ADD A PATH TO EACH DIRCTORY IN CONTRIBUTED addContributed(gismopath,'contributed'); @@ -44,15 +44,33 @@ function startup_GISMO(gismopath) addpath(genpath(fullfile(gismopath,'applications'))); % ADD A PATH TO JAR FILES -javaaddpath(fullfile(gismopath,'contributed','iris_dmc_tools','IRIS-WS-2.0.15.jar')) -javaaddpath(fullfile(gismopath,'core','swarm.jar')) +f = fullfile(gismopath,'contributed','iris_dmc_tools','IRIS-WS-2.0.15.jar'); +try + javaaddpath(f); + disp(['Adding path: ', f]); +catch + disp(['Failed to add path: ', f]); +end +f = fullfile(gismopath,'core','swarm.jar'); +try + javaaddpath(f); + disp(['Adding path: ', f]); +catch + disp(['Failed to add path: ', f]); +end % ADD PATH TO GISMO LIBRARY FUNCTIONS +disp('Adding path: libgismo') addpath(fullfile(gismopath, 'libgismo')); % ADD PATH TO TESTS +disp('Adding path: tests') addpath(fullfile(gismopath, 'tests')); +% ADD PATH TO COOKBOOKS +disp('Adding path: cookbooks') +addpath(fullfile(gismopath, 'cookbooks')); + %% function addContributed(gismopath, contribDir) % add each subdirectory within gismopath/contribDir/ to the matlab path diff --git a/tests/amplitude_spectrum_test.m b/tests/amplitude_spectrum_test.m old mode 100644 new mode 100755 diff --git a/tests/antelope2waveform_test.m b/tests/antelope2waveform_test.m old mode 100644 new mode 100755 diff --git a/tests/correlation_diagnostics.m b/tests/correlation_diagnostics.m old mode 100644 new mode 100755 diff --git a/tests/obsolete/medfilt1_test.m b/tests/obsolete/medfilt1_test.m old mode 100644 new mode 100755 diff --git a/tests/testChanneltag.m b/tests/testChanneltag.m old mode 100644 new mode 100755 diff --git a/tests/testScnlobject.m b/tests/testScnlobject.m old mode 100644 new mode 100755 diff --git a/tests/testWaveform.m b/tests/testWaveform.m old mode 100644 new mode 100755 diff --git a/tests/test_ChanDetails.m b/tests/test_ChanDetails.m old mode 100644 new mode 100755 diff --git a/tests/test_Correlation.m b/tests/test_Correlation.m old mode 100644 new mode 100755 diff --git a/tests/test_Trace.m b/tests/test_Trace.m old mode 100644 new mode 100755 diff --git a/tests/test_TraceData.m b/tests/test_TraceData.m old mode 100644 new mode 100755 diff --git a/tests/test_waveform_constructor.m b/tests/test_waveform_constructor.m old mode 100644 new mode 100755 diff --git a/tests/trloadcss_test.m b/tests/trloadcss_test.m old mode 100644 new mode 100755 diff --git a/tests/waveform_antelope_test.m b/tests/waveform_antelope_test.m old mode 100644 new mode 100755 index dc6f45f..cad91c2 --- a/tests/waveform_antelope_test.m +++ b/tests/waveform_antelope_test.m @@ -1,23 +1,35 @@ %% Main function to generate tests function tests = waveform_antelope_test() -tests = functiontests(localfunctions); + tests = []; + try + if ~admin.antelope_exists() + disp('ATM not working') + return + end + catch + disp('Probably could not find admin.antelope_exists()') + end + tests = functiontests(localfunctions); end %% Test Functions function w=testFunctionOne(testCase) %% old style + global TESTDATA w=waveform(); gismodir = fileparts(which('startup_GISMO')); - demodbpath = fullfile(gismodir, 'tests', 'test_data', 'demodb'); + demodbpath = fullfile(TESTDATA, 'css3.0', 'demodb') ds = datasource('antelope',demodbpath); scnl = scnlobject('RSO','EHZ'); - flist=antelope.listMiniseedFiles(ds,scnl,datenum(2009,3,20),datenum(2009,3,20,1,0,0)); - if sum(flist.exists)>0 - w=waveform(ds,scnl,datenum(2009,3,20),datenum(2009,3,20,1,0,0)); + snum = datenum(2009,3,20); + enum = datenum(2009,3,20,1,0,0); + flist=antelope.listMiniseedFiles(ds,scnl,snum,enum) + %if sum(flist.exists)>0 + w=waveform(ds,scnl,snum,enum); w=combine(w); - else + %else disp('No waveform files found') - end + %end w %% end diff --git a/tests/waveform_load_test.m b/tests/waveform_load_test.m old mode 100644 new mode 100755 diff --git a/training/gismo_examples.m b/training/gismo_examples.m old mode 100644 new mode 100755 diff --git a/training/separating_tremor_and_events.m b/training/separating_tremor_and_events.m old mode 100644 new mode 100755 diff --git a/uaf_internal/AEIC_AVO/+aeic_catalog/about.m b/uaf_internal/AEIC_AVO/+aeic_catalog/about.m old mode 100644 new mode 100755 diff --git a/uaf_internal/AEIC_AVO/+aeic_catalog/get_total.m b/uaf_internal/AEIC_AVO/+aeic_catalog/get_total.m old mode 100644 new mode 100755 diff --git a/uaf_internal/AEIC_AVO/+check/about.m b/uaf_internal/AEIC_AVO/+check/about.m old mode 100644 new mode 100755 diff --git a/uaf_internal/AEIC_AVO/+check/crossfeed.m b/uaf_internal/AEIC_AVO/+check/crossfeed.m old mode 100644 new mode 100755 diff --git a/uaf_internal/AEIC_AVO/aeic_total_catalog.m b/uaf_internal/AEIC_AVO/aeic_total_catalog.m old mode 100644 new mode 100755 diff --git a/uaf_internal/IDEAS_format/ideas2waveform.m b/uaf_internal/IDEAS_format/ideas2waveform.m old mode 100644 new mode 100755 diff --git a/uaf_internal/gismo_management/whichgismo.m b/uaf_internal/gismo_management/whichgismo.m old mode 100644 new mode 100755 diff --git a/uaf_internal/gismo_management/whichgismo_paths.m b/uaf_internal/gismo_management/whichgismo_paths.m old mode 100644 new mode 100755