comparison m-toolbox/classes/@ao/buildWhitener1D.m @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f0afece42f48
1 % BUILDWHITENER1D builds a whitening filter based on the input frequency-series.
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %
4 % DESCRIPTION:BUILDWHITENER1D builds a whitening filter based on the input frequency-series.
5 % The filter is built by fitting to the model provided.
6 % If no model is provided, a fit is made to a spectral-density estimate of the
7 % input time-series (made using psd+bin_data or lpsd).
8 % Note: The function assumes that the input model corresponds
9 % to the one-sided psd of the data to be whitened.
10 %
11 % ALGORITHM:
12 % 1) If no model provided, make psd+bin_data or lpsd
13 % of time-series and take it as a model
14 % for the data power spectral density
15 % 2) Fit a set of partial fraction z-domain filters using
16 % utils.math.psd2wf. The fit is automatically stopped when
17 % the accuracy tolerance is reached.
18 % 3) Convert to array of MIIR filters
19 % 4) Assemble into a parallel filterbank object
20 %
21 %
22 % CALL: b = buildWhitener1D(a, pl)
23 % [b1,b2,...,bn] = buildWhitener1D(a1,a2,...,an, pl);
24 %
25 % INPUT:
26 % - as is a time-series analysis object or a vector of
27 % analysis objects
28 % - pl is a plist with the input parameters
29 %
30 % OUTPUT:
31 % - b "whitening" filters, stored into a filterbank.
32 %
33 % <a href="matlab:utils.helper.displayMethodInfo('ao', 'buildWhitener1D')">Parameters Description</a>
34 %
35 % VERSION: $Id: buildWhitener1D.m,v 1.11 2011/04/18 19:46:57 mauro Exp $
36 %
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38
39 function varargout = buildWhitener1D(varargin)
40
41 % Check if this is a call for parameters
42 if utils.helper.isinfocall(varargin{:})
43 varargout{1} = getInfo(varargin{3});
44 return
45 end
46
47 import utils.const.*
48 utils.helper.msg(msg.PROC3, 'running %s/%s', mfilename('class'), mfilename);
49
50 % Collect input variable names
51 in_names = cell(size(varargin));
52 for ii = 1:nargin,in_names{ii} = inputname(ii);end
53
54 % Collect all AOs and plists
55 [as, ao_invars] = utils.helper.collect_objects(varargin(:), 'ao', in_names);
56 pl = utils.helper.collect_objects(varargin(:), 'plist', in_names);
57
58 if nargout == 0
59 error('### buildWhitener cannot be used as a modifier. Please give an output variable.');
60 end
61
62 % combine plists
63 if isempty(pl)
64 model = 'psd';
65 else
66 model = find(pl, 'model');
67 if isempty(model)
68 model = 'psd';
69 pl.pset('model', model);
70 end
71 end
72
73 if ischar(model)
74 pl = parse(pl, getDefaultPlist(model));
75 else
76 pl = parse(pl, getDefaultPlist('Default'));
77 end
78 pl.getSetRandState();
79
80 % Collect input histories
81 inhists = [as.hist];
82
83 % Initialize output objects
84 bs = filterbank.initObjectWithSize(1, numel(as));
85
86
87 % Loop over input AOs
88 for jj = 1:numel(as)
89 % 1) searching for input model
90 switch class(as(jj).data)
91 case 'tsdata'
92 % Build the model based on input time-series
93 utils.helper.msg(msg.PROC1, 'user input tsdata object, estimating the model from it');
94 model = estimateModel(as(jj), pl);
95 case 'fsdata'
96 % The input data are the model
97 utils.helper.msg(msg.PROC1, 'user input fsdata object, taking it as the model');
98 model = as(jj);
99 otherwise
100 warning('!!! %s expects ao/tsdata or ao/fsdata objects. Skipping AO %s', mfilename, ao_invars{jj});
101 return;
102 end
103
104 %-------------- Whiten this AO
105
106 % Extract necessary parameters
107
108 % Tolerance for MSE Value
109 lrscond = find(pl, 'FITTOL');
110 % give an error for strange values of lrscond
111 if lrscond < 0
112 error('!!! Negative values for FITTOL are not allowed !!!')
113 end
114 % handling data
115 lrscond = -1 * log10(lrscond);
116 % give a warning for strange values of lrscond
117 if lrscond<0
118 warning('You are searching for a MSE lower than %s', num2str(10^(-1*lrscond)))
119 end
120 params.lrscond = lrscond;
121
122 % Tolerance for the MSE relative variation
123 msevar = find(pl, 'MSEVARTOL');
124 % handling data
125 msevar = -1 * log10(msevar);
126 % give a warning for strange values of msevar
127 if msevar<0
128 warning('You are searching for MSE relative variation lower than %s', num2str(10^(-1*msevar)))
129 end
130 params.msevar = msevar;
131
132 if isempty(params.msevar)
133 params.ctp = 'chival';
134 else
135 params.ctp = 'chivar';
136 end
137
138 % Weights
139 switch find(pl, 'Weights')
140 case {'equal', 'flat', 1}
141 params.weightparam = 1;
142 case {'1/abs', '1./abs', 2}
143 params.weightparam = 2;
144 case {'1/abs^2', '1/abs2', '1./abs^2', '1./abs2', 3}
145 params.weightparam = 3;
146 otherwise
147 warning('Unrecognized weights option %s', find(pl, 'Weights'))
148 end
149
150 % 2) Build filters
151
152 % Build input structure for psd2wf
153 params.idtp = 1;
154 params.Nmaxiter = find(pl, 'MaxIter');
155 params.minorder = find(pl, 'MinOrder');
156 params.maxorder = find(pl, 'MaxOrder');
157 params.spolesopt = find(pl, 'PoleType');
158 params.spy = find(pl, 'Disp');
159
160
161 if (find(pl, 'plot'))
162 params.plot = 1;
163 else
164 params.plot = 0;
165 end
166
167 fs = find(pl, 'fs');
168 if isempty(fs) || fs <= 0 || ~isfinite(fs)
169 if isempty(model.fs) || model.fs <= 0 || ~isfinite(model.fs)
170 error('### Invalid fs value %s. Please specify a meaningful fs, either via the model or in the plist', num2str(fs));
171 else
172 fs = model.fs;
173 end
174 end
175
176 params.fs = fs;
177 params.usesym = 0;
178 params.dterm = 0; % it is better to fit without direct term
179 params.fullauto = 1;
180
181 % call psd2wf
182 [res, poles, dterm, mresp, rdl] = ...
183 utils.math.psd2wf(model.y,[],[],[],model.x,params);
184
185 % 3) Convert to MIIR filters
186
187 % filtering with a stable model
188 pfilts = [];
189 for kk = 1:numel(res)
190 ft = miir(res(kk), [ 1 -poles(kk)], fs);
191 pfilts = [pfilts ft];
192 end
193
194 % 4) Build the output filterbank object
195 bs(jj) = filterbank(plist('filters', pfilts, 'type', 'parallel'));
196 % set the input units to be the same as the model
197 bs(jj).setIunits(sqrt(model.yunits * unit('Hz')));
198 % set the output units to be empty
199 bs(jj).setOunits(unit());
200
201 % set the name for this object
202 bs(jj).name = sprintf('buildWhitener1D(%s)', ao_invars{jj});
203 % add history
204 bs(jj).addHistory(getInfo('None'), pl, ao_invars(jj), inhists(jj));
205 end
206
207 % Set output
208 if nargout == numel(bs)
209 % List of outputs
210 for ii = 1:numel(bs)
211 varargout{ii} = bs(ii);
212 end
213 else
214 % Single output
215 varargout{1} = bs;
216 end
217 end
218
219 %--------------------------------------------------------------------------
220 % Get Info Object
221 %--------------------------------------------------------------------------
222 function ii = getInfo(varargin)
223 if nargin == 1 && strcmpi(varargin{1}, 'None')
224 sets = {};
225 pl = [];
226 elseif nargin == 1 && ~isempty(varargin{1}) && ischar(varargin{1})
227 sets{1} = varargin{1};
228 pl = getDefaultPlist(sets{1});
229 else
230 sets = SETS();
231 % get plists
232 pl(size(sets)) = plist;
233 for kk = 1:numel(sets)
234 pl(kk) = getDefaultPlist(sets{kk});
235 end
236 end
237 % Build info object
238 ii = minfo(mfilename, 'ao', 'ltpda', utils.const.categories.sigproc, '$Id: buildWhitener1D.m,v 1.11 2011/04/18 19:46:57 mauro Exp $', sets, pl);
239 end
240
241
242 %--------------------------------------------------------------------------
243 % Defintion of Sets
244 %--------------------------------------------------------------------------
245
246 function out = SETS()
247 out = {...
248 'Default', ...
249 'PSD', ...
250 'LPSD' ...
251 };
252 end
253
254 %--------------------------------------------------------------------------
255 % Get Default Plist
256 %--------------------------------------------------------------------------
257 function plout = getDefaultPlist(set)
258 persistent pl;
259 persistent lastset;
260 if ~exist('pl', 'var') || isempty(pl) || ~strcmp(lastset, set)
261 pl = buildplist(set);
262 lastset = set;
263 end
264 plout = pl;
265 end
266
267
268 function pl = buildplist(set)
269 pl = plist();
270
271 % Model
272 p = param({'model', ['A model estimation technique in the case of tsdata input:<br>'...
273 '<li>PSD - using <tt>psd</tt> + <tt>bin_data</tt></li>'...
274 '<li>LPSD - using <tt>lpsd</tt></li>']}, {1, {'PSD', 'LPSD'}, paramValue.SINGLE});
275 pl.append(p);
276
277 % Range
278 p = param({'range', ['The frequency range to evaluate the fitting.<br>' ...
279 'An empty value or [-inf inf] will include the whole range.<br>' ...
280 'The remaining part of the model will be completed according<br>' ...
281 'to the option chosen in the ''complete'' parameter.<br>' ...
282 ]}, paramValue.EMPTY_DOUBLE);
283 pl.append(p);
284
285 % Complete
286 p = param({'complete_hf', ['Choose how to complete the frequency range up to fs/2.<ol>' ...
287 '<li>Assumes flat response</li>' ...
288 '<li>Assumes 4 poles low-pass type response</li>' ...
289 ]}, {1,{'flat', 'lowpass'}, paramValue.SINGLE});
290 pl.append(p);
291
292 % fs
293 p = param({'fs', ['The sampling frequency to design the output filter on.<br>' ...
294 'If it is not a positive number, it will be taken from the model' ...
295 ]}, paramValue.EMPTY_DOUBLE);
296 pl.append(p);
297
298 % MaxIter
299 p = param({'MaxIter', 'Maximum number of iterations in fit routine.'}, paramValue.DOUBLE_VALUE(30));
300 pl.append(p);
301
302 % PoleType
303 p = param({'PoleType', ['Choose the pole type for fitting:<ol>'...
304 '<li>use real starting poles</li>'...
305 '<li>generates complex conjugate poles of the<br>'...
306 'type <tt>a.*exp(theta*pi*j)</tt>'...
307 'with <tt>theta = linspace(0,pi,N/2+1)</tt></li>'...
308 '<li>generates complex conjugate poles of the type<br>'...
309 '<tt>a.*exp(theta*pi*j)</tt><br>'...
310 'with <tt>theta = linspace(0,pi,N/2+2)</tt></li></ol>']}, {1, {1, 2, 3}, paramValue.SINGLE});
311 pl.append(p);
312
313 % MinOrder
314 p = param({'MinOrder', 'Minimum order to fit with.'}, paramValue.DOUBLE_VALUE(2));
315 pl.append(p);
316
317 % MaxOrder
318 p = param({'MaxOrder', 'Maximum order to fit with.'}, paramValue.DOUBLE_VALUE(25));
319 pl.append(p);
320
321 % Weights
322 p = param({'Weights', ['Choose weighting method:<ol>'...
323 '<li>equal weights for each point</li>'...
324 '<li>weight with <tt>1/abs(model)</tt></li>'...
325 '<li>weight with <tt>1/abs(model).^2</tt></li></ol>']}, ...
326 {2, {'equal', '1/abs', '1/abs^2'}, paramValue.SINGLE});
327 pl.append(p);
328
329 % Plot
330 p = param({'Plot', 'Plot results of each fitting step.'}, paramValue.FALSE_TRUE);
331 pl.append(p);
332
333 % Disp
334 p = param({'Disp', 'Display the progress of the fitting iteration.'}, paramValue.FALSE_TRUE);
335 pl.append(p);
336
337 % MSEVARTOL
338 p = param({'MSEVARTOL', ['Mean Squared Error Variation - Check if the<br>'...
339 'relative variation of the mean squared error is<br>'...
340 'smaller than the value specified. This<br>'...
341 'option is useful for finding the minimum of Chi-squared.']}, ...
342 paramValue.DOUBLE_VALUE(1e-1));
343 pl.append(p);
344
345 % FITTOL
346 p = param({'FITTOL', ['Mean Squared Error Value - Check if the mean<br>'...
347 'squared error value is lower than the value<br>'...
348 'specified.']}, paramValue.DOUBLE_VALUE(1e-2));
349 pl.append(p);
350
351 % Append sets of parameters according to the chosen spectral estimator
352 if ~utils.helper.ismember(lower(SETS), lower(set))
353 error('### Unknown set [%s]', set);
354 end
355
356 switch lower(set)
357 case 'default'
358 pl.remove('model');
359 case 'psd'
360 pl = combine(pl, ao.getInfo('psd').plists);
361 pl.pset(...
362 'model', 'PSD', ...
363 'Navs', 16, ...
364 'order', 1, ...
365 'olap', 50 ...
366 );
367 pl = combine(pl, ao.getInfo('bin_data').plists);
368 pl.pset(...
369 'method', 'MEAN', ...
370 'resolution', 50 ...
371 );
372 case 'lpsd'
373 pl = combine(pl, ao.getInfo('lpsd').plists);
374 pl.pset(...
375 'model', 'LPSD' ...
376 );
377 otherwise
378 end
379 end
380
381
382 %--------------------------------------------------------------------------
383 % Estimate a model from the data or from user input
384 %--------------------------------------------------------------------------
385 function model = estimateModel(b, pl)
386
387 import utils.const.*
388
389
390 % Estimate a model for the PSD
391 model_all = find(pl, 'model');
392 if ischar(model_all)
393 switch lower(model_all)
394 case 'psd'
395 % Select only the parameters associated to ao/psd
396 pls = ao.getInfo('psd').plists;
397 % Call ao/psd
398 sp = psd(b, pl.subset(pls.getKeys()));
399 % Select only the parameters associated to ao/bin_data
400 pls = ao.getInfo('bin_data').plists;
401 % Call ao/bin_data
402 model_all = bin_data(sp, pl.subset(pls.getKeys()));
403 case 'lpsd'
404 % Select only the parameters associated to ao/lpsd
405 pls = ao.getInfo('lpsd').plists;
406 model_all = lpsd(b, pl.subset(pls.getKeys()));
407 otherwise
408 error('### Unknown model [%s]', model_all);
409 end
410 end
411
412 % Select only a limited frequency range
413 frange = find(pl, 'range');
414 if isempty(frange)
415 frange = [-inf inf];
416 end
417 model = model_all.split(plist('frequencies', frange));
418 f1 = frange(1);
419 f2 = frange(2);
420
421 if isfinite(f2)
422 % Select a technique to complete the high frequency range
423 complete_up_opt = find(pl, 'complete_hf');
424 switch complete_up_opt
425 case {'flat', 'allpass', 'all pass', 'all-pass'}
426 utils.helper.msg(msg.PROC1, 'Completing the frequency range from %s to %s with flat model', ...
427 num2str(f2), num2str(b.fs/2));
428 % Build a flat model response
429 r = ones(size(model_all.x));
430 case {'lowpass', 'low pass', 'low-pass'}
431 utils.helper.msg(msg.PROC1, 'Completing the frequency range from %s to %s with 4 poles low-pass model', ...
432 num2str(f2), num2str(b.fs/2));
433 % Build a 4 poles low pass resp
434 r = abs(resp(pzmodel(plist('gain', 1, 'poles', {10*f2,11*f2,12*f2,13*f2})), plist('f', model_all.x)));
435 r = r.y;
436 otherwise
437 error('### Unknown option [%s] for high frequency completion', complete_up_opt);
438 end
439 model_hf = r * model.y(end);
440 model = join(model, ...
441 ao(plist('type', 'fsdata', 'xvals', model_all.x, 'yvals', model_hf, 'fs', model_all.fs, 'yunits', model_all.yunits)));
442 end
443
444 end
445
446