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