view trunk/carfac/agc_coeffs.cc @ 672:a9694d0bb55a

Fix scons build of library and test. Deleted main.cc since libgtest comes with a corresponding main library. Everything compiles and the tests run on Ubuntu, but the tests all fail.
author ronw@google.com
date Thu, 23 May 2013 18:12:22 +0000
parents 443b522fb593
children
line wrap: on
line source
//
//  agc_coeffs.cc
//  CARFAC Open Source C++ Library
//
//  Created by Alex Brandmeyer on 5/18/13.
//
// This C++ file is part of an implementation of Lyon's cochlear model:
// "Cascade of Asymmetric Resonators with Fast-Acting Compression"
// to supplement Lyon's upcoming book "Human and Machine Hearing"
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <assert.h>

#include "agc_coeffs.h"

void AGCCoeffs::Design(const AGCParams& agc_params, const int stage,
                       const FPType fs, const FPType previous_stage_gain,
                       FPType decim) {
  n_agc_stages_ = agc_params.n_stages_;
  agc_stage_gain_ = agc_params.agc_stage_gain_;
  std::vector<FPType> agc1_scales = agc_params.agc1_scales_;
  std::vector<FPType> agc2_scales = agc_params.agc2_scales_;
  std::vector<FPType> time_constants = agc_params.time_constants_;
  FPType mix_coeff = agc_params.agc_mix_coeff_;
  decimation_ = agc_params.decimation_[stage];
  FPType total_dc_gain = previous_stage_gain;
  // Here we calculate the parameters for the current stage.
  FPType tau = time_constants[stage];
  decim_ = decim;
  decim_ *= decimation_;
  agc_epsilon_ = 1 - exp((-1 * decim_) / (tau * fs));
  FPType n_times = tau * (fs / decim_);
  FPType delay = (agc2_scales[stage] - agc1_scales[stage]) / n_times;
  FPType spread_sq = (pow(agc1_scales[stage], 2) +
                      pow(agc2_scales[stage], 2)) / n_times;
  FPType u = 1 + (1 / spread_sq);
  FPType p = u - sqrt(pow(u, 2) - 1);
  FPType dp = delay * (1 - (2 * p) + (p * p)) / 2;
  agc_pole_z1_ = p - dp;
  agc_pole_z2_ = p + dp;
  int n_taps = 0;
  bool fir_ok = false;
  int n_iterations = 1;
  // This section initializes the FIR coeffs settings at each stage.
  agc_spatial_fir_.resize(3);
  std::vector<FPType> fir(3);
  while (! fir_ok) {
    switch (n_taps) {
      case 0:
        n_taps = 3;
        break;
      case 3:
        n_taps = 5;
        break;
      case 5:
        n_iterations++;
        assert(n_iterations < 16 &&
               "Too many iterations needed in AGC spatial smoothing.");
        break;
      default:
        assert(true && "Bad n_taps; should be 3 or 5.");
        break;
    }
    // The smoothing function is a space-domain smoothing, but it considered
    // here by analogy to time-domain smoothing, which is why its potential
    // off-centeredness is called a delay.  Since it's a smoothing filter, it
    // is also analogous to a discrete probability distribution (a p.m.f.),
    // with mean corresponding to delay and variance corresponding to squared
    // spatial spread (in samples, or channels, and the square thereof,
    // respecitively). Here we design a filter implementation's coefficient
    // via the method of moment matching, so we get the intended delay and
    // spread, and don't worry too much about the shape of the distribution,
    // which will be some kind of blob not too far from Gaussian if we run
    // several FIR iterations.
    FPType delay_variance = spread_sq / n_iterations;
    FPType mean_delay = delay / n_iterations;
    FPType a, b;
    switch (n_taps) {
      case 3:
        a = (delay_variance + (mean_delay*mean_delay) - mean_delay) / 2.0;
        b = (delay_variance + (mean_delay*mean_delay) + mean_delay) / 2.0;
        fir[0] = a;
        fir[1] = 1 - a - b;
        fir[2] = b;
        fir_ok = fir[1] >= 0.2 ? true : false;
        break;
      case 5:
        a = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) -
             (mean_delay * 2.0/3.0)) / 2.0;
        b = (((delay_variance + (mean_delay*mean_delay)) * 2.0/5.0) +
             (mean_delay * 2.0/3.0)) / 2.0;
        fir[0] = a / 2.0;
        fir[1] = 1 - a - b;
        fir[2] = b / 2.0;
        fir_ok = fir[1] >= 0.1 ? true : false;
        break;
      default:
        assert(true && "Bad n_taps; should be 3 or 5.");
        break;
    }
  }
  // Once we have the FIR design for this stage we can assign it to the
  // appropriate data members.
  agc_spatial_iterations_ = n_iterations;
  agc_spatial_n_taps_ = n_taps;
  agc_spatial_fir_[0] = fir[0];
  agc_spatial_fir_[1] = fir[1];
  agc_spatial_fir_[2] = fir[2];
  total_dc_gain += pow(agc_stage_gain_, stage);
  agc_mix_coeffs_ = stage == 0 ? 0 : mix_coeff / (tau * (fs /decim_));
  agc_gain_ = total_dc_gain;
  detect_scale_ = 1 / total_dc_gain;
}