annotate carfac/carfac.cc @ 641:fe8ac95fcf9e

Delete CARFAC::Run to tighten up the interface.
author ronw@google.com
date Wed, 29 May 2013 20:33:06 +0000
parents d08c02c8e26f
children 8b70f4cf00c7
rev   line source
alexbrandmeyer@609 1 //
alexbrandmeyer@609 2 // carfac.cc
alexbrandmeyer@609 3 // CARFAC Open Source C++ Library
alexbrandmeyer@609 4 //
alexbrandmeyer@609 5 // Created by Alex Brandmeyer on 5/10/13.
alexbrandmeyer@609 6 //
alexbrandmeyer@609 7 // This C++ file is part of an implementation of Lyon's cochlear model:
alexbrandmeyer@609 8 // "Cascade of Asymmetric Resonators with Fast-Acting Compression"
alexbrandmeyer@609 9 // to supplement Lyon's upcoming book "Human and Machine Hearing"
alexbrandmeyer@609 10 //
alexbrandmeyer@609 11 // Licensed under the Apache License, Version 2.0 (the "License");
alexbrandmeyer@609 12 // you may not use this file except in compliance with the License.
alexbrandmeyer@609 13 // You may obtain a copy of the License at
alexbrandmeyer@609 14 //
alexbrandmeyer@609 15 // http://www.apache.org/licenses/LICENSE-2.0
alexbrandmeyer@609 16 //
alexbrandmeyer@609 17 // Unless required by applicable law or agreed to in writing, software
alexbrandmeyer@609 18 // distributed under the License is distributed on an "AS IS" BASIS,
alexbrandmeyer@609 19 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
alexbrandmeyer@609 20 // See the License for the specific language governing permissions and
alexbrandmeyer@609 21 // limitations under the License.
alexbrandmeyer@640 22
alexbrandmeyer@636 23 #include <assert.h>
alexbrandmeyer@609 24 #include "carfac.h"
alexbrandmeyer@636 25 using std::vector;
ronw@625 26
alexbrandmeyer@626 27 void CARFAC::Design(const int n_ears, const FPType fs,
alexbrandmeyer@626 28 const CARParams& car_params, const IHCParams& ihc_params,
alexbrandmeyer@626 29 const AGCParams& agc_params) {
alexbrandmeyer@609 30 n_ears_ = n_ears;
alexbrandmeyer@609 31 fs_ = fs;
alexbrandmeyer@611 32 ears_.resize(n_ears_);
alexbrandmeyer@610 33 n_ch_ = 0;
alexbrandmeyer@640 34 FPType pole_hz = car_params.first_pole_theta * fs / (2 * kPi);
alexbrandmeyer@640 35 while (pole_hz > car_params.min_pole_hz) {
alexbrandmeyer@626 36 ++n_ch_;
alexbrandmeyer@640 37 pole_hz = pole_hz - car_params.erb_per_step *
alexbrandmeyer@640 38 ERBHz(pole_hz, car_params.erb_break_freq, car_params.erb_q);
alexbrandmeyer@609 39 }
alexbrandmeyer@626 40 pole_freqs_.resize(n_ch_);
alexbrandmeyer@640 41 pole_hz = car_params.first_pole_theta * fs / (2 * kPi);
alexbrandmeyer@626 42 for (int ch = 0; ch < n_ch_; ++ch) {
alexbrandmeyer@626 43 pole_freqs_(ch) = pole_hz;
alexbrandmeyer@640 44 pole_hz = pole_hz - car_params.erb_per_step *
alexbrandmeyer@640 45 ERBHz(pole_hz, car_params.erb_break_freq, car_params.erb_q);
alexbrandmeyer@610 46 }
alexbrandmeyer@626 47 max_channels_per_octave_ = log(2) / log(pole_freqs_(0) / pole_freqs_(1));
alexbrandmeyer@636 48 CARCoeffs car_coeffs;
alexbrandmeyer@636 49 IHCCoeffs ihc_coeffs;
alexbrandmeyer@636 50 std::vector<AGCCoeffs> agc_coeffs;
alexbrandmeyer@636 51 DesignCARCoeffs(car_params, fs, pole_freqs_, &car_coeffs);
alexbrandmeyer@636 52 DesignIHCCoeffs(ihc_params, fs, &ihc_coeffs);
alexbrandmeyer@636 53 // This code initializes the coefficients for each of the AGC stages.
alexbrandmeyer@636 54 DesignAGCCoeffs(agc_params, fs, &agc_coeffs);
alexbrandmeyer@636 55 // Once we have the coefficient structure we can design the ears.
alexbrandmeyer@626 56 for (auto& ear : ears_) {
alexbrandmeyer@637 57 ear.InitEar(n_ch_, fs_, car_coeffs, ihc_coeffs,
alexbrandmeyer@636 58 agc_coeffs);
alexbrandmeyer@610 59 }
alexbrandmeyer@609 60 }
alexbrandmeyer@609 61
alexbrandmeyer@636 62 void CARFAC::RunSegment(const vector<vector<float>>& sound_data,
alexbrandmeyer@626 63 const int32_t start, const int32_t length,
alexbrandmeyer@636 64 const bool open_loop, CARFACOutput* seg_output) {
alexbrandmeyer@610 65 // A nested loop structure is used to iterate through the individual samples
alexbrandmeyer@610 66 // for each ear (audio channel).
alexbrandmeyer@626 67 bool updated; // This variable is used by the AGC stage.
alexbrandmeyer@636 68 for (int32_t i = 0; i < length; ++i) {
alexbrandmeyer@636 69 for (int j = 0; j < n_ears_; ++j) {
alexbrandmeyer@626 70 // First we create a reference to the current Ear object.
alexbrandmeyer@626 71 Ear& ear = ears_[j];
alexbrandmeyer@610 72 // This stores the audio sample currently being processed.
alexbrandmeyer@636 73 FPType input = sound_data[j][start+i];
alexbrandmeyer@610 74 // Now we apply the three stages of the model in sequence to the current
alexbrandmeyer@610 75 // audio sample.
alexbrandmeyer@637 76 ear.CARStep(input);
alexbrandmeyer@637 77 ear.IHCStep(ear.car_out());
alexbrandmeyer@637 78 updated = ear.AGCStep(ear.ihc_out());
alexbrandmeyer@610 79 }
alexbrandmeyer@637 80 seg_output->StoreOutput(ears_);
alexbrandmeyer@636 81 if (updated) {
alexbrandmeyer@636 82 if (n_ears_ > 1) {
alexbrandmeyer@636 83 CrossCouple();
alexbrandmeyer@636 84 }
alexbrandmeyer@636 85 if (! open_loop) {
alexbrandmeyer@636 86 CloseAGCLoop();
alexbrandmeyer@636 87 }
alexbrandmeyer@626 88 }
alexbrandmeyer@610 89 }
alexbrandmeyer@610 90 }
alexbrandmeyer@610 91
alexbrandmeyer@610 92 void CARFAC::CrossCouple() {
alexbrandmeyer@626 93 for (int stage = 0; stage < ears_[0].agc_nstages(); ++stage) {
alexbrandmeyer@626 94 if (ears_[0].agc_decim_phase(stage) > 0) {
alexbrandmeyer@610 95 break;
alexbrandmeyer@610 96 } else {
alexbrandmeyer@626 97 FPType mix_coeff = ears_[0].agc_mix_coeff(stage);
alexbrandmeyer@610 98 if (mix_coeff > 0) {
alexbrandmeyer@610 99 FloatArray stage_state;
alexbrandmeyer@610 100 FloatArray this_stage_values = FloatArray::Zero(n_ch_);
alexbrandmeyer@626 101 for (auto& ear : ears_) {
alexbrandmeyer@626 102 stage_state = ear.agc_memory(stage);
alexbrandmeyer@610 103 this_stage_values += stage_state;
alexbrandmeyer@610 104 }
alexbrandmeyer@610 105 this_stage_values /= n_ears_;
alexbrandmeyer@626 106 for (auto& ear : ears_) {
alexbrandmeyer@626 107 stage_state = ear.agc_memory(stage);
alexbrandmeyer@626 108 ear.set_agc_memory(stage, stage_state + mix_coeff *
alexbrandmeyer@626 109 (this_stage_values - stage_state));
alexbrandmeyer@610 110 }
alexbrandmeyer@610 111 }
alexbrandmeyer@609 112 }
alexbrandmeyer@609 113 }
alexbrandmeyer@609 114 }
alexbrandmeyer@610 115
alexbrandmeyer@610 116 void CARFAC::CloseAGCLoop() {
alexbrandmeyer@626 117 for (auto& ear: ears_) {
alexbrandmeyer@626 118 FloatArray undamping = 1 - ear.agc_memory(0);
alexbrandmeyer@610 119 // This updates the target stage gain for the new damping.
alexbrandmeyer@626 120 ear.set_dzb_memory((ear.zr_coeffs() * undamping - ear.zb_memory()) /
alexbrandmeyer@626 121 ear.agc_decimation(0));
alexbrandmeyer@626 122 ear.set_dg_memory((ear.StageGValue(undamping) - ear.g_memory()) /
alexbrandmeyer@626 123 ear.agc_decimation(0));
alexbrandmeyer@610 124 }
alexbrandmeyer@636 125 }
alexbrandmeyer@636 126
alexbrandmeyer@636 127 void CARFAC::DesignCARCoeffs(const CARParams& car_params, const FPType fs,
alexbrandmeyer@636 128 const FloatArray& pole_freqs,
alexbrandmeyer@636 129 CARCoeffs* car_coeffs) {
alexbrandmeyer@636 130 n_ch_ = pole_freqs.size();
alexbrandmeyer@640 131 car_coeffs->velocity_scale = car_params.velocity_scale;
alexbrandmeyer@640 132 car_coeffs->v_offset = car_params.v_offset;
alexbrandmeyer@640 133 car_coeffs->r1_coeffs.resize(n_ch_);
alexbrandmeyer@640 134 car_coeffs->a0_coeffs.resize(n_ch_);
alexbrandmeyer@640 135 car_coeffs->c0_coeffs.resize(n_ch_);
alexbrandmeyer@640 136 car_coeffs->h_coeffs.resize(n_ch_);
alexbrandmeyer@640 137 car_coeffs->g0_coeffs.resize(n_ch_);
alexbrandmeyer@640 138 FPType f = car_params.zero_ratio * car_params.zero_ratio - 1.0;
alexbrandmeyer@637 139 FloatArray theta = pole_freqs * ((2.0 * kPi) / fs);
alexbrandmeyer@640 140 car_coeffs->c0_coeffs = theta.sin();
alexbrandmeyer@640 141 car_coeffs->a0_coeffs = theta.cos();
alexbrandmeyer@640 142 FPType ff = car_params.high_f_damping_compression;
alexbrandmeyer@637 143 FloatArray x = theta / kPi;
alexbrandmeyer@640 144 car_coeffs->zr_coeffs = kPi * (x - (ff * (x*x*x)));
alexbrandmeyer@640 145 FPType max_zeta = car_params.max_zeta;
alexbrandmeyer@640 146 FPType min_zeta = car_params.min_zeta;
alexbrandmeyer@640 147 car_coeffs->r1_coeffs = (1.0 - (car_coeffs->zr_coeffs * max_zeta));
alexbrandmeyer@636 148 FloatArray erb_freqs(n_ch_);
alexbrandmeyer@636 149 for (int ch=0; ch < n_ch_; ++ch) {
alexbrandmeyer@640 150 erb_freqs(ch) = ERBHz(pole_freqs(ch), car_params.erb_break_freq,
alexbrandmeyer@640 151 car_params.erb_q);
alexbrandmeyer@636 152 }
alexbrandmeyer@636 153 FloatArray min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) -
alexbrandmeyer@636 154 min_zeta));
alexbrandmeyer@640 155 car_coeffs->zr_coeffs *= max_zeta - min_zetas;
alexbrandmeyer@640 156 car_coeffs->h_coeffs = car_coeffs->c0_coeffs * f;
alexbrandmeyer@636 157 FloatArray relative_undamping = FloatArray::Ones(n_ch_);
alexbrandmeyer@640 158 FloatArray r = car_coeffs->r1_coeffs + (car_coeffs->zr_coeffs *
alexbrandmeyer@636 159 relative_undamping);
alexbrandmeyer@640 160 car_coeffs->g0_coeffs = (1.0 - (2.0 * r * car_coeffs->a0_coeffs) + (r*r)) /
alexbrandmeyer@640 161 (1 - (2 * r * car_coeffs->a0_coeffs) +
alexbrandmeyer@640 162 (car_coeffs->h_coeffs * r * car_coeffs->c0_coeffs) + (r*r));
alexbrandmeyer@636 163 }
alexbrandmeyer@636 164
alexbrandmeyer@636 165 void CARFAC::DesignIHCCoeffs(const IHCParams& ihc_params, const FPType fs,
alexbrandmeyer@636 166 IHCCoeffs* ihc_coeffs) {
alexbrandmeyer@640 167 if (ihc_params.just_half_wave_rectify) {
alexbrandmeyer@640 168 ihc_coeffs->just_half_wave_rectify = ihc_params.just_half_wave_rectify;
alexbrandmeyer@636 169 } else {
alexbrandmeyer@636 170 // This section calculates conductance values using two pre-defined scalars.
alexbrandmeyer@636 171 FloatArray x(1);
alexbrandmeyer@636 172 FPType conduct_at_10, conduct_at_0;
alexbrandmeyer@636 173 x(0) = 10.0;
alexbrandmeyer@636 174 x = CARFACDetect(x);
alexbrandmeyer@636 175 conduct_at_10 = x(0);
alexbrandmeyer@636 176 x(0) = 0.0;
alexbrandmeyer@636 177 x = CARFACDetect(x);
alexbrandmeyer@636 178 conduct_at_0 = x(0);
alexbrandmeyer@640 179 if (ihc_params.one_capacitor) {
alexbrandmeyer@636 180 FPType ro = 1 / conduct_at_10 ;
alexbrandmeyer@640 181 FPType c = ihc_params.tau1_out / ro;
alexbrandmeyer@640 182 FPType ri = ihc_params.tau1_in / c;
alexbrandmeyer@636 183 FPType saturation_output = 1 / ((2 * ro) + ri);
alexbrandmeyer@636 184 FPType r0 = 1 / conduct_at_0;
alexbrandmeyer@636 185 FPType current = 1 / (ri + r0);
alexbrandmeyer@640 186 ihc_coeffs->cap1_voltage = 1 - (current * ri);
alexbrandmeyer@640 187 ihc_coeffs->just_half_wave_rectify = false;
alexbrandmeyer@640 188 ihc_coeffs->lpf_coeff = 1 - exp( -1 / (ihc_params.tau_lpf * fs));
alexbrandmeyer@640 189 ihc_coeffs->out1_rate = ro / (ihc_params.tau1_out * fs);
alexbrandmeyer@640 190 ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * fs);
alexbrandmeyer@640 191 ihc_coeffs->one_capacitor = ihc_params.one_capacitor;
alexbrandmeyer@640 192 ihc_coeffs->output_gain = 1 / (saturation_output - current);
alexbrandmeyer@640 193 ihc_coeffs->rest_output = current / (saturation_output - current);
alexbrandmeyer@640 194 ihc_coeffs->rest_cap1 = ihc_coeffs->cap1_voltage;
alexbrandmeyer@636 195 } else {
alexbrandmeyer@636 196 FPType ro = 1 / conduct_at_10;
alexbrandmeyer@640 197 FPType c2 = ihc_params.tau2_out / ro;
alexbrandmeyer@640 198 FPType r2 = ihc_params.tau2_in / c2;
alexbrandmeyer@640 199 FPType c1 = ihc_params.tau1_out / r2;
alexbrandmeyer@640 200 FPType r1 = ihc_params.tau1_in / c1;
alexbrandmeyer@636 201 FPType saturation_output = 1 / (2 * ro + r2 + r1);
alexbrandmeyer@636 202 FPType r0 = 1 / conduct_at_0;
alexbrandmeyer@636 203 FPType current = 1 / (r1 + r2 + r0);
alexbrandmeyer@640 204 ihc_coeffs->cap1_voltage = 1 - (current * r1);
alexbrandmeyer@640 205 ihc_coeffs->cap2_voltage = ihc_coeffs->cap1_voltage - (current * r2);
alexbrandmeyer@640 206 ihc_coeffs->just_half_wave_rectify = false;
alexbrandmeyer@640 207 ihc_coeffs->lpf_coeff = 1 - exp(-1 / (ihc_params.tau_lpf * fs));
alexbrandmeyer@640 208 ihc_coeffs->out1_rate = 1 / (ihc_params.tau1_out * fs);
alexbrandmeyer@640 209 ihc_coeffs->in1_rate = 1 / (ihc_params.tau1_in * fs);
alexbrandmeyer@640 210 ihc_coeffs->out2_rate = ro / (ihc_params.tau2_out * fs);
alexbrandmeyer@640 211 ihc_coeffs->in2_rate = 1 / (ihc_params.tau2_in * fs);
alexbrandmeyer@640 212 ihc_coeffs->one_capacitor = ihc_params.one_capacitor;
alexbrandmeyer@640 213 ihc_coeffs->output_gain = 1 / (saturation_output - current);
alexbrandmeyer@640 214 ihc_coeffs->rest_output = current / (saturation_output - current);
alexbrandmeyer@640 215 ihc_coeffs->rest_cap1 = ihc_coeffs->cap1_voltage;
alexbrandmeyer@640 216 ihc_coeffs->rest_cap2 = ihc_coeffs->cap2_voltage;
alexbrandmeyer@636 217 }
alexbrandmeyer@636 218 }
alexbrandmeyer@640 219 ihc_coeffs->ac_coeff = 2 * kPi * ihc_params.ac_corner_hz / fs;
alexbrandmeyer@636 220 }
alexbrandmeyer@636 221
alexbrandmeyer@636 222 void CARFAC::DesignAGCCoeffs(const AGCParams& agc_params, const FPType fs,
alexbrandmeyer@636 223 vector<AGCCoeffs>* agc_coeffs) {
alexbrandmeyer@640 224 agc_coeffs->resize(agc_params.n_stages);
alexbrandmeyer@636 225 FPType previous_stage_gain = 0.0;
alexbrandmeyer@636 226 FPType decim = 1.0;
alexbrandmeyer@640 227 for (int stage = 0; stage < agc_params.n_stages; ++stage) {
alexbrandmeyer@636 228 AGCCoeffs& agc_coeff = agc_coeffs->at(stage);
alexbrandmeyer@640 229 agc_coeff.n_agc_stages = agc_params.n_stages;
alexbrandmeyer@640 230 agc_coeff.agc_stage_gain = agc_params.agc_stage_gain;
alexbrandmeyer@640 231 vector<FPType> agc1_scales = agc_params.agc1_scales;
alexbrandmeyer@640 232 vector<FPType> agc2_scales = agc_params.agc2_scales;
alexbrandmeyer@640 233 vector<FPType> time_constants = agc_params.time_constants;
alexbrandmeyer@640 234 FPType mix_coeff = agc_params.agc_mix_coeff;
alexbrandmeyer@640 235 agc_coeff.decimation = agc_params.decimation[stage];
alexbrandmeyer@636 236 FPType total_dc_gain = previous_stage_gain;
alexbrandmeyer@636 237 // Here we calculate the parameters for the current stage.
alexbrandmeyer@636 238 FPType tau = time_constants[stage];
alexbrandmeyer@640 239 agc_coeff.decim = decim;
alexbrandmeyer@640 240 agc_coeff.decim *= agc_coeff.decimation;
alexbrandmeyer@640 241 agc_coeff.agc_epsilon = 1 - exp((-1 * agc_coeff.decim) / (tau * fs));
alexbrandmeyer@640 242 FPType n_times = tau * (fs / agc_coeff.decim);
alexbrandmeyer@636 243 FPType delay = (agc2_scales[stage] - agc1_scales[stage]) / n_times;
alexbrandmeyer@636 244 FPType spread_sq = (pow(agc1_scales[stage], 2) +
alexbrandmeyer@636 245 pow(agc2_scales[stage], 2)) / n_times;
alexbrandmeyer@636 246 FPType u = 1 + (1 / spread_sq);
alexbrandmeyer@636 247 FPType p = u - sqrt(pow(u, 2) - 1);
alexbrandmeyer@636 248 FPType dp = delay * (1 - (2 * p) + (p*p)) / 2;
alexbrandmeyer@640 249 agc_coeff.agc_pole_z1 = p - dp;
alexbrandmeyer@640 250 agc_coeff.agc_pole_z2 = p + dp;
alexbrandmeyer@636 251 int n_taps = 0;
alexbrandmeyer@636 252 bool fir_ok = false;
alexbrandmeyer@636 253 int n_iterations = 1;
alexbrandmeyer@636 254 // This section initializes the FIR coeffs settings at each stage.
alexbrandmeyer@636 255 FPType fir_left, fir_mid, fir_right;
alexbrandmeyer@636 256 while (! fir_ok) {
alexbrandmeyer@636 257 switch (n_taps) {
alexbrandmeyer@636 258 case 0:
alexbrandmeyer@636 259 n_taps = 3;
alexbrandmeyer@636 260 break;
alexbrandmeyer@636 261 case 3:
alexbrandmeyer@636 262 n_taps = 5;
alexbrandmeyer@636 263 break;
alexbrandmeyer@636 264 case 5:
alexbrandmeyer@636 265 n_iterations++;
alexbrandmeyer@636 266 assert(n_iterations < 16 &&
alexbrandmeyer@636 267 "Too many iterations needed in AGC spatial smoothing.");
alexbrandmeyer@636 268 break;
alexbrandmeyer@636 269 default:
alexbrandmeyer@636 270 assert(true && "Bad n_taps; should be 3 or 5.");
alexbrandmeyer@636 271 break;
alexbrandmeyer@636 272 }
alexbrandmeyer@636 273 // The smoothing function is a space-domain smoothing, but it considered
alexbrandmeyer@636 274 // here by analogy to time-domain smoothing, which is why its potential
alexbrandmeyer@636 275 // off-centeredness is called a delay. Since it's a smoothing filter, it
alexbrandmeyer@636 276 // is also analogous to a discrete probability distribution (a p.m.f.),
alexbrandmeyer@636 277 // with mean corresponding to delay and variance corresponding to squared
alexbrandmeyer@636 278 // spatial spread (in samples, or channels, and the square thereof,
alexbrandmeyer@636 279 // respecitively). Here we design a filter implementation's coefficient
alexbrandmeyer@636 280 // via the method of moment matching, so we get the intended delay and
alexbrandmeyer@636 281 // spread, and don't worry too much about the shape of the distribution,
alexbrandmeyer@636 282 // which will be some kind of blob not too far from Gaussian if we run
alexbrandmeyer@636 283 // several FIR iterations.
alexbrandmeyer@636 284 FPType delay_variance = spread_sq / n_iterations;
alexbrandmeyer@636 285 FPType mean_delay = delay / n_iterations;
alexbrandmeyer@636 286 FPType a, b;
alexbrandmeyer@636 287 switch (n_taps) {
alexbrandmeyer@636 288 case 3:
alexbrandmeyer@636 289 a = (delay_variance + (mean_delay*mean_delay) - mean_delay) / 2.0;
alexbrandmeyer@636 290 b = (delay_variance + (mean_delay*mean_delay) + mean_delay) / 2.0;
alexbrandmeyer@636 291 fir_left = a;
alexbrandmeyer@636 292 fir_mid = 1 - a - b;
alexbrandmeyer@636 293 fir_right = b;
alexbrandmeyer@636 294 fir_ok = fir_mid >= 0.2 ? true : false;
alexbrandmeyer@636 295 break;
alexbrandmeyer@636 296 case 5:
alexbrandmeyer@636 297 a = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) -
alexbrandmeyer@636 298 (mean_delay * 2.0/3.0)) / 2.0;
alexbrandmeyer@636 299 b = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) +
alexbrandmeyer@636 300 (mean_delay * 2.0/3.0)) / 2.0;
alexbrandmeyer@636 301 fir_left = a / 2.0;
alexbrandmeyer@636 302 fir_mid = 1 - a - b;
alexbrandmeyer@636 303 fir_right = b / 2.0;
alexbrandmeyer@636 304 fir_ok = fir_mid >= 0.1 ? true : false;
alexbrandmeyer@636 305 break;
alexbrandmeyer@636 306 default:
alexbrandmeyer@636 307 assert(true && "Bad n_taps; should be 3 or 5.");
alexbrandmeyer@636 308 break;
alexbrandmeyer@636 309 }
alexbrandmeyer@636 310 }
alexbrandmeyer@636 311 // Once we have the FIR design for this stage we can assign it to the
alexbrandmeyer@636 312 // appropriate data members.
alexbrandmeyer@640 313 agc_coeff.agc_spatial_iterations = n_iterations;
alexbrandmeyer@640 314 agc_coeff.agc_spatial_n_taps = n_taps;
alexbrandmeyer@640 315 agc_coeff.agc_spatial_fir_left = fir_left;
alexbrandmeyer@640 316 agc_coeff.agc_spatial_fir_mid = fir_mid;
alexbrandmeyer@640 317 agc_coeff.agc_spatial_fir_right = fir_right;
alexbrandmeyer@640 318 total_dc_gain += pow(agc_coeff.agc_stage_gain, stage);
alexbrandmeyer@640 319 agc_coeff.agc_mix_coeffs = stage == 0 ? 0 : mix_coeff /
alexbrandmeyer@640 320 (tau * (fs / agc_coeff.decim));
alexbrandmeyer@640 321 agc_coeff.agc_gain = total_dc_gain;
alexbrandmeyer@640 322 agc_coeff.detect_scale = 1 / total_dc_gain;
alexbrandmeyer@640 323 previous_stage_gain = agc_coeff.agc_gain;
alexbrandmeyer@640 324 decim = agc_coeff.decim;
alexbrandmeyer@636 325 }
ronw@641 326 }