dicklyon@465
|
1 function [complex_transfns_freqs, ...
|
dicklyon@465
|
2 stage_numerators, stage_denominators] = ...
|
dicklyon@465
|
3 CARFAC_Transfer_Functions(CF, freqs, to_channels, from_channels)
|
dicklyon@465
|
4 % function [complex_transfns_freqs, ...
|
dicklyon@465
|
5 % stage_z_numerators, stage_z_denominators] = ...
|
dicklyon@465
|
6 % CARFAC_Transfer_Functions(CF, freqs, to_channels, from_channels)
|
dicklyon@465
|
7 % Return transfer functions as polynomials in z (nums & denoms);
|
dicklyon@465
|
8 % And evaluate them at freqs if it's given, to selected output,
|
dicklyon@465
|
9 % optionally from selected starting points (from 0, input, by default).
|
dicklyon@465
|
10 % complex_transfns_freqs has a row of complex gains per to_channel.
|
dicklyon@465
|
11
|
dicklyon@465
|
12 % always start with the rational functions, whether we want to return
|
dicklyon@465
|
13 % them or not:
|
dicklyon@465
|
14 [stage_numerators, stage_denominators] = CARFAC_Rational_Functions(CF);
|
dicklyon@465
|
15
|
dicklyon@465
|
16 if nargin >= 2
|
dicklyon@465
|
17 % Evaluate at the provided list of frequencies.
|
dicklyon@465
|
18 if ~isrow(freqs)
|
dicklyon@465
|
19 if iscolumn(freqs)
|
dicklyon@465
|
20 freqs = freqs';
|
dicklyon@465
|
21 else
|
dicklyon@465
|
22 error('Bad freqs_row in CARFAC_Transfer_Functions');
|
dicklyon@465
|
23 end
|
dicklyon@465
|
24 end
|
dicklyon@465
|
25 if any(freqs < 0)
|
dicklyon@465
|
26 error('Negatives in freqs_row in CARFAC_Transfer_Functions');
|
dicklyon@465
|
27 end
|
dicklyon@465
|
28 z_row = exp((i * 2 * pi / CF.fs) * freqs); % z = exp(sT)
|
dicklyon@465
|
29 gains = Rational_Eval(stage_numerators, stage_denominators, z_row);
|
dicklyon@465
|
30
|
dicklyon@465
|
31 % Now multiply gains from input to output places; use logs?
|
dicklyon@465
|
32 log_gains = log(gains);
|
dicklyon@465
|
33 cum_log_gains = cumsum(log_gains);
|
dicklyon@465
|
34
|
dicklyon@465
|
35 % And figure out which cascade products we want:
|
dicklyon@465
|
36 n_ch = CF.n_ch;
|
dicklyon@465
|
37 if nargin < 3
|
dicklyon@465
|
38 to_channels = 1:n_ch;
|
dicklyon@465
|
39 end
|
dicklyon@465
|
40 if isempty(to_channels) || any(to_channels < 1 | to_channels > n_ch)
|
dicklyon@465
|
41 error('Bad to_channels in CARFAC_Transfer_Functions');
|
dicklyon@465
|
42 end
|
dicklyon@465
|
43 if nargin < 4 || isempty(from_channels)
|
dicklyon@465
|
44 from_channels = 0; % tranfuns from input, called channel 0.
|
dicklyon@465
|
45 end
|
dicklyon@465
|
46 if length(from_channels) == 1
|
dicklyon@465
|
47 from_channels = from_channels * ones(length(to_channels));
|
dicklyon@465
|
48 end
|
dicklyon@465
|
49 % Default to cum gain of 1 (log is 0), from input channel 0:
|
dicklyon@465
|
50 from_cum = zeros(length(to_channels), length(z_row));
|
dicklyon@465
|
51 not_input = from_channels > 0;
|
dicklyon@465
|
52 from_cum(not_input, :) = cum_log_gains(from_channels(not_input), :);
|
dicklyon@465
|
53 log_transfns = cum_log_gains(to_channels, :) - from_cum;
|
dicklyon@465
|
54 complex_transfns_freqs = exp(log_transfns);
|
dicklyon@465
|
55 else
|
dicklyon@465
|
56 % If no freqs are provided, do nothing but return the stage info above:
|
dicklyon@465
|
57 complex_transfns_freqs = [];
|
dicklyon@465
|
58 end
|
dicklyon@465
|
59
|
dicklyon@465
|
60
|
dicklyon@465
|
61
|
dicklyon@465
|
62 function gains = Rational_Eval(numerators, denominators, z_row)
|
dicklyon@465
|
63 % function gains = Rational_Eval(numerators, denominators, z_row)
|
dicklyon@465
|
64 % Evaluate rational function at row of z values.
|
dicklyon@465
|
65
|
dicklyon@465
|
66 zz = [z_row .* z_row; z_row; ones(size(z_row))];
|
dicklyon@465
|
67 % dot product of each poly row with each [z2; z; 1] col:
|
dicklyon@465
|
68 gains = (numerators * zz) ./ (denominators * zz);
|
dicklyon@465
|
69
|
dicklyon@465
|
70
|
dicklyon@465
|
71 function [stage_numerators, stage_denominators] = ...
|
dicklyon@465
|
72 CARFAC_Rational_Functions(CF, chans)
|
dicklyon@465
|
73 % function [stage_z_numerators, stage_z_denominators] = ...
|
dicklyon@465
|
74 % CARFAC_Rational_Functions(CF, chans)
|
dicklyon@465
|
75 % Return transfer functions of all stages as rational functions.
|
dicklyon@465
|
76
|
dicklyon@465
|
77 if nargin < 2
|
dicklyon@465
|
78 n_ch = CF.n_ch;
|
dicklyon@465
|
79 chans = 1:n_ch;
|
dicklyon@465
|
80 else
|
dicklyon@465
|
81 n_ch = length(chans);
|
dicklyon@465
|
82 end
|
dicklyon@465
|
83 coeffs = CF.filter_coeffs;
|
dicklyon@465
|
84 r = coeffs.r_coeffs(chans);
|
dicklyon@465
|
85 a = coeffs.a_coeffs(chans) .* r;
|
dicklyon@465
|
86 c = coeffs.c_coeffs(chans) .* r;
|
dicklyon@465
|
87 r2 = r .* r;
|
dicklyon@465
|
88 h = coeffs.h_coeffs(chans);
|
dicklyon@465
|
89 g = coeffs.g_coeffs(chans);
|
dicklyon@465
|
90 stage_denominators = [ones(n_ch, 1), -2 * a, r2];
|
dicklyon@465
|
91 stage_numerators = [g .* ones(n_ch, 1), g .* (-2 * a + h .* c), g .* r2];
|
dicklyon@465
|
92
|
dicklyon@465
|
93
|
dicklyon@465
|
94 %% example
|
dicklyon@465
|
95 % CF = CARFAC_Design
|
dicklyon@465
|
96 % f = (0:100).^2; % frequencies to 10 kHz, unequally spaced
|
dicklyon@465
|
97 % to_ch = 10:10:96; % selected output channels
|
dicklyon@465
|
98 % from_ch = to_ch - 10; % test the inclusion of 0 in from list
|
dicklyon@465
|
99 % tf = CARFAC_Transfer_Functions(CF, f, to_ch, from_ch);
|
dicklyon@465
|
100 % figure
|
dicklyon@465
|
101 % plot(f, 20*log(abs(tf)')/log(10)); % dB vs lin. freq for 10 taps
|
dicklyon@465
|
102
|