Mercurial > hg > ltpda
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 |