Commit b3b851a23e6321841a2d789d31d3d48b06557378
1 parent
3124193a36
Exists in
master
add cov test scripts
Showing 2 changed files with 616 additions and 0 deletions Side-by-side Diff
allan_cov.m
1 | +function [retval, s, errorb, tau] = allan_cov(data,tau,name,verbose) | |
2 | +% ALLAN Compute the Allan deviation for a set of time-domain frequency data | |
3 | +% [RETVAL, S, ERRORB, TAU] = ALLAN(DATA,TAU,NAME,VERBOSE) | |
4 | +% | |
5 | +% Inputs: | |
6 | +% DATA should be a structure and have the following fields: | |
7 | +% DATA.freq or DATA.phase | |
8 | +% A vector of fractional frequency measurements (df/f) in | |
9 | +% DATA.freq *or* phase offset data (seconds) in DATA.phase . | |
10 | +% If frequency data is not present, it will be generated by | |
11 | +% differentiating the phase data. | |
12 | +% If both fields are present, then DATA.freq will be used. | |
13 | +% Note: for general-purpose calculations of Allan deviation, | |
14 | +% (i.e. a two-sample variance) use DATA.freq . | |
15 | +% | |
16 | +% DATA.rate or DATA.time | |
17 | +% The sampling rate in Hertz (DATA.rate) or a vector of | |
18 | +% timestamps for each measurement in seconds (DATA.time). | |
19 | +% DATA.rate is used if both fields are present. | |
20 | +% If DATA.rate == 0, then the timestamps are used. | |
21 | +% | |
22 | +% DATA.units (optional) | |
23 | +% The units for the data. If present, the string DATA.units | |
24 | +% is added to the plot y-axis label. | |
25 | +% | |
26 | +% TAU is an array of tau values for computing Allan deviation. | |
27 | +% TAU values must be divisible by 1/DATA.rate (data points cannot be | |
28 | +% grouped in fractional quantities!) and invalid values are ignored. | |
29 | +% Leave empty to use default values. | |
30 | +% NAME is an optional label that is added to the plot titles. | |
31 | +% VERBOSE sets the level of status messages: | |
32 | +% 0 = silent & no data plots; | |
33 | +% 1 = status messages & minimum plots; | |
34 | +% 2 = all messages and plots (default) | |
35 | +% | |
36 | +% Outputs: | |
37 | +% RETVAL is the array of Allan deviation values at each TAU. | |
38 | +% S is an optional output of other statistical measures of the data (mean, std, etc). | |
39 | +% ERRORB is an optional output containing the error estimates for a 1-sigma | |
40 | +% confidence interval. These values are shown on the figure for each point. | |
41 | +% TAU is an optional output containing the array of tau values used in the | |
42 | +% calculation (which may be a truncated subset of the input or default values). | |
43 | +% | |
44 | +% Example: | |
45 | +% | |
46 | +% To compute the Allan deviation for the data in the variable "lt": | |
47 | +% >> lt | |
48 | +% lt = | |
49 | +% freq: [1x86400 double] | |
50 | +% rate: 0.5 | |
51 | +% | |
52 | +% Use: | |
53 | +% | |
54 | +% >> ad = allan(lt,[2 10 100],'lt data',1); | |
55 | +% | |
56 | +% The Allan deviation will be computed and plotted at tau = 2,10,100 seconds. | |
57 | +% 1-sigma confidence intervals will be indicated by vertical lines at each point. | |
58 | +% You can also use the default settings, which are usually a good starting point: | |
59 | +% | |
60 | +% >> ad = allan(lt); | |
61 | +% | |
62 | +% | |
63 | +% Notes: | |
64 | +% This function calculates the standard Allan deviation (ADEV), *not* the | |
65 | +% overlapping ADEV. Use "allan_overlap.m" for overlapping ADEV. | |
66 | +% The calculation is performed using fractional frequency data. If only | |
67 | +% phase data is provided, frequency data is generated by differentiating | |
68 | +% the phase data. | |
69 | +% No pre-processing of the data is performed, except to remove any | |
70 | +% initial offset (i.e., starting gap) in the time record. | |
71 | +% For rate-based data, ADEV is computed only for tau values greater than the | |
72 | +% minimum time between samples and less than the half the total time. For | |
73 | +% time-stamped data, only tau values greater than the maximum gap between | |
74 | +% samples and less than half the total time are used. | |
75 | +% The calculation for fixed sample rate data is *much* faster than for | |
76 | +% time-stamp data. You may wish to run the rate-based calculation first, | |
77 | +% then compare with time-stamp-based. Often the differences are insignificant. | |
78 | +% To show the "tau bins" (y_k samples) on the data plot, set the variable | |
79 | +% TAUBIN to 1 (search for "#TAUBIN"). | |
80 | +% You can choose between loglog and semilog plotting of results by | |
81 | +% commenting in/out the appropriate line. Search for "#PLOTLOG". | |
82 | +% I recommend installing "dsplot.m", which improves the performance of | |
83 | +% plotting large data sets. Download from File Exchange, File ID: #15850. | |
84 | +% allan.m will use dsplot.m if it is present on your MATLAB path. | |
85 | +% This function has been validated using the test data from NBS Monograph | |
86 | +% 140, the 1000-point test data set given by Riley [1], and the example data | |
87 | +% given in IEEE standard 1139-1999, Annex C. | |
88 | +% The author welcomes other validation results, see contact info below. | |
89 | +% | |
90 | +% For more information, see: | |
91 | +% [1] W. J. Riley, "The Calculation of Time Domain Frequency Stability," | |
92 | +% Available on the web: | |
93 | +% http://www.ieee-uffc.org/frequency_control/teaching.asp?name=paper1ht | |
94 | +% | |
95 | +% | |
96 | +% M.A. Hopcroft | |
97 | +% mhopeng at gmail dot com | |
98 | +% | |
99 | +% I welcome your comments and feedback! | |
100 | +% | |
101 | +% MH Mar2014 | |
102 | +% v2.24 fix bug related to generating freq data from phase with timestamps | |
103 | +% (thanks to S. David-Grignot for finding the bug) | |
104 | +% MH Oct2010 | |
105 | +% v2.22 tau truncation to integer groups; tau sort | |
106 | +% plotting bugfix | |
107 | +% v2.20 sychronize updates across allan, allan_overlap, allan_modified | |
108 | +% v2.16 add TAU as output, fixed unusual error with dsplot v1.1 | |
109 | +% v2.14 update plotting behaviour, default tau values | |
110 | +% | |
111 | + | |
112 | +versionstr = 'allan v2.24'; | |
113 | + | |
114 | +% MH Jun2010 | |
115 | +% v2.12 bugfix for rate data row/col orientation | |
116 | +% add DATA.units for plotting | |
117 | +% use dsplot.m for plotting | |
118 | +% | |
119 | +% MH MAR2010 | |
120 | +% v2.1 minor interface and bugfixes | |
121 | +% update data consistency check | |
122 | +% | |
123 | +% MH FEB2010 | |
124 | +% v2.0 Consistent code behaviour for all "allan_x.m" functions: | |
125 | +% accept phase data | |
126 | +% verbose levels | |
127 | +% | |
128 | +% | |
129 | +% MH JAN2010 | |
130 | +% v1.84 code cleanup | |
131 | +% v1.82 typos in comments and code cleanup | |
132 | +% tau bin plotting changed for performance improvement | |
133 | +% v1.8 Performance improvements: | |
134 | +% vectorize code for rate data | |
135 | +% logical indexing for irregular rate data | |
136 | +% MH APR2008 | |
137 | +% v1.62 loglog plot option | |
138 | +% v1.61 improve error handling, plotting | |
139 | +% fix bug in regular data calc for high-rate data | |
140 | +% fix bug in timestamp data calc for large starting gap | |
141 | +% (thanks to C. B. Ruiz for identifying these bugs) | |
142 | +% uses timestamps for DATA.rate=0 | |
143 | +% progress indicator for large timestamp data processing | |
144 | +% MH JUN2007 | |
145 | +% v1.54 Improve data plotting and optional bin plotting | |
146 | +% MH FEB2007 | |
147 | +% v1.5 use difference from median for plotting | |
148 | +% added MAD calculation for outlier detection | |
149 | +% MH JAN2007 | |
150 | +% v1.48 plotting typos fixes | |
151 | +% MH DEC2006 | |
152 | +% v1.46 hack to plot error bars | |
153 | +% v1.44 further validation (Riley 1000-pt) | |
154 | +% plot mean and std | |
155 | +% MH NOV2006 | |
156 | +% v1.42 typo fix comments | |
157 | +% v1.4 fix irregular rate algorithm | |
158 | +% irregular algorithm rejects tau less than max gap in time data | |
159 | +% validate both algorithms using test data from NBS Monograph 140 | |
160 | +% v1.3 fix time calc if data.time not present | |
161 | +% add error bars (not possible due to bug in MATLAB R14SP3) | |
162 | +% remove offset calculation | |
163 | +% v1.24 improve feedback | |
164 | +% MH SEP2006 | |
165 | +% v1.22 updated comments | |
166 | +% v1.2 errors and warnings | |
167 | +% v1.1 handle irregular interval data | |
168 | +%#ok<*AGROW> | |
169 | + | |
170 | +% defaults | |
171 | +if nargin < 4, verbose=2; end | |
172 | +if nargin < 3, name=''; end | |
173 | +if nargin < 2 || isempty(tau), tau=2.^(-10:10); end | |
174 | + | |
175 | +% plot "tau bins"? #TAUBIN | |
176 | +TAUBIN = 0; % set 0 or 1 % WARNING: this has a significant impact on performance | |
177 | + | |
178 | +% Formatting for plots | |
179 | +FontName = 'Arial'; | |
180 | +FontSize = 14; | |
181 | +plotlinewidth=2; | |
182 | + | |
183 | +if verbose >= 1, fprintf(1,'allan: %s\n\n',versionstr); end | |
184 | + | |
185 | +%% Data consistency checks | |
186 | +if ~(isfield(data,'phase') || isfield(data,'freq')) | |
187 | + error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
188 | +end | |
189 | +if isfield(data,'time') | |
190 | + if isfield(data,'phase') && (length(data.phase) ~= length(data.time)) | |
191 | + if isfield(data,'freq') && (length(data.freq) ~= length(data.time)) | |
192 | + error('The time and freq vectors are not the same length. See help for details. [con2]'); | |
193 | + else | |
194 | + error('The time and phase vectors are not the same length. See help for details. [con1]'); | |
195 | + end | |
196 | + end | |
197 | + if isfield(data,'phase') && (any(isnan(data.phase)) || any(isinf(data.phase))) | |
198 | + error('The phase vector contains invalid elements (NaN/Inf). [con3]'); | |
199 | + end | |
200 | + if isfield(data,'freq') && (any(isnan(data.freq)) || any(isinf(data.freq))) | |
201 | + error('The freq vector contains invalid elements (NaN/Inf). [con4]'); | |
202 | + end | |
203 | + if isfield(data,'time') && (any(isnan(data.time)) || any(isinf(data.time))) | |
204 | + error('The time vector contains invalid elements (NaN/Inf). [con5]'); | |
205 | + end | |
206 | +end | |
207 | + | |
208 | +% sort tau vector | |
209 | +tau=sort(tau); | |
210 | + | |
211 | + | |
212 | +%% Basic statistical tests on the data set | |
213 | +if ~isfield(data,'freq') | |
214 | + if isfield(data,'rate') && data.rate ~= 0 | |
215 | + data.freq=diff(data.phase).*data.rate; | |
216 | + elseif isfield(data,'time') | |
217 | + data.freq=diff(data.phase)./diff(data.time); | |
218 | + end | |
219 | + if verbose >= 1, fprintf(1,'allan: Fractional frequency data generated from phase data (M=%g).\n',length(data.freq)); end | |
220 | + data.time(1)=[]; % make time stamps correspond to freq data | |
221 | +end | |
222 | +if size(data.freq,2) > size(data.freq,1), data.freq=data.freq'; end % ensure columns | |
223 | + | |
224 | +s.numpoints=length(data.freq); | |
225 | +s.max=max(data.freq); | |
226 | +s.min=min(data.freq); | |
227 | +s.mean=mean(data.freq); | |
228 | +s.median=median(data.freq); | |
229 | +if isfield(data,'time') | |
230 | + if size(data.time,2) > size(data.time,1), data.time=data.time'; end % ensure columns | |
231 | + s.linear=polyfit(data.time(1:length(data.freq)),data.freq,1); | |
232 | +elseif isfield(data,'rate') && data.rate ~= 0; | |
233 | + s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
234 | +else | |
235 | + error('Either "time" or "rate" must be present in DATA. Type "help allan" for details. [err1]'); | |
236 | +end | |
237 | +s.std=std(data.freq); | |
238 | + | |
239 | +if verbose >= 2 | |
240 | + fprintf(1,'allan: input data statistics:\n'); | |
241 | + disp(s); | |
242 | +end | |
243 | + | |
244 | + | |
245 | +% center at median for plotting | |
246 | +medianfreq=data.freq-s.median; | |
247 | +sm=[]; sme=[]; | |
248 | + | |
249 | +% Screen for outliers using 5x Median Absolute Deviation (MAD) criteria | |
250 | +s.MAD = median(abs(medianfreq)/0.6745); | |
251 | +if verbose >= 2 | |
252 | + fprintf(1, 'allan: 5x MAD value for outlier detection: %g\n',5*s.MAD); | |
253 | +end | |
254 | +if verbose >= 1 && any(abs(medianfreq) > 5*s.MAD) | |
255 | + fprintf(1, 'allan: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
256 | +end | |
257 | + | |
258 | + | |
259 | +%%%% | |
260 | +% There are two cases, either using timestamps or fixed sample rate: | |
261 | + | |
262 | +%% Fixed Sample Rate Data | |
263 | +% If there is a regular interval between measurements, calculation is much | |
264 | +% easier/faster | |
265 | +if isfield(data,'rate') && data.rate > 0 % if data rate was given | |
266 | + if verbose >= 1, fprintf(1, 'allan: regular data (%g data points @ %g Hz)\n',length(data.freq),data.rate); end | |
267 | + | |
268 | + % string for plot title | |
269 | + name=[name ' (' num2str(data.rate) ' Hz)']; | |
270 | + | |
271 | + % what is the time interval between data points? | |
272 | + tmstep = 1/data.rate; | |
273 | + | |
274 | + % Is there time data? Just for curiosity/plotting, does not impact calculation | |
275 | + if isfield(data,'time') | |
276 | + % adjust time data to remove any starting gap; first time step | |
277 | + % should not be zero for comparison with freq data | |
278 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
279 | + if verbose >= 2 | |
280 | + fprintf(1,'allan: End of timestamp data: %g sec.\n',dtime(end)); | |
281 | + if (data.rate - 1/mean(diff(dtime))) > 1e-6 | |
282 | + fprintf(1,'allan: NOTE: data.rate (%f Hz) does not match average timestamped sample rate (%f Hz)\n',data.rate,1/mean(diff(dtime))); | |
283 | + end | |
284 | + end | |
285 | + else | |
286 | + % create time axis data using rate (for plotting only) | |
287 | + dtime=(tmstep:tmstep:length(data.freq)*tmstep)'; % column oriented | |
288 | + end | |
289 | + | |
290 | + % check the range of tau values and truncate if necessary | |
291 | + % find halfway point of time record | |
292 | + halftime = round(tmstep*length(data.freq)/2); | |
293 | + % truncate tau to appropriate values | |
294 | + tau = tau(tau >= tmstep & tau <= halftime); | |
295 | + if verbose >= 2, fprintf(1, 'allan: allowable tau range: %g to %g sec. (1/rate to total_time/2)\n',tmstep,halftime); end | |
296 | + | |
297 | + % save the freq data for the loop | |
298 | + dfreq=data.freq; | |
299 | + dfreq2=data.freq2; | |
300 | + % find the number of data points in each tau group | |
301 | + m = data.rate.*tau; | |
302 | + % only integer values allowed (no fractional groups of points) | |
303 | + %tau = tau(m-round(m)<1e-8); % numerical precision issues (v2.1) | |
304 | + tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
305 | + %m = m(m-round(m)<1e-8); % change to round(m) for integer test v2.22 | |
306 | + m = m(m==round(m)); | |
307 | + %m=round(m); | |
308 | + | |
309 | + if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n '); end | |
310 | + | |
311 | + % calculate the Allan deviation for each value of tau | |
312 | + k=0; tic; | |
313 | + for i = tau | |
314 | + if verbose >= 2, fprintf(1,'%g ',i); end | |
315 | + k=k+1; | |
316 | + | |
317 | + % truncate frequency set to an even multiple of this tau value | |
318 | + freq=dfreq(1:end-rem(length(dfreq),m(k))); | |
319 | + freq2=dfreq2(1:end-rem(length(dfreq2),m(k))); | |
320 | + % group the data into tau-length groups or bins | |
321 | + f = reshape(freq,m(k),[]); % Vectorize! | |
322 | + f2 = reshape(freq2,m(k),[]); % Vectorize! | |
323 | + % find average in each "tau group", y_k (each colummn of f) | |
324 | + fa=mean(f,1); | |
325 | + fa2=mean(f2,1); | |
326 | + % first finite difference | |
327 | + fd=diff(fa); | |
328 | + fd2=diff(fa2); | |
329 | + % calculate two-sample variance for this tau | |
330 | + M=length(fa); | |
331 | + sm(k)=sqrt(0.5/(M-1)*(sum(fd.*fd2))); | |
332 | + | |
333 | + % estimate error bars | |
334 | + sme(k)=sm(k)/sqrt(M+1); | |
335 | + | |
336 | + if TAUBIN == 1 | |
337 | + % save the binning points for plotting | |
338 | + fs(k,1:length(freq)/m(k))=m(k):m(k):length(freq); fval{k}=mean(f,1); | |
339 | + end | |
340 | + | |
341 | + end % repeat for each value of tau | |
342 | + | |
343 | + if verbose >= 2, fprintf(1,'\n'); end | |
344 | + calctime=toc; if verbose >= 2, fprintf(1,'allan: Elapsed time for calculation: %e seconds\n',calctime); end | |
345 | + | |
346 | + | |
347 | + | |
348 | +%% Irregular data (timestamp) | |
349 | +elseif isfield(data,'time') | |
350 | + % the interval between measurements is irregular | |
351 | + % so we must group the data by time | |
352 | + if verbose >= 1, fprintf(1, 'allan: irregular rate data (no fixed sample rate)\n'); end | |
353 | + | |
354 | + % string for plot title | |
355 | + name=[name ' (timestamp)']; | |
356 | + | |
357 | + % adjust time to remove any initial offset or zero | |
358 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
359 | + %dtime=data.time; | |
360 | + % where is the maximum gap in time record? | |
361 | + gap_pos=find(diff(dtime)==max(diff(dtime))); | |
362 | + % what is average data spacing? | |
363 | + avg_gap = mean(diff(dtime)); | |
364 | + | |
365 | + if verbose >= 2 | |
366 | + fprintf(1, 'allan: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
367 | + fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
368 | + fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
369 | + fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
370 | + pause; | |
371 | + end | |
372 | + | |
373 | + if verbose >= 1 | |
374 | + fprintf(1, 'allan: End of timestamp data: %g sec\n',dtime(end)); | |
375 | + fprintf(1, ' Average rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
376 | + if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
377 | + fprintf(1, ' Max. gap: %g sec at position %d\n',max(diff(dtime)),gap_pos(1)); | |
378 | + end | |
379 | + if max(diff(dtime)) > 5*avg_gap | |
380 | + fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
381 | + end | |
382 | + end | |
383 | + | |
384 | + | |
385 | + % find halfway point | |
386 | + halftime = fix(dtime(end)/2); | |
387 | + % truncate tau to appropriate values | |
388 | + tau = tau(tau >= max(diff(dtime)) & tau <= halftime); | |
389 | + if isempty(tau) | |
390 | + error('allan: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(diff(dtime)),halftime); | |
391 | + end | |
392 | + | |
393 | + % save the freq data for the loop | |
394 | + dfreq=data.freq; | |
395 | + dtime=dtime(1:length(dfreq)); | |
396 | + | |
397 | + if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n'); end | |
398 | + | |
399 | + k=0; tic; | |
400 | + for i = tau | |
401 | + if verbose >= 2, fprintf(1,'%d ',i); end | |
402 | + | |
403 | + k=k+1; fa=[]; %f=[]; | |
404 | + km=0; | |
405 | + | |
406 | + % truncate data set to an even multiple of this tau value | |
407 | + freq=dfreq(dtime <= dtime(end)-rem(dtime(end),i)); | |
408 | + time=dtime(dtime <= dtime(end)-rem(dtime(end),i)); | |
409 | + %freq=dfreq; | |
410 | + %time=dtime; | |
411 | + | |
412 | + % break up the data into groups of tau length in sec | |
413 | + while i*km < time(end) | |
414 | + km=km+1; | |
415 | + | |
416 | + % progress bar | |
417 | + if verbose >= 2 | |
418 | + if rem(km,100)==0, fprintf(1,'.'); end | |
419 | + if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
420 | + end | |
421 | + | |
422 | + f = freq(i*(km-1) < time & time <= i*km); | |
423 | + f = f(~isnan(f)); % make sure values are valid | |
424 | + | |
425 | + if ~isempty(f) | |
426 | + fa(km)=mean(f); | |
427 | + else | |
428 | + fa(km)=0; | |
429 | + end | |
430 | + | |
431 | + if TAUBIN == 1 % WARNING: this has a significant impact on performance | |
432 | + % save the binning points for plotting | |
433 | + %if find(time <= i*km) > 0 | |
434 | + fs(k,km)=max(time(time <= i*km)); | |
435 | + %else | |
436 | + if isempty(fs(k,km)) | |
437 | + fs(k,km)=0; | |
438 | + end | |
439 | + fval{k}=fa; | |
440 | + end % save tau bin plot points | |
441 | + | |
442 | + end | |
443 | + | |
444 | + if verbose >= 2, fprintf(1,'\n'); end | |
445 | + | |
446 | + % first finite difference of the averaged results | |
447 | + fd=diff(fa); | |
448 | + % calculate Allan deviation for this tau | |
449 | + M=length(fa); | |
450 | + sm(k)=sqrt(0.5/(M-1)*(sum(fd.^2))); | |
451 | + | |
452 | + % estimate error bars | |
453 | + sme(k)=sm(k)/sqrt(M+1); | |
454 | + | |
455 | + | |
456 | + end | |
457 | + | |
458 | + if verbose == 2, fprintf(1,'\n'); end | |
459 | + calctime=toc; if verbose >= 2, fprintf(1,'allan: Elapsed time for calculation: %e seconds\n',calctime); end | |
460 | + | |
461 | + | |
462 | +else | |
463 | + error('allan: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
464 | +end | |
465 | + | |
466 | + | |
467 | +%%%%%%%% | |
468 | +%% Plotting | |
469 | + | |
470 | +if verbose >= 2 % show all data | |
471 | + | |
472 | + % plot the frequency data, centered on median | |
473 | + if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
474 | + try | |
475 | + % dsplot makes a new figure | |
476 | + hd=dsplot(dtime,medianfreq); | |
477 | + catch ME | |
478 | + figure; | |
479 | + if length(dtime) ~= length(medianfreq) | |
480 | + fprintf(1,'allan: Warning: length of time axis (%d) is not equal to data array (%d)\n',length(dtime),length(medianfreq)); | |
481 | + end | |
482 | + hd=plot(dtime,medianfreq); | |
483 | + if verbose >= 1, fprintf(1,'allan: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
484 | + if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
485 | + end | |
486 | + set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
487 | + hold on; | |
488 | + | |
489 | + % show center (0) | |
490 | + plot(xlim,[0 0],':k'); | |
491 | + % show 5x Median Absolute Deviation (MAD) values | |
492 | + hm=plot(xlim,[5*s.MAD 5*s.MAD],'-r'); | |
493 | + plot(xlim,[-5*s.MAD -5*s.MAD],'-r'); | |
494 | + % show linear fit line | |
495 | + hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
496 | + title(['Data: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
497 | + %set(get(gca,'Title'),'Interpreter','none'); | |
498 | + xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
499 | + if isfield(data,'units') | |
500 | + ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
501 | + else | |
502 | + ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
503 | + end | |
504 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
505 | + legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
506 | + % tighten up | |
507 | + xlim([dtime(1) dtime(end)]); | |
508 | + | |
509 | + | |
510 | + % Optional tau bin (y_k samples) plot | |
511 | + if TAUBIN == 1 | |
512 | + % plot the tau divisions on the data plot | |
513 | + rfs=size(fs,1); | |
514 | + colororder=get(gca,'ColorOrder'); | |
515 | + axis tight; kc=2; | |
516 | + %ap=axis; | |
517 | + for j=1:rfs | |
518 | + kc=kc+1; if rem(kc,length(colororder))==1, kc=2; end | |
519 | + %for b=1:max(find(fs(j,:))); % new form of "find" in r2009a | |
520 | + for b=1:find(fs(j,:), 1, 'last' ); | |
521 | + % plot the tau division boundaries | |
522 | + %plot([fs(j,b) fs(j,b)],[ap(3)*1.1 ap(4)*1.1],'-','Color',colororder(kc,:)); | |
523 | + % plot tau group y values | |
524 | + if b == 1 | |
525 | + plot([dtime(1) fs(j,b)],[fval{j}(b)-s.median fval{j}(b)-s.median],'-','Color',colororder(kc,:),'LineWidth',4); | |
526 | + else | |
527 | + plot([fs(j,b-1) fs(j,b)],[fval{j}(b)-s.median fval{j}(b)-s.median],'-','Color',colororder(kc,:),'LineWidth',4); | |
528 | + end | |
529 | + end | |
530 | + end | |
531 | + axis auto | |
532 | + end % End optional bin plot | |
533 | + | |
534 | +end % end plot raw data | |
535 | + | |
536 | + | |
537 | +if verbose >= 1 % show ADEV results | |
538 | + | |
539 | + % plot Allan deviation results | |
540 | + if ~isempty(sm) | |
541 | + figure | |
542 | + | |
543 | + % Choose loglog or semilogx plot here #PLOTLOG | |
544 | + %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
545 | + loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
546 | + | |
547 | + % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
548 | + % When this is fixed in a future release, uncomment below to use normal errorbars | |
549 | + %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
550 | + % this is a hack to approximate the error bars | |
551 | + hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
552 | + | |
553 | + grid on; | |
554 | + title(['Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
555 | + %set(get(gca,'Title'),'Interpreter','none'); | |
556 | + xlabel('\tau [sec]','FontSize',FontSize,'FontName',FontName); | |
557 | + if isfield(data,'units') | |
558 | + ylabel(['\sigma_y(\tau) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
559 | + else | |
560 | + ylabel('\sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
561 | + end | |
562 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
563 | + % expand the x axis a little bit so that the errors bars look nice | |
564 | + adax = axis; | |
565 | + axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
566 | + | |
567 | + % display the minimum value | |
568 | + fprintf(1,'allan: Minimum ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
569 | + | |
570 | + elseif verbose >= 1 | |
571 | + fprintf(1,'allan: WARNING: no values calculated.\n'); | |
572 | + fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
573 | + fprintf(1,'Type "help allan" for more information.\n\n'); | |
574 | + end | |
575 | + | |
576 | +end % end plot ADEV data | |
577 | + | |
578 | +retval = sm; | |
579 | +errorb = sme; | |
580 | + | |
581 | +return |
allanplot_cov.m
1 | +#!/usr/bin/octave-cli --persist | |
2 | + | |
3 | +filename = argv(){1}; | |
4 | +col1 = eval(argv(){2}); | |
5 | +col2 = eval(argv(){3}); | |
6 | +mult1 = eval(argv(){4}); | |
7 | +mult2 = eval(argv(){5}); | |
8 | + | |
9 | +if length(col1) == length(mult1) | |
10 | + figure | |
11 | + hold all | |
12 | + grid on | |
13 | + cc = 'bkcgmry'; | |
14 | + for i = [1:length(col1)] | |
15 | + data.freq = load(filename)(:,col1(i)).*mult1(i); | |
16 | + data.freq2 = load(filename)(:,col2(i)).*mult2(i); | |
17 | + if eval(argv(){end-1}) == 1 | |
18 | + printf('\ndata1 drift removed\n\n') | |
19 | + data.freq = detrend(data.freq); | |
20 | + end | |
21 | + if eval(argv(){end}) == 1 | |
22 | + printf('\ndata2 drift removed\n\n') | |
23 | + data.freq2 = detrend(data.freq2); | |
24 | + end | |
25 | + data.rate = 1; | |
26 | + [ad, S, err, tau] = allan_cov(data, 2.^[0:nextpow2(length(data.freq))-3]./data.rate, strcat(strsplit(filename, '/'){end}, num2str(i)), 0); | |
27 | + loglogerr(tau, ad, err, strcat(cc(mod(i, length(cc))), '-s')) | |
28 | + leg{i} = strcat(filename, ' cov col', num2str(col1(i)), ' col', num2str(col2(i))); | |
29 | + axis(10.^ceil(log10([tau(1), tau(end)]))) | |
30 | + hold on | |
31 | + end | |
32 | + legend(leg) | |
33 | + input("Press to continue..."); | |
34 | +end | |
35 | +exit |