alexbrandmeyer@609
|
1 //
|
alexbrandmeyer@609
|
2 // ear.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@609
|
22
|
alexbrandmeyer@609
|
23 #include "ear.h"
|
alexbrandmeyer@609
|
24
|
alexbrandmeyer@610
|
25 // The 'InitEar' function takes a set of model parameters and initializes the
|
alexbrandmeyer@610
|
26 // design coefficients and model state variables needed for running the model
|
alexbrandmeyer@610
|
27 // on a single audio channel.
|
alexbrandmeyer@610
|
28 void Ear::InitEar(int n_ch, long fs, FloatArray pole_freqs,
|
alexbrandmeyer@610
|
29 CARParams car_p, IHCParams ihc_p, AGCParams agc_p) {
|
alexbrandmeyer@610
|
30 // The first section of code determines the number of channels that will be
|
alexbrandmeyer@610
|
31 // used in the model on the basis of the sample rate and the CAR parameters
|
alexbrandmeyer@610
|
32 // that have been passed to this function.
|
alexbrandmeyer@610
|
33 n_ch_ = n_ch;
|
alexbrandmeyer@610
|
34 // These functions use the parameters that have been passed to design the
|
alexbrandmeyer@610
|
35 // coefficients for the three stages of the model.
|
alexbrandmeyer@610
|
36 DesignFilters(car_p, fs, pole_freqs);
|
alexbrandmeyer@610
|
37 DesignIHC(ihc_p, fs);
|
alexbrandmeyer@610
|
38 DesignAGC(agc_p, fs);
|
alexbrandmeyer@610
|
39 // Once the coefficients have been determined, we can initialize the state
|
alexbrandmeyer@610
|
40 // variables that will be used during runtime.
|
alexbrandmeyer@610
|
41 InitCARState();
|
alexbrandmeyer@610
|
42 InitIHCState();
|
alexbrandmeyer@610
|
43 InitAGCState();
|
alexbrandmeyer@609
|
44 }
|
alexbrandmeyer@609
|
45
|
alexbrandmeyer@610
|
46 void Ear::DesignFilters(CARParams car_params, long fs,
|
alexbrandmeyer@610
|
47 FloatArray pole_freqs) {
|
alexbrandmeyer@610
|
48 car_coeffs_.velocity_scale_ = car_params.velocity_scale_;
|
alexbrandmeyer@610
|
49 car_coeffs_.v_offset_ = car_params.v_offset_;
|
alexbrandmeyer@610
|
50 car_coeffs_.r1_coeffs_.resize(n_ch_);
|
alexbrandmeyer@610
|
51 car_coeffs_.a0_coeffs_.resize(n_ch_);
|
alexbrandmeyer@610
|
52 car_coeffs_.c0_coeffs_.resize(n_ch_);
|
alexbrandmeyer@610
|
53 car_coeffs_.h_coeffs_.resize(n_ch_);
|
alexbrandmeyer@610
|
54 car_coeffs_.g0_coeffs_.resize(n_ch_);
|
alexbrandmeyer@610
|
55 FPType f = car_params.zero_ratio_ * car_params.zero_ratio_ - 1;
|
alexbrandmeyer@610
|
56 FloatArray theta = pole_freqs * ((2 * PI) / fs);
|
alexbrandmeyer@610
|
57 car_coeffs_.c0_coeffs_ = sin(theta);
|
alexbrandmeyer@610
|
58 car_coeffs_.a0_coeffs_ = cos(theta);
|
alexbrandmeyer@610
|
59 FPType ff = car_params.high_f_damping_compression_;
|
alexbrandmeyer@610
|
60 FloatArray x = theta / PI;
|
alexbrandmeyer@610
|
61 car_coeffs_.zr_coeffs_ = PI * (x - (ff * (x * x * x)));
|
alexbrandmeyer@610
|
62 FPType max_zeta = car_params.max_zeta_;
|
alexbrandmeyer@610
|
63 FPType min_zeta = car_params.min_zeta_;
|
alexbrandmeyer@610
|
64 car_coeffs_.r1_coeffs_ = (1 - (car_coeffs_.zr_coeffs_ * max_zeta));
|
alexbrandmeyer@610
|
65 FPType curfreq;
|
alexbrandmeyer@610
|
66 FloatArray erb_freqs(n_ch_);
|
alexbrandmeyer@610
|
67 for (int ch=0; ch < n_ch_; ch++) {
|
alexbrandmeyer@610
|
68 curfreq = pole_freqs(ch);
|
alexbrandmeyer@610
|
69 erb_freqs(ch) = ERBHz(curfreq, car_params.erb_break_freq_,
|
alexbrandmeyer@610
|
70 car_params.erb_q_);
|
alexbrandmeyer@610
|
71 }
|
alexbrandmeyer@610
|
72 FloatArray min_zetas = min_zeta + (0.25 * ((erb_freqs / pole_freqs) - min_zeta));
|
alexbrandmeyer@610
|
73 car_coeffs_.zr_coeffs_ *= max_zeta - min_zetas;
|
alexbrandmeyer@610
|
74 car_coeffs_.h_coeffs_ = car_coeffs_.c0_coeffs_ * f;
|
alexbrandmeyer@610
|
75 FloatArray relative_undamping = FloatArray::Ones(n_ch_);
|
alexbrandmeyer@610
|
76 FloatArray r = car_coeffs_.r1_coeffs_ +
|
alexbrandmeyer@610
|
77 (car_coeffs_.zr_coeffs_ * relative_undamping);
|
alexbrandmeyer@610
|
78 car_coeffs_.g0_coeffs_ = (1 - (2 * r * car_coeffs_.a0_coeffs_) + (r * r)) /
|
alexbrandmeyer@610
|
79 (1 - (2 * r * car_coeffs_.a0_coeffs_) +
|
alexbrandmeyer@610
|
80 (car_coeffs_.h_coeffs_ * r * car_coeffs_.c0_coeffs_) +
|
alexbrandmeyer@610
|
81 (r * r));
|
alexbrandmeyer@610
|
82 }
|
alexbrandmeyer@610
|
83
|
alexbrandmeyer@610
|
84
|
alexbrandmeyer@610
|
85 void Ear::DesignAGC(AGCParams agc_params, long fs) {
|
alexbrandmeyer@610
|
86 // These data members could probably be initialized locally within the design
|
alexbrandmeyer@610
|
87 // function.
|
alexbrandmeyer@610
|
88 agc_coeffs_.n_agc_stages_ = agc_params.n_stages_;
|
alexbrandmeyer@610
|
89 agc_coeffs_.agc_stage_gain_ = agc_params.agc_stage_gain_;
|
alexbrandmeyer@610
|
90 FloatArray agc1_scales = agc_params.agc1_scales_;
|
alexbrandmeyer@610
|
91 FloatArray agc2_scales = agc_params.agc2_scales_;
|
alexbrandmeyer@610
|
92 FloatArray time_constants = agc_params.time_constants_;
|
alexbrandmeyer@610
|
93 agc_coeffs_.agc_epsilon_.resize(agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
94 agc_coeffs_.agc_pole_z1_.resize(agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
95 agc_coeffs_.agc_pole_z2_.resize(agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
96 agc_coeffs_.agc_spatial_iterations_.resize(agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
97 agc_coeffs_.agc_spatial_n_taps_.resize(agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
98 agc_coeffs_.agc_spatial_fir_.resize(3,agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
99 agc_coeffs_.agc_mix_coeffs_.resize(agc_coeffs_.n_agc_stages_);
|
alexbrandmeyer@610
|
100 FPType mix_coeff = agc_params.agc_mix_coeff_;
|
alexbrandmeyer@610
|
101 int decim = 1;
|
alexbrandmeyer@610
|
102 agc_coeffs_.decimation_ = agc_params.decimation_;
|
alexbrandmeyer@610
|
103 FPType total_dc_gain = 0;
|
alexbrandmeyer@610
|
104 // Here we loop through each of the stages of the AGC.
|
alexbrandmeyer@610
|
105 for (int stage=0; stage < agc_coeffs_.n_agc_stages_; stage++) {
|
alexbrandmeyer@610
|
106 FPType tau = time_constants(stage);
|
alexbrandmeyer@610
|
107 decim *= agc_coeffs_.decimation_.at(stage);
|
alexbrandmeyer@610
|
108 agc_coeffs_.agc_epsilon_(stage) = 1 - exp((-1 * decim) / (tau * fs));
|
alexbrandmeyer@610
|
109 FPType n_times = tau * (fs / decim);
|
alexbrandmeyer@610
|
110 FPType delay = (agc2_scales(stage) - agc1_scales(stage)) / n_times;
|
alexbrandmeyer@610
|
111 FPType spread_sq = (pow(agc1_scales(stage), 2) +
|
alexbrandmeyer@610
|
112 pow(agc2_scales(stage), 2)) / n_times;
|
alexbrandmeyer@610
|
113 FPType u = 1 + (1 / spread_sq);
|
alexbrandmeyer@610
|
114 FPType p = u - sqrt(pow(u, 2) - 1);
|
alexbrandmeyer@610
|
115 FPType dp = delay * (1 - (2 * p) + (p * p)) / 2;
|
alexbrandmeyer@610
|
116 FPType pole_z1 = p - dp;
|
alexbrandmeyer@610
|
117 FPType pole_z2 = p + dp;
|
alexbrandmeyer@610
|
118 agc_coeffs_.agc_pole_z1_(stage) = pole_z1;
|
alexbrandmeyer@610
|
119 agc_coeffs_.agc_pole_z2_(stage) = pole_z2;
|
alexbrandmeyer@610
|
120 int n_taps = 0;
|
alexbrandmeyer@610
|
121 bool fir_ok = false;
|
alexbrandmeyer@610
|
122 int n_iterations = 1;
|
alexbrandmeyer@610
|
123 // This section initializes the FIR coeffs settings at each stage.
|
alexbrandmeyer@610
|
124 FloatArray fir(3);
|
alexbrandmeyer@610
|
125 while (! fir_ok) {
|
alexbrandmeyer@610
|
126 switch (n_taps) {
|
alexbrandmeyer@610
|
127 case 0:
|
alexbrandmeyer@610
|
128 n_taps = 3;
|
alexbrandmeyer@610
|
129 break;
|
alexbrandmeyer@610
|
130 case 3:
|
alexbrandmeyer@610
|
131 n_taps = 5;
|
alexbrandmeyer@610
|
132 break;
|
alexbrandmeyer@610
|
133 case 5:
|
alexbrandmeyer@610
|
134 n_iterations ++;
|
alexbrandmeyer@610
|
135 if (n_iterations > 16){
|
alexbrandmeyer@610
|
136 // This implies too many iterations, so we shoud indicate and error.
|
alexbrandmeyer@610
|
137 // TODO alexbrandmeyer, proper Google error handling.
|
alexbrandmeyer@610
|
138 }
|
alexbrandmeyer@610
|
139 break;
|
alexbrandmeyer@610
|
140 default:
|
alexbrandmeyer@610
|
141 // This means a bad n_taps has been provided, so there should again be
|
alexbrandmeyer@610
|
142 // an error.
|
alexbrandmeyer@610
|
143 // TODO alexbrandmeyer, proper Google error handling.
|
alexbrandmeyer@610
|
144 break;
|
alexbrandmeyer@610
|
145 }
|
alexbrandmeyer@610
|
146 // Now we can design the FIR coefficients.
|
alexbrandmeyer@610
|
147 FPType var = spread_sq / n_iterations;
|
alexbrandmeyer@610
|
148 FPType mn = delay / n_iterations;
|
alexbrandmeyer@610
|
149 FPType a, b;
|
alexbrandmeyer@610
|
150 switch (n_taps) {
|
alexbrandmeyer@610
|
151 case 3:
|
alexbrandmeyer@610
|
152 a = (var + pow(mn, 2) - mn) / 2;
|
alexbrandmeyer@610
|
153 b = (var + pow(mn, 2) + mn) / 2;
|
alexbrandmeyer@610
|
154 fir << a, 1 - a - b, b;
|
alexbrandmeyer@610
|
155 fir_ok = (fir(2) >= 0.2) ? true : false;
|
alexbrandmeyer@610
|
156 break;
|
alexbrandmeyer@610
|
157 case 5:
|
alexbrandmeyer@610
|
158 a = (((var + pow(mn, 2)) * 2/5) - (mn * 2/3)) / 2;
|
alexbrandmeyer@610
|
159 b = (((var + pow(mn, 2)) * 2/5) + (mn * 2/3)) / 2;
|
alexbrandmeyer@610
|
160 fir << a/2, 1 - a - b, b/2;
|
alexbrandmeyer@610
|
161 fir_ok = (fir(2) >= 0.1) ? true : false;
|
alexbrandmeyer@610
|
162 break;
|
alexbrandmeyer@610
|
163 default:
|
alexbrandmeyer@610
|
164 break; // Again, we've arrived at a bad n_taps in the design.
|
alexbrandmeyer@610
|
165 // TODO alexbrandmeyer. Add proper Google error handling.
|
alexbrandmeyer@610
|
166 }
|
alexbrandmeyer@610
|
167 }
|
alexbrandmeyer@610
|
168 // Once we have the FIR design for this stage we can assign it to the
|
alexbrandmeyer@610
|
169 // appropriate data members.
|
alexbrandmeyer@610
|
170 agc_coeffs_.agc_spatial_iterations_(stage) = n_iterations;
|
alexbrandmeyer@610
|
171 agc_coeffs_.agc_spatial_n_taps_(stage) = n_taps;
|
alexbrandmeyer@610
|
172 // TODO alexbrandmeyer replace using Eigen block method
|
alexbrandmeyer@610
|
173 agc_coeffs_.agc_spatial_fir_(0, stage) = fir(0);
|
alexbrandmeyer@610
|
174 agc_coeffs_.agc_spatial_fir_(1, stage) = fir(1);
|
alexbrandmeyer@610
|
175 agc_coeffs_.agc_spatial_fir_(2, stage) = fir(2);
|
alexbrandmeyer@610
|
176 total_dc_gain += pow(agc_coeffs_.agc_stage_gain_, (stage));
|
alexbrandmeyer@610
|
177 agc_coeffs_.agc_mix_coeffs_(stage) =
|
alexbrandmeyer@610
|
178 (stage == 0) ? 0 : mix_coeff / (tau * (fs /decim));
|
alexbrandmeyer@610
|
179 }
|
alexbrandmeyer@610
|
180 agc_coeffs_.agc_gain_ = total_dc_gain;
|
alexbrandmeyer@610
|
181 agc_coeffs_.detect_scale_ = 1 / total_dc_gain;
|
alexbrandmeyer@610
|
182 }
|
alexbrandmeyer@610
|
183
|
alexbrandmeyer@610
|
184 void Ear::DesignIHC(IHCParams ihc_params, long fs) {
|
alexbrandmeyer@610
|
185 if (ihc_params.just_hwr_) {
|
alexbrandmeyer@610
|
186 ihc_coeffs_.just_hwr_ = ihc_params.just_hwr_;
|
alexbrandmeyer@610
|
187 } else {
|
alexbrandmeyer@610
|
188 // This section calculates conductance values using two pre-defined scalars.
|
alexbrandmeyer@610
|
189 FloatArray x(1);
|
alexbrandmeyer@610
|
190 FPType conduct_at_10, conduct_at_0;
|
alexbrandmeyer@610
|
191 x << 10;
|
alexbrandmeyer@610
|
192 x = CARFACDetect(x);
|
alexbrandmeyer@610
|
193 conduct_at_10 = x(0);
|
alexbrandmeyer@610
|
194 x << 0;
|
alexbrandmeyer@610
|
195 x = CARFACDetect(x);
|
alexbrandmeyer@610
|
196 conduct_at_0 = x(0);
|
alexbrandmeyer@610
|
197 if (ihc_params.one_cap_) {
|
alexbrandmeyer@610
|
198 FPType ro = 1 / conduct_at_10 ;
|
alexbrandmeyer@610
|
199 FPType c = ihc_params.tau1_out_ / ro;
|
alexbrandmeyer@610
|
200 FPType ri = ihc_params.tau1_in_ / c;
|
alexbrandmeyer@610
|
201 FPType saturation_output = 1 / ((2 * ro) + ri);
|
alexbrandmeyer@610
|
202 FPType r0 = 1 / conduct_at_0;
|
alexbrandmeyer@610
|
203 FPType current = 1 / (ri + r0);
|
alexbrandmeyer@610
|
204 ihc_coeffs_.cap1_voltage_ = 1 - (current * ri);
|
alexbrandmeyer@610
|
205 ihc_coeffs_.just_hwr_ = false;
|
alexbrandmeyer@610
|
206 ihc_coeffs_.lpf_coeff_ = 1 - exp( -1 / (ihc_params.tau_lpf_ * fs));
|
alexbrandmeyer@610
|
207 ihc_coeffs_.out1_rate_ = ro / (ihc_params.tau1_out_ * fs);
|
alexbrandmeyer@610
|
208 ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs);
|
alexbrandmeyer@610
|
209 ihc_coeffs_.one_cap_ = ihc_params.one_cap_;
|
alexbrandmeyer@610
|
210 ihc_coeffs_.output_gain_ = 1 / (saturation_output - current);
|
alexbrandmeyer@610
|
211 ihc_coeffs_.rest_output_ = current / (saturation_output - current);
|
alexbrandmeyer@610
|
212 ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_;
|
alexbrandmeyer@610
|
213 } else {
|
alexbrandmeyer@610
|
214 FPType ro = 1 / conduct_at_10;
|
alexbrandmeyer@610
|
215 FPType c2 = ihc_params.tau2_out_ / ro;
|
alexbrandmeyer@610
|
216 FPType r2 = ihc_params.tau2_in_ / c2;
|
alexbrandmeyer@610
|
217 FPType c1 = ihc_params.tau1_out_ / r2;
|
alexbrandmeyer@610
|
218 FPType r1 = ihc_params.tau1_in_ / c1;
|
alexbrandmeyer@610
|
219 FPType saturation_output = 1 / (2 * ro + r2 + r1);
|
alexbrandmeyer@610
|
220 FPType r0 = 1 / conduct_at_0;
|
alexbrandmeyer@610
|
221 FPType current = 1 / (r1 + r2 + r0);
|
alexbrandmeyer@610
|
222 ihc_coeffs_.cap1_voltage_ = 1 - (current * r1);
|
alexbrandmeyer@610
|
223 ihc_coeffs_.cap2_voltage_ = ihc_coeffs_.cap1_voltage_ - (current * r2);
|
alexbrandmeyer@610
|
224 ihc_coeffs_.just_hwr_ = false;
|
alexbrandmeyer@610
|
225 ihc_coeffs_.lpf_coeff_ = 1 - exp(-1 / (ihc_params.tau_lpf_ * fs));
|
alexbrandmeyer@610
|
226 ihc_coeffs_.out1_rate_ = 1 / (ihc_params.tau1_out_ * fs);
|
alexbrandmeyer@610
|
227 ihc_coeffs_.in1_rate_ = 1 / (ihc_params.tau1_in_ * fs);
|
alexbrandmeyer@610
|
228 ihc_coeffs_.out2_rate_ = ro / (ihc_params.tau2_out_ * fs);
|
alexbrandmeyer@610
|
229 ihc_coeffs_.in2_rate_ = 1 / (ihc_params.tau2_in_ * fs);
|
alexbrandmeyer@610
|
230 ihc_coeffs_.one_cap_ = ihc_params.one_cap_;
|
alexbrandmeyer@610
|
231 ihc_coeffs_.output_gain_ = 1 / (saturation_output - current);
|
alexbrandmeyer@610
|
232 ihc_coeffs_.rest_output_ = current / (saturation_output - current);
|
alexbrandmeyer@610
|
233 ihc_coeffs_.rest_cap1_ = ihc_coeffs_.cap1_voltage_;
|
alexbrandmeyer@610
|
234 ihc_coeffs_.rest_cap2_ = ihc_coeffs_.cap2_voltage_;
|
alexbrandmeyer@610
|
235 }
|
alexbrandmeyer@610
|
236 }
|
alexbrandmeyer@610
|
237 ihc_coeffs_.ac_coeff_ = 2 * PI * ihc_params.ac_corner_hz_ / fs;
|
alexbrandmeyer@610
|
238 }
|
alexbrandmeyer@610
|
239
|
alexbrandmeyer@610
|
240 void Ear::InitCARState() {
|
alexbrandmeyer@610
|
241 car_state_.z1_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
242 car_state_.z2_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
243 car_state_.za_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
244 car_state_.zb_memory_ = car_coeffs_.zr_coeffs_;
|
alexbrandmeyer@610
|
245 car_state_.dzb_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
246 car_state_.zy_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
247 car_state_.g_memory_ = car_coeffs_.g0_coeffs_;
|
alexbrandmeyer@610
|
248 car_state_.dg_memory_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
249 }
|
alexbrandmeyer@610
|
250
|
alexbrandmeyer@610
|
251 void Ear::InitIHCState() {
|
alexbrandmeyer@610
|
252 ihc_state_.ihc_accum_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
253 if (! ihc_coeffs_.just_hwr_) {
|
alexbrandmeyer@610
|
254 ihc_state_.ac_coupler_ = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
255 ihc_state_.lpf1_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
|
alexbrandmeyer@610
|
256 ihc_state_.lpf2_state_ = ihc_coeffs_.rest_output_ * FloatArray::Ones(n_ch_);
|
alexbrandmeyer@610
|
257 if (ihc_coeffs_.one_cap_) {
|
alexbrandmeyer@610
|
258 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
|
alexbrandmeyer@610
|
259 FloatArray::Ones(n_ch_);
|
alexbrandmeyer@610
|
260 } else {
|
alexbrandmeyer@610
|
261 ihc_state_.cap1_voltage_ = ihc_coeffs_.rest_cap1_ *
|
alexbrandmeyer@610
|
262 FloatArray::Ones(n_ch_);
|
alexbrandmeyer@610
|
263 ihc_state_.cap2_voltage_ = ihc_coeffs_.rest_cap2_ *
|
alexbrandmeyer@610
|
264 FloatArray::Ones(n_ch_);
|
alexbrandmeyer@610
|
265 }
|
alexbrandmeyer@610
|
266 }
|
alexbrandmeyer@610
|
267 }
|
alexbrandmeyer@610
|
268
|
alexbrandmeyer@610
|
269 void Ear::InitAGCState() {
|
alexbrandmeyer@610
|
270 agc_state_.n_agc_stages_ = agc_coeffs_.n_agc_stages_;
|
alexbrandmeyer@610
|
271 agc_state_.agc_memory_.resize(agc_state_.n_agc_stages_);
|
alexbrandmeyer@610
|
272 agc_state_.input_accum_.resize(agc_state_.n_agc_stages_);
|
alexbrandmeyer@610
|
273 agc_state_.decim_phase_.resize(agc_state_.n_agc_stages_);
|
alexbrandmeyer@610
|
274 for (int i = 0; i < agc_state_.n_agc_stages_; i++) {
|
alexbrandmeyer@610
|
275 agc_state_.decim_phase_.at(i) = 0;
|
alexbrandmeyer@610
|
276 agc_state_.agc_memory_.at(i) = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
277 agc_state_.input_accum_.at(i) = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
278 }
|
alexbrandmeyer@610
|
279 }
|
alexbrandmeyer@610
|
280
|
alexbrandmeyer@610
|
281 FloatArray Ear::CARStep(FPType input) {
|
alexbrandmeyer@609
|
282 FloatArray g(n_ch_);
|
alexbrandmeyer@609
|
283 FloatArray zb(n_ch_);
|
alexbrandmeyer@609
|
284 FloatArray za(n_ch_);
|
alexbrandmeyer@609
|
285 FloatArray v(n_ch_);
|
alexbrandmeyer@609
|
286 FloatArray nlf(n_ch_);
|
alexbrandmeyer@609
|
287 FloatArray r(n_ch_);
|
alexbrandmeyer@609
|
288 FloatArray z1(n_ch_);
|
alexbrandmeyer@609
|
289 FloatArray z2(n_ch_);
|
alexbrandmeyer@609
|
290 FloatArray zy(n_ch_);
|
alexbrandmeyer@609
|
291 FPType in_out;
|
alexbrandmeyer@610
|
292 // This performs the DOHC stuff.
|
alexbrandmeyer@610
|
293 g = car_state_.g_memory_ + car_state_.dg_memory_; // This interpolates g.
|
alexbrandmeyer@610
|
294 // This calculates the AGC interpolation state.
|
alexbrandmeyer@610
|
295 zb = car_state_.zb_memory_ + car_state_.dzb_memory_;
|
alexbrandmeyer@610
|
296 // This updates the nonlinear function of 'velocity' along with zA, which is
|
alexbrandmeyer@610
|
297 // a delay of z2.
|
alexbrandmeyer@609
|
298 za = car_state_.za_memory_;
|
alexbrandmeyer@609
|
299 v = car_state_.z2_memory_ - za;
|
alexbrandmeyer@609
|
300 nlf = OHC_NLF(v);
|
alexbrandmeyer@610
|
301 // Here, zb * nfl is "undamping" delta r.
|
alexbrandmeyer@610
|
302 r = car_coeffs_.r1_coeffs_ + (zb * nlf);
|
alexbrandmeyer@609
|
303 za = car_state_.z2_memory_;
|
alexbrandmeyer@610
|
304 // Here we reduce the CAR state by r and rotate with the fixed cos/sin coeffs.
|
alexbrandmeyer@609
|
305 z1 = r * ((car_coeffs_.a0_coeffs_ * car_state_.z1_memory_) -
|
alexbrandmeyer@609
|
306 (car_coeffs_.c0_coeffs_ * car_state_.z2_memory_));
|
alexbrandmeyer@609
|
307 z2 = r * ((car_coeffs_.c0_coeffs_ * car_state_.z1_memory_) +
|
alexbrandmeyer@609
|
308 (car_coeffs_.a0_coeffs_ * car_state_.z2_memory_));
|
alexbrandmeyer@609
|
309 zy = car_coeffs_.h_coeffs_ * z2;
|
alexbrandmeyer@610
|
310 // This section ripples the input-output path, to avoid delay...
|
alexbrandmeyer@610
|
311 // It's the only part that doesn't get computed "in parallel":
|
alexbrandmeyer@609
|
312 in_out = input;
|
alexbrandmeyer@610
|
313 for (int ch = 0; ch < n_ch_; ch++) {
|
alexbrandmeyer@609
|
314 z1(ch) = z1(ch) + in_out;
|
alexbrandmeyer@610
|
315 // This performs the ripple, and saves the final channel outputs in zy.
|
alexbrandmeyer@609
|
316 in_out = g(ch) * (in_out + zy(ch));
|
alexbrandmeyer@609
|
317 zy(ch) = in_out;
|
alexbrandmeyer@609
|
318 }
|
alexbrandmeyer@609
|
319 car_state_.z1_memory_ = z1;
|
alexbrandmeyer@609
|
320 car_state_.z2_memory_ = z2;
|
alexbrandmeyer@609
|
321 car_state_.za_memory_ = za;
|
alexbrandmeyer@609
|
322 car_state_.zb_memory_ = zb;
|
alexbrandmeyer@609
|
323 car_state_.zy_memory_ = zy;
|
alexbrandmeyer@609
|
324 car_state_.g_memory_ = g;
|
alexbrandmeyer@610
|
325 // The output of the CAR is equal to the zy state.
|
alexbrandmeyer@609
|
326 return zy;
|
alexbrandmeyer@609
|
327 }
|
alexbrandmeyer@609
|
328
|
alexbrandmeyer@610
|
329 // We start with a quadratic nonlinear function, and limit it via a
|
alexbrandmeyer@610
|
330 // rational function. This makes the result go to zero at high
|
alexbrandmeyer@609
|
331 // absolute velocities, so it will do nothing there.
|
alexbrandmeyer@610
|
332 FloatArray Ear::OHC_NLF(FloatArray velocities) {
|
alexbrandmeyer@610
|
333 return 1 / (1 +
|
alexbrandmeyer@610
|
334 (((velocities * car_coeffs_.velocity_scale_) + car_coeffs_.v_offset_) *
|
alexbrandmeyer@610
|
335 ((velocities * car_coeffs_.velocity_scale_) + car_coeffs_.v_offset_)));
|
alexbrandmeyer@610
|
336
|
alexbrandmeyer@609
|
337 }
|
alexbrandmeyer@609
|
338
|
alexbrandmeyer@610
|
339 // This step is a one sample-time update of the inner-hair-cell (IHC) model,
|
alexbrandmeyer@610
|
340 // including the detection nonlinearity and either one or two capacitor state
|
alexbrandmeyer@610
|
341 // variables.
|
alexbrandmeyer@610
|
342 FloatArray Ear::IHCStep(FloatArray car_out) {
|
alexbrandmeyer@609
|
343 FloatArray ihc_out(n_ch_);
|
alexbrandmeyer@609
|
344 FloatArray ac_diff(n_ch_);
|
alexbrandmeyer@609
|
345 FloatArray conductance(n_ch_);
|
alexbrandmeyer@609
|
346 ac_diff = car_out - ihc_state_.ac_coupler_;
|
alexbrandmeyer@609
|
347 ihc_state_.ac_coupler_ = ihc_state_.ac_coupler_ +
|
alexbrandmeyer@609
|
348 (ihc_coeffs_.ac_coeff_ * ac_diff);
|
alexbrandmeyer@609
|
349 if (ihc_coeffs_.just_hwr_) {
|
alexbrandmeyer@610
|
350 for (int ch = 0; ch < n_ch_; ch++) {
|
alexbrandmeyer@609
|
351 FPType a;
|
alexbrandmeyer@610
|
352 a = (ac_diff(ch) > 0) ? ac_diff(ch) : 0;
|
alexbrandmeyer@610
|
353 ihc_out(ch) = (a < 2) ? a : 2;
|
alexbrandmeyer@609
|
354 }
|
alexbrandmeyer@609
|
355 } else {
|
alexbrandmeyer@609
|
356 conductance = CARFACDetect(ac_diff);
|
alexbrandmeyer@609
|
357 if (ihc_coeffs_.one_cap_) {
|
alexbrandmeyer@609
|
358 ihc_out = conductance * ihc_state_.cap1_voltage_;
|
alexbrandmeyer@609
|
359 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
|
alexbrandmeyer@609
|
360 (ihc_out * ihc_coeffs_.out1_rate_) +
|
alexbrandmeyer@609
|
361 ((1 - ihc_state_.cap1_voltage_)
|
alexbrandmeyer@609
|
362 * ihc_coeffs_.in1_rate_);
|
alexbrandmeyer@609
|
363 } else {
|
alexbrandmeyer@609
|
364 ihc_out = conductance * ihc_state_.cap2_voltage_;
|
alexbrandmeyer@609
|
365 ihc_state_.cap1_voltage_ = ihc_state_.cap1_voltage_ -
|
alexbrandmeyer@609
|
366 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
|
alexbrandmeyer@609
|
367 * ihc_coeffs_.out1_rate_) +
|
alexbrandmeyer@609
|
368 ((1 - ihc_state_.cap1_voltage_) * ihc_coeffs_.in1_rate_);
|
alexbrandmeyer@609
|
369 ihc_state_.cap2_voltage_ = ihc_state_.cap2_voltage_ -
|
alexbrandmeyer@609
|
370 (ihc_out * ihc_coeffs_.out2_rate_) +
|
alexbrandmeyer@609
|
371 ((ihc_state_.cap1_voltage_ - ihc_state_.cap2_voltage_)
|
alexbrandmeyer@609
|
372 * ihc_coeffs_.in2_rate_);
|
alexbrandmeyer@609
|
373 }
|
alexbrandmeyer@610
|
374 // Here we smooth the output twice using a LPF.
|
alexbrandmeyer@610
|
375 ihc_out *= ihc_coeffs_.output_gain_;
|
alexbrandmeyer@609
|
376 ihc_state_.lpf1_state_ = ihc_state_.lpf1_state_ +
|
alexbrandmeyer@609
|
377 (ihc_coeffs_.lpf_coeff_ * (ihc_out - ihc_state_.lpf1_state_));
|
alexbrandmeyer@609
|
378 ihc_state_.lpf2_state_ = ihc_state_.lpf2_state_ +
|
alexbrandmeyer@609
|
379 (ihc_coeffs_.lpf_coeff_ *
|
alexbrandmeyer@609
|
380 (ihc_state_.lpf1_state_ - ihc_state_.lpf2_state_));
|
alexbrandmeyer@609
|
381 ihc_out = ihc_state_.lpf2_state_ - ihc_coeffs_.rest_output_;
|
alexbrandmeyer@609
|
382 }
|
alexbrandmeyer@609
|
383 ihc_state_.ihc_accum_ += ihc_out;
|
alexbrandmeyer@609
|
384 return ihc_out;
|
alexbrandmeyer@609
|
385 }
|
alexbrandmeyer@609
|
386
|
alexbrandmeyer@610
|
387 bool Ear::AGCStep(FloatArray ihc_out) {
|
alexbrandmeyer@609
|
388 int stage = 0;
|
alexbrandmeyer@609
|
389 FloatArray agc_in(n_ch_);
|
alexbrandmeyer@609
|
390 agc_in = agc_coeffs_.detect_scale_ * ihc_out;
|
alexbrandmeyer@609
|
391 bool updated = AGCRecurse(stage, agc_in);
|
alexbrandmeyer@609
|
392 return updated;
|
alexbrandmeyer@609
|
393 }
|
alexbrandmeyer@609
|
394
|
alexbrandmeyer@610
|
395 bool Ear::AGCRecurse(int stage, FloatArray agc_in) {
|
alexbrandmeyer@609
|
396 bool updated = true;
|
alexbrandmeyer@610
|
397 // This is the decim factor for this stage, relative to input or prev. stage:
|
alexbrandmeyer@610
|
398 int decim = agc_coeffs_.decimation_.at(stage);
|
alexbrandmeyer@610
|
399 // This is the decim phase of this stage (do work on phase 0 only):
|
alexbrandmeyer@610
|
400 int decim_phase = agc_state_.decim_phase_.at(stage);
|
alexbrandmeyer@609
|
401 decim_phase = decim_phase % decim;
|
alexbrandmeyer@610
|
402 agc_state_.decim_phase_.at(stage) = decim_phase;
|
alexbrandmeyer@610
|
403 // Here we accumulate input for this stage from the previous stage:
|
alexbrandmeyer@610
|
404 agc_state_.input_accum_.at(stage) += agc_in;
|
alexbrandmeyer@610
|
405 // We don't do anything if it's not the right decim_phase.
|
alexbrandmeyer@610
|
406 if (decim_phase == 0) {
|
alexbrandmeyer@610
|
407 // Now we do lots of work, at the decimated rate.
|
alexbrandmeyer@610
|
408 // These are the decimated inputs for this stage, which will be further
|
alexbrandmeyer@610
|
409 // decimated at the next stage.
|
alexbrandmeyer@610
|
410 agc_in = agc_state_.input_accum_.at(stage) / decim;
|
alexbrandmeyer@610
|
411 // This resets the accumulator.
|
alexbrandmeyer@610
|
412 agc_state_.input_accum_.at(stage) = FloatArray::Zero(n_ch_);
|
alexbrandmeyer@610
|
413 if (stage < (agc_coeffs_.decimation_.size() - 1)) {
|
alexbrandmeyer@610
|
414 // Now we recurse to evaluate the next stage(s).
|
alexbrandmeyer@610
|
415 updated = AGCRecurse(stage + 1, agc_in);
|
alexbrandmeyer@610
|
416 // Afterwards we add its output to this stage input, whether it updated or
|
alexbrandmeyer@610
|
417 // not.
|
alexbrandmeyer@610
|
418 agc_in += (agc_coeffs_.agc_stage_gain_ *
|
alexbrandmeyer@610
|
419 agc_state_.agc_memory_.at(stage + 1));
|
alexbrandmeyer@609
|
420 }
|
alexbrandmeyer@610
|
421 FloatArray agc_stage_state = agc_state_.agc_memory_.at(stage);
|
alexbrandmeyer@610
|
422 // This performs a first-order recursive smoothing filter update, in time.
|
alexbrandmeyer@609
|
423 agc_stage_state = agc_stage_state + (agc_coeffs_.agc_epsilon_(stage) *
|
alexbrandmeyer@609
|
424 (agc_in - agc_stage_state));
|
alexbrandmeyer@609
|
425 agc_stage_state = AGCSpatialSmooth(stage, agc_stage_state);
|
alexbrandmeyer@610
|
426 agc_state_.agc_memory_.at(stage) = agc_stage_state;
|
alexbrandmeyer@609
|
427 } else {
|
alexbrandmeyer@609
|
428 updated = false;
|
alexbrandmeyer@609
|
429 }
|
alexbrandmeyer@609
|
430 return updated;
|
alexbrandmeyer@609
|
431 }
|
alexbrandmeyer@609
|
432
|
alexbrandmeyer@610
|
433 FloatArray Ear::AGCSpatialSmooth(int stage, FloatArray stage_state) {
|
alexbrandmeyer@609
|
434 int n_iterations = agc_coeffs_.agc_spatial_iterations_(stage);
|
alexbrandmeyer@609
|
435 bool use_fir;
|
alexbrandmeyer@610
|
436 use_fir = (n_iterations < 4) ? true : false;
|
alexbrandmeyer@609
|
437 if (use_fir) {
|
alexbrandmeyer@610
|
438 FloatArray fir_coeffs = agc_coeffs_.agc_spatial_fir_.block(0, stage, 3, 1);
|
alexbrandmeyer@609
|
439 FloatArray ss_tap1(n_ch_);
|
alexbrandmeyer@609
|
440 FloatArray ss_tap2(n_ch_);
|
alexbrandmeyer@609
|
441 FloatArray ss_tap3(n_ch_);
|
alexbrandmeyer@609
|
442 FloatArray ss_tap4(n_ch_);
|
alexbrandmeyer@609
|
443 int n_taps = agc_coeffs_.agc_spatial_n_taps_(stage);
|
alexbrandmeyer@610
|
444 // This initializes the first two taps of stage state, which are used for
|
alexbrandmeyer@610
|
445 // both possible cases.
|
alexbrandmeyer@609
|
446 ss_tap1(0) = stage_state(0);
|
alexbrandmeyer@610
|
447 ss_tap1.block(1, 0, n_ch_ - 1, 1) = stage_state.block(0, 0, n_ch_ - 1, 1);
|
alexbrandmeyer@610
|
448 ss_tap2(n_ch_ - 1) = stage_state(n_ch_ - 1);
|
alexbrandmeyer@610
|
449 ss_tap2.block(0, 0, n_ch_ - 1, 1) = stage_state.block(1, 0, n_ch_ - 1, 1);
|
alexbrandmeyer@609
|
450 switch (n_taps) {
|
alexbrandmeyer@609
|
451 case 3:
|
alexbrandmeyer@609
|
452 stage_state = (fir_coeffs(0) * ss_tap1) +
|
alexbrandmeyer@609
|
453 (fir_coeffs(1) * stage_state) +
|
alexbrandmeyer@609
|
454 (fir_coeffs(2) * ss_tap2);
|
alexbrandmeyer@609
|
455 break;
|
alexbrandmeyer@609
|
456 case 5:
|
alexbrandmeyer@610
|
457 // Now we initialize last two taps of stage state, which are only used
|
alexbrandmeyer@610
|
458 // for the 5-tap case.
|
alexbrandmeyer@609
|
459 ss_tap3(0) = stage_state(0);
|
alexbrandmeyer@609
|
460 ss_tap3(1) = stage_state(1);
|
alexbrandmeyer@610
|
461 ss_tap3.block(2, 0, n_ch_ - 2, 1) =
|
alexbrandmeyer@610
|
462 stage_state.block(0, 0, n_ch_ - 2, 1);
|
alexbrandmeyer@610
|
463 ss_tap4(n_ch_ - 2) = stage_state(n_ch_ - 1);
|
alexbrandmeyer@610
|
464 ss_tap4(n_ch_ - 1) = stage_state(n_ch_ - 2);
|
alexbrandmeyer@610
|
465 ss_tap4.block(0, 0, n_ch_ - 2, 1) =
|
alexbrandmeyer@610
|
466 stage_state.block(2, 0, n_ch_ - 2, 1);
|
alexbrandmeyer@609
|
467 stage_state = (fir_coeffs(0) * (ss_tap3 + ss_tap1)) +
|
alexbrandmeyer@609
|
468 (fir_coeffs(1) * stage_state) +
|
alexbrandmeyer@609
|
469 (fir_coeffs(2) * (ss_tap2 + ss_tap4));
|
alexbrandmeyer@609
|
470 break;
|
alexbrandmeyer@609
|
471 default:
|
alexbrandmeyer@610
|
472 // TODO alexbrandmeyer: determine proper error handling implementation.
|
alexbrandmeyer@609
|
473 std::cout << "Error: bad n-taps in AGCSpatialSmooth" << std::endl;
|
alexbrandmeyer@609
|
474 }
|
alexbrandmeyer@609
|
475 } else {
|
alexbrandmeyer@610
|
476 stage_state = AGCSmoothDoubleExponential(stage_state,
|
alexbrandmeyer@610
|
477 agc_coeffs_.agc_pole_z1_(stage),
|
alexbrandmeyer@610
|
478 agc_coeffs_.agc_pole_z2_(stage));
|
alexbrandmeyer@609
|
479 }
|
alexbrandmeyer@609
|
480 return stage_state;
|
alexbrandmeyer@609
|
481 }
|
alexbrandmeyer@609
|
482
|
alexbrandmeyer@610
|
483 FloatArray Ear::AGCSmoothDoubleExponential(FloatArray stage_state,
|
alexbrandmeyer@610
|
484 FPType pole_z1, FPType pole_z2) {
|
alexbrandmeyer@610
|
485 int n_pts = stage_state.size();
|
alexbrandmeyer@610
|
486 FPType input;
|
alexbrandmeyer@610
|
487 FPType state = 0;
|
alexbrandmeyer@610
|
488 // TODO alexbrandmeyer: I'm assuming one dimensional input for now, but this
|
alexbrandmeyer@610
|
489 // should be verified with Dick for the final version
|
alexbrandmeyer@610
|
490 for (int i = n_pts - 11; i < n_pts; i ++){
|
alexbrandmeyer@610
|
491 input = stage_state(i);
|
alexbrandmeyer@610
|
492 state = state + (1 - pole_z1) * (input - state);
|
alexbrandmeyer@610
|
493 }
|
alexbrandmeyer@610
|
494 for (int i = n_pts - 1; i > -1; i --){
|
alexbrandmeyer@610
|
495 input = stage_state(i);
|
alexbrandmeyer@610
|
496 state = state + (1 - pole_z2) * (input - state);
|
alexbrandmeyer@610
|
497 }
|
alexbrandmeyer@610
|
498 for (int i = 0; i < n_pts; i ++){
|
alexbrandmeyer@610
|
499 input = stage_state(i);
|
alexbrandmeyer@610
|
500 state = state + (1 - pole_z1) * (input - state);
|
alexbrandmeyer@610
|
501 stage_state(i) = state;
|
alexbrandmeyer@610
|
502 }
|
alexbrandmeyer@609
|
503 return stage_state;
|
alexbrandmeyer@609
|
504 }
|
alexbrandmeyer@610
|
505
|
alexbrandmeyer@610
|
506 FloatArray Ear::ReturnZAMemory() {
|
alexbrandmeyer@610
|
507 return car_state_.za_memory_;
|
alexbrandmeyer@610
|
508 }
|
alexbrandmeyer@610
|
509
|
alexbrandmeyer@610
|
510 FloatArray Ear::ReturnZBMemory() {
|
alexbrandmeyer@610
|
511 return car_state_.zb_memory_;
|
alexbrandmeyer@610
|
512 }
|
alexbrandmeyer@610
|
513
|
alexbrandmeyer@610
|
514 FloatArray Ear::ReturnGMemory() {
|
alexbrandmeyer@610
|
515 return car_state_.g_memory_;
|
alexbrandmeyer@610
|
516 };
|
alexbrandmeyer@610
|
517
|
alexbrandmeyer@610
|
518 FloatArray Ear::ReturnZRCoeffs() {
|
alexbrandmeyer@610
|
519 return car_coeffs_.zr_coeffs_;
|
alexbrandmeyer@610
|
520 };
|
alexbrandmeyer@610
|
521
|
alexbrandmeyer@610
|
522 int Ear::ReturnAGCNStages() {
|
alexbrandmeyer@610
|
523 return agc_coeffs_.n_agc_stages_;
|
alexbrandmeyer@610
|
524 }
|
alexbrandmeyer@610
|
525
|
alexbrandmeyer@610
|
526 int Ear::ReturnAGCStateDecimPhase(int stage) {
|
alexbrandmeyer@610
|
527 return agc_state_.decim_phase_.at(stage);
|
alexbrandmeyer@610
|
528 }
|
alexbrandmeyer@610
|
529
|
alexbrandmeyer@610
|
530 FPType Ear::ReturnAGCMixCoeff(int stage) {
|
alexbrandmeyer@610
|
531 return agc_coeffs_.agc_mix_coeffs_(stage);
|
alexbrandmeyer@610
|
532 }
|
alexbrandmeyer@610
|
533
|
alexbrandmeyer@610
|
534 FloatArray Ear::ReturnAGCStateMemory(int stage) {
|
alexbrandmeyer@610
|
535 return agc_state_.agc_memory_.at(stage);
|
alexbrandmeyer@610
|
536 }
|
alexbrandmeyer@610
|
537
|
alexbrandmeyer@610
|
538 int Ear::ReturnAGCDecimation(int stage) {
|
alexbrandmeyer@610
|
539 return agc_coeffs_.decimation_.at(stage);
|
alexbrandmeyer@610
|
540 }
|
alexbrandmeyer@610
|
541
|
alexbrandmeyer@610
|
542 void Ear::SetAGCStateMemory(int stage, FloatArray new_values) {
|
alexbrandmeyer@610
|
543 agc_state_.agc_memory_.at(stage) = new_values;
|
alexbrandmeyer@610
|
544 }
|
alexbrandmeyer@610
|
545
|
alexbrandmeyer@610
|
546 void Ear::SetCARStateDZBMemory(FloatArray new_values) {
|
alexbrandmeyer@610
|
547 car_state_.dzb_memory_ = new_values;
|
alexbrandmeyer@610
|
548 }
|
alexbrandmeyer@610
|
549
|
alexbrandmeyer@610
|
550 void Ear::SetCARStateDGMemory(FloatArray new_values) {
|
alexbrandmeyer@610
|
551 car_state_.dg_memory_ = new_values;
|
alexbrandmeyer@610
|
552 }
|
alexbrandmeyer@610
|
553
|
alexbrandmeyer@610
|
554 FloatArray Ear::StageGValue(FloatArray undamping) {
|
alexbrandmeyer@610
|
555 FloatArray r1 = car_coeffs_.r1_coeffs_;
|
alexbrandmeyer@610
|
556 FloatArray a0 = car_coeffs_.a0_coeffs_;
|
alexbrandmeyer@610
|
557 FloatArray c0 = car_coeffs_.c0_coeffs_;
|
alexbrandmeyer@610
|
558 FloatArray h = car_coeffs_.h_coeffs_;
|
alexbrandmeyer@610
|
559 FloatArray zr = car_coeffs_.zr_coeffs_;
|
alexbrandmeyer@610
|
560 FloatArray r = r1 + zr * undamping;
|
alexbrandmeyer@610
|
561 return (1 - 2 * r * a0 + (r * r)) / (1 - 2 * r * a0 + h * r * c0 + (r * r));
|
alexbrandmeyer@610
|
562 } |