diff trunk/matlab/bmm/carfac/CARFAC_Design.m @ 523:2b96cb7ea4f7

Major AGC improvements mostly
author dicklyon@google.com
date Thu, 01 Mar 2012 19:49:24 +0000
parents 68c15d43fcc8
children 58d7d67bd138
line wrap: on
line diff
--- a/trunk/matlab/bmm/carfac/CARFAC_Design.m	Mon Feb 27 21:50:20 2012 +0000
+++ b/trunk/matlab/bmm/carfac/CARFAC_Design.m	Thu Mar 01 19:49:24 2012 +0000
@@ -51,14 +51,14 @@
   else
     if one_cap
       CF_IHC_params = struct( ...
-        'just_hwr', 0, ...        % not just a simple HWR
+        'just_hwr', just_hwr, ...        % not just a simple HWR
         'one_cap', one_cap, ...   % bool; 0 for new two-cap hack
         'tau_lpf', 0.000080, ...  % 80 microseconds smoothing twice
         'tau_out', 0.0005, ...    % depletion tau is pretty fast
         'tau_in', 0.010 );        % recovery tau is slower
     else
       CF_IHC_params = struct( ...
-        'just_hwr', 0, ...        % not just a simple HWR
+        'just_hwr', just_hwr, ...        % not just a simple HWR
         'one_cap', one_cap, ...   % bool; 0 for new two-cap hack
         'tau_lpf', 0.000080, ...  % 80 microseconds smoothing twice
         'tau1_out', 0.020, ...    % depletion tau is pretty fast
@@ -83,16 +83,19 @@
     'n_stages', 4, ...
     'time_constants', [1, 4, 16, 64]*0.002, ...
     'AGC_stage_gain', 2, ...  % gain from each stage to next slower stage
-    'decimation', 16, ...  % how often to update the AGC states
-    'AGC1_scales', [1, 2, 3, 4]*1, ...   % in units of channels
-    'AGC2_scales', [1, 2, 3, 4]*1.25, ... % spread more toward base
+    'decimation', [8, 2, 2, 2], ...  % how often to update the AGC states
+    'AGC1_scales', [1, 2, 4, 8]*1, ...   % in units of channels
+    'AGC2_scales', [1, 2, 4, 8]*1.5, ... % spread more toward base
     'detect_scale', 0.15, ...  % the desired damping range
-    'AGC_mix_coeff', 0.25);
+    'AGC_mix_coeff', 0.5);
 end
 
 if nargin < 2
   CF_filter_params = struct( ...
-    'velocity_scale', 0.2, ...  % for the cubic nonlinearity
+    'velocity_scale', 0.2, ...  % for the "cubic" velocity nonlinearity
+    'v_offset', 0.01, ...  % offset gives a quadratic part
+    'v2_corner', 0.2, ...  % corner for essential nonlin
+    'v_damp_max', 0.01, ... % damping delta damping from velocity nonlin
     'min_zeta', 0.12, ...
     'first_pole_theta', 0.78*pi, ...
     'zero_ratio', sqrt(2), ...
@@ -147,7 +150,11 @@
 
 % the filter design coeffs:
 
-filter_coeffs = struct('velocity_scale', filter_params.velocity_scale);
+filter_coeffs = struct('velocity_scale', filter_params.velocity_scale, ...
+  'v_offset', filter_params.v_offset, ...
+  'v2_corner', filter_params.v2_corner, ...
+  'v_damp_max', filter_params.v_damp_max ...
+  );
 
 filter_coeffs.r_coeffs = zeros(n_ch, 1);
 filter_coeffs.a_coeffs = zeros(n_ch, 1);
@@ -187,9 +194,7 @@
 %% the AGC design coeffs:
 function AGC_coeffs = CARFAC_DesignAGC(AGC_params, fs)
 
-AGC_coeffs = struct('AGC_stage_gain', AGC_params.AGC_stage_gain, ...
-  'AGC_mix_coeff', AGC_params.AGC_mix_coeff);
-
+AGC_coeffs = struct('AGC_stage_gain', AGC_params.AGC_stage_gain);
 
 % AGC1 pass is smoothing from base toward apex;
 % AGC2 pass is back, which is done first now
@@ -198,23 +203,95 @@
 
 n_AGC_stages = AGC_params.n_stages;
 AGC_coeffs.AGC_epsilon = zeros(1, n_AGC_stages);  % the 1/(tau*fs) roughly
-decim = AGC_params.decimation;
-gain = 0;
+decim = 1;
+AGC_coeffs.decimation = AGC_params.decimation;
+
+total_DC_gain = 0;
 for stage = 1:n_AGC_stages
   tau = AGC_params.time_constants(stage);
+  decim = decim * AGC_params.decimation(stage);
   % epsilon is how much new input to take at each update step:
   AGC_coeffs.AGC_epsilon(stage) = 1 - exp(-decim / (tau * fs));
   % and these are the smoothing scales and poles for decimated rate:
-  ntimes = tau * (fs / decim);  % effective number of smoothings
+  
+  n_iterations = 1;  % how many times to apply smoothing filter in a stage
+  % effective number of smoothings in a time constant:
+  ntimes = n_iterations * tau * (fs / decim);  
   % divide the spatial variance by effective number of smoothings:
-  t = (AGC1_scales(stage)^2) / ntimes;  % adjust scale for diffusion
-  AGC_coeffs.AGC1_polez(stage) = 1 + 1/t - sqrt((1+1/t)^2 - 1);
-  t = (AGC2_scales(stage)^2) / ntimes;  % adjust scale for diffusion
-  AGC_coeffs.AGC2_polez(stage) = 1 + 1/t - sqrt((1+1/t)^2 - 1);
-  gain = gain + AGC_params.AGC_stage_gain^(stage-1);
+  % t is the variance of the distribution (impulse response width squared)
+  t1 = (AGC1_scales(stage)^2) / ntimes;
+  t2 = (AGC2_scales(stage)^2) / ntimes;
+  % the back-and-forth IIR method coefficients:
+  polez1 = 1 + 1/t1 - sqrt((1+1/t1)^2 - 1);
+  polez2 = 1 + 1/t2 - sqrt((1+1/t2)^2 - 1);
+  AGC_coeffs.AGC_polez1(stage) = polez1;
+  AGC_coeffs.AGC_polez2(stage) = polez2;
+  
+  % above method has spatial "delay" near sqrt(t2) - sqrt(t1) per time,
+  % or net delay that is not independent of ntimes.  A problem???
+  
+  % try a 3-tap FIR as an alternative:
+  n_taps = 3;
+  % from geometric distribution mean and variance from wikipedia:
+  delay = polez2/(1-polez2) - polez1/(1-polez1);
+  total_delay = delay * ntimes
+  spread_sq = polez1/(1-polez1)^2 + polez2/(1-polez2)^2;
+  total_spread = sqrt(spread_sq * ntimes)
+  
+  delay1 = delay;  % keep this as 1-iteration delay reference...
+  a = (spread_sq + delay*delay - delay) / 2;
+  b = (spread_sq + delay*delay + delay) / 2;
+  AGC_spatial_FIR = [a, 1 - a - b, b];  % stored as 5 taps
+  done = AGC_spatial_FIR(2) > 0.1;  % not OK if center tap is too low
+  % if 1 iteration is not good with 3 taps go to 5 taps, then more
+  % iterations if needed, and then fall back to double-exponential IIR:
+  while ~done % smoothing condition, middle value
+    if n_taps == 3
+      % first time through, go wider but stick to 1 iteration
+      n_taps = 5;
+      n_iterations = 1;
+    else
+      % already at 5 taps, so just increase iterations
+      n_iterations = n_iterations + 1;  % number of times to apply spatial
+    end
+    ntimes = n_iterations * tau * (fs / decim);  % effective number of smoothings
+    % divide the spatial variance by effective number of smoothings:
+    % t is the variance of the distribution (impulse response width squared)
+    t1 = (AGC1_scales(stage)^2) / ntimes;
+    t2 = (AGC2_scales(stage)^2) / ntimes;
+    spread_sq = t1 + t2;
+    delay = delay1 / n_iterations;  %  maybe better than sqrt(t2) - sqrt(t1);
+    % 5-tap design duplicates the a and b coeffs; stores just 3 coeffs:
+    % a and b from their sum and diff as before: (sum \pm diff) / 2:
+    a = ((spread_sq + delay*delay)*2/5 - delay*2/3) / 2;
+    b = ((spread_sq + delay*delay)*2/5 + delay*2/3) / 2;
+    AGC_spatial_FIR = [a/2, 1 - a - b, b/2];  % implicit dup of a and b
+    done = AGC_spatial_FIR(2) > 0.1;
+  end
+  % store the resulting FIR design in coeffs:
+  AGC_coeffs.AGC_spatial_iterations(stage) = n_iterations;
+  AGC_coeffs.AGC_spatial_FIR(:,stage) = AGC_spatial_FIR;
+  AGC_coeffs.AGC_n_taps(stage) = n_taps;
+  
+  total_DC_gain = total_DC_gain + AGC_params.AGC_stage_gain^(stage-1);
+  
+  % TODO (dicklyon) -- is this what we want?
+  if stage == 1
+    AGC_coeffs.AGC_mix_coeffs(stage) = 0;
+  else
+    AGC_coeffs.AGC_mix_coeffs(stage) = AGC_params.AGC_mix_coeff / ...
+      (tau * (fs / decim));
+  end
 end
 
-AGC_coeffs.AGC_gain = gain;
+AGC_coeffs.AGC_spatial_FIR
+AGC_coeffs.AGC_spatial_iterations
+AGC_coeffs.AGC_n_taps
+AGC_coeffs.AGC_polez1
+AGC_coeffs.AGC_polez2
+
+AGC_coeffs.AGC_gain = total_DC_gain;
+
 
 %% the IHC design coeffs:
 function IHC_coeffs = CARFAC_DesignIHC(IHC_params, fs)
@@ -276,6 +353,7 @@
 % default design result, running this function with no args, should look
 % like this, before CARFAC_Init puts state storage into it:
 %
+%
 % CF = CARFAC_Design
 % CF.filter_params
 % CF.AGC_params
@@ -296,6 +374,9 @@
 %            n_mics: 0
 % ans =
 %       velocity_scale: 0.2000
+%             v_offset: 0.0100
+%            v2_corner: 0.2000
+%           v_damp_max: 0.0100
 %             min_zeta: 0.1200
 %     first_pole_theta: 2.4504
 %           zero_ratio: 1.4142
@@ -305,27 +386,33 @@
 %           n_stages: 4
 %     time_constants: [0.0020 0.0080 0.0320 0.1280]
 %     AGC_stage_gain: 2
-%         decimation: 16
-%        AGC1_scales: [1 2 3 4]
-%        AGC2_scales: [1.2500 2.5000 3.7500 5]
+%         decimation: [8 2 2 2]
+%        AGC1_scales: [1 2 4 8]
+%        AGC2_scales: [1.5000 3 6 12]
 %       detect_scale: 0.1500
-%      AGC_mix_coeff: 0.2500
+%      AGC_mix_coeff: 0.3500
 % ans =
 %     velocity_scale: 0.2000
+%           v_offset: 0.0100
+%          v2_corner: 0.2000
+%         v_damp_max: 0.0100
 %           r_coeffs: [96x1 double]
 %           a_coeffs: [96x1 double]
 %           c_coeffs: [96x1 double]
 %           h_coeffs: [96x1 double]
 %           g_coeffs: [96x1 double]
 % ans =
-%     AGC_stage_gain: 2
-%      AGC_mix_coeff: 0.2500
-%        AGC_epsilon: [0.3043 0.0867 0.0224 0.0057]
-%         AGC1_polez: [0.1356 0.1356 0.0854 0.0417]
-%         AGC2_polez: [0.1872 0.1872 0.1227 0.0623]
-%           AGC_gain: 15
-%       detect_scale: 0.0630
+%             AGC_stage_gain: 2
+%                AGC_epsilon: [0.1659 0.0867 0.0443 0.0224]
+%                 decimation: [8 2 2 2]
+%     AGC_spatial_iterations: [1 1 2 3]
+%            AGC_spatial_FIR: [3x4 double]
+%                 AGC_n_taps: [3 5 5 5]
+%             AGC_mix_coeffs: [0 0.0317 0.0159 0.0079]
+%                   AGC_gain: 15
+%               detect_scale: 0.0664
 % ans =
+%              just_hwr: 0
 %             lpf_coeff: 0.4327
 %             out1_rate: 0.0023
 %              in1_rate: 0.0023
@@ -336,7 +423,7 @@
 %              rest_cap: 0
 %             rest_cap1: 0.9635
 %             rest_cap2: 0.9269
-%     saturation_output: 0.1587
+%     saturation_output: 0.1507