Commit 1a0e88f0ce6e736bf1f59e7b02cc394896768bff
1 parent
183c460e4e
Exists in
master
replace 4-spaces by tab
Showing 8 changed files with 1688 additions and 1680 deletions Side-by-side Diff
allan.m
| ... | ... | @@ -5,33 +5,33 @@ |
| 5 | 5 | % Inputs: |
| 6 | 6 | % DATA should be a structure and have the following fields: |
| 7 | 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 . | |
| 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 | 15 | % |
| 16 | 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. | |
| 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 | 21 | % |
| 22 | 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. | |
| 23 | +% The units for the data. If present, the string DATA.units | |
| 24 | +% is added to the plot y-axis label. | |
| 25 | 25 | % |
| 26 | 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. | |
| 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 | 30 | % NAME is an optional label that is added to the plot titles. |
| 31 | 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) | |
| 32 | +% 0 = silent & no data plots; | |
| 33 | +% 1 = status messages & minimum plots; | |
| 34 | +% 2 = all messages and plots (default) | |
| 35 | 35 | % |
| 36 | 36 | % Outputs: |
| 37 | 37 | % RETVAL is the array of Allan deviation values at each TAU. |
| ... | ... | @@ -46,8 +46,8 @@ |
| 46 | 46 | % To compute the Allan deviation for the data in the variable "lt": |
| 47 | 47 | % >> lt |
| 48 | 48 | % lt = |
| 49 | -% freq: [1x86400 double] | |
| 50 | -% rate: 0.5 | |
| 49 | +% freq: [1x86400 double] | |
| 50 | +% rate: 0.5 | |
| 51 | 51 | % |
| 52 | 52 | % Use: |
| 53 | 53 | % |
| 54 | 54 | |
| 55 | 55 | |
| ... | ... | @@ -94,16 +94,16 @@ |
| 94 | 94 | % |
| 95 | 95 | % |
| 96 | 96 | % M.A. Hopcroft |
| 97 | -% mhopeng at gmail dot com | |
| 97 | +% mhopeng at gmail dot com | |
| 98 | 98 | % |
| 99 | 99 | % I welcome your comments and feedback! |
| 100 | 100 | % |
| 101 | 101 | % MH Mar2014 |
| 102 | 102 | % v2.24 fix bug related to generating freq data from phase with timestamps |
| 103 | -% (thanks to S. David-Grignot for finding the bug) | |
| 103 | +% (thanks to S. David-Grignot for finding the bug) | |
| 104 | 104 | % MH Oct2010 |
| 105 | 105 | % v2.22 tau truncation to integer groups; tau sort |
| 106 | -% plotting bugfix | |
| 106 | +% plotting bugfix | |
| 107 | 107 | % v2.20 sychronize updates across allan, allan_overlap, allan_modified |
| 108 | 108 | % v2.16 add TAU as output, fixed unusual error with dsplot v1.1 |
| 109 | 109 | % v2.14 update plotting behaviour, default tau values |
| 110 | 110 | |
| 111 | 111 | |
| 112 | 112 | |
| 113 | 113 | |
| 114 | 114 | |
| 115 | 115 | |
| 116 | 116 | |
| 117 | 117 | |
| 118 | 118 | |
| ... | ... | @@ -113,53 +113,53 @@ |
| 113 | 113 | |
| 114 | 114 | % MH Jun2010 |
| 115 | 115 | % v2.12 bugfix for rate data row/col orientation |
| 116 | -% add DATA.units for plotting | |
| 117 | -% use dsplot.m for plotting | |
| 116 | +% add DATA.units for plotting | |
| 117 | +% use dsplot.m for plotting | |
| 118 | 118 | % |
| 119 | 119 | % MH MAR2010 |
| 120 | 120 | % v2.1 minor interface and bugfixes |
| 121 | -% update data consistency check | |
| 121 | +% update data consistency check | |
| 122 | 122 | % |
| 123 | 123 | % MH FEB2010 |
| 124 | 124 | % v2.0 Consistent code behaviour for all "allan_x.m" functions: |
| 125 | -% accept phase data | |
| 126 | -% verbose levels | |
| 125 | +% accept phase data | |
| 126 | +% verbose levels | |
| 127 | 127 | % |
| 128 | 128 | % |
| 129 | 129 | % MH JAN2010 |
| 130 | 130 | % v1.84 code cleanup |
| 131 | 131 | % v1.82 typos in comments and code cleanup |
| 132 | -% tau bin plotting changed for performance improvement | |
| 132 | +% tau bin plotting changed for performance improvement | |
| 133 | 133 | % v1.8 Performance improvements: |
| 134 | -% vectorize code for rate data | |
| 135 | -% logical indexing for irregular rate data | |
| 134 | +% vectorize code for rate data | |
| 135 | +% logical indexing for irregular rate data | |
| 136 | 136 | % MH APR2008 |
| 137 | 137 | % v1.62 loglog plot option |
| 138 | 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 | |
| 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 | 144 | % MH JUN2007 |
| 145 | 145 | % v1.54 Improve data plotting and optional bin plotting |
| 146 | 146 | % MH FEB2007 |
| 147 | 147 | % v1.5 use difference from median for plotting |
| 148 | -% added MAD calculation for outlier detection | |
| 148 | +% added MAD calculation for outlier detection | |
| 149 | 149 | % MH JAN2007 |
| 150 | 150 | % v1.48 plotting typos fixes |
| 151 | 151 | % MH DEC2006 |
| 152 | 152 | % v1.46 hack to plot error bars |
| 153 | 153 | % v1.44 further validation (Riley 1000-pt) |
| 154 | -% plot mean and std | |
| 154 | +% plot mean and std | |
| 155 | 155 | % MH NOV2006 |
| 156 | 156 | % v1.42 typo fix comments |
| 157 | 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 | |
| 158 | +% irregular algorithm rejects tau less than max gap in time data | |
| 159 | +% validate both algorithms using test data from NBS Monograph 140 | |
| 160 | 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 | |
| 161 | +% add error bars (not possible due to bug in MATLAB R14SP3) | |
| 162 | +% remove offset calculation | |
| 163 | 163 | % v1.24 improve feedback |
| 164 | 164 | % MH SEP2006 |
| 165 | 165 | % v1.22 updated comments |
| 166 | 166 | |
| ... | ... | @@ -184,25 +184,25 @@ |
| 184 | 184 | |
| 185 | 185 | %% Data consistency checks |
| 186 | 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]'); | |
| 187 | + error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
| 188 | 188 | end |
| 189 | 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 | |
| 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 | 206 | end |
| 207 | 207 | |
| 208 | 208 | % sort tau vector |
| 209 | 209 | |
| 210 | 210 | |
| 211 | 211 | |
| 212 | 212 | |
| 213 | 213 | |
| ... | ... | @@ -211,34 +211,34 @@ |
| 211 | 211 | |
| 212 | 212 | %% Basic statistical tests on the data set |
| 213 | 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 | |
| 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 | 221 | end |
| 222 | 222 | if size(data.freq,2) > size(data.freq,1), data.freq=data.freq'; end % ensure columns |
| 223 | - | |
| 223 | + | |
| 224 | 224 | s.numpoints=length(data.freq); |
| 225 | 225 | s.max=max(data.freq); |
| 226 | 226 | s.min=min(data.freq); |
| 227 | 227 | s.mean=mean(data.freq); |
| 228 | 228 | s.median=median(data.freq); |
| 229 | 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); | |
| 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 | 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); | |
| 233 | + s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
| 234 | 234 | else |
| 235 | - error('Either "time" or "rate" must be present in DATA. Type "help allan" for details. [err1]'); | |
| 235 | + error('Either "time" or "rate" must be present in DATA. Type "help allan" for details. [err1]'); | |
| 236 | 236 | end |
| 237 | 237 | s.std=std(data.freq); |
| 238 | 238 | |
| 239 | 239 | if verbose >= 2 |
| 240 | - fprintf(1,'allan: input data statistics:\n'); | |
| 241 | - disp(s); | |
| 240 | + fprintf(1,'allan: input data statistics:\n'); | |
| 241 | + disp(s); | |
| 242 | 242 | end |
| 243 | 243 | |
| 244 | 244 | |
| 245 | 245 | |
| ... | ... | @@ -249,10 +249,10 @@ |
| 249 | 249 | % Screen for outliers using 5x Median Absolute Deviation (MAD) criteria |
| 250 | 250 | s.MAD = median(abs(medianfreq)/0.6745); |
| 251 | 251 | if verbose >= 2 |
| 252 | - fprintf(1, 'allan: 5x MAD value for outlier detection: %g\n',5*s.MAD); | |
| 252 | + fprintf(1, 'allan: 5x MAD value for outlier detection: %g\n',5*s.MAD); | |
| 253 | 253 | end |
| 254 | 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'); | |
| 255 | + fprintf(1, 'allan: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
| 256 | 256 | end |
| 257 | 257 | |
| 258 | 258 | |
| 259 | 259 | |
| 260 | 260 | |
| 261 | 261 | |
| 262 | 262 | |
| 263 | 263 | |
| 264 | 264 | |
| 265 | 265 | |
| 266 | 266 | |
| 267 | 267 | |
| 268 | 268 | |
| 269 | 269 | |
| 270 | 270 | |
| 271 | 271 | |
| 272 | 272 | |
| 273 | 273 | |
| ... | ... | @@ -263,199 +263,199 @@ |
| 263 | 263 | % If there is a regular interval between measurements, calculation is much |
| 264 | 264 | % easier/faster |
| 265 | 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; | |
| 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 | 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 | |
| 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 | 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 | - % find the number of data points in each tau group | |
| 300 | - m = data.rate.*tau; | |
| 301 | - % only integer values allowed (no fractional groups of points) | |
| 302 | - %tau = tau(m-round(m)<1e-8); % numerical precision issues (v2.1) | |
| 303 | - tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
| 304 | - %m = m(m-round(m)<1e-8); % change to round(m) for integer test v2.22 | |
| 305 | - m = m(m==round(m)); | |
| 306 | - %m=round(m); | |
| 307 | - | |
| 308 | - if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n '); end | |
| 309 | - | |
| 310 | - % calculate the Allan deviation for each value of tau | |
| 311 | - k=0; tic; | |
| 312 | - for i = tau | |
| 313 | - if verbose >= 2, fprintf(1,'%g ',i); end | |
| 314 | - k=k+1; | |
| 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 | + % find the number of data points in each tau group | |
| 300 | + m = data.rate.*tau; | |
| 301 | + % only integer values allowed (no fractional groups of points) | |
| 302 | + %tau = tau(m-round(m)<1e-8); % numerical precision issues (v2.1) | |
| 303 | + tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
| 304 | + %m = m(m-round(m)<1e-8); % change to round(m) for integer test v2.22 | |
| 305 | + m = m(m==round(m)); | |
| 306 | + %m=round(m); | |
| 307 | + | |
| 308 | + if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n '); end | |
| 309 | + | |
| 310 | + % calculate the Allan deviation for each value of tau | |
| 311 | + k=0; tic; | |
| 312 | + for i = tau | |
| 313 | + if verbose >= 2, fprintf(1,'%g ',i); end | |
| 314 | + k=k+1; | |
| 315 | 315 | |
| 316 | - % truncate frequency set to an even multiple of this tau value | |
| 317 | - freq=dfreq(1:end-rem(length(dfreq),m(k))); | |
| 318 | - % group the data into tau-length groups or bins | |
| 319 | - f = reshape(freq,m(k),[]); % Vectorize! | |
| 320 | - % find average in each "tau group", y_k (each colummn of f) | |
| 321 | - fa=mean(f,1); | |
| 322 | - % first finite difference | |
| 323 | - fd=diff(fa); | |
| 324 | - % calculate two-sample variance for this tau | |
| 325 | - M=length(fa); | |
| 326 | - sm(k)=sqrt(0.5/(M-1)*(sum(fd.^2))); | |
| 316 | + % truncate frequency set to an even multiple of this tau value | |
| 317 | + freq=dfreq(1:end-rem(length(dfreq),m(k))); | |
| 318 | + % group the data into tau-length groups or bins | |
| 319 | + f = reshape(freq,m(k),[]); % Vectorize! | |
| 320 | + % find average in each "tau group", y_k (each colummn of f) | |
| 321 | + fa=mean(f,1); | |
| 322 | + % first finite difference | |
| 323 | + fd=diff(fa); | |
| 324 | + % calculate two-sample variance for this tau | |
| 325 | + M=length(fa); | |
| 326 | + sm(k)=sqrt(0.5/(M-1)*(sum(fd.^2))); | |
| 327 | 327 | |
| 328 | - % estimate error bars | |
| 329 | - sme(k)=sm(k)/sqrt(M+1); | |
| 330 | - | |
| 331 | - if TAUBIN == 1 | |
| 332 | - % save the binning points for plotting | |
| 333 | - fs(k,1:length(freq)/m(k))=m(k):m(k):length(freq); fval{k}=mean(f,1); | |
| 334 | - end | |
| 335 | - | |
| 336 | - end % repeat for each value of tau | |
| 337 | - | |
| 338 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 339 | - calctime=toc; if verbose >= 2, fprintf(1,'allan: Elapsed time for calculation: %e seconds\n',calctime); end | |
| 340 | - | |
| 341 | - | |
| 342 | - | |
| 328 | + % estimate error bars | |
| 329 | + sme(k)=sm(k)/sqrt(M+1); | |
| 330 | + | |
| 331 | + if TAUBIN == 1 | |
| 332 | + % save the binning points for plotting | |
| 333 | + fs(k,1:length(freq)/m(k))=m(k):m(k):length(freq); fval{k}=mean(f,1); | |
| 334 | + end | |
| 335 | + | |
| 336 | + end % repeat for each value of tau | |
| 337 | + | |
| 338 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 339 | + calctime=toc; if verbose >= 2, fprintf(1,'allan: Elapsed time for calculation: %e seconds\n',calctime); end | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | 343 | %% Irregular data (timestamp) |
| 344 | 344 | elseif isfield(data,'time') |
| 345 | - % the interval between measurements is irregular | |
| 346 | - % so we must group the data by time | |
| 347 | - if verbose >= 1, fprintf(1, 'allan: irregular rate data (no fixed sample rate)\n'); end | |
| 348 | - | |
| 349 | - % string for plot title | |
| 350 | - name=[name ' (timestamp)']; | |
| 351 | - | |
| 352 | - % adjust time to remove any initial offset or zero | |
| 353 | - dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 354 | - %dtime=data.time; | |
| 355 | - % where is the maximum gap in time record? | |
| 356 | - gap_pos=find(diff(dtime)==max(diff(dtime))); | |
| 357 | - % what is average data spacing? | |
| 358 | - avg_gap = mean(diff(dtime)); | |
| 359 | - | |
| 360 | - if verbose >= 2 | |
| 361 | - fprintf(1, 'allan: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
| 362 | - fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
| 363 | - fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
| 364 | - fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
| 365 | - pause; | |
| 366 | - end | |
| 367 | - | |
| 368 | - if verbose >= 1 | |
| 369 | - fprintf(1, 'allan: End of timestamp data: %g sec\n',dtime(end)); | |
| 370 | - fprintf(1, ' Average rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
| 371 | - if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
| 372 | - fprintf(1, ' Max. gap: %g sec at position %d\n',max(diff(dtime)),gap_pos(1)); | |
| 373 | - end | |
| 374 | - if max(diff(dtime)) > 5*avg_gap | |
| 375 | - fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
| 376 | - end | |
| 377 | - end | |
| 345 | + % the interval between measurements is irregular | |
| 346 | + % so we must group the data by time | |
| 347 | + if verbose >= 1, fprintf(1, 'allan: irregular rate data (no fixed sample rate)\n'); end | |
| 348 | + | |
| 349 | + % string for plot title | |
| 350 | + name=[name ' (timestamp)']; | |
| 351 | + | |
| 352 | + % adjust time to remove any initial offset or zero | |
| 353 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 354 | + %dtime=data.time; | |
| 355 | + % where is the maximum gap in time record? | |
| 356 | + gap_pos=find(diff(dtime)==max(diff(dtime))); | |
| 357 | + % what is average data spacing? | |
| 358 | + avg_gap = mean(diff(dtime)); | |
| 359 | + | |
| 360 | + if verbose >= 2 | |
| 361 | + fprintf(1, 'allan: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
| 362 | + fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
| 363 | + fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
| 364 | + fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
| 365 | + pause; | |
| 366 | + end | |
| 367 | + | |
| 368 | + if verbose >= 1 | |
| 369 | + fprintf(1, 'allan: End of timestamp data: %g sec\n',dtime(end)); | |
| 370 | + fprintf(1, ' Average rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
| 371 | + if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
| 372 | + fprintf(1, ' Max. gap: %g sec at position %d\n',max(diff(dtime)),gap_pos(1)); | |
| 373 | + end | |
| 374 | + if max(diff(dtime)) > 5*avg_gap | |
| 375 | + fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
| 376 | + end | |
| 377 | + end | |
| 378 | 378 | |
| 379 | 379 | |
| 380 | - % find halfway point | |
| 381 | - halftime = fix(dtime(end)/2); | |
| 382 | - % truncate tau to appropriate values | |
| 383 | - tau = tau(tau >= max(diff(dtime)) & tau <= halftime); | |
| 384 | - if isempty(tau) | |
| 385 | - error('allan: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(diff(dtime)),halftime); | |
| 386 | - end | |
| 387 | - | |
| 388 | - % save the freq data for the loop | |
| 389 | - dfreq=data.freq; | |
| 390 | - dtime=dtime(1:length(dfreq)); | |
| 380 | + % find halfway point | |
| 381 | + halftime = fix(dtime(end)/2); | |
| 382 | + % truncate tau to appropriate values | |
| 383 | + tau = tau(tau >= max(diff(dtime)) & tau <= halftime); | |
| 384 | + if isempty(tau) | |
| 385 | + error('allan: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(diff(dtime)),halftime); | |
| 386 | + end | |
| 387 | + | |
| 388 | + % save the freq data for the loop | |
| 389 | + dfreq=data.freq; | |
| 390 | + dtime=dtime(1:length(dfreq)); | |
| 391 | 391 | |
| 392 | - if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n'); end | |
| 392 | + if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n'); end | |
| 393 | 393 | |
| 394 | - k=0; tic; | |
| 395 | - for i = tau | |
| 396 | - if verbose >= 2, fprintf(1,'%d ',i); end | |
| 397 | - | |
| 398 | - k=k+1; fa=[]; %f=[]; | |
| 399 | - km=0; | |
| 400 | - | |
| 401 | - % truncate data set to an even multiple of this tau value | |
| 402 | - freq=dfreq(dtime <= dtime(end)-rem(dtime(end),i)); | |
| 403 | - time=dtime(dtime <= dtime(end)-rem(dtime(end),i)); | |
| 404 | - %freq=dfreq; | |
| 405 | - %time=dtime; | |
| 406 | - | |
| 407 | - % break up the data into groups of tau length in sec | |
| 408 | - while i*km < time(end) | |
| 409 | - km=km+1; | |
| 410 | - | |
| 411 | - % progress bar | |
| 412 | - if verbose >= 2 | |
| 413 | - if rem(km,100)==0, fprintf(1,'.'); end | |
| 414 | - if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
| 415 | - end | |
| 394 | + k=0; tic; | |
| 395 | + for i = tau | |
| 396 | + if verbose >= 2, fprintf(1,'%d ',i); end | |
| 397 | + | |
| 398 | + k=k+1; fa=[]; %f=[]; | |
| 399 | + km=0; | |
| 400 | + | |
| 401 | + % truncate data set to an even multiple of this tau value | |
| 402 | + freq=dfreq(dtime <= dtime(end)-rem(dtime(end),i)); | |
| 403 | + time=dtime(dtime <= dtime(end)-rem(dtime(end),i)); | |
| 404 | + %freq=dfreq; | |
| 405 | + %time=dtime; | |
| 406 | + | |
| 407 | + % break up the data into groups of tau length in sec | |
| 408 | + while i*km < time(end) | |
| 409 | + km=km+1; | |
| 410 | + | |
| 411 | + % progress bar | |
| 412 | + if verbose >= 2 | |
| 413 | + if rem(km,100)==0, fprintf(1,'.'); end | |
| 414 | + if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
| 415 | + end | |
| 416 | 416 | |
| 417 | - f = freq(i*(km-1) < time & time <= i*km); | |
| 418 | - f = f(~isnan(f)); % make sure values are valid | |
| 419 | - | |
| 420 | - if ~isempty(f) | |
| 421 | - fa(km)=mean(f); | |
| 422 | - else | |
| 423 | - fa(km)=0; | |
| 424 | - end | |
| 417 | + f = freq(i*(km-1) < time & time <= i*km); | |
| 418 | + f = f(~isnan(f)); % make sure values are valid | |
| 419 | + | |
| 420 | + if ~isempty(f) | |
| 421 | + fa(km)=mean(f); | |
| 422 | + else | |
| 423 | + fa(km)=0; | |
| 424 | + end | |
| 425 | 425 | |
| 426 | - if TAUBIN == 1 % WARNING: this has a significant impact on performance | |
| 427 | - % save the binning points for plotting | |
| 428 | - %if find(time <= i*km) > 0 | |
| 429 | - fs(k,km)=max(time(time <= i*km)); | |
| 430 | - %else | |
| 431 | - if isempty(fs(k,km)) | |
| 432 | - fs(k,km)=0; | |
| 433 | - end | |
| 434 | - fval{k}=fa; | |
| 435 | - end % save tau bin plot points | |
| 436 | - | |
| 437 | - end | |
| 438 | - | |
| 439 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 426 | + if TAUBIN == 1 % WARNING: this has a significant impact on performance | |
| 427 | + % save the binning points for plotting | |
| 428 | + %if find(time <= i*km) > 0 | |
| 429 | + fs(k,km)=max(time(time <= i*km)); | |
| 430 | + %else | |
| 431 | + if isempty(fs(k,km)) | |
| 432 | + fs(k,km)=0; | |
| 433 | + end | |
| 434 | + fval{k}=fa; | |
| 435 | + end % save tau bin plot points | |
| 436 | + | |
| 437 | + end | |
| 438 | + | |
| 439 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 440 | 440 | |
| 441 | - % first finite difference of the averaged results | |
| 442 | - fd=diff(fa); | |
| 443 | - % calculate Allan deviation for this tau | |
| 444 | - M=length(fa); | |
| 445 | - sm(k)=sqrt(0.5/(M-1)*(sum(fd.^2))); | |
| 441 | + % first finite difference of the averaged results | |
| 442 | + fd=diff(fa); | |
| 443 | + % calculate Allan deviation for this tau | |
| 444 | + M=length(fa); | |
| 445 | + sm(k)=sqrt(0.5/(M-1)*(sum(fd.^2))); | |
| 446 | 446 | |
| 447 | - % estimate error bars | |
| 448 | - sme(k)=sm(k)/sqrt(M+1); | |
| 449 | - | |
| 447 | + % estimate error bars | |
| 448 | + sme(k)=sm(k)/sqrt(M+1); | |
| 449 | + | |
| 450 | 450 | |
| 451 | - end | |
| 451 | + end | |
| 452 | 452 | |
| 453 | - if verbose == 2, fprintf(1,'\n'); end | |
| 454 | - calctime=toc; if verbose >= 2, fprintf(1,'allan: Elapsed time for calculation: %e seconds\n',calctime); end | |
| 455 | - | |
| 453 | + if verbose == 2, fprintf(1,'\n'); end | |
| 454 | + calctime=toc; if verbose >= 2, fprintf(1,'allan: Elapsed time for calculation: %e seconds\n',calctime); end | |
| 455 | + | |
| 456 | 456 | |
| 457 | 457 | else |
| 458 | - error('allan: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
| 458 | + error('allan: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
| 459 | 459 | end |
| 460 | 460 | |
| 461 | 461 | |
| 462 | 462 | |
| 463 | 463 | |
| 464 | 464 | |
| 465 | 465 | |
| 466 | 466 | |
| 467 | 467 | |
| 468 | 468 | |
| ... | ... | @@ -463,113 +463,113 @@ |
| 463 | 463 | %% Plotting |
| 464 | 464 | |
| 465 | 465 | if verbose >= 2 % show all data |
| 466 | - | |
| 467 | - % plot the frequency data, centered on median | |
| 468 | - if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
| 469 | - try | |
| 470 | - % dsplot makes a new figure | |
| 471 | - hd=dsplot(dtime,medianfreq); | |
| 472 | - catch ME | |
| 473 | - figure; | |
| 474 | - if length(dtime) ~= length(medianfreq) | |
| 475 | - fprintf(1,'allan: Warning: length of time axis (%d) is not equal to data array (%d)\n',length(dtime),length(medianfreq)); | |
| 476 | - end | |
| 477 | - hd=plot(dtime,medianfreq); | |
| 478 | - if verbose >= 1, fprintf(1,'allan: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
| 479 | - if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
| 480 | - end | |
| 481 | - set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
| 482 | - hold on; | |
| 466 | + | |
| 467 | + % plot the frequency data, centered on median | |
| 468 | + if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
| 469 | + try | |
| 470 | + % dsplot makes a new figure | |
| 471 | + hd=dsplot(dtime,medianfreq); | |
| 472 | + catch ME | |
| 473 | + figure; | |
| 474 | + if length(dtime) ~= length(medianfreq) | |
| 475 | + fprintf(1,'allan: Warning: length of time axis (%d) is not equal to data array (%d)\n',length(dtime),length(medianfreq)); | |
| 476 | + end | |
| 477 | + hd=plot(dtime,medianfreq); | |
| 478 | + if verbose >= 1, fprintf(1,'allan: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
| 479 | + if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
| 480 | + end | |
| 481 | + set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
| 482 | + hold on; | |
| 483 | 483 | |
| 484 | - % show center (0) | |
| 485 | - plot(xlim,[0 0],':k'); | |
| 486 | - % show 5x Median Absolute Deviation (MAD) values | |
| 487 | - hm=plot(xlim,[5*s.MAD 5*s.MAD],'-r'); | |
| 488 | - plot(xlim,[-5*s.MAD -5*s.MAD],'-r'); | |
| 489 | - % show linear fit line | |
| 490 | - hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
| 491 | - title(['Data: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 492 | - %set(get(gca,'Title'),'Interpreter','none'); | |
| 493 | - xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
| 494 | - if isfield(data,'units') | |
| 495 | - ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 496 | - else | |
| 497 | - ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
| 498 | - end | |
| 499 | - set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 500 | - legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
| 501 | - % tighten up | |
| 502 | - xlim([dtime(1) dtime(end)]); | |
| 484 | + % show center (0) | |
| 485 | + plot(xlim,[0 0],':k'); | |
| 486 | + % show 5x Median Absolute Deviation (MAD) values | |
| 487 | + hm=plot(xlim,[5*s.MAD 5*s.MAD],'-r'); | |
| 488 | + plot(xlim,[-5*s.MAD -5*s.MAD],'-r'); | |
| 489 | + % show linear fit line | |
| 490 | + hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
| 491 | + title(['Data: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 492 | + %set(get(gca,'Title'),'Interpreter','none'); | |
| 493 | + xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
| 494 | + if isfield(data,'units') | |
| 495 | + ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 496 | + else | |
| 497 | + ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
| 498 | + end | |
| 499 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 500 | + legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
| 501 | + % tighten up | |
| 502 | + xlim([dtime(1) dtime(end)]); | |
| 503 | 503 | |
| 504 | 504 | |
| 505 | - % Optional tau bin (y_k samples) plot | |
| 506 | - if TAUBIN == 1 | |
| 507 | - % plot the tau divisions on the data plot | |
| 508 | - rfs=size(fs,1); | |
| 509 | - colororder=get(gca,'ColorOrder'); | |
| 510 | - axis tight; kc=2; | |
| 511 | - %ap=axis; | |
| 512 | - for j=1:rfs | |
| 513 | - kc=kc+1; if rem(kc,length(colororder))==1, kc=2; end | |
| 514 | - %for b=1:max(find(fs(j,:))); % new form of "find" in r2009a | |
| 515 | - for b=1:find(fs(j,:), 1, 'last' ); | |
| 516 | - % plot the tau division boundaries | |
| 517 | - %plot([fs(j,b) fs(j,b)],[ap(3)*1.1 ap(4)*1.1],'-','Color',colororder(kc,:)); | |
| 518 | - % plot tau group y values | |
| 519 | - if b == 1 | |
| 520 | - plot([dtime(1) fs(j,b)],[fval{j}(b)-s.median fval{j}(b)-s.median],'-','Color',colororder(kc,:),'LineWidth',4); | |
| 521 | - else | |
| 522 | - plot([fs(j,b-1) fs(j,b)],[fval{j}(b)-s.median fval{j}(b)-s.median],'-','Color',colororder(kc,:),'LineWidth',4); | |
| 523 | - end | |
| 524 | - end | |
| 525 | - end | |
| 526 | - axis auto | |
| 527 | - end % End optional bin plot | |
| 528 | - | |
| 505 | + % Optional tau bin (y_k samples) plot | |
| 506 | + if TAUBIN == 1 | |
| 507 | + % plot the tau divisions on the data plot | |
| 508 | + rfs=size(fs,1); | |
| 509 | + colororder=get(gca,'ColorOrder'); | |
| 510 | + axis tight; kc=2; | |
| 511 | + %ap=axis; | |
| 512 | + for j=1:rfs | |
| 513 | + kc=kc+1; if rem(kc,length(colororder))==1, kc=2; end | |
| 514 | + %for b=1:max(find(fs(j,:))); % new form of "find" in r2009a | |
| 515 | + for b=1:find(fs(j,:), 1, 'last' ); | |
| 516 | + % plot the tau division boundaries | |
| 517 | + %plot([fs(j,b) fs(j,b)],[ap(3)*1.1 ap(4)*1.1],'-','Color',colororder(kc,:)); | |
| 518 | + % plot tau group y values | |
| 519 | + if b == 1 | |
| 520 | + plot([dtime(1) fs(j,b)],[fval{j}(b)-s.median fval{j}(b)-s.median],'-','Color',colororder(kc,:),'LineWidth',4); | |
| 521 | + else | |
| 522 | + plot([fs(j,b-1) fs(j,b)],[fval{j}(b)-s.median fval{j}(b)-s.median],'-','Color',colororder(kc,:),'LineWidth',4); | |
| 523 | + end | |
| 524 | + end | |
| 525 | + end | |
| 526 | + axis auto | |
| 527 | + end % End optional bin plot | |
| 528 | + | |
| 529 | 529 | end % end plot raw data |
| 530 | 530 | |
| 531 | 531 | |
| 532 | 532 | if verbose >= 1 % show ADEV results |
| 533 | 533 | |
| 534 | - % plot Allan deviation results | |
| 535 | - if ~isempty(sm) | |
| 536 | - figure | |
| 534 | + % plot Allan deviation results | |
| 535 | + if ~isempty(sm) | |
| 536 | + figure | |
| 537 | 537 | |
| 538 | - % Choose loglog or semilogx plot here #PLOTLOG | |
| 539 | - %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 540 | - loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 538 | + % Choose loglog or semilogx plot here #PLOTLOG | |
| 539 | + %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 540 | + loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 541 | 541 | |
| 542 | - % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
| 543 | - % When this is fixed in a future release, uncomment below to use normal errorbars | |
| 544 | - %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
| 545 | - % this is a hack to approximate the error bars | |
| 546 | - hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
| 542 | + % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
| 543 | + % When this is fixed in a future release, uncomment below to use normal errorbars | |
| 544 | + %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
| 545 | + % this is a hack to approximate the error bars | |
| 546 | + hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
| 547 | 547 | |
| 548 | - grid on; | |
| 549 | - title(['Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 550 | - %set(get(gca,'Title'),'Interpreter','none'); | |
| 551 | - xlabel('\tau [sec]','FontSize',FontSize,'FontName',FontName); | |
| 552 | - if isfield(data,'units') | |
| 553 | - ylabel(['\sigma_y(\tau) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 554 | - else | |
| 555 | - ylabel('\sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
| 556 | - end | |
| 557 | - set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 558 | - % expand the x axis a little bit so that the errors bars look nice | |
| 559 | - adax = axis; | |
| 560 | - axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
| 561 | - | |
| 562 | - % display the minimum value | |
| 563 | - fprintf(1,'allan: Minimum ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
| 564 | - | |
| 565 | - elseif verbose >= 1 | |
| 566 | - fprintf(1,'allan: WARNING: no values calculated.\n'); | |
| 567 | - fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
| 568 | - fprintf(1,'Type "help allan" for more information.\n\n'); | |
| 569 | - end | |
| 548 | + grid on; | |
| 549 | + title(['Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 550 | + %set(get(gca,'Title'),'Interpreter','none'); | |
| 551 | + xlabel('\tau [sec]','FontSize',FontSize,'FontName',FontName); | |
| 552 | + if isfield(data,'units') | |
| 553 | + ylabel(['\sigma_y(\tau) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 554 | + else | |
| 555 | + ylabel('\sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
| 556 | + end | |
| 557 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 558 | + % expand the x axis a little bit so that the errors bars look nice | |
| 559 | + adax = axis; | |
| 560 | + axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
| 561 | + | |
| 562 | + % display the minimum value | |
| 563 | + fprintf(1,'allan: Minimum ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
| 564 | + | |
| 565 | + elseif verbose >= 1 | |
| 566 | + fprintf(1,'allan: WARNING: no values calculated.\n'); | |
| 567 | + fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
| 568 | + fprintf(1,'Type "help allan" for more information.\n\n'); | |
| 569 | + end | |
| 570 | 570 | |
| 571 | 571 | end % end plot ADEV data |
| 572 | - | |
| 572 | + | |
| 573 | 573 | retval = sm; |
| 574 | 574 | errorb = sme; |
| 575 | 575 |
allan_cov.m
| ... | ... | @@ -5,33 +5,33 @@ |
| 5 | 5 | % Inputs: |
| 6 | 6 | % DATA should be a structure and have the following fields: |
| 7 | 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 . | |
| 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 | 15 | % |
| 16 | 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. | |
| 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 | 21 | % |
| 22 | 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. | |
| 23 | +% The units for the data. If present, the string DATA.units | |
| 24 | +% is added to the plot y-axis label. | |
| 25 | 25 | % |
| 26 | 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. | |
| 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 | 30 | % NAME is an optional label that is added to the plot titles. |
| 31 | 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) | |
| 32 | +% 0 = silent & no data plots; | |
| 33 | +% 1 = status messages & minimum plots; | |
| 34 | +% 2 = all messages and plots (default) | |
| 35 | 35 | % |
| 36 | 36 | % Outputs: |
| 37 | 37 | % RETVAL is the array of Allan deviation values at each TAU. |
| ... | ... | @@ -46,8 +46,8 @@ |
| 46 | 46 | % To compute the Allan deviation for the data in the variable "lt": |
| 47 | 47 | % >> lt |
| 48 | 48 | % lt = |
| 49 | -% freq: [1x86400 double] | |
| 50 | -% rate: 0.5 | |
| 49 | +% freq: [1x86400 double] | |
| 50 | +% rate: 0.5 | |
| 51 | 51 | % |
| 52 | 52 | % Use: |
| 53 | 53 | % |
| 54 | 54 | |
| 55 | 55 | |
| ... | ... | @@ -94,16 +94,16 @@ |
| 94 | 94 | % |
| 95 | 95 | % |
| 96 | 96 | % M.A. Hopcroft |
| 97 | -% mhopeng at gmail dot com | |
| 97 | +% mhopeng at gmail dot com | |
| 98 | 98 | % |
| 99 | 99 | % I welcome your comments and feedback! |
| 100 | 100 | % |
| 101 | 101 | % MH Mar2014 |
| 102 | 102 | % v2.24 fix bug related to generating freq data from phase with timestamps |
| 103 | -% (thanks to S. David-Grignot for finding the bug) | |
| 103 | +% (thanks to S. David-Grignot for finding the bug) | |
| 104 | 104 | % MH Oct2010 |
| 105 | 105 | % v2.22 tau truncation to integer groups; tau sort |
| 106 | -% plotting bugfix | |
| 106 | +% plotting bugfix | |
| 107 | 107 | % v2.20 sychronize updates across allan, allan_overlap, allan_modified |
| 108 | 108 | % v2.16 add TAU as output, fixed unusual error with dsplot v1.1 |
| 109 | 109 | % v2.14 update plotting behaviour, default tau values |
| 110 | 110 | |
| 111 | 111 | |
| 112 | 112 | |
| 113 | 113 | |
| 114 | 114 | |
| 115 | 115 | |
| 116 | 116 | |
| 117 | 117 | |
| 118 | 118 | |
| ... | ... | @@ -113,53 +113,53 @@ |
| 113 | 113 | |
| 114 | 114 | % MH Jun2010 |
| 115 | 115 | % v2.12 bugfix for rate data row/col orientation |
| 116 | -% add DATA.units for plotting | |
| 117 | -% use dsplot.m for plotting | |
| 116 | +% add DATA.units for plotting | |
| 117 | +% use dsplot.m for plotting | |
| 118 | 118 | % |
| 119 | 119 | % MH MAR2010 |
| 120 | 120 | % v2.1 minor interface and bugfixes |
| 121 | -% update data consistency check | |
| 121 | +% update data consistency check | |
| 122 | 122 | % |
| 123 | 123 | % MH FEB2010 |
| 124 | 124 | % v2.0 Consistent code behaviour for all "allan_x.m" functions: |
| 125 | -% accept phase data | |
| 126 | -% verbose levels | |
| 125 | +% accept phase data | |
| 126 | +% verbose levels | |
| 127 | 127 | % |
| 128 | 128 | % |
| 129 | 129 | % MH JAN2010 |
| 130 | 130 | % v1.84 code cleanup |
| 131 | 131 | % v1.82 typos in comments and code cleanup |
| 132 | -% tau bin plotting changed for performance improvement | |
| 132 | +% tau bin plotting changed for performance improvement | |
| 133 | 133 | % v1.8 Performance improvements: |
| 134 | -% vectorize code for rate data | |
| 135 | -% logical indexing for irregular rate data | |
| 134 | +% vectorize code for rate data | |
| 135 | +% logical indexing for irregular rate data | |
| 136 | 136 | % MH APR2008 |
| 137 | 137 | % v1.62 loglog plot option |
| 138 | 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 | |
| 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 | 144 | % MH JUN2007 |
| 145 | 145 | % v1.54 Improve data plotting and optional bin plotting |
| 146 | 146 | % MH FEB2007 |
| 147 | 147 | % v1.5 use difference from median for plotting |
| 148 | -% added MAD calculation for outlier detection | |
| 148 | +% added MAD calculation for outlier detection | |
| 149 | 149 | % MH JAN2007 |
| 150 | 150 | % v1.48 plotting typos fixes |
| 151 | 151 | % MH DEC2006 |
| 152 | 152 | % v1.46 hack to plot error bars |
| 153 | 153 | % v1.44 further validation (Riley 1000-pt) |
| 154 | -% plot mean and std | |
| 154 | +% plot mean and std | |
| 155 | 155 | % MH NOV2006 |
| 156 | 156 | % v1.42 typo fix comments |
| 157 | 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 | |
| 158 | +% irregular algorithm rejects tau less than max gap in time data | |
| 159 | +% validate both algorithms using test data from NBS Monograph 140 | |
| 160 | 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 | |
| 161 | +% add error bars (not possible due to bug in MATLAB R14SP3) | |
| 162 | +% remove offset calculation | |
| 163 | 163 | % v1.24 improve feedback |
| 164 | 164 | % MH SEP2006 |
| 165 | 165 | % v1.22 updated comments |
| 166 | 166 | |
| ... | ... | @@ -184,25 +184,25 @@ |
| 184 | 184 | |
| 185 | 185 | %% Data consistency checks |
| 186 | 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]'); | |
| 187 | + error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
| 188 | 188 | end |
| 189 | 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 | |
| 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 | 206 | end |
| 207 | 207 | |
| 208 | 208 | % sort tau vector |
| 209 | 209 | |
| 210 | 210 | |
| 211 | 211 | |
| 212 | 212 | |
| 213 | 213 | |
| ... | ... | @@ -211,34 +211,34 @@ |
| 211 | 211 | |
| 212 | 212 | %% Basic statistical tests on the data set |
| 213 | 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 | |
| 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 | 221 | end |
| 222 | 222 | if size(data.freq,2) > size(data.freq,1), data.freq=data.freq'; end % ensure columns |
| 223 | - | |
| 223 | + | |
| 224 | 224 | s.numpoints=length(data.freq); |
| 225 | 225 | s.max=max(data.freq); |
| 226 | 226 | s.min=min(data.freq); |
| 227 | 227 | s.mean=mean(data.freq); |
| 228 | 228 | s.median=median(data.freq); |
| 229 | 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); | |
| 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 | 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); | |
| 233 | + s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
| 234 | 234 | else |
| 235 | - error('Either "time" or "rate" must be present in DATA. Type "help allan" for details. [err1]'); | |
| 235 | + error('Either "time" or "rate" must be present in DATA. Type "help allan" for details. [err1]'); | |
| 236 | 236 | end |
| 237 | 237 | s.std=std(data.freq); |
| 238 | 238 | |
| 239 | 239 | if verbose >= 2 |
| 240 | - fprintf(1,'allan: input data statistics:\n'); | |
| 241 | - disp(s); | |
| 240 | + fprintf(1,'allan: input data statistics:\n'); | |
| 241 | + disp(s); | |
| 242 | 242 | end |
| 243 | 243 | |
| 244 | 244 | |
| 245 | 245 | |
| ... | ... | @@ -249,10 +249,10 @@ |
| 249 | 249 | % Screen for outliers using 5x Median Absolute Deviation (MAD) criteria |
| 250 | 250 | s.MAD = median(abs(medianfreq)/0.6745); |
| 251 | 251 | if verbose >= 2 |
| 252 | - fprintf(1, 'allan: 5x MAD value for outlier detection: %g\n',5*s.MAD); | |
| 252 | + fprintf(1, 'allan: 5x MAD value for outlier detection: %g\n',5*s.MAD); | |
| 253 | 253 | end |
| 254 | 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'); | |
| 255 | + fprintf(1, 'allan: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
| 256 | 256 | end |
| 257 | 257 | |
| 258 | 258 | |
| 259 | 259 | |
| 260 | 260 | |
| 261 | 261 | |
| 262 | 262 | |
| 263 | 263 | |
| 264 | 264 | |
| 265 | 265 | |
| 266 | 266 | |
| 267 | 267 | |
| 268 | 268 | |
| 269 | 269 | |
| 270 | 270 | |
| 271 | 271 | |
| 272 | 272 | |
| 273 | 273 | |
| ... | ... | @@ -263,204 +263,204 @@ |
| 263 | 263 | % If there is a regular interval between measurements, calculation is much |
| 264 | 264 | % easier/faster |
| 265 | 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; | |
| 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 | 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 | |
| 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 | 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; | |
| 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 | 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)*(abs(sum(fd.*fd2)))); | |
| 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)*(abs(sum(fd.*fd2)))); | |
| 332 | 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 | - | |
| 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 | 348 | %% Irregular data (timestamp) |
| 349 | 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 | |
| 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 | 383 | |
| 384 | 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)); | |
| 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 | 396 | |
| 397 | - if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n'); end | |
| 397 | + if verbose >= 1, fprintf(1,'allan: calculating Allan deviation...\n'); end | |
| 398 | 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 | |
| 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 | 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 | |
| 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 | 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 | |
| 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 | 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))); | |
| 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 | 451 | |
| 452 | - % estimate error bars | |
| 453 | - sme(k)=sm(k)/sqrt(M+1); | |
| 454 | - | |
| 452 | + % estimate error bars | |
| 453 | + sme(k)=sm(k)/sqrt(M+1); | |
| 454 | + | |
| 455 | 455 | |
| 456 | - end | |
| 456 | + end | |
| 457 | 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 | - | |
| 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 | 461 | |
| 462 | 462 | else |
| 463 | - error('allan: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
| 463 | + error('allan: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
| 464 | 464 | end |
| 465 | 465 | |
| 466 | 466 | |
| 467 | 467 | |
| 468 | 468 | |
| 469 | 469 | |
| 470 | 470 | |
| 471 | 471 | |
| 472 | 472 | |
| 473 | 473 | |
| ... | ... | @@ -468,113 +468,113 @@ |
| 468 | 468 | %% Plotting |
| 469 | 469 | |
| 470 | 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; | |
| 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 | 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)]); | |
| 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 | 508 | |
| 509 | 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 | - | |
| 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 | 534 | end % end plot raw data |
| 535 | 535 | |
| 536 | 536 | |
| 537 | 537 | if verbose >= 1 % show ADEV results |
| 538 | 538 | |
| 539 | - % plot Allan deviation results | |
| 540 | - if ~isempty(sm) | |
| 541 | - figure | |
| 539 | + % plot Allan deviation results | |
| 540 | + if ~isempty(sm) | |
| 541 | + figure | |
| 542 | 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); | |
| 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 | 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)); | |
| 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 | 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 | |
| 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 | 575 | |
| 576 | 576 | end % end plot ADEV data |
| 577 | - | |
| 577 | + | |
| 578 | 578 | retval = sm; |
| 579 | 579 | errorb = sme; |
| 580 | 580 |
allan_modified.m
| ... | ... | @@ -6,24 +6,24 @@ |
| 6 | 6 | % Inputs: |
| 7 | 7 | % DATA should be a struct and have the following fields: |
| 8 | 8 | % DATA.freq or DATA.phase |
| 9 | -% A vector of fractional frequency measurements (df/f) in | |
| 10 | -% DATA.freq *or* phase offset data (seconds) in DATA.phase | |
| 11 | -% If phase data is not present, it will be generated by | |
| 12 | -% integrating the fractional frequency data. | |
| 13 | -% If both fields are present, then DATA.phase will be used. | |
| 9 | +% A vector of fractional frequency measurements (df/f) in | |
| 10 | +% DATA.freq *or* phase offset data (seconds) in DATA.phase | |
| 11 | +% If phase data is not present, it will be generated by | |
| 12 | +% integrating the fractional frequency data. | |
| 13 | +% If both fields are present, then DATA.phase will be used. | |
| 14 | 14 | % |
| 15 | 15 | % DATA.rate or DATA.time |
| 16 | -% The sampling rate in Hertz (DATA.rate) or a vector of | |
| 17 | -% timestamps for each measurement in seconds (DATA.time). | |
| 18 | -% DATA.rate is used if both fields are present. | |
| 19 | -% If DATA.rate == 0, then the timestamps are used. | |
| 16 | +% The sampling rate in Hertz (DATA.rate) or a vector of | |
| 17 | +% timestamps for each measurement in seconds (DATA.time). | |
| 18 | +% DATA.rate is used if both fields are present. | |
| 19 | +% If DATA.rate == 0, then the timestamps are used. | |
| 20 | 20 | % |
| 21 | 21 | % TAU is an array of tau values for computing Allan deviation. |
| 22 | -% TAU values must be divisible by 1/DATA.rate (data points cannot be | |
| 23 | -% grouped in fractional quantities!). Invalid values are ignored. | |
| 22 | +% TAU values must be divisible by 1/DATA.rate (data points cannot be | |
| 23 | +% grouped in fractional quantities!). Invalid values are ignored. | |
| 24 | 24 | % NAME is an optional label that is added to the plot titles. |
| 25 | 25 | % VERBOSE sets the level of status messages: |
| 26 | -% 0 = silent & no data plots; 1 = status messages; 2 = all messages | |
| 26 | +% 0 = silent & no data plots; 1 = status messages; 2 = all messages | |
| 27 | 27 | % |
| 28 | 28 | % Outputs: |
| 29 | 29 | % RETVAL is the array of modified Allan deviation values at each TAU. |
| ... | ... | @@ -38,8 +38,8 @@ |
| 38 | 38 | % To compute the modified Allan deviation for the data in the variable "lt": |
| 39 | 39 | % >> lt |
| 40 | 40 | % lt = |
| 41 | -% freq: [1x86400 double] | |
| 42 | -% rate: 0.5 | |
| 41 | +% freq: [1x86400 double] | |
| 42 | +% rate: 0.5 | |
| 43 | 43 | % |
| 44 | 44 | % Use: |
| 45 | 45 | % |
| 46 | 46 | |
| 47 | 47 | |
| 48 | 48 | |
| 49 | 49 | |
| ... | ... | @@ -83,25 +83,25 @@ |
| 83 | 83 | % |
| 84 | 84 | % |
| 85 | 85 | % M.A. Hopcroft |
| 86 | -% mhopeng at gmail dot com | |
| 86 | +% mhopeng at gmail dot com | |
| 87 | 87 | % |
| 88 | 88 | % I welcome your comments and feedback! |
| 89 | 89 | % |
| 90 | 90 | % MH Mar2014 |
| 91 | 91 | % v1.24 fix bug related to generating freq data from phase with timestamps |
| 92 | -% (thanks to S. David-Grignot for finding the bug) | |
| 92 | +% (thanks to S. David-Grignot for finding the bug) | |
| 93 | 93 | % MH Oct2010 |
| 94 | 94 | % v1.22 tau truncation to integer groups; tau sort |
| 95 | -% plotting bugfix | |
| 95 | +% plotting bugfix | |
| 96 | 96 | % v1.20 update to match allan.m (dsplot.m, columns) |
| 97 | -% discard tau values with timestamp irregularities | |
| 97 | +% discard tau values with timestamp irregularities | |
| 98 | 98 | % |
| 99 | 99 | |
| 100 | 100 | versionstr = 'allan_modified v1.24'; |
| 101 | 101 | |
| 102 | 102 | % MH MAR2010 |
| 103 | 103 | % v1.1 bugfixes for irregular sample rates |
| 104 | -% update consistency check | |
| 104 | +% update consistency check | |
| 105 | 105 | % |
| 106 | 106 | % MH FEB2010 |
| 107 | 107 | % v1.0 based on allan_overlap v2.0 |
| 108 | 108 | |
| ... | ... | @@ -125,25 +125,25 @@ |
| 125 | 125 | |
| 126 | 126 | %% Data consistency checks |
| 127 | 127 | if ~(isfield(data,'phase') || isfield(data,'freq')) |
| 128 | - error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
| 128 | + error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
| 129 | 129 | end |
| 130 | 130 | if isfield(data,'time') |
| 131 | - if isfield(data,'phase') && (length(data.phase) ~= length(data.time)) | |
| 132 | - if isfield(data,'freq') && (length(data.freq) ~= length(data.time)) | |
| 133 | - error('The time and freq vectors are not the same length. See help for details. [con2]'); | |
| 134 | - else | |
| 135 | - error('The time and phase vectors are not the same length. See help for details. [con1]'); | |
| 136 | - end | |
| 137 | - end | |
| 138 | - if isfield(data,'phase') && (any(isnan(data.phase)) || any(isinf(data.phase))) | |
| 139 | - error('The phase vector contains invalid elements (NaN/Inf). [con3]'); | |
| 140 | - end | |
| 141 | - if isfield(data,'freq') && (any(isnan(data.freq)) || any(isinf(data.freq))) | |
| 142 | - error('The freq vector contains invalid elements (NaN/Inf). [con4]'); | |
| 143 | - end | |
| 144 | - if isfield(data,'time') && (any(isnan(data.time)) || any(isinf(data.time))) | |
| 145 | - error('The time vector contains invalid elements (NaN/Inf). [con5]'); | |
| 146 | - end | |
| 131 | + if isfield(data,'phase') && (length(data.phase) ~= length(data.time)) | |
| 132 | + if isfield(data,'freq') && (length(data.freq) ~= length(data.time)) | |
| 133 | + error('The time and freq vectors are not the same length. See help for details. [con2]'); | |
| 134 | + else | |
| 135 | + error('The time and phase vectors are not the same length. See help for details. [con1]'); | |
| 136 | + end | |
| 137 | + end | |
| 138 | + if isfield(data,'phase') && (any(isnan(data.phase)) || any(isinf(data.phase))) | |
| 139 | + error('The phase vector contains invalid elements (NaN/Inf). [con3]'); | |
| 140 | + end | |
| 141 | + if isfield(data,'freq') && (any(isnan(data.freq)) || any(isinf(data.freq))) | |
| 142 | + error('The freq vector contains invalid elements (NaN/Inf). [con4]'); | |
| 143 | + end | |
| 144 | + if isfield(data,'time') && (any(isnan(data.time)) || any(isinf(data.time))) | |
| 145 | + error('The time vector contains invalid elements (NaN/Inf). [con5]'); | |
| 146 | + end | |
| 147 | 147 | end |
| 148 | 148 | |
| 149 | 149 | % sort tau vector |
| ... | ... | @@ -152,12 +152,12 @@ |
| 152 | 152 | |
| 153 | 153 | %% Basic statistical tests on the data set |
| 154 | 154 | if ~isfield(data,'freq') |
| 155 | - if isfield(data,'rate') && data.rate ~= 0 | |
| 156 | - data.freq=diff(data.phase).*data.rate; | |
| 157 | - elseif isfield(data,'time') | |
| 158 | - data.freq=diff(data.phase)./diff(data.time); | |
| 159 | - end | |
| 160 | - if verbose >= 1, fprintf(1,'allan_modified: Fractional frequency data generated from phase data (M=%g).\n',length(data.freq)); end | |
| 155 | + if isfield(data,'rate') && data.rate ~= 0 | |
| 156 | + data.freq=diff(data.phase).*data.rate; | |
| 157 | + elseif isfield(data,'time') | |
| 158 | + data.freq=diff(data.phase)./diff(data.time); | |
| 159 | + end | |
| 160 | + if verbose >= 1, fprintf(1,'allan_modified: Fractional frequency data generated from phase data (M=%g).\n',length(data.freq)); end | |
| 161 | 161 | end |
| 162 | 162 | if size(data.freq,2) > size(data.freq,1), data.freq=data.freq'; end % ensure columns |
| 163 | 163 | |
| 164 | 164 | |
| 165 | 165 | |
| 166 | 166 | |
| ... | ... | @@ -167,18 +167,18 @@ |
| 167 | 167 | s.mean=mean(data.freq); |
| 168 | 168 | s.median=median(data.freq); |
| 169 | 169 | if isfield(data,'time') |
| 170 | - if size(data.time,2) > size(data.time,1), data.time=data.time'; end % ensure columns | |
| 171 | - s.linear=polyfit(data.time(1:length(data.freq)),data.freq,1); | |
| 170 | + if size(data.time,2) > size(data.time,1), data.time=data.time'; end % ensure columns | |
| 171 | + s.linear=polyfit(data.time(1:length(data.freq)),data.freq,1); | |
| 172 | 172 | elseif isfield(data,'rate') && data.rate ~= 0; |
| 173 | - s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
| 173 | + s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
| 174 | 174 | else |
| 175 | - error('Either "time" or "rate" must be present in DATA. Type "help allan_modified" for details. [err1]'); | |
| 175 | + error('Either "time" or "rate" must be present in DATA. Type "help allan_modified" for details. [err1]'); | |
| 176 | 176 | end |
| 177 | 177 | s.std=std(data.freq); |
| 178 | 178 | |
| 179 | 179 | if verbose >= 2 |
| 180 | - fprintf(1,'allan_modified: fractional frequency data statistics:\n'); | |
| 181 | - disp(s); | |
| 180 | + fprintf(1,'allan_modified: fractional frequency data statistics:\n'); | |
| 181 | + disp(s); | |
| 182 | 182 | end |
| 183 | 183 | |
| 184 | 184 | % scale to median for plotting |
| ... | ... | @@ -188,7 +188,7 @@ |
| 188 | 188 | % Screen for outliers using 5x Median Absolute Deviation (MAD) criteria |
| 189 | 189 | MAD = median(abs(medianfreq)/0.6745); |
| 190 | 190 | if verbose >= 1 && any(abs(medianfreq) > 5*MAD) |
| 191 | - fprintf(1, 'allan_modified: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
| 191 | + fprintf(1, 'allan_modified: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
| 192 | 192 | end |
| 193 | 193 | |
| 194 | 194 | %%%% |
| 195 | 195 | |
| 196 | 196 | |
| 197 | 197 | |
| 198 | 198 | |
| 199 | 199 | |
| 200 | 200 | |
| 201 | 201 | |
| 202 | 202 | |
| 203 | 203 | |
| 204 | 204 | |
| 205 | 205 | |
| 206 | 206 | |
| 207 | 207 | |
| 208 | 208 | |
| 209 | 209 | |
| 210 | 210 | |
| 211 | 211 | |
| 212 | 212 | |
| 213 | 213 | |
| 214 | 214 | |
| 215 | 215 | |
| 216 | 216 | |
| ... | ... | @@ -198,279 +198,279 @@ |
| 198 | 198 | % If there is a regular interval between measurements, calculation is much |
| 199 | 199 | % easier/faster |
| 200 | 200 | if isfield(data,'rate') && data.rate > 0 % if data rate was given |
| 201 | - if verbose >= 1 | |
| 202 | - fprintf(1, 'allan_modified: regular data '); | |
| 203 | - if isfield(data,'freq') | |
| 204 | - fprintf(1, '(%g freq data points @ %g Hz)\n',length(data.freq),data.rate); | |
| 205 | - elseif isfield(data,'phase') | |
| 206 | - fprintf(1, '(%g phase data points @ %g Hz)\n',length(data.phase),data.rate); | |
| 207 | - else | |
| 208 | - error('\n phase or freq data missing [err10]'); | |
| 209 | - end | |
| 210 | - end | |
| 211 | - | |
| 212 | - % string for plot title | |
| 213 | - name=[name ' (' num2str(data.rate) ' Hz)']; | |
| 201 | + if verbose >= 1 | |
| 202 | + fprintf(1, 'allan_modified: regular data '); | |
| 203 | + if isfield(data,'freq') | |
| 204 | + fprintf(1, '(%g freq data points @ %g Hz)\n',length(data.freq),data.rate); | |
| 205 | + elseif isfield(data,'phase') | |
| 206 | + fprintf(1, '(%g phase data points @ %g Hz)\n',length(data.phase),data.rate); | |
| 207 | + else | |
| 208 | + error('\n phase or freq data missing [err10]'); | |
| 209 | + end | |
| 210 | + end | |
| 211 | + | |
| 212 | + % string for plot title | |
| 213 | + name=[name ' (' num2str(data.rate) ' Hz)']; | |
| 214 | 214 | |
| 215 | - % what is the time interval between data points? | |
| 216 | - tmstep = 1/data.rate; | |
| 217 | - | |
| 218 | - % Is there time data? Just for curiosity/plotting, does not impact calculation | |
| 219 | - if isfield(data,'time') | |
| 220 | - % adjust time data to remove any starting gap; first time step | |
| 221 | - % should not be zero for comparison with freq data | |
| 222 | - dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 223 | - dtime=dtime(1:length(medianfreq)); % equalize the data vector lengths for plotting (v1.1) | |
| 224 | - if verbose >= 2 | |
| 225 | - fprintf(1,'allan_modified: End of timestamp data: %g sec.\n',dtime(end)); | |
| 226 | - if (data.rate - 1/mean(diff(dtime))) > 1e-6 | |
| 227 | - fprintf(1,'allan_modified: NOTE: data.rate (%f Hz) does not match average timestamped sample rate (%f Hz)\n',data.rate,1/mean(diff(dtime))); | |
| 228 | - end | |
| 229 | - end | |
| 230 | - else | |
| 231 | - % create time axis data using rate (for plotting only) | |
| 232 | - dtime=(tmstep:tmstep:length(data.freq)*tmstep); | |
| 233 | - end | |
| 234 | - | |
| 235 | - | |
| 236 | - % is phase data present? If not, generate it | |
| 237 | - if ~isfield(data,'phase') | |
| 238 | - nfreq=data.freq-s.mean; | |
| 239 | - dphase=zeros(1,length(nfreq)+1); | |
| 240 | - dphase(2:end) = cumsum(nfreq).*tmstep; | |
| 241 | - if verbose >= 1, fprintf(1,'allan_modified: phase data generated from fractional frequency data (N=%g).\n',length(dphase)); end | |
| 242 | - else | |
| 243 | - dphase=data.phase; | |
| 244 | - end | |
| 215 | + % what is the time interval between data points? | |
| 216 | + tmstep = 1/data.rate; | |
| 217 | + | |
| 218 | + % Is there time data? Just for curiosity/plotting, does not impact calculation | |
| 219 | + if isfield(data,'time') | |
| 220 | + % adjust time data to remove any starting gap; first time step | |
| 221 | + % should not be zero for comparison with freq data | |
| 222 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 223 | + dtime=dtime(1:length(medianfreq)); % equalize the data vector lengths for plotting (v1.1) | |
| 224 | + if verbose >= 2 | |
| 225 | + fprintf(1,'allan_modified: End of timestamp data: %g sec.\n',dtime(end)); | |
| 226 | + if (data.rate - 1/mean(diff(dtime))) > 1e-6 | |
| 227 | + fprintf(1,'allan_modified: NOTE: data.rate (%f Hz) does not match average timestamped sample rate (%f Hz)\n',data.rate,1/mean(diff(dtime))); | |
| 228 | + end | |
| 229 | + end | |
| 230 | + else | |
| 231 | + % create time axis data using rate (for plotting only) | |
| 232 | + dtime=(tmstep:tmstep:length(data.freq)*tmstep); | |
| 233 | + end | |
| 234 | + | |
| 235 | + | |
| 236 | + % is phase data present? If not, generate it | |
| 237 | + if ~isfield(data,'phase') | |
| 238 | + nfreq=data.freq-s.mean; | |
| 239 | + dphase=zeros(1,length(nfreq)+1); | |
| 240 | + dphase(2:end) = cumsum(nfreq).*tmstep; | |
| 241 | + if verbose >= 1, fprintf(1,'allan_modified: phase data generated from fractional frequency data (N=%g).\n',length(dphase)); end | |
| 242 | + else | |
| 243 | + dphase=data.phase; | |
| 244 | + end | |
| 245 | 245 | |
| 246 | - | |
| 247 | - % check the range of tau values and truncate if necessary | |
| 248 | - % find halfway point of time record | |
| 249 | - halftime = round(tmstep*length(data.freq)/2); | |
| 250 | - % truncate tau to appropriate values | |
| 251 | - tau = tau(tau >= tmstep & tau <= halftime); | |
| 252 | - if verbose >= 2, fprintf(1, 'allan_modified: allowable tau range: %g to %g sec. (1/rate to total_time/2)\n',tmstep,halftime); end | |
| 246 | + | |
| 247 | + % check the range of tau values and truncate if necessary | |
| 248 | + % find halfway point of time record | |
| 249 | + halftime = round(tmstep*length(data.freq)/2); | |
| 250 | + % truncate tau to appropriate values | |
| 251 | + tau = tau(tau >= tmstep & tau <= halftime); | |
| 252 | + if verbose >= 2, fprintf(1, 'allan_modified: allowable tau range: %g to %g sec. (1/rate to total_time/2)\n',tmstep,halftime); end | |
| 253 | 253 | |
| 254 | - % find the number of data points in each tau group | |
| 255 | - % number of samples | |
| 256 | - N=length(dphase); | |
| 257 | - m = data.rate.*tau; | |
| 258 | - % only integer values allowed (no fractional groups of points) | |
| 259 | - %tau = tau(m-round(m)<1e-8); % numerical precision issues (v1.1) | |
| 260 | - tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
| 261 | - %m = m(m-round(m)<1e-8); % change to round(m) for integer test v1.22 | |
| 262 | - m = m(m==round(m)); | |
| 263 | - %m=round(m); | |
| 264 | - | |
| 265 | - if verbose >= 1, fprintf(1,'allan_modified: calculating modified Allan deviation...\n '); end | |
| 266 | - | |
| 267 | - | |
| 268 | - % calculate the modified Allan deviation for each value of tau | |
| 269 | - k=0; tic; | |
| 270 | - for i = tau | |
| 271 | - k=k+1; | |
| 272 | - pa=[]; | |
| 273 | - if verbose >= 2, fprintf(1,'%d ',i); end | |
| 274 | - | |
| 275 | - mphase = dphase; | |
| 276 | - | |
| 277 | - % calculate overlapping "phase averages" (x_k) | |
| 278 | - for p=1:m(k) | |
| 279 | - | |
| 280 | - % truncate frequency set length to an even multiple of this tau value | |
| 281 | - mphase=mphase(1:end-rem(length(mphase),m(k))); | |
| 282 | - % group phase values | |
| 283 | - mp=reshape(mphase,m(k),[]); | |
| 284 | - % find average in each "tau group" (each column of mp) | |
| 285 | - pa(p,:)=mean(mp,1); | |
| 286 | - % shift data vector by -1 and repeat | |
| 287 | - mphase=circshift(dphase,(size(dphase)>1)*-p); | |
| 288 | - | |
| 289 | - end | |
| 290 | - | |
| 291 | - % create "modified" y_k freq values | |
| 292 | - mfreq=diff(pa,1,2)./i; | |
| 293 | - mfreq=reshape(mfreq,1,[]); | |
| 294 | - | |
| 295 | - % calculate modified frequency differences | |
| 296 | - mfreqd=reshape(mfreq,m(k),[]); % Vectorize! | |
| 297 | - mfreqd=diff(mfreqd,1,2); | |
| 298 | - mfreqd=reshape(mfreqd,1,[]); | |
| 299 | - | |
| 300 | - | |
| 301 | - % calculate two-sample variance for this tau | |
| 302 | - sm(k)=sqrt((1/(2*(N-3*m(k)+1)))*(sum(mfreqd(1:N-3*m(k)+1).^2))); | |
| 254 | + % find the number of data points in each tau group | |
| 255 | + % number of samples | |
| 256 | + N=length(dphase); | |
| 257 | + m = data.rate.*tau; | |
| 258 | + % only integer values allowed (no fractional groups of points) | |
| 259 | + %tau = tau(m-round(m)<1e-8); % numerical precision issues (v1.1) | |
| 260 | + tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
| 261 | + %m = m(m-round(m)<1e-8); % change to round(m) for integer test v1.22 | |
| 262 | + m = m(m==round(m)); | |
| 263 | + %m=round(m); | |
| 264 | + | |
| 265 | + if verbose >= 1, fprintf(1,'allan_modified: calculating modified Allan deviation...\n '); end | |
| 266 | + | |
| 267 | + | |
| 268 | + % calculate the modified Allan deviation for each value of tau | |
| 269 | + k=0; tic; | |
| 270 | + for i = tau | |
| 271 | + k=k+1; | |
| 272 | + pa=[]; | |
| 273 | + if verbose >= 2, fprintf(1,'%d ',i); end | |
| 274 | + | |
| 275 | + mphase = dphase; | |
| 276 | + | |
| 277 | + % calculate overlapping "phase averages" (x_k) | |
| 278 | + for p=1:m(k) | |
| 279 | + | |
| 280 | + % truncate frequency set length to an even multiple of this tau value | |
| 281 | + mphase=mphase(1:end-rem(length(mphase),m(k))); | |
| 282 | + % group phase values | |
| 283 | + mp=reshape(mphase,m(k),[]); | |
| 284 | + % find average in each "tau group" (each column of mp) | |
| 285 | + pa(p,:)=mean(mp,1); | |
| 286 | + % shift data vector by -1 and repeat | |
| 287 | + mphase=circshift(dphase,(size(dphase)>1)*-p); | |
| 288 | + | |
| 289 | + end | |
| 290 | + | |
| 291 | + % create "modified" y_k freq values | |
| 292 | + mfreq=diff(pa,1,2)./i; | |
| 293 | + mfreq=reshape(mfreq,1,[]); | |
| 294 | + | |
| 295 | + % calculate modified frequency differences | |
| 296 | + mfreqd=reshape(mfreq,m(k),[]); % Vectorize! | |
| 297 | + mfreqd=diff(mfreqd,1,2); | |
| 298 | + mfreqd=reshape(mfreqd,1,[]); | |
| 299 | + | |
| 300 | + | |
| 301 | + % calculate two-sample variance for this tau | |
| 302 | + sm(k)=sqrt((1/(2*(N-3*m(k)+1)))*(sum(mfreqd(1:N-3*m(k)+1).^2))); | |
| 303 | 303 | |
| 304 | - % estimate error bars | |
| 305 | - sme(k)=sm(k)/sqrt(N-3*m(k)+1); | |
| 304 | + % estimate error bars | |
| 305 | + sme(k)=sm(k)/sqrt(N-3*m(k)+1); | |
| 306 | 306 | |
| 307 | - | |
| 308 | - end % repeat for each value of tau | |
| 309 | - | |
| 310 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 311 | - calctime=toc; if verbose >= 2, fprintf(1,'allan_modified: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 307 | + | |
| 308 | + end % repeat for each value of tau | |
| 309 | + | |
| 310 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 311 | + calctime=toc; if verbose >= 2, fprintf(1,'allan_modified: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 312 | 312 | |
| 313 | - | |
| 314 | - | |
| 313 | + | |
| 314 | + | |
| 315 | 315 | %% Irregular data (timestamp) |
| 316 | 316 | elseif isfield(data,'time') |
| 317 | - % the interval between measurements is irregular | |
| 318 | - % so we must group the data by time | |
| 319 | - if verbose >= 1, fprintf(1, 'allan_modified: irregular rate data (no fixed sample rate)\n'); end | |
| 320 | - | |
| 321 | - % string for plot title | |
| 322 | - name=[name ' (timestamp)']; | |
| 323 | - | |
| 324 | - % adjust time to remove any initial offset | |
| 325 | - dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 326 | - %dtime=data.time-data.time(1); | |
| 327 | - % where is the maximum gap in time record? | |
| 328 | - gap_pos=find(diff(dtime)==max(diff(dtime))); | |
| 329 | - % what is average data spacing? | |
| 330 | - avg_gap = mean(diff(dtime)); | |
| 331 | - | |
| 332 | - if verbose >= 2 | |
| 333 | - fprintf(1, 'allan_modified: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
| 334 | - fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
| 335 | - fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
| 336 | - fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
| 337 | - pause; | |
| 338 | - end | |
| 339 | - | |
| 340 | - if verbose >= 1 | |
| 341 | - fprintf(1, 'allan_modified: End of timestamp data: %g sec\n',dtime(end)); | |
| 342 | - fprintf(1, ' Average sample rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
| 343 | - if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
| 344 | - fprintf(1, ' Max. gap in time record: %g sec at position %d\n',max(diff(dtime)),gap_pos(1)); | |
| 345 | - end | |
| 346 | - if max(diff(dtime)) > 5*avg_gap | |
| 347 | - fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
| 348 | - end | |
| 349 | - end | |
| 317 | + % the interval between measurements is irregular | |
| 318 | + % so we must group the data by time | |
| 319 | + if verbose >= 1, fprintf(1, 'allan_modified: irregular rate data (no fixed sample rate)\n'); end | |
| 320 | + | |
| 321 | + % string for plot title | |
| 322 | + name=[name ' (timestamp)']; | |
| 323 | + | |
| 324 | + % adjust time to remove any initial offset | |
| 325 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 326 | + %dtime=data.time-data.time(1); | |
| 327 | + % where is the maximum gap in time record? | |
| 328 | + gap_pos=find(diff(dtime)==max(diff(dtime))); | |
| 329 | + % what is average data spacing? | |
| 330 | + avg_gap = mean(diff(dtime)); | |
| 331 | + | |
| 332 | + if verbose >= 2 | |
| 333 | + fprintf(1, 'allan_modified: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
| 334 | + fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
| 335 | + fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
| 336 | + fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
| 337 | + pause; | |
| 338 | + end | |
| 339 | + | |
| 340 | + if verbose >= 1 | |
| 341 | + fprintf(1, 'allan_modified: End of timestamp data: %g sec\n',dtime(end)); | |
| 342 | + fprintf(1, ' Average sample rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
| 343 | + if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
| 344 | + fprintf(1, ' Max. gap in time record: %g sec at position %d\n',max(diff(dtime)),gap_pos(1)); | |
| 345 | + end | |
| 346 | + if max(diff(dtime)) > 5*avg_gap | |
| 347 | + fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
| 348 | + end | |
| 349 | + end | |
| 350 | 350 | |
| 351 | - % is phase data present? If not, generate it | |
| 352 | - if ~isfield(data,'phase') | |
| 353 | - nfreq=data.freq-s.mean; | |
| 354 | - % NOTE: uncommenting the following two lines will artificially | |
| 355 | - % allow the code to give equivalent results for validation data | |
| 356 | - % sets using fixed rate data and timestamped data by adding a | |
| 357 | - % "phantom" data point for frequency integration. Use of this | |
| 358 | - % phantom point can skew the results of calculations on real data. | |
| 359 | - %nfreq(end+1)=0; % phantom freq point, with average value | |
| 360 | - %dtime(end+1)=dtime(end)+avg_gap; % phantom average time step | |
| 361 | - dphase=zeros(1,length(nfreq)); | |
| 362 | - dphase(2:end) = cumsum(nfreq(1:end-1)).*diff(dtime); | |
| 363 | - if verbose >= 1, fprintf(1,'allan_modified: Phase data generated from fractional frequency data (N=%g).\n',length(dphase)); end | |
| 364 | - else | |
| 365 | - dphase=data.phase; | |
| 366 | - end | |
| 351 | + % is phase data present? If not, generate it | |
| 352 | + if ~isfield(data,'phase') | |
| 353 | + nfreq=data.freq-s.mean; | |
| 354 | + % NOTE: uncommenting the following two lines will artificially | |
| 355 | + % allow the code to give equivalent results for validation data | |
| 356 | + % sets using fixed rate data and timestamped data by adding a | |
| 357 | + % "phantom" data point for frequency integration. Use of this | |
| 358 | + % phantom point can skew the results of calculations on real data. | |
| 359 | + %nfreq(end+1)=0; % phantom freq point, with average value | |
| 360 | + %dtime(end+1)=dtime(end)+avg_gap; % phantom average time step | |
| 361 | + dphase=zeros(1,length(nfreq)); | |
| 362 | + dphase(2:end) = cumsum(nfreq(1:end-1)).*diff(dtime); | |
| 363 | + if verbose >= 1, fprintf(1,'allan_modified: Phase data generated from fractional frequency data (N=%g).\n',length(dphase)); end | |
| 364 | + else | |
| 365 | + dphase=data.phase; | |
| 366 | + end | |
| 367 | 367 | |
| 368 | - % find halfway point | |
| 369 | - halftime = fix(dtime(end)/2); | |
| 370 | - % truncate tau to appropriate values | |
| 371 | - tau = tau(tau >= max(diff(dtime)) & tau <= halftime); | |
| 372 | - if isempty(tau) | |
| 373 | - error('allan_modified: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(diff(dtime)),halftime); | |
| 374 | - end | |
| 368 | + % find halfway point | |
| 369 | + halftime = fix(dtime(end)/2); | |
| 370 | + % truncate tau to appropriate values | |
| 371 | + tau = tau(tau >= max(diff(dtime)) & tau <= halftime); | |
| 372 | + if isempty(tau) | |
| 373 | + error('allan_modified: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(diff(dtime)),halftime); | |
| 374 | + end | |
| 375 | 375 | |
| 376 | -% % save the freq data for the loop | |
| 377 | -% dfreq=data.freq; | |
| 378 | - | |
| 379 | - % number of samples | |
| 380 | - N=length(dphase); | |
| 381 | - m=round(tau./mean(diff(dtime))); | |
| 382 | - | |
| 383 | - if verbose >= 1, fprintf(1,'allan_modified: calculating modified Allan deviation...\n'); end | |
| 376 | +% % save the freq data for the loop | |
| 377 | +% dfreq=data.freq; | |
| 378 | + | |
| 379 | + % number of samples | |
| 380 | + N=length(dphase); | |
| 381 | + m=round(tau./mean(diff(dtime))); | |
| 382 | + | |
| 383 | + if verbose >= 1, fprintf(1,'allan_modified: calculating modified Allan deviation...\n'); end | |
| 384 | 384 | |
| 385 | - k=0; tic; | |
| 386 | - for i = tau | |
| 387 | - | |
| 388 | - k=k+1; pa=[]; | |
| 389 | - | |
| 390 | - mphase = dphase; time = dtime; | |
| 385 | + k=0; tic; | |
| 386 | + for i = tau | |
| 387 | + | |
| 388 | + k=k+1; pa=[]; | |
| 389 | + | |
| 390 | + mphase = dphase; time = dtime; | |
| 391 | 391 | |
| 392 | - if verbose >= 2, fprintf(1,'%d ',i); end | |
| 393 | - | |
| 394 | - % calculate overlapping "phase averages" (x_k) | |
| 395 | - %for j = 1:i | |
| 396 | - for j = 1:m(k) % (v1.1) | |
| 397 | - km=0; | |
| 398 | - %fprintf(1,'j: %d ',j); | |
| 399 | - | |
| 400 | - % (v1.1) truncating not correct for overlapping samples | |
| 401 | - % truncate data set to an even multiple of this tau value | |
| 402 | - %mphase = mphase(time <= time(end)-rem(time(end),i)); | |
| 403 | - %time = time(time <= time(end)-rem(time(end),i)); | |
| 404 | - | |
| 405 | - | |
| 406 | - % break up the data into overlapping groups of tau length | |
| 407 | - while i*km < time(end) | |
| 408 | - km=km+1; | |
| 392 | + if verbose >= 2, fprintf(1,'%d ',i); end | |
| 393 | + | |
| 394 | + % calculate overlapping "phase averages" (x_k) | |
| 395 | + %for j = 1:i | |
| 396 | + for j = 1:m(k) % (v1.1) | |
| 397 | + km=0; | |
| 398 | + %fprintf(1,'j: %d ',j); | |
| 399 | + | |
| 400 | + % (v1.1) truncating not correct for overlapping samples | |
| 401 | + % truncate data set to an even multiple of this tau value | |
| 402 | + %mphase = mphase(time <= time(end)-rem(time(end),i)); | |
| 403 | + %time = time(time <= time(end)-rem(time(end),i)); | |
| 404 | + | |
| 405 | + | |
| 406 | + % break up the data into overlapping groups of tau length | |
| 407 | + while i*km < time(end) | |
| 408 | + km=km+1; | |
| 409 | 409 | |
| 410 | - % progress bar | |
| 411 | - if verbose >= 2 | |
| 412 | - if rem(km,100)==0, fprintf(1,'.'); end | |
| 413 | - if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
| 414 | - end | |
| 410 | + % progress bar | |
| 411 | + if verbose >= 2 | |
| 412 | + if rem(km,100)==0, fprintf(1,'.'); end | |
| 413 | + if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
| 414 | + end | |
| 415 | 415 | |
| 416 | - mp = mphase(i*(km-1) < (time) & (time) <= i*km); | |
| 416 | + mp = mphase(i*(km-1) < (time) & (time) <= i*km); | |
| 417 | 417 | |
| 418 | - if ~isempty(mp) | |
| 419 | - pa(j,km)=mean(mp); | |
| 420 | - else | |
| 421 | - pa(j,km)=0; | |
| 422 | - end | |
| 418 | + if ~isempty(mp) | |
| 419 | + pa(j,km)=mean(mp); | |
| 420 | + else | |
| 421 | + pa(j,km)=0; | |
| 422 | + end | |
| 423 | 423 | |
| 424 | - end | |
| 425 | - | |
| 426 | - % shift data vector by -1 and repeat | |
| 427 | - mphase=circshift(dphase,(size(mphase)>1)*-j); | |
| 428 | - mphase(end-j+1:end)=[]; | |
| 429 | - time=circshift(dtime,(size(time)>1)*-j); | |
| 430 | - time(end-j+1:end)=[]; | |
| 431 | - time=time-time(1)+avg_gap; % remove time offset | |
| 432 | - | |
| 433 | - end | |
| 424 | + end | |
| 425 | + | |
| 426 | + % shift data vector by -1 and repeat | |
| 427 | + mphase=circshift(dphase,(size(mphase)>1)*-j); | |
| 428 | + mphase(end-j+1:end)=[]; | |
| 429 | + time=circshift(dtime,(size(time)>1)*-j); | |
| 430 | + time(end-j+1:end)=[]; | |
| 431 | + time=time-time(1)+avg_gap; % remove time offset | |
| 432 | + | |
| 433 | + end | |
| 434 | 434 | |
| 435 | - % create "modified" y_k freq values | |
| 436 | - mfreq=diff(pa,1,2)./i; | |
| 437 | - mfreq=reshape(mfreq,1,[]); | |
| 438 | - | |
| 439 | - % calculate modified frequency differences | |
| 440 | - mfreqd=reshape(mfreq,m(k),[]); % Vectorize! | |
| 441 | - mfreqd=diff(mfreqd,1,2); | |
| 442 | - mfreqd=reshape(mfreqd,1,[]); | |
| 435 | + % create "modified" y_k freq values | |
| 436 | + mfreq=diff(pa,1,2)./i; | |
| 437 | + mfreq=reshape(mfreq,1,[]); | |
| 438 | + | |
| 439 | + % calculate modified frequency differences | |
| 440 | + mfreqd=reshape(mfreq,m(k),[]); % Vectorize! | |
| 441 | + mfreqd=diff(mfreqd,1,2); | |
| 442 | + mfreqd=reshape(mfreqd,1,[]); | |
| 443 | 443 | |
| 444 | - % calculate two-sample variance for this tau | |
| 445 | - % only the first N-3*m(k)+1 samples are valid | |
| 446 | - if length(mfreqd) >= N-3*m(k)+1 | |
| 447 | - sm(k)=sqrt((1/(2*(N-3*m(k)+1)))*(sum(mfreqd(1:N-3*m(k)+1).^2))); | |
| 444 | + % calculate two-sample variance for this tau | |
| 445 | + % only the first N-3*m(k)+1 samples are valid | |
| 446 | + if length(mfreqd) >= N-3*m(k)+1 | |
| 447 | + sm(k)=sqrt((1/(2*(N-3*m(k)+1)))*(sum(mfreqd(1:N-3*m(k)+1).^2))); | |
| 448 | 448 | |
| 449 | - % estimate error bars | |
| 450 | - sme(k)=sm(k)/sqrt(N); | |
| 451 | - | |
| 452 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 453 | - else | |
| 454 | - if verbose >=2, fprintf(1,' tau=%g dropped due to timestamp irregularities\n',tau(k)); end | |
| 455 | - sm(k)=0; sme(k)=0; | |
| 456 | - end | |
| 457 | - | |
| 449 | + % estimate error bars | |
| 450 | + sme(k)=sm(k)/sqrt(N); | |
| 451 | + | |
| 452 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 453 | + else | |
| 454 | + if verbose >=2, fprintf(1,' tau=%g dropped due to timestamp irregularities\n',tau(k)); end | |
| 455 | + sm(k)=0; sme(k)=0; | |
| 456 | + end | |
| 457 | + | |
| 458 | 458 | |
| 459 | - end | |
| 459 | + end | |
| 460 | 460 | |
| 461 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 462 | - calctime=toc; if verbose >= 2, fprintf(1,'allan_modified: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 463 | - | |
| 464 | - % remove any points that were dropped | |
| 465 | - tau(sm==0)=[]; | |
| 466 | - sm(sm==0)=[]; | |
| 467 | - sme(sme==0)=[]; | |
| 468 | - | |
| 469 | - % modify time vector for plotting | |
| 470 | - dtime=dtime(1:length(medianfreq)); | |
| 461 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 462 | + calctime=toc; if verbose >= 2, fprintf(1,'allan_modified: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 463 | + | |
| 464 | + % remove any points that were dropped | |
| 465 | + tau(sm==0)=[]; | |
| 466 | + sm(sm==0)=[]; | |
| 467 | + sme(sme==0)=[]; | |
| 468 | + | |
| 469 | + % modify time vector for plotting | |
| 470 | + dtime=dtime(1:length(medianfreq)); | |
| 471 | 471 | |
| 472 | 472 | else |
| 473 | - error('allan_modified: WARNING: no DATA.rate or DATA.time! Type "help allan_modified" for more information. [err2]'); | |
| 473 | + error('allan_modified: WARNING: no DATA.rate or DATA.time! Type "help allan_modified" for more information. [err2]'); | |
| 474 | 474 | end |
| 475 | 475 | |
| 476 | 476 | |
| 477 | 477 | |
| ... | ... | @@ -478,41 +478,41 @@ |
| 478 | 478 | %% Plotting |
| 479 | 479 | |
| 480 | 480 | if verbose >= 2 % show all data |
| 481 | - | |
| 482 | - % plot the frequency data, centered on median | |
| 483 | - if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
| 484 | - try | |
| 485 | - % dsplot makes a new figure | |
| 486 | - hd=dsplot(dtime,medianfreq); | |
| 487 | - catch ME | |
| 488 | - figure; | |
| 489 | - hd=plot(dtime,medianfreq); | |
| 490 | - if verbose >= 1, fprintf(1,'allan_modified: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
| 491 | - if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
| 492 | - end | |
| 493 | - set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
| 494 | - hold on; | |
| 481 | + | |
| 482 | + % plot the frequency data, centered on median | |
| 483 | + if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
| 484 | + try | |
| 485 | + % dsplot makes a new figure | |
| 486 | + hd=dsplot(dtime,medianfreq); | |
| 487 | + catch ME | |
| 488 | + figure; | |
| 489 | + hd=plot(dtime,medianfreq); | |
| 490 | + if verbose >= 1, fprintf(1,'allan_modified: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
| 491 | + if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
| 492 | + end | |
| 493 | + set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
| 494 | + hold on; | |
| 495 | 495 | |
| 496 | - fx = xlim; | |
| 497 | - % plot([fx(1) fx(2)],[s.median s.median],'-k'); | |
| 498 | - plot([fx(1) fx(2)],[0 0],':k'); | |
| 499 | - % show 5x Median Absolute deviation (MAD) values | |
| 500 | - hm=plot([fx(1) fx(2)],[5*MAD 5*MAD],'-r'); | |
| 501 | - plot([fx(1) fx(2)],[-5*MAD -5*MAD],'-r'); | |
| 502 | - % show linear fit line | |
| 503 | - hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
| 504 | - title(['Data: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 505 | - %set(get(gca,'Title'),'Interpreter','none'); | |
| 506 | - xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
| 507 | - if isfield(data,'units') | |
| 508 | - ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 509 | - else | |
| 510 | - ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
| 511 | - end | |
| 512 | - set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 513 | - legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
| 514 | - % tighten up | |
| 515 | - xlim([dtime(1) dtime(end)]); | |
| 496 | + fx = xlim; | |
| 497 | + % plot([fx(1) fx(2)],[s.median s.median],'-k'); | |
| 498 | + plot([fx(1) fx(2)],[0 0],':k'); | |
| 499 | + % show 5x Median Absolute deviation (MAD) values | |
| 500 | + hm=plot([fx(1) fx(2)],[5*MAD 5*MAD],'-r'); | |
| 501 | + plot([fx(1) fx(2)],[-5*MAD -5*MAD],'-r'); | |
| 502 | + % show linear fit line | |
| 503 | + hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
| 504 | + title(['Data: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 505 | + %set(get(gca,'Title'),'Interpreter','none'); | |
| 506 | + xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
| 507 | + if isfield(data,'units') | |
| 508 | + ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 509 | + else | |
| 510 | + ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
| 511 | + end | |
| 512 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 513 | + legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
| 514 | + % tighten up | |
| 515 | + xlim([dtime(1) dtime(end)]); | |
| 516 | 516 | |
| 517 | 517 | |
| 518 | 518 | end % end plot raw data |
| 519 | 519 | |
| 520 | 520 | |
| 521 | 521 | |
| 522 | 522 | |
| ... | ... | @@ -520,41 +520,41 @@ |
| 520 | 520 | |
| 521 | 521 | if verbose >= 1 % show analysis results |
| 522 | 522 | |
| 523 | - % plot Allan deviation results | |
| 524 | - if ~isempty(sm) | |
| 525 | - figure | |
| 523 | + % plot Allan deviation results | |
| 524 | + if ~isempty(sm) | |
| 525 | + figure | |
| 526 | 526 | |
| 527 | - % Choose loglog or semilogx plot here #PLOTLOG | |
| 528 | - %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 529 | - loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 527 | + % Choose loglog or semilogx plot here #PLOTLOG | |
| 528 | + %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 529 | + loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 530 | 530 | |
| 531 | - % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
| 532 | - % When this is fixed, uncomment below to use normal errorbars | |
| 533 | - %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
| 534 | - % this is a hack to approximate the error bars | |
| 535 | - hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
| 531 | + % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
| 532 | + % When this is fixed, uncomment below to use normal errorbars | |
| 533 | + %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
| 534 | + % this is a hack to approximate the error bars | |
| 535 | + hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
| 536 | 536 | |
| 537 | - grid on; | |
| 538 | - title(['Modified Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 539 | - %set(get(gca,'Title'),'Interpreter','none'); | |
| 540 | - xlabel('\tau [sec]','FontSize',FontSize,'FontName',FontName); | |
| 541 | - ylabel('Modified \sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
| 542 | - set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 543 | - % expand the x axis a little bit so that the errors bars look nice | |
| 544 | - adax = axis; | |
| 545 | - axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
| 546 | - | |
| 547 | - % display the minimum value | |
| 548 | - fprintf(1,'allan: Minimum modified ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
| 549 | - | |
| 550 | - elseif verbose >= 1 | |
| 551 | - fprintf(1,'allan_modified: WARNING: no values calculated.\n'); | |
| 552 | - fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
| 553 | - fprintf(1,'Type "help allan_modified" for more information.\n\n'); | |
| 554 | - end | |
| 537 | + grid on; | |
| 538 | + title(['Modified Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 539 | + %set(get(gca,'Title'),'Interpreter','none'); | |
| 540 | + xlabel('\tau [sec]','FontSize',FontSize,'FontName',FontName); | |
| 541 | + ylabel('Modified \sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
| 542 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 543 | + % expand the x axis a little bit so that the errors bars look nice | |
| 544 | + adax = axis; | |
| 545 | + axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
| 546 | + | |
| 547 | + % display the minimum value | |
| 548 | + fprintf(1,'allan: Minimum modified ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
| 549 | + | |
| 550 | + elseif verbose >= 1 | |
| 551 | + fprintf(1,'allan_modified: WARNING: no values calculated.\n'); | |
| 552 | + fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
| 553 | + fprintf(1,'Type "help allan_modified" for more information.\n\n'); | |
| 554 | + end | |
| 555 | 555 | |
| 556 | 556 | end % end plot analysis |
| 557 | - | |
| 557 | + | |
| 558 | 558 | retval = sm; |
| 559 | 559 | errorb = sme; |
| 560 | 560 |
allan_overlap.m
| ... | ... | @@ -6,24 +6,24 @@ |
| 6 | 6 | % Inputs: |
| 7 | 7 | % DATA should be a struct and have the following fields: |
| 8 | 8 | % DATA.freq or DATA.phase |
| 9 | -% A vector of fractional frequency measurements (df/f) in | |
| 10 | -% DATA.freq *or* phase offset data (seconds) in DATA.phase | |
| 11 | -% If phase data is not present, it will be generated by | |
| 12 | -% integrating the fractional frequency data. | |
| 13 | -% If both fields are present, then DATA.phase will be used. | |
| 9 | +% A vector of fractional frequency measurements (df/f) in | |
| 10 | +% DATA.freq *or* phase offset data (seconds) in DATA.phase | |
| 11 | +% If phase data is not present, it will be generated by | |
| 12 | +% integrating the fractional frequency data. | |
| 13 | +% If both fields are present, then DATA.phase will be used. | |
| 14 | 14 | % |
| 15 | 15 | % DATA.rate or DATA.time |
| 16 | -% The sampling rate in Hertz (DATA.rate) or a vector of | |
| 17 | -% timestamps for each measurement in seconds (DATA.time). | |
| 18 | -% DATA.rate is used if both fields are present. | |
| 19 | -% If DATA.rate == 0, then the timestamps are used. | |
| 16 | +% The sampling rate in Hertz (DATA.rate) or a vector of | |
| 17 | +% timestamps for each measurement in seconds (DATA.time). | |
| 18 | +% DATA.rate is used if both fields are present. | |
| 19 | +% If DATA.rate == 0, then the timestamps are used. | |
| 20 | 20 | % |
| 21 | 21 | % TAU is an array of tau values for computing Allan deviation. |
| 22 | -% TAU values must be divisible by 1/DATA.rate (data points cannot be | |
| 23 | -% grouped in fractional quantities!). Invalid values are ignored. | |
| 22 | +% TAU values must be divisible by 1/DATA.rate (data points cannot be | |
| 23 | +% grouped in fractional quantities!). Invalid values are ignored. | |
| 24 | 24 | % NAME is an optional label that is added to the plot titles. |
| 25 | 25 | % VERBOSE sets the level of status messages: |
| 26 | -% 0 = silent & no data plots; 1 = status messages; 2 = all messages | |
| 26 | +% 0 = silent & no data plots; 1 = status messages; 2 = all messages | |
| 27 | 27 | % |
| 28 | 28 | % Outputs: |
| 29 | 29 | % RETVAL is the array of overlapping Allan deviation values at each TAU. |
| ... | ... | @@ -38,8 +38,8 @@ |
| 38 | 38 | % To compute the overlapping Allan deviation for the data in the variable "lt": |
| 39 | 39 | % >> lt |
| 40 | 40 | % lt = |
| 41 | -% freq: [1x86400 double] | |
| 42 | -% rate: 0.5 | |
| 41 | +% freq: [1x86400 double] | |
| 42 | +% rate: 0.5 | |
| 43 | 43 | % |
| 44 | 44 | % Use: |
| 45 | 45 | % |
| 46 | 46 | |
| 47 | 47 | |
| 48 | 48 | |
| 49 | 49 | |
| 50 | 50 | |
| ... | ... | @@ -89,34 +89,34 @@ |
| 89 | 89 | % |
| 90 | 90 | % |
| 91 | 91 | % M.A. Hopcroft |
| 92 | -% mhopeng at gmail dot com | |
| 92 | +% mhopeng at gmail dot com | |
| 93 | 93 | % |
| 94 | 94 | % I welcome your comments and feedback! |
| 95 | 95 | % |
| 96 | 96 | % MH Mar2014 |
| 97 | 97 | % v2.24 fix bug related to generating freq data from phase with timestamps |
| 98 | -% (thanks to S. David-Grignot for finding the bug) | |
| 98 | +% (thanks to S. David-Grignot for finding the bug) | |
| 99 | 99 | % MH Oct2010 |
| 100 | 100 | % v2.22 tau truncation to integer groups; tau sort |
| 101 | -% plotting bugfix | |
| 101 | +% plotting bugfix | |
| 102 | 102 | % v2.20 update to match allan.m (dsplot.m, columns) |
| 103 | -% discard tau values with timestamp irregularities | |
| 103 | +% discard tau values with timestamp irregularities | |
| 104 | 104 | |
| 105 | 105 | versionstr = 'allan_overlap v2.24'; |
| 106 | 106 | |
| 107 | 107 | % |
| 108 | 108 | % MH MAR2010 |
| 109 | 109 | % v2.1 bugfixes for irregular sample rates |
| 110 | -% (thanks to Ryad Ben-El-Kezadri for feedback and testing) | |
| 111 | -% handle empty rate field | |
| 112 | -% fix integer comparisons for fractional sample rates | |
| 113 | -% update consistency check | |
| 110 | +% (thanks to Ryad Ben-El-Kezadri for feedback and testing) | |
| 111 | +% handle empty rate field | |
| 112 | +% fix integer comparisons for fractional sample rates | |
| 113 | +% update consistency check | |
| 114 | 114 | % |
| 115 | 115 | % MH FEB2010 |
| 116 | 116 | % v2.0 use phase data for calculation- much faster |
| 117 | -% Consistent code behaviour for all "allan_x.m" functions: | |
| 118 | -% accept phase data | |
| 119 | -% verbose levels | |
| 117 | +% Consistent code behaviour for all "allan_x.m" functions: | |
| 118 | +% accept phase data | |
| 119 | +% verbose levels | |
| 120 | 120 | % |
| 121 | 121 | % MH JAN2010 |
| 122 | 122 | % v1.0 based on allan v1.84 |
| 123 | 123 | |
| ... | ... | @@ -140,25 +140,25 @@ |
| 140 | 140 | |
| 141 | 141 | %% Data consistency checks v2.1 |
| 142 | 142 | if ~(isfield(data,'phase') || isfield(data,'freq')) |
| 143 | - error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
| 143 | + error('Either ''phase'' or ''freq'' must be present in DATA. See help file for details. [con0]'); | |
| 144 | 144 | end |
| 145 | 145 | if isfield(data,'time') |
| 146 | - if isfield(data,'phase') && (length(data.phase) ~= length(data.time)) | |
| 147 | - if isfield(data,'freq') && (length(data.freq) ~= length(data.time)) | |
| 148 | - error('The time and freq vectors are not the same length. See help for details. [con2]'); | |
| 149 | - else | |
| 150 | - error('The time and phase vectors are not the same length. See help for details. [con1]'); | |
| 151 | - end | |
| 152 | - end | |
| 153 | - if isfield(data,'phase') && (any(isnan(data.phase)) || any(isinf(data.phase))) | |
| 154 | - error('The phase vector contains invalid elements (NaN/Inf). [con3]'); | |
| 155 | - end | |
| 156 | - if isfield(data,'freq') && (any(isnan(data.freq)) || any(isinf(data.freq))) | |
| 157 | - error('The freq vector contains invalid elements (NaN/Inf). [con4]'); | |
| 158 | - end | |
| 159 | - if isfield(data,'time') && (any(isnan(data.time)) || any(isinf(data.time))) | |
| 160 | - error('The time vector contains invalid elements (NaN/Inf). [con5]'); | |
| 161 | - end | |
| 146 | + if isfield(data,'phase') && (length(data.phase) ~= length(data.time)) | |
| 147 | + if isfield(data,'freq') && (length(data.freq) ~= length(data.time)) | |
| 148 | + error('The time and freq vectors are not the same length. See help for details. [con2]'); | |
| 149 | + else | |
| 150 | + error('The time and phase vectors are not the same length. See help for details. [con1]'); | |
| 151 | + end | |
| 152 | + end | |
| 153 | + if isfield(data,'phase') && (any(isnan(data.phase)) || any(isinf(data.phase))) | |
| 154 | + error('The phase vector contains invalid elements (NaN/Inf). [con3]'); | |
| 155 | + end | |
| 156 | + if isfield(data,'freq') && (any(isnan(data.freq)) || any(isinf(data.freq))) | |
| 157 | + error('The freq vector contains invalid elements (NaN/Inf). [con4]'); | |
| 158 | + end | |
| 159 | + if isfield(data,'time') && (any(isnan(data.time)) || any(isinf(data.time))) | |
| 160 | + error('The time vector contains invalid elements (NaN/Inf). [con5]'); | |
| 161 | + end | |
| 162 | 162 | end |
| 163 | 163 | |
| 164 | 164 | % sort tau vector |
| 165 | 165 | |
| 166 | 166 | |
| 167 | 167 | |
| 168 | 168 | |
| 169 | 169 | |
| ... | ... | @@ -166,33 +166,33 @@ |
| 166 | 166 | |
| 167 | 167 | %% Basic statistical tests on the data set |
| 168 | 168 | if ~isfield(data,'freq') |
| 169 | - if isfield(data,'rate') && data.rate ~= 0 | |
| 170 | - data.freq=diff(data.phase).*data.rate; | |
| 171 | - elseif isfield(data,'time') | |
| 172 | - data.freq=diff(data.phase)./diff(data.time); | |
| 173 | - end | |
| 174 | - if verbose >= 1, fprintf(1,'allan_overlap: Fractional frequency data generated from phase data (M=%g).\n',length(data.freq)); end | |
| 169 | + if isfield(data,'rate') && data.rate ~= 0 | |
| 170 | + data.freq=diff(data.phase).*data.rate; | |
| 171 | + elseif isfield(data,'time') | |
| 172 | + data.freq=diff(data.phase)./diff(data.time); | |
| 173 | + end | |
| 174 | + if verbose >= 1, fprintf(1,'allan_overlap: Fractional frequency data generated from phase data (M=%g).\n',length(data.freq)); end | |
| 175 | 175 | end |
| 176 | 176 | if size(data.freq,2) > size(data.freq,1), data.freq=data.freq'; end % ensure columns |
| 177 | - | |
| 177 | + | |
| 178 | 178 | s.numpoints=length(data.freq); |
| 179 | 179 | s.max=max(data.freq); |
| 180 | 180 | s.min=min(data.freq); |
| 181 | 181 | s.mean=mean(data.freq); |
| 182 | 182 | s.median=median(data.freq); |
| 183 | 183 | if isfield(data,'time') |
| 184 | - if size(data.time,2) > size(data.time,1), data.time=data.time'; end % ensure columns | |
| 185 | - s.linear=polyfit(data.time(1:length(data.freq)),data.freq,1); | |
| 184 | + if size(data.time,2) > size(data.time,1), data.time=data.time'; end % ensure columns | |
| 185 | + s.linear=polyfit(data.time(1:length(data.freq)),data.freq,1); | |
| 186 | 186 | elseif isfield(data,'rate') && data.rate ~= 0; |
| 187 | - s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
| 187 | + s.linear=polyfit((1/data.rate:1/data.rate:length(data.freq)/data.rate)',data.freq,1); | |
| 188 | 188 | else |
| 189 | - error('Either "time" or "rate" must be present in DATA. Type "help allan_overlap" for details. [err1]'); | |
| 189 | + error('Either "time" or "rate" must be present in DATA. Type "help allan_overlap" for details. [err1]'); | |
| 190 | 190 | end |
| 191 | 191 | s.std=std(data.freq); |
| 192 | 192 | |
| 193 | 193 | if verbose >= 2 |
| 194 | - fprintf(1,'allan_overlap: fractional frequency data statistics:\n'); | |
| 195 | - disp(s); | |
| 194 | + fprintf(1,'allan_overlap: fractional frequency data statistics:\n'); | |
| 195 | + disp(s); | |
| 196 | 196 | end |
| 197 | 197 | |
| 198 | 198 | |
| ... | ... | @@ -203,7 +203,7 @@ |
| 203 | 203 | % Screen for outliers using 5x Median Absolute Deviation (MAD) criteria |
| 204 | 204 | MAD = median(abs(medianfreq)/0.6745); |
| 205 | 205 | if verbose >= 1 && any(abs(medianfreq) > 5*MAD) |
| 206 | - fprintf(1, 'allan_overlap: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
| 206 | + fprintf(1, 'allan_overlap: NOTE: There appear to be outliers in the frequency data. See plot.\n'); | |
| 207 | 207 | end |
| 208 | 208 | |
| 209 | 209 | %%%% |
| 210 | 210 | |
| 211 | 211 | |
| 212 | 212 | |
| 213 | 213 | |
| 214 | 214 | |
| 215 | 215 | |
| 216 | 216 | |
| 217 | 217 | |
| 218 | 218 | |
| 219 | 219 | |
| 220 | 220 | |
| 221 | 221 | |
| 222 | 222 | |
| 223 | 223 | |
| 224 | 224 | |
| 225 | 225 | |
| 226 | 226 | |
| 227 | 227 | |
| 228 | 228 | |
| 229 | 229 | |
| 230 | 230 | |
| 231 | 231 | |
| 232 | 232 | |
| 233 | 233 | |
| ... | ... | @@ -213,250 +213,250 @@ |
| 213 | 213 | % If there is a regular interval between measurements, calculation is much |
| 214 | 214 | % easier/faster |
| 215 | 215 | if isfield(data,'rate') && data.rate > 0 % if data rate was given |
| 216 | - if verbose >= 1 | |
| 217 | - fprintf(1, 'allan_overlap: regular data '); | |
| 218 | - if isfield(data,'freq') | |
| 219 | - fprintf(1, '(%g freq data points @ %g Hz)\n',length(data.freq),data.rate); | |
| 220 | - elseif isfield(data,'phase') | |
| 221 | - fprintf(1, '(%g phase data points @ %g Hz)\n',length(data.phase),data.rate); | |
| 222 | - else | |
| 223 | - error('\n phase or freq data missing [err10]'); | |
| 224 | - end | |
| 225 | - end | |
| 216 | + if verbose >= 1 | |
| 217 | + fprintf(1, 'allan_overlap: regular data '); | |
| 218 | + if isfield(data,'freq') | |
| 219 | + fprintf(1, '(%g freq data points @ %g Hz)\n',length(data.freq),data.rate); | |
| 220 | + elseif isfield(data,'phase') | |
| 221 | + fprintf(1, '(%g phase data points @ %g Hz)\n',length(data.phase),data.rate); | |
| 222 | + else | |
| 223 | + error('\n phase or freq data missing [err10]'); | |
| 224 | + end | |
| 225 | + end | |
| 226 | 226 | |
| 227 | - % string for plot title | |
| 228 | - name=[name ' (' num2str(data.rate) ' Hz)']; | |
| 227 | + % string for plot title | |
| 228 | + name=[name ' (' num2str(data.rate) ' Hz)']; | |
| 229 | 229 | |
| 230 | - % what is the time interval between data points? | |
| 231 | - tmstep = 1/data.rate; | |
| 232 | - | |
| 233 | - % Is there time data? Just for curiosity/plotting, does not impact calculation | |
| 234 | - if isfield(data,'time') | |
| 235 | - % adjust time data to remove any starting gap; first time step | |
| 236 | - % should not be zero for comparison with freq data | |
| 237 | - dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 238 | - dtime=dtime(1:length(medianfreq)); % equalize the data vector lengths for plotting (v2.1) | |
| 239 | - if verbose >= 2 | |
| 240 | - fprintf(1,'allan_overlap: End of timestamp data: %g sec.\n',dtime(end)); | |
| 241 | - if (data.rate - 1/mean(diff(dtime))) > 1e-6 | |
| 242 | - fprintf(1,'allan_overlap: NOTE: data.rate (%f Hz) does not match average timestamped sample rate (%f Hz)\n',data.rate,1/mean(diff(dtime))); | |
| 243 | - end | |
| 244 | - end | |
| 245 | - else | |
| 246 | - % create time axis data using rate (for plotting only) | |
| 247 | - dtime=(tmstep:tmstep:length(data.freq)*tmstep); | |
| 248 | - end | |
| 230 | + % what is the time interval between data points? | |
| 231 | + tmstep = 1/data.rate; | |
| 232 | + | |
| 233 | + % Is there time data? Just for curiosity/plotting, does not impact calculation | |
| 234 | + if isfield(data,'time') | |
| 235 | + % adjust time data to remove any starting gap; first time step | |
| 236 | + % should not be zero for comparison with freq data | |
| 237 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 238 | + dtime=dtime(1:length(medianfreq)); % equalize the data vector lengths for plotting (v2.1) | |
| 239 | + if verbose >= 2 | |
| 240 | + fprintf(1,'allan_overlap: End of timestamp data: %g sec.\n',dtime(end)); | |
| 241 | + if (data.rate - 1/mean(diff(dtime))) > 1e-6 | |
| 242 | + fprintf(1,'allan_overlap: NOTE: data.rate (%f Hz) does not match average timestamped sample rate (%f Hz)\n',data.rate,1/mean(diff(dtime))); | |
| 243 | + end | |
| 244 | + end | |
| 245 | + else | |
| 246 | + % create time axis data using rate (for plotting only) | |
| 247 | + dtime=(tmstep:tmstep:length(data.freq)*tmstep); | |
| 248 | + end | |
| 249 | 249 | |
| 250 | 250 | |
| 251 | - % is phase data present? If not, generate it | |
| 252 | - if ~isfield(data,'phase') | |
| 253 | - nfreq=data.freq-s.mean; | |
| 254 | - dphase=zeros(1,length(nfreq)+1); | |
| 255 | - dphase(2:end) = cumsum(nfreq)./data.rate; | |
| 256 | - if verbose >= 1, fprintf(1,'allan_overlap: phase data generated from fractional frequency data (N=%g).\n',length(dphase)); end | |
| 257 | - else | |
| 258 | - dphase=data.phase; | |
| 259 | - end | |
| 260 | - | |
| 261 | - % check the range of tau values and truncate if necessary | |
| 262 | - % find halfway point of time record | |
| 263 | - halftime = round(tmstep*length(data.freq)/2); | |
| 264 | - % truncate tau to appropriate values | |
| 265 | - tau = tau(tau >= tmstep & tau <= halftime); | |
| 266 | - if verbose >= 2, fprintf(1, 'allan_overlap: allowable tau range: %g to %g sec. (1/rate to total_time/2)\n',tmstep,halftime); end | |
| 267 | - | |
| 268 | - % number of samples | |
| 269 | - N=length(dphase); | |
| 270 | - % number of samples per tau period | |
| 271 | - m = data.rate.*tau; | |
| 272 | - % only integer values allowed for m (no fractional groups of points) | |
| 273 | - %tau = tau(m-round(m)<1e-8); % numerical precision issues (v2.1) | |
| 274 | - tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
| 275 | - %m = m(m-round(m)<1e-8); % change to round(m) for integer test v2.22 | |
| 276 | - m = m(m==round(m)); | |
| 277 | - %m=round(m); | |
| 278 | - %fprintf(1,'m: %.50f\n',m) | |
| 279 | - | |
| 280 | - if verbose >= 1, fprintf(1,'allan_overlap: calculating overlapping Allan deviation...\n '); end | |
| 281 | - | |
| 282 | - % calculate the Allan deviation for each value of tau | |
| 283 | - k=0; tic; | |
| 284 | - for i = tau | |
| 285 | - k=k+1; | |
| 286 | - if verbose >= 2, fprintf(1,'%d ',i); end | |
| 251 | + % is phase data present? If not, generate it | |
| 252 | + if ~isfield(data,'phase') | |
| 253 | + nfreq=data.freq-s.mean; | |
| 254 | + dphase=zeros(1,length(nfreq)+1); | |
| 255 | + dphase(2:end) = cumsum(nfreq)./data.rate; | |
| 256 | + if verbose >= 1, fprintf(1,'allan_overlap: phase data generated from fractional frequency data (N=%g).\n',length(dphase)); end | |
| 257 | + else | |
| 258 | + dphase=data.phase; | |
| 259 | + end | |
| 260 | + | |
| 261 | + % check the range of tau values and truncate if necessary | |
| 262 | + % find halfway point of time record | |
| 263 | + halftime = round(tmstep*length(data.freq)/2); | |
| 264 | + % truncate tau to appropriate values | |
| 265 | + tau = tau(tau >= tmstep & tau <= halftime); | |
| 266 | + if verbose >= 2, fprintf(1, 'allan_overlap: allowable tau range: %g to %g sec. (1/rate to total_time/2)\n',tmstep,halftime); end | |
| 267 | + | |
| 268 | + % number of samples | |
| 269 | + N=length(dphase); | |
| 270 | + % number of samples per tau period | |
| 271 | + m = data.rate.*tau; | |
| 272 | + % only integer values allowed for m (no fractional groups of points) | |
| 273 | + %tau = tau(m-round(m)<1e-8); % numerical precision issues (v2.1) | |
| 274 | + tau = tau(m==round(m)); % The round() test is only correct for values < 2^53 | |
| 275 | + %m = m(m-round(m)<1e-8); % change to round(m) for integer test v2.22 | |
| 276 | + m = m(m==round(m)); | |
| 277 | + %m=round(m); | |
| 278 | + %fprintf(1,'m: %.50f\n',m) | |
| 279 | + | |
| 280 | + if verbose >= 1, fprintf(1,'allan_overlap: calculating overlapping Allan deviation...\n '); end | |
| 281 | + | |
| 282 | + % calculate the Allan deviation for each value of tau | |
| 283 | + k=0; tic; | |
| 284 | + for i = tau | |
| 285 | + k=k+1; | |
| 286 | + if verbose >= 2, fprintf(1,'%d ',i); end | |
| 287 | 287 | |
| 288 | 288 | |
| 289 | - % pad phase data set length to an even multiple of this tau value | |
| 290 | - mphase=zeros(ceil(length(dphase)./m(k))*m(k),1); | |
| 291 | - mphase(1:N)=dphase; | |
| 292 | - % group phase values | |
| 293 | - mp=reshape(mphase,m(k),[]); | |
| 294 | - % compute second differences of phase values (x_k+m - x_k) | |
| 295 | - md1=diff(mp,1,2); | |
| 296 | - md2=diff(md1,1,2); | |
| 297 | - md1=reshape(md2,1,[]); | |
| 298 | - | |
| 299 | - % compute overlapping ADEV from phase values | |
| 300 | - % only the first N-2*m(k) samples are valid | |
| 301 | - sm(k)=sqrt((1/(2*(N-2*m(k))*i^2))*sum(md1(1:N-2*m(k)).^2)); | |
| 302 | - | |
| 303 | - % estimate error bars | |
| 304 | - sme(k)=sm(k)/sqrt(N-2*m(k)); | |
| 305 | - | |
| 289 | + % pad phase data set length to an even multiple of this tau value | |
| 290 | + mphase=zeros(ceil(length(dphase)./m(k))*m(k),1); | |
| 291 | + mphase(1:N)=dphase; | |
| 292 | + % group phase values | |
| 293 | + mp=reshape(mphase,m(k),[]); | |
| 294 | + % compute second differences of phase values (x_k+m - x_k) | |
| 295 | + md1=diff(mp,1,2); | |
| 296 | + md2=diff(md1,1,2); | |
| 297 | + md1=reshape(md2,1,[]); | |
| 298 | + | |
| 299 | + % compute overlapping ADEV from phase values | |
| 300 | + % only the first N-2*m(k) samples are valid | |
| 301 | + sm(k)=sqrt((1/(2*(N-2*m(k))*i^2))*sum(md1(1:N-2*m(k)).^2)); | |
| 302 | + | |
| 303 | + % estimate error bars | |
| 304 | + sme(k)=sm(k)/sqrt(N-2*m(k)); | |
| 305 | + | |
| 306 | 306 | |
| 307 | - end % repeat for each value of tau | |
| 308 | - | |
| 309 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 310 | - calctime=toc; if verbose >= 2, fprintf(1,'allan_overlap: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 307 | + end % repeat for each value of tau | |
| 308 | + | |
| 309 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 310 | + calctime=toc; if verbose >= 2, fprintf(1,'allan_overlap: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 311 | 311 | |
| 312 | - | |
| 313 | - | |
| 314 | -%% Irregular data, no fixed interval | |
| 312 | + | |
| 313 | + | |
| 314 | +%% Irregular data, no fixed interval | |
| 315 | 315 | elseif isfield(data,'time') |
| 316 | - % the interval between measurements is irregular | |
| 317 | - % so we must group the data by time | |
| 318 | - if verbose >= 1, fprintf(1, 'allan_overlap: irregular rate data (no fixed sample rate)\n'); end | |
| 316 | + % the interval between measurements is irregular | |
| 317 | + % so we must group the data by time | |
| 318 | + if verbose >= 1, fprintf(1, 'allan_overlap: irregular rate data (no fixed sample rate)\n'); end | |
| 319 | 319 | |
| 320 | - | |
| 321 | - % string for plot title | |
| 322 | - name=[name ' (timestamp)']; | |
| 323 | - | |
| 320 | + | |
| 321 | + % string for plot title | |
| 322 | + name=[name ' (timestamp)']; | |
| 323 | + | |
| 324 | 324 | |
| 325 | - % adjust time to remove any starting offset | |
| 326 | - dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 327 | - | |
| 328 | - % save the freq data for the loop | |
| 329 | - dfreq=data.freq; | |
| 330 | - dtime=dtime(1:length(dfreq)); | |
| 331 | - | |
| 332 | - dfdtime=diff(dtime); % only need to do this once (v2.1) | |
| 333 | - % where is the maximum gap in time record? | |
| 334 | - gap_pos=find(dfdtime==max(dfdtime)); | |
| 335 | - % what is average data spacing? | |
| 336 | - avg_gap = mean(dfdtime); | |
| 337 | - s.avg_rate = 1/avg_gap; % save avg rate for user (v2.1) | |
| 338 | - | |
| 339 | - if verbose >= 2 | |
| 340 | - fprintf(1, 'allan_overlap: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
| 341 | - fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
| 342 | - fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
| 343 | - fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
| 344 | - pause; | |
| 345 | - end | |
| 346 | - | |
| 347 | - if verbose >= 1 | |
| 348 | - fprintf(1, 'allan_overlap: End of timestamp data: %g sec\n',dtime(end)); | |
| 349 | - fprintf(1, ' Average rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
| 350 | - if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
| 351 | - fprintf(1, ' Max. gap in time record: %g sec at position %d\n',max(dfdtime),gap_pos(1)); | |
| 352 | - end | |
| 353 | - if max(diff(dtime)) > 5*avg_gap | |
| 354 | - fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
| 355 | - end | |
| 356 | - end | |
| 357 | - | |
| 325 | + % adjust time to remove any starting offset | |
| 326 | + dtime=data.time-data.time(1)+mean(diff(data.time)); | |
| 327 | + | |
| 328 | + % save the freq data for the loop | |
| 329 | + dfreq=data.freq; | |
| 330 | + dtime=dtime(1:length(dfreq)); | |
| 331 | + | |
| 332 | + dfdtime=diff(dtime); % only need to do this once (v2.1) | |
| 333 | + % where is the maximum gap in time record? | |
| 334 | + gap_pos=find(dfdtime==max(dfdtime)); | |
| 335 | + % what is average data spacing? | |
| 336 | + avg_gap = mean(dfdtime); | |
| 337 | + s.avg_rate = 1/avg_gap; % save avg rate for user (v2.1) | |
| 338 | + | |
| 339 | + if verbose >= 2 | |
| 340 | + fprintf(1, 'allan_overlap: WARNING: irregular timestamp data (no fixed sample rate).\n'); | |
| 341 | + fprintf(1, ' Calculation time may be long and the results subject to interpretation.\n'); | |
| 342 | + fprintf(1, ' You are advised to estimate using an average sample rate (%g Hz) instead of timestamps.\n',1/avg_gap); | |
| 343 | + fprintf(1, ' Continue at your own risk! (press any key to continue)\n'); | |
| 344 | + pause; | |
| 345 | + end | |
| 346 | + | |
| 347 | + if verbose >= 1 | |
| 348 | + fprintf(1, 'allan_overlap: End of timestamp data: %g sec\n',dtime(end)); | |
| 349 | + fprintf(1, ' Average rate: %g Hz (%g sec/measurement)\n',1/avg_gap,avg_gap); | |
| 350 | + if max(diff(dtime)) ~= 1/mean(diff(dtime)) | |
| 351 | + fprintf(1, ' Max. gap in time record: %g sec at position %d\n',max(dfdtime),gap_pos(1)); | |
| 352 | + end | |
| 353 | + if max(diff(dtime)) > 5*avg_gap | |
| 354 | + fprintf(1, ' WARNING: Max. gap in time record is suspiciously large (>5x the average interval).\n'); | |
| 355 | + end | |
| 356 | + end | |
| 357 | + | |
| 358 | 358 | |
| 359 | - % find halfway point | |
| 360 | - halftime = fix(dtime(end)/2); | |
| 361 | - % truncate tau to appropriate values | |
| 362 | - tau = tau(tau >= max(dfdtime) & tau <= halftime); | |
| 363 | - if isempty(tau) | |
| 364 | - error('allan_overlap: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(dfdtime),halftime); | |
| 365 | - end | |
| 366 | - | |
| 359 | + % find halfway point | |
| 360 | + halftime = fix(dtime(end)/2); | |
| 361 | + % truncate tau to appropriate values | |
| 362 | + tau = tau(tau >= max(dfdtime) & tau <= halftime); | |
| 363 | + if isempty(tau) | |
| 364 | + error('allan_overlap: ERROR: no appropriate tau values (> %g s, < %g s)\n',max(dfdtime),halftime); | |
| 365 | + end | |
| 366 | + | |
| 367 | 367 | |
| 368 | - % number of samples | |
| 369 | - M=length(dfreq); | |
| 370 | - % number of samples per tau period | |
| 371 | - m=round(tau./avg_gap); | |
| 368 | + % number of samples | |
| 369 | + M=length(dfreq); | |
| 370 | + % number of samples per tau period | |
| 371 | + m=round(tau./avg_gap); | |
| 372 | 372 | |
| 373 | - if verbose >= 1, fprintf(1,'allan_overlap: calculating overlapping Allan deviation...\n'); end | |
| 373 | + if verbose >= 1, fprintf(1,'allan_overlap: calculating overlapping Allan deviation...\n'); end | |
| 374 | 374 | |
| 375 | - k=0; tic; | |
| 376 | - for i = tau | |
| 377 | - k=k+1; | |
| 378 | - fa=[]; | |
| 375 | + k=0; tic; | |
| 376 | + for i = tau | |
| 377 | + k=k+1; | |
| 378 | + fa=[]; | |
| 379 | 379 | |
| 380 | - if verbose >= 2, fprintf(1,'%d ',i); end | |
| 381 | - | |
| 382 | - freq = dfreq; time = dtime; | |
| 383 | - | |
| 384 | - | |
| 385 | - % compute overlapping samples (y_k) for this tau | |
| 386 | - %for j = 1:i | |
| 387 | - for j = 1:m(k) % (v2.1) | |
| 388 | - km=0; | |
| 389 | - %fprintf(1,'j: %d ',j); | |
| 380 | + if verbose >= 2, fprintf(1,'%d ',i); end | |
| 381 | + | |
| 382 | + freq = dfreq; time = dtime; | |
| 383 | + | |
| 384 | + | |
| 385 | + % compute overlapping samples (y_k) for this tau | |
| 386 | + %for j = 1:i | |
| 387 | + for j = 1:m(k) % (v2.1) | |
| 388 | + km=0; | |
| 389 | + %fprintf(1,'j: %d ',j); | |
| 390 | 390 | |
| 391 | - % (v2.1) truncating not correct for overlapping samples | |
| 392 | - % truncate data set to an even multiple of this tau value | |
| 393 | - %freq = freq(time <= time(end)-rem(time(end),i)); | |
| 394 | - %time = time(time <= time(end)-rem(time(end),i)); | |
| 395 | - | |
| 396 | - % break up the data into overlapping groups of tau length | |
| 397 | - while i*km <= time(end) | |
| 398 | - km=km+1; | |
| 399 | - %i*km | |
| 391 | + % (v2.1) truncating not correct for overlapping samples | |
| 392 | + % truncate data set to an even multiple of this tau value | |
| 393 | + %freq = freq(time <= time(end)-rem(time(end),i)); | |
| 394 | + %time = time(time <= time(end)-rem(time(end),i)); | |
| 395 | + | |
| 396 | + % break up the data into overlapping groups of tau length | |
| 397 | + while i*km <= time(end) | |
| 398 | + km=km+1; | |
| 399 | + %i*km | |
| 400 | 400 | |
| 401 | - % progress bar | |
| 402 | - if verbose >= 2 | |
| 403 | - if rem(km,100)==0, fprintf(1,'.'); end | |
| 404 | - if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
| 405 | - end | |
| 401 | + % progress bar | |
| 402 | + if verbose >= 2 | |
| 403 | + if rem(km,100)==0, fprintf(1,'.'); end | |
| 404 | + if rem(km,1000)==0, fprintf(1,'%g/%g\n',km,round(time(end)/i)); end | |
| 405 | + end | |
| 406 | 406 | |
| 407 | - f = freq(i*(km-1) < (time) & (time) <= i*km); | |
| 407 | + f = freq(i*(km-1) < (time) & (time) <= i*km); | |
| 408 | 408 | |
| 409 | - if ~isempty(f) | |
| 410 | - fa(j,km)=mean(f); | |
| 411 | - else | |
| 412 | - fa(j,km)=0; | |
| 413 | - end | |
| 409 | + if ~isempty(f) | |
| 410 | + fa(j,km)=mean(f); | |
| 411 | + else | |
| 412 | + fa(j,km)=0; | |
| 413 | + end | |
| 414 | 414 | |
| 415 | - end | |
| 416 | - %fa | |
| 417 | - | |
| 418 | - % shift data vector by -1 and repeat | |
| 419 | - freq=circshift(dfreq,(size(freq)>1)*-j); | |
| 420 | - freq(end-j+1:end)=[]; | |
| 421 | - time=circshift(dtime,(size(time)>1)*-j); | |
| 422 | - time(end-j+1:end)=[]; | |
| 423 | - time=time-time(1)+avg_gap; % remove time offset | |
| 424 | - | |
| 425 | - end | |
| 426 | - | |
| 427 | - % compute second differences of fractional frequency values (y_k+m - y_k) | |
| 428 | - fd1=diff(fa,1,2); | |
| 429 | - fd1=reshape(fd1,1,[]); | |
| 430 | - % compute overlapping ADEV from fractional frequency values | |
| 431 | - % only the first M-2*m(k)+1 samples are valid | |
| 432 | - if length(fd1) >= M-2*m(k)+1 | |
| 433 | - sm(k)=sqrt((1/(2*(M-2*m(k)+1)))*sum(fd1(1:M-2*m(k)+1).^2)); | |
| 415 | + end | |
| 416 | + %fa | |
| 417 | + | |
| 418 | + % shift data vector by -1 and repeat | |
| 419 | + freq=circshift(dfreq,(size(freq)>1)*-j); | |
| 420 | + freq(end-j+1:end)=[]; | |
| 421 | + time=circshift(dtime,(size(time)>1)*-j); | |
| 422 | + time(end-j+1:end)=[]; | |
| 423 | + time=time-time(1)+avg_gap; % remove time offset | |
| 424 | + | |
| 425 | + end | |
| 426 | + | |
| 427 | + % compute second differences of fractional frequency values (y_k+m - y_k) | |
| 428 | + fd1=diff(fa,1,2); | |
| 429 | + fd1=reshape(fd1,1,[]); | |
| 430 | + % compute overlapping ADEV from fractional frequency values | |
| 431 | + % only the first M-2*m(k)+1 samples are valid | |
| 432 | + if length(fd1) >= M-2*m(k)+1 | |
| 433 | + sm(k)=sqrt((1/(2*(M-2*m(k)+1)))*sum(fd1(1:M-2*m(k)+1).^2)); | |
| 434 | 434 | |
| 435 | - % estimate error bars | |
| 436 | - sme(k)=sm(k)/sqrt(M+1); | |
| 437 | - | |
| 438 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 439 | - | |
| 440 | - else | |
| 441 | - if verbose >=2, fprintf(1,' tau=%g dropped due to timestamp irregularities\n',tau(k)); end | |
| 442 | - sm(k)=0; sme(k)=0; | |
| 443 | - end | |
| 444 | - | |
| 435 | + % estimate error bars | |
| 436 | + sme(k)=sm(k)/sqrt(M+1); | |
| 437 | + | |
| 438 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 439 | + | |
| 440 | + else | |
| 441 | + if verbose >=2, fprintf(1,' tau=%g dropped due to timestamp irregularities\n',tau(k)); end | |
| 442 | + sm(k)=0; sme(k)=0; | |
| 443 | + end | |
| 444 | + | |
| 445 | 445 | |
| 446 | - end | |
| 446 | + end | |
| 447 | 447 | |
| 448 | - if verbose >= 2, fprintf(1,'\n'); end | |
| 449 | - calctime=toc; if verbose >= 1, fprintf(1,'allan_overlap: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 448 | + if verbose >= 2, fprintf(1,'\n'); end | |
| 449 | + calctime=toc; if verbose >= 1, fprintf(1,'allan_overlap: Elapsed time for calculation: %g seconds\n',calctime); end | |
| 450 | 450 | |
| 451 | - % remove any points that were dropped | |
| 452 | - tau(sm==0)=[]; | |
| 453 | - sm(sm==0)=[]; | |
| 454 | - sme(sme==0)=[]; | |
| 451 | + % remove any points that were dropped | |
| 452 | + tau(sm==0)=[]; | |
| 453 | + sm(sm==0)=[]; | |
| 454 | + sme(sme==0)=[]; | |
| 455 | 455 | |
| 456 | 456 | |
| 457 | 457 | |
| 458 | 458 | else |
| 459 | - error('allan_overlap: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
| 459 | + error('allan_overlap: WARNING: no DATA.rate or DATA.time! Type "help allan" for more information. [err2]'); | |
| 460 | 460 | end |
| 461 | 461 | |
| 462 | 462 | |
| 463 | 463 | |
| 464 | 464 | |
| 465 | 465 | |
| 466 | 466 | |
| 467 | 467 | |
| 468 | 468 | |
| 469 | 469 | |
| ... | ... | @@ -464,83 +464,83 @@ |
| 464 | 464 | %% Plotting |
| 465 | 465 | |
| 466 | 466 | if verbose >= 2 % show all data |
| 467 | - | |
| 468 | - % plot the frequency data, centered on median | |
| 469 | - if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
| 470 | - try | |
| 471 | - % dsplot makes a new figure | |
| 472 | - hd=dsplot(dtime,medianfreq); | |
| 473 | - catch ME | |
| 474 | - figure; | |
| 475 | - hd=plot(dtime,medianfreq); | |
| 476 | - if verbose >= 1, fprintf(1,'allan_overlap: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
| 477 | - if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
| 478 | - end | |
| 479 | - set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
| 480 | - hold on; | |
| 467 | + | |
| 468 | + % plot the frequency data, centered on median | |
| 469 | + if size(dtime,2) > size(dtime,1), dtime=dtime'; end % this should not be necessary, but dsplot 1.1 is a little bit brittle | |
| 470 | + try | |
| 471 | + % dsplot makes a new figure | |
| 472 | + hd=dsplot(dtime,medianfreq); | |
| 473 | + catch ME | |
| 474 | + figure; | |
| 475 | + hd=plot(dtime,medianfreq); | |
| 476 | + if verbose >= 1, fprintf(1,'allan_overlap: Note: Install dsplot.m for improved plotting of large data sets (File Exchange File ID: #15850).\n'); end | |
| 477 | + if verbose >= 2, fprintf(1,' (Message: %s)\n',ME.message); end | |
| 478 | + end | |
| 479 | + set(hd,'Marker','.','LineStyle','none','Color','b'); % equivalent to '.-' | |
| 480 | + hold on; | |
| 481 | 481 | |
| 482 | - fx = xlim; | |
| 483 | - % plot([fx(1) fx(2)],[s.median s.median],'-k'); | |
| 484 | - plot([fx(1) fx(2)],[0 0],':k'); | |
| 485 | - % show 5x Median Absolute deviation (MAD) values | |
| 486 | - hm=plot([fx(1) fx(2)],[5*MAD 5*MAD],'-r'); | |
| 487 | - plot([fx(1) fx(2)],[-5*MAD -5*MAD],'-r'); | |
| 488 | - % show linear fit line | |
| 489 | - hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
| 490 | - title(['Data: ' name],'FontSize',FontSize+2,'FontName','Arial'); | |
| 491 | - %set(get(gca,'Title'),'Interpreter','none'); | |
| 492 | - xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
| 493 | - if isfield(data,'units') | |
| 494 | - ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 495 | - else | |
| 496 | - ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
| 497 | - end | |
| 498 | - set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 499 | - legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
| 500 | - % tighten up | |
| 501 | - xlim([dtime(1) dtime(end)]); | |
| 482 | + fx = xlim; | |
| 483 | + % plot([fx(1) fx(2)],[s.median s.median],'-k'); | |
| 484 | + plot([fx(1) fx(2)],[0 0],':k'); | |
| 485 | + % show 5x Median Absolute deviation (MAD) values | |
| 486 | + hm=plot([fx(1) fx(2)],[5*MAD 5*MAD],'-r'); | |
| 487 | + plot([fx(1) fx(2)],[-5*MAD -5*MAD],'-r'); | |
| 488 | + % show linear fit line | |
| 489 | + hf=plot(xlim,polyval(s.linear,xlim)-s.median,'-g'); | |
| 490 | + title(['Data: ' name],'FontSize',FontSize+2,'FontName','Arial'); | |
| 491 | + %set(get(gca,'Title'),'Interpreter','none'); | |
| 492 | + xlabel('Time [sec]','FontSize',FontSize,'FontName',FontName); | |
| 493 | + if isfield(data,'units') | |
| 494 | + ylabel(['data - median(data) [' data.units ']'],'FontSize',FontSize,'FontName',FontName); | |
| 495 | + else | |
| 496 | + ylabel('freq - median(freq)','FontSize',FontSize,'FontName',FontName); | |
| 497 | + end | |
| 498 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 499 | + legend([hd hm hf],{'data (centered on median)','5x MAD outliers',['Linear Fit (' num2str(s.linear(1),'%g') ')']},'FontSize',max(10,FontSize-2)); | |
| 500 | + % tighten up | |
| 501 | + xlim([dtime(1) dtime(end)]); | |
| 502 | 502 | |
| 503 | - | |
| 503 | + | |
| 504 | 504 | end % end plot raw data |
| 505 | 505 | |
| 506 | 506 | |
| 507 | 507 | if verbose >= 1 % show analysis results |
| 508 | 508 | |
| 509 | - % plot Allan deviation results | |
| 510 | - if ~isempty(sm) | |
| 511 | - figure | |
| 509 | + % plot Allan deviation results | |
| 510 | + if ~isempty(sm) | |
| 511 | + figure | |
| 512 | 512 | |
| 513 | - % Choose loglog or semilogx plot here #PLOTLOG | |
| 514 | - %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 515 | - loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 513 | + % Choose loglog or semilogx plot here #PLOTLOG | |
| 514 | + %semilogx(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 515 | + loglog(tau,sm,'.-b','LineWidth',plotlinewidth,'MarkerSize',24); | |
| 516 | 516 | |
| 517 | - % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
| 518 | - % When this is fixed, uncomment below to use normal errorbars | |
| 519 | - %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
| 520 | - % this is a hack to approximate the error bars | |
| 521 | - hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
| 517 | + % in R14SP3, there is a bug that screws up the error bars on a semilog plot. | |
| 518 | + % When this is fixed, uncomment below to use normal errorbars | |
| 519 | + %errorbar(tau,sm,sme,'.-b'); set(gca,'XScale','log'); | |
| 520 | + % this is a hack to approximate the error bars | |
| 521 | + hold on; plot([tau; tau],[sm+sme; sm-sme],'-k','LineWidth',max(plotlinewidth-1,2)); | |
| 522 | 522 | |
| 523 | - grid on; | |
| 524 | - title(['Overlapping Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 525 | - %set(get(gca,'Title'),'Interpreter','none'); | |
| 526 | - xlabel('\tau [sec]','FontSize',FontSize,'FontName','Arial'); | |
| 527 | - ylabel(' Overlapping \sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
| 528 | - set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 529 | - % expand the x axis a little bit so that the errors bars look nice | |
| 530 | - adax = axis; | |
| 531 | - axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
| 532 | - | |
| 533 | - % display the minimum value | |
| 534 | - fprintf(1,'allan: Minimum overlapping ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
| 535 | - | |
| 536 | - elseif verbose >= 1 | |
| 537 | - fprintf(1,'allan_overlap: WARNING: no values calculated.\n'); | |
| 538 | - fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
| 539 | - fprintf(1,'Type "help allan_overlap" for more information.\n\n'); | |
| 540 | - end | |
| 541 | - | |
| 523 | + grid on; | |
| 524 | + title(['Overlapping Allan Deviation: ' name],'FontSize',FontSize+2,'FontName',FontName); | |
| 525 | + %set(get(gca,'Title'),'Interpreter','none'); | |
| 526 | + xlabel('\tau [sec]','FontSize',FontSize,'FontName','Arial'); | |
| 527 | + ylabel(' Overlapping \sigma_y(\tau)','FontSize',FontSize,'FontName',FontName); | |
| 528 | + set(gca,'FontSize',FontSize,'FontName',FontName); | |
| 529 | + % expand the x axis a little bit so that the errors bars look nice | |
| 530 | + adax = axis; | |
| 531 | + axis([adax(1)*0.9 adax(2)*1.1 adax(3) adax(4)]); | |
| 532 | + | |
| 533 | + % display the minimum value | |
| 534 | + fprintf(1,'allan: Minimum overlapping ADEV value: %g at tau = %g seconds\n',min(sm),tau(sm==min(sm))); | |
| 535 | + | |
| 536 | + elseif verbose >= 1 | |
| 537 | + fprintf(1,'allan_overlap: WARNING: no values calculated.\n'); | |
| 538 | + fprintf(1,' Check that TAU > 1/DATA.rate and TAU values are divisible by 1/DATA.rate\n'); | |
| 539 | + fprintf(1,'Type "help allan_overlap" for more information.\n\n'); | |
| 540 | + end | |
| 541 | + | |
| 542 | 542 | end % end plot analysis |
| 543 | - | |
| 543 | + | |
| 544 | 544 | retval = sm; |
| 545 | 545 | errorb = sme; |
| 546 | 546 |
allanplot.m
| ... | ... | @@ -5,25 +5,33 @@ |
| 5 | 5 | mult = eval(argv(){3}); |
| 6 | 6 | |
| 7 | 7 | if length(col) == length(mult) |
| 8 | - figure | |
| 9 | - hold all | |
| 10 | - grid on | |
| 11 | - cc = 'bkcgmry'; | |
| 12 | - for i = [1:length(col)] | |
| 13 | - data.freq = load(filename)(:,col(i)).*mult(i); | |
| 14 | - if eval(argv(){4})(i) == 1 | |
| 15 | - printf(strcat(filename, ' col', num2str(col(i)), ' drift removed\n\n')) | |
| 16 | - data.freq = detrend(data.freq); | |
| 17 | - end | |
| 18 | - data.rate = 1; | |
| 19 | - [ad, S, err, tau] = allan(data, 2.^[0:nextpow2(length(data.freq))-3]./data.rate, strcat(strsplit(filename, '/'){end}, num2str(i)), 0); | |
| 20 | - loglogerr(tau, ad, err, strcat(cc(mod(i, length(cc))), '-s')) | |
| 21 | - leg{i} = strcat(filename, ' col', num2str(col(i))); | |
| 22 | - axis(10.^ceil(log10([tau(1), tau(end)]))) | |
| 23 | - hold on | |
| 24 | - end | |
| 25 | - legend(leg) | |
| 26 | - input("Press to continue..."); | |
| 8 | + figure | |
| 9 | + hold all | |
| 10 | + grid on | |
| 11 | + cc = 'bkcgmry'; | |
| 12 | + for i = [1:length(col)] | |
| 13 | + data.freq = load(filename)(:,col(i)).*mult(i); | |
| 14 | + if nargin == 4 | |
| 15 | + if eval(argv(){4})(i) == 1 | |
| 16 | + printf(strcat(filename, ' col', num2str(col(i)), ' drift removed\n\n')) | |
| 17 | + data.freq = detrend(data.freq); | |
| 18 | + elseif eval(argv(){4})(i) == 2 | |
| 19 | + printf(strcat(filename, ' col', num2str(col(i)), ' relative ad\n\n')) | |
| 20 | + data.freq = data.freq./mean(data.freq); | |
| 21 | + elseif eval(argv(){4})(i) == 3 | |
| 22 | + printf(strcat(filename, ' col', num2str(col(i)), ' drift removed relative ad\n\n')) | |
| 23 | + data.freq = detrend(data.freq./mean(data.freq)); | |
| 24 | + end | |
| 25 | + endif | |
| 26 | + data.rate = 1; | |
| 27 | + [ad, S, err, tau] = allan(data, 2.^[0:nextpow2(length(data.freq))-3]./data.rate, strcat(strsplit(filename, '/'){end}, num2str(i)), 0); | |
| 28 | + loglogerr(tau, ad, err, strcat(cc(mod(i, length(cc))), '-s')) | |
| 29 | + leg{i} = strcat(filename, ' col', num2str(col(i))); | |
| 30 | + axis(10.^ceil(log10([tau(1), tau(end)]))) | |
| 31 | + hold on | |
| 32 | + end | |
| 33 | + legend(leg) | |
| 34 | + input("Press to continue..."); | |
| 27 | 35 | end |
| 28 | 36 | exit |
allanplot_cov.m
| ... | ... | @@ -7,32 +7,32 @@ |
| 7 | 7 | mult2 = eval(argv(){5}); |
| 8 | 8 | |
| 9 | 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 | - data.freq = data.freq(1:min(length(data.freq), length(data.freq2))); | |
| 18 | - data.freq2 = data.freq2(1:min(length(data.freq), length(data.freq2))); | |
| 19 | - if eval(argv(){end-1}) == 1 | |
| 20 | - printf('\ndata1 drift removed\n\n') | |
| 21 | - data.freq = detrend(data.freq); | |
| 22 | - end | |
| 23 | - if eval(argv(){end}) == 1 | |
| 24 | - printf('\ndata2 drift removed\n\n') | |
| 25 | - data.freq2 = detrend(data.freq2); | |
| 26 | - end | |
| 27 | - data.rate = 1; | |
| 28 | - [ad, S, err, tau] = allan_cov(data, 2.^[0:nextpow2(length(data.freq))-3]./data.rate, strcat(strsplit(filename, '/'){end}, num2str(i)), 0); | |
| 29 | - loglogerr(tau, ad, err, strcat(cc(mod(i, length(cc))), '-s')) | |
| 30 | - leg{i} = strcat(filename, ' cov col', num2str(col1(i)), ' col', num2str(col2(i))); | |
| 31 | - axis(10.^ceil(log10([tau(1), tau(end)]))) | |
| 32 | - hold on | |
| 33 | - end | |
| 34 | - legend(leg) | |
| 35 | - input("Press to continue..."); | |
| 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 | + data.freq = data.freq(1:min(length(data.freq), length(data.freq2))); | |
| 18 | + data.freq2 = data.freq2(1:min(length(data.freq), length(data.freq2))); | |
| 19 | + if eval(argv(){end-1}) == 1 | |
| 20 | + printf('\ndata1 drift removed\n\n') | |
| 21 | + data.freq = detrend(data.freq); | |
| 22 | + end | |
| 23 | + if eval(argv(){end}) == 1 | |
| 24 | + printf('\ndata2 drift removed\n\n') | |
| 25 | + data.freq2 = detrend(data.freq2); | |
| 26 | + end | |
| 27 | + data.rate = 1; | |
| 28 | + [ad, S, err, tau] = allan_cov(data, 2.^[0:nextpow2(length(data.freq))-3]./data.rate, strcat(strsplit(filename, '/'){end}, num2str(i)), 0); | |
| 29 | + loglogerr(tau, ad, err, strcat(cc(mod(i, length(cc))), '-s')) | |
| 30 | + leg{i} = strcat(filename, ' cov col', num2str(col1(i)), ' col', num2str(col2(i))); | |
| 31 | + axis(10.^ceil(log10([tau(1), tau(end)]))) | |
| 32 | + hold on | |
| 33 | + end | |
| 34 | + legend(leg) | |
| 35 | + input("Press to continue..."); | |
| 36 | 36 | end |
| 37 | 37 | exit |
dsplot.m
| ... | ... | @@ -6,9 +6,9 @@ |
| 6 | 6 | % |
| 7 | 7 | % DSPLOT(X, Y) plots Y versus X by downsampling if there are large number |
| 8 | 8 | % of elements. X and Y needs to obey the following: |
| 9 | -% 1. X must be a monotonically increasing vector. | |
| 10 | -% 2. If Y is a vector, it must be the same size as X. | |
| 11 | -% 3. If Y is a matrix, one of the dimensions must line up with X. | |
| 9 | +% 1. X must be a monotonically increasing vector. | |
| 10 | +% 2. If Y is a vector, it must be the same size as X. | |
| 11 | +% 3. If Y is a matrix, one of the dimensions must line up with X. | |
| 12 | 12 | % |
| 13 | 13 | % DSPLOT(Y) plots the columns of Y versus their index. |
| 14 | 14 | % |
| ... | ... | @@ -58,9 +58,9 @@ |
| 58 | 58 | % Version: |
| 59 | 59 | % v1.0 - first version (Aug 1, 2007) |
| 60 | 60 | % v1.1 - added CreateFcn for the figure so that when the figure is saved |
| 61 | -% and re-loaded, the zooming and panning works. Also added a menu | |
| 62 | -% item for saving out the original data back to the base | |
| 63 | -% workspace. (Aug 10, 2007) | |
| 61 | +% and re-loaded, the zooming and panning works. Also added a menu | |
| 62 | +% item for saving out the original data back to the base | |
| 63 | +% workspace. (Aug 10, 2007) | |
| 64 | 64 | % |
| 65 | 65 | % Jiro Doke |
| 66 | 66 | % August 1, 2007 |
| 67 | 67 | |
| ... | ... | @@ -101,13 +101,13 @@ |
| 101 | 101 | % orientation. |
| 102 | 102 | if numSignals > size(y, 1) |
| 103 | 103 | s = input(sprintf('Are you sure you want to plot %d lines? (y/n) ', ... |
| 104 | - numSignals), 's'); | |
| 104 | + numSignals), 's'); | |
| 105 | 105 | if ~strcmpi(s, 'y') |
| 106 | - disp('Canceled. You may want to transpose the matrix.'); | |
| 107 | - if nargout == 1 | |
| 108 | - hL = []; | |
| 109 | - end | |
| 110 | - return; | |
| 106 | + disp('Canceled. You may want to transpose the matrix.'); | |
| 107 | + if nargout == 1 | |
| 108 | + hL = []; | |
| 109 | + end | |
| 110 | + return; | |
| 111 | 111 | end |
| 112 | 112 | end |
| 113 | 113 | |
| ... | ... | @@ -120,7 +120,7 @@ |
| 120 | 120 | |
| 121 | 121 | % Always create new figure because it messes around with zoom, pan, |
| 122 | 122 | % datacursors. |
| 123 | -hFig = figure; | |
| 123 | +hFig = figure; | |
| 124 | 124 | figName = ''; |
| 125 | 125 | |
| 126 | 126 | % Create template plot using NaNs |
| 127 | 127 | |
| 128 | 128 | |
| 129 | 129 | |
| 130 | 130 | |
| 131 | 131 | |
| 132 | 132 | |
| 133 | 133 | |
| 134 | 134 | |
| 135 | 135 | |
| 136 | 136 | |
| 137 | 137 | |
| 138 | 138 | |
| 139 | 139 | |
| 140 | 140 | |
| 141 | 141 | |
| 142 | 142 | |
| 143 | 143 | |
| 144 | 144 | |
| 145 | 145 | |
| 146 | 146 | |
| 147 | 147 | |
| ... | ... | @@ -147,173 +147,173 @@ |
| 147 | 147 | |
| 148 | 148 | %-------------------------------------------------------------------------- |
| 149 | 149 | function myExportFcn(varargin) |
| 150 | - % This callback allows for extracting the actual data from the figure. | |
| 151 | - % This means that if you save this figure and load it back later, you | |
| 152 | - % can get back the data. | |
| 153 | - | |
| 154 | - % Determine the variable name | |
| 155 | - allVarNames = evalin('base', 'who'); | |
| 156 | - newVarName = genvarname('dsplotData', allVarNames); | |
| 157 | - | |
| 158 | - % X | |
| 159 | - if ~noXVar | |
| 160 | - if varTranspose | |
| 161 | - dat.x = x'; | |
| 162 | - else | |
| 163 | - dat.x = x; | |
| 164 | - end | |
| 165 | - end | |
| 166 | - | |
| 167 | - % Y | |
| 168 | - if varTranspose | |
| 169 | - dat.y = y'; | |
| 170 | - else | |
| 171 | - dat.y = y; | |
| 172 | - end | |
| 173 | - | |
| 174 | - assignin('base', newVarName, dat); | |
| 175 | - | |
| 176 | - msgbox(sprintf('Data saved to the base workspace as ''%s''.', ... | |
| 177 | - newVarName), 'Saved', 'modal'); | |
| 178 | - | |
| 150 | + % This callback allows for extracting the actual data from the figure. | |
| 151 | + % This means that if you save this figure and load it back later, you | |
| 152 | + % can get back the data. | |
| 153 | + | |
| 154 | + % Determine the variable name | |
| 155 | + allVarNames = evalin('base', 'who'); | |
| 156 | + newVarName = genvarname('dsplotData', allVarNames); | |
| 157 | + | |
| 158 | + % X | |
| 159 | + if ~noXVar | |
| 160 | + if varTranspose | |
| 161 | + dat.x = x'; | |
| 162 | + else | |
| 163 | + dat.x = x; | |
| 164 | + end | |
| 165 | + end | |
| 166 | + | |
| 167 | + % Y | |
| 168 | + if varTranspose | |
| 169 | + dat.y = y'; | |
| 170 | + else | |
| 171 | + dat.y = y; | |
| 172 | + end | |
| 173 | + | |
| 174 | + assignin('base', newVarName, dat); | |
| 175 | + | |
| 176 | + msgbox(sprintf('Data saved to the base workspace as ''%s''.', ... | |
| 177 | + newVarName), 'Saved', 'modal'); | |
| 178 | + | |
| 179 | 179 | end |
| 180 | 180 | |
| 181 | 181 | %-------------------------------------------------------------------------- |
| 182 | 182 | function mycreatefcn(varargin) |
| 183 | - % This callback defines the custom zoom/pan functions. It is defined as | |
| 184 | - % the CreateFcn of the figure, so it allows for saving and reloading of | |
| 185 | - % the figure. | |
| 183 | + % This callback defines the custom zoom/pan functions. It is defined as | |
| 184 | + % the CreateFcn of the figure, so it allows for saving and reloading of | |
| 185 | + % the figure. | |
| 186 | 186 | |
| 187 | - if nargin > 0 | |
| 188 | - hFig = varargin{1}; | |
| 189 | - end | |
| 190 | - hLine = findobj(hFig, 'type', 'axes'); | |
| 191 | - hLine(strmatch('legend', get(hLine, 'tag'))) = []; | |
| 192 | - hLine = get(hLine, 'Children'); | |
| 193 | - | |
| 194 | - % Create Zoom, Pan, Datacursor objects | |
| 195 | - hZoom = zoom(hFig); | |
| 196 | - hPan = pan(hFig); | |
| 197 | - hDc = datacursormode(hFig); | |
| 198 | - set(hZoom, 'ActionPostCallback', @mypostcallback); | |
| 199 | - set(hPan , 'ActionPostCallback', @mypostcallback); | |
| 200 | - set(hDc , 'UpdateFcn' , @myDCupdatefcn); | |
| 187 | + if nargin > 0 | |
| 188 | + hFig = varargin{1}; | |
| 189 | + end | |
| 190 | + hLine = findobj(hFig, 'type', 'axes'); | |
| 191 | + hLine(strmatch('legend', get(hLine, 'tag'))) = []; | |
| 192 | + hLine = get(hLine, 'Children'); | |
| 193 | + | |
| 194 | + % Create Zoom, Pan, Datacursor objects | |
| 195 | + hZoom = zoom(hFig); | |
| 196 | + hPan = pan(hFig); | |
| 197 | + hDc = datacursormode(hFig); | |
| 198 | + set(hZoom, 'ActionPostCallback', @mypostcallback); | |
| 199 | + set(hPan , 'ActionPostCallback', @mypostcallback); | |
| 200 | + set(hDc , 'UpdateFcn' , @myDCupdatefcn); | |
| 201 | 201 | |
| 202 | 202 | end |
| 203 | 203 | |
| 204 | 204 | %-------------------------------------------------------------------------- |
| 205 | 205 | function mypostcallback(obj, evd) %#ok |
| 206 | - % This callback that gets called when the mouse is released after | |
| 207 | - % zooming or panning. | |
| 206 | + % This callback that gets called when the mouse is released after | |
| 207 | + % zooming or panning. | |
| 208 | 208 | |
| 209 | - % single or double-click | |
| 210 | - switch get(hFig, 'SelectionType') | |
| 211 | - case {'normal', 'alt'} | |
| 212 | - updateLines(xlim(evd.Axes)); | |
| 209 | + % single or double-click | |
| 210 | + switch get(hFig, 'SelectionType') | |
| 211 | + case {'normal', 'alt'} | |
| 212 | + updateLines(xlim(evd.Axes)); | |
| 213 | 213 | |
| 214 | - case 'open' | |
| 215 | - updateLines([min(x), max(x)]); | |
| 214 | + case 'open' | |
| 215 | + updateLines([min(x), max(x)]); | |
| 216 | 216 | |
| 217 | - end | |
| 217 | + end | |
| 218 | 218 | |
| 219 | 219 | end |
| 220 | 220 | |
| 221 | 221 | %-------------------------------------------------------------------------- |
| 222 | 222 | function updateLines(rng) |
| 223 | - % This helper function is for determining the points to plot on the | |
| 224 | - % screen based on which portion is visible in the current limits. | |
| 223 | + % This helper function is for determining the points to plot on the | |
| 224 | + % screen based on which portion is visible in the current limits. | |
| 225 | 225 | |
| 226 | - % find indeces inside the range | |
| 227 | - id = find(x >= rng(1) & x <= rng(2)); | |
| 226 | + % find indeces inside the range | |
| 227 | + id = find(x >= rng(1) & x <= rng(2)); | |
| 228 | 228 | |
| 229 | - % if there are more points than we want | |
| 230 | - if length(id) > numPoints / numSignals | |
| 229 | + % if there are more points than we want | |
| 230 | + if length(id) > numPoints / numSignals | |
| 231 | 231 | |
| 232 | - % see how many outlier points are in this range | |
| 233 | - blah = iOutliers > id(1) & iOutliers < id(end); | |
| 232 | + % see how many outlier points are in this range | |
| 233 | + blah = iOutliers > id(1) & iOutliers < id(end); | |
| 234 | 234 | |
| 235 | - % determine indeces of points to plot. | |
| 236 | - idid = round(linspace(id(1), id(end), round(numPoints/numSignals)))'; | |
| 235 | + % determine indeces of points to plot. | |
| 236 | + idid = round(linspace(id(1), id(end), round(numPoints/numSignals)))'; | |
| 237 | 237 | |
| 238 | - x2 = cell(numSignals, 1); | |
| 239 | - y2 = x2; | |
| 240 | - for iSignals = 1:numSignals | |
| 241 | - % add outlier points | |
| 242 | - ididid = unique([idid; iOutliers(blah & jOutliers == iSignals)]); | |
| 243 | - x2{iSignals} = x(ididid); | |
| 244 | - y2{iSignals} = y(ididid, iSignals); | |
| 245 | - end | |
| 238 | + x2 = cell(numSignals, 1); | |
| 239 | + y2 = x2; | |
| 240 | + for iSignals = 1:numSignals | |
| 241 | + % add outlier points | |
| 242 | + ididid = unique([idid; iOutliers(blah & jOutliers == iSignals)]); | |
| 243 | + x2{iSignals} = x(ididid); | |
| 244 | + y2{iSignals} = y(ididid, iSignals); | |
| 245 | + end | |
| 246 | 246 | |
| 247 | - if debugMode | |
| 248 | - figName = ['downsampled - ', sprintf('%d, ', cellfun('length', y2))]; | |
| 249 | - else | |
| 250 | - figName = 'downsampled'; | |
| 251 | - end | |
| 247 | + if debugMode | |
| 248 | + figName = ['downsampled - ', sprintf('%d, ', cellfun('length', y2))]; | |
| 249 | + else | |
| 250 | + figName = 'downsampled'; | |
| 251 | + end | |
| 252 | 252 | |
| 253 | - else % no need to down sample | |
| 254 | - figName = 'true'; | |
| 253 | + else % no need to down sample | |
| 254 | + figName = 'true'; | |
| 255 | 255 | |
| 256 | - x2 = repmat({x(id)}, numSignals, 1); | |
| 257 | - y2 = mat2cell(y(id, :), length(id), ones(1, numSignals))'; | |
| 256 | + x2 = repmat({x(id)}, numSignals, 1); | |
| 257 | + y2 = mat2cell(y(id, :), length(id), ones(1, numSignals))'; | |
| 258 | 258 | |
| 259 | - end | |
| 259 | + end | |
| 260 | 260 | |
| 261 | - % Update plot | |
| 262 | - set(hLine, {'xdata', 'ydata'} , [x2, y2]); | |
| 263 | - set(hFig, 'Name', figName); | |
| 261 | + % Update plot | |
| 262 | + set(hLine, {'xdata', 'ydata'} , [x2, y2]); | |
| 263 | + set(hFig, 'Name', figName); | |
| 264 | 264 | |
| 265 | 265 | end |
| 266 | 266 | |
| 267 | 267 | %-------------------------------------------------------------------------- |
| 268 | 268 | function txt = myDCupdatefcn(empt, event_obj) %#ok |
| 269 | - % This function displays appropriate data cursor message based on the | |
| 270 | - % display type | |
| 269 | + % This function displays appropriate data cursor message based on the | |
| 270 | + % display type | |
| 271 | 271 | |
| 272 | - pos = get(event_obj,'Position'); | |
| 273 | - switch figName | |
| 274 | - case 'true' | |
| 275 | - txt = {['X: ',num2str(pos(1))],... | |
| 276 | - ['Y: ',num2str(pos(2))]}; | |
| 277 | - otherwise | |
| 278 | - txt = {['X: ',num2str(pos(1))],... | |
| 279 | - ['Y: ',num2str(pos(2))], ... | |
| 280 | - 'Warning: Downsampled', ... | |
| 281 | - 'May not be accurate'}; | |
| 282 | - end | |
| 272 | + pos = get(event_obj,'Position'); | |
| 273 | + switch figName | |
| 274 | + case 'true' | |
| 275 | + txt = {['X: ',num2str(pos(1))],... | |
| 276 | + ['Y: ',num2str(pos(2))]}; | |
| 277 | + otherwise | |
| 278 | + txt = {['X: ',num2str(pos(1))],... | |
| 279 | + ['Y: ',num2str(pos(2))], ... | |
| 280 | + 'Warning: Downsampled', ... | |
| 281 | + 'May not be accurate'}; | |
| 282 | + end | |
| 283 | 283 | end |
| 284 | 284 | |
| 285 | 285 | %-------------------------------------------------------------------------- |
| 286 | 286 | function myErrorCheck |
| 287 | - % Do some error checking on the input arguments. | |
| 287 | + % Do some error checking on the input arguments. | |
| 288 | 288 | |
| 289 | - if ~isa(numPoints, 'double') || numel(numPoints) > 1 || numPoints < 500 | |
| 290 | - error('Third argument must be a scalar greater than 500'); | |
| 291 | - end | |
| 292 | - if ~isnumeric(x) || ~isnumeric(y) | |
| 293 | - error('Arguments must be numeric'); | |
| 294 | - end | |
| 295 | - if length(size(x)) > 2 || length(size(y)) > 2 | |
| 296 | - error('Only 2-D data accepted'); | |
| 297 | - end | |
| 298 | - | |
| 299 | - % If only one input, create index vector X | |
| 300 | - if isempty(x) | |
| 301 | - if ismember(1, size(y)) | |
| 302 | - x = reshape(1:numel(y), size(y)); | |
| 303 | - else | |
| 304 | - x = (1:size(y, 1))'; | |
| 305 | - end | |
| 306 | - end | |
| 307 | - | |
| 308 | - if ~ismember(1, size(x)) | |
| 309 | - error('First argument has to be a vector'); | |
| 310 | - end | |
| 311 | - if ~isequal(size(x, 1), size(y, 1)) && ~isequal(size(x, 2), size(y, 2)) | |
| 312 | - error('One of the dimensions of the two arguments must match'); | |
| 313 | - end | |
| 314 | - if any(diff(x) <= 0) | |
| 315 | - error('The first argument has to be a monotonically increasing vector'); | |
| 316 | - end | |
| 289 | + if ~isa(numPoints, 'double') || numel(numPoints) > 1 || numPoints < 500 | |
| 290 | + error('Third argument must be a scalar greater than 500'); | |
| 291 | + end | |
| 292 | + if ~isnumeric(x) || ~isnumeric(y) | |
| 293 | + error('Arguments must be numeric'); | |
| 294 | + end | |
| 295 | + if length(size(x)) > 2 || length(size(y)) > 2 | |
| 296 | + error('Only 2-D data accepted'); | |
| 297 | + end | |
| 298 | + | |
| 299 | + % If only one input, create index vector X | |
| 300 | + if isempty(x) | |
| 301 | + if ismember(1, size(y)) | |
| 302 | + x = reshape(1:numel(y), size(y)); | |
| 303 | + else | |
| 304 | + x = (1:size(y, 1))'; | |
| 305 | + end | |
| 306 | + end | |
| 307 | + | |
| 308 | + if ~ismember(1, size(x)) | |
| 309 | + error('First argument has to be a vector'); | |
| 310 | + end | |
| 311 | + if ~isequal(size(x, 1), size(y, 1)) && ~isequal(size(x, 2), size(y, 2)) | |
| 312 | + error('One of the dimensions of the two arguments must match'); | |
| 313 | + end | |
| 314 | + if any(diff(x) <= 0) | |
| 315 | + error('The first argument has to be a monotonically increasing vector'); | |
| 316 | + end | |
| 317 | 317 | end |
| 318 | 318 | |
| 319 | 319 | end |
psdplot.m
| ... | ... | @@ -5,20 +5,20 @@ |
| 5 | 5 | mult = eval(argv(){3}); |
| 6 | 6 | |
| 7 | 7 | if length(col) == length(mult) |
| 8 | - figure | |
| 9 | - hold all | |
| 10 | - grid on | |
| 11 | - cc = 'bkcgmry'; | |
| 12 | - for i = [1:length(col)] | |
| 13 | - data.freq = load(filename)(:,col(i)).*mult(i); | |
| 14 | - data.rate = 1; | |
| 15 | - [p, f] = pwelch(data.freq, [], 0.95, [], data.rate.*mult(i), 'onesided'); | |
| 16 | - semilogx(f, 10*log10(p), cc(mod(i, length(cc)))) | |
| 17 | - leg{i} = strcat(filename, ' col', num2str(col(i))); | |
| 18 | - hold on | |
| 19 | - end | |
| 20 | - legend(leg) | |
| 21 | - input("Press to continue..."); | |
| 8 | + figure | |
| 9 | + hold all | |
| 10 | + grid on | |
| 11 | + cc = 'bkcgmry'; | |
| 12 | + for i = [1:length(col)] | |
| 13 | + data.freq = load(filename)(:,col(i)).*mult(i); | |
| 14 | + data.rate = 1; | |
| 15 | + [p, f] = pwelch(data.freq, [], 0.95, [], data.rate.*mult(i), 'onesided'); | |
| 16 | + semilogx(f, 10*log10(p), cc(mod(i, length(cc)))) | |
| 17 | + leg{i} = strcat(filename, ' col', num2str(col(i))); | |
| 18 | + hold on | |
| 19 | + end | |
| 20 | + legend(leg) | |
| 21 | + input("Press to continue..."); | |
| 22 | 22 | end |
| 23 | 23 | exit |