SAF
Loading...
Searching...
No Matches
saf_sh.c
Go to the documentation of this file.
1/*
2 * Copyright 2016-2018 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
38#include "saf_sh.h"
39#include "saf_sh_internal.h"
40
42const float wxyzCoeffs[4][4] = {
43 {3.544907701811032f, 0.0f, 0.0f, 0.0f},
44 {0.0f, 0.0f, 0.0f, 2.046653415892977f},
45 {0.0f, 2.046653415892977f, 0.0f, 0.0f},
46 {0.0f, 0.0f, 2.046653415892977f, 0.0f} };
47
48
49/* ========================================================================== */
50/* Misc. Functions */
51/* ========================================================================== */
52
54(
55 int n,
56 double* x,
57 int lenX,
58 double* y /* FLAT: (n+1) x lenX */
59)
60{
61 int i, m;
62 double s, norm, scale;
63 double* P, *s_n, *tc, *sqrt_n;
64
65 if(n==0){
66 for(i=0; i<lenX; i++)
67 y[i] = 1.0;
68 return;
69 }
70
71 /* alloc */
72 P = calloc1d((n+3)*lenX,sizeof(double));
73 s_n = malloc1d(lenX*sizeof(double));
74 tc = malloc1d(lenX*sizeof(double));
75 sqrt_n = malloc1d((2*n+1)*sizeof(double));
76
77 /* init */
78 for(i=0; i<lenX; i++){
79 s = sqrt(1.0-pow(x[i],2.0)) + 2.23e-20;
80 s_n[i] = pow(-s, (double)n);
81 tc[i] = -2.0 * x[i]/s;
82 }
83 for(i=0; i<2*n+1; i++)
84 sqrt_n[i] = sqrt((double)i);
85 norm = 1.0;
86 for(i=1; i<=n; i++)
87 norm *= 1.0 - 1.0/(2.0*(double)i);
88
89 /* Starting values for downwards recursion */
90 for(i=0; i<lenX; i++){
91 P[(n)*lenX+i] = sqrt(norm)*s_n[i];
92 P[(n-1)*lenX+i] = P[(n)*lenX+i] * tc[i] * (double)n/sqrt_n[2*n];
93 }
94
95 /* 3-step downwards recursion to m == 0 */
96 for(m=n-2; m>=0; m--)
97 for(i=0; i<lenX; i++)
98 P[(m)*lenX+i] = (P[(m+1)*lenX+i]*tc[i]*((double)m+1.0) - P[(m+2)*lenX+i] * sqrt_n[n+m+2] * sqrt_n[n-m-1])/(sqrt_n[n+m+1]*sqrt_n[n-m]);
99
100 /* keep up to the last 3 elements in P */
101 for(i=0; i<n+1; i++)
102 memcpy(&(y[i*lenX]), &(P[i*lenX]), lenX*sizeof(double));
103
104 /* Account for polarity when x == -/+1 for first value of P */
105 for(i=0; i<lenX; i++)
106 if(sqrt(1.0-pow(x[i],2.0))==0)
107 y[i] = pow(x[i],(double)n);
108
109 /* scale each row by: sqrt((n+m)!/(n-m)!) */
110 for(m=1; m<n; m++){
111 scale = 1.0;
112 for(i=n-m+1; i<n+m+1; i++)
113 scale*=sqrt_n[i];
114 for(i=0; i<lenX; i++)
115 y[m*lenX+i] *= scale;
116 }
117 scale = 1.0;
118 for(i=1; i<2*n+1; i++)
119 scale*=sqrt_n[i];
120 for(i=0; i<lenX; i++)
121 y[n*lenX+i] *= scale;
122
123 free(P);
124 free(s_n);
125 free(tc);
126 free(sqrt_n);
127}
128
130(
131 int n,
132 float* x,
133 int lenX,
134 float* Pnm_minus1,
135 float* Pnm_minus2,
136 float* Pnm
137)
138{
139 int i, m, k, kk;
140 float x2, one_min_x2, dfact_k;
141
142 if(n==0){
143 for(i=0; i<lenX; i++)
144 Pnm[i] = 1.0f;
145 return;
146 }
147
148 for(i=0; i<lenX; i++){
149 x2 = (x[i])*(x[i]);
150 switch(n) {
151 case 1:
152 Pnm[0*lenX+i] = x[i];
153 Pnm[1*lenX+i] = sqrtf(1.0f-x2);
154 break;
155 case 2:
156 Pnm[0*lenX+i] = (3.0f*x2-1.0f)/2.0f;
157 Pnm[1*lenX+i] = (x[i])*3.0f*sqrtf(1.0f-x2);
158 Pnm[2*lenX+i] = 3.0f*(1.0f-x2);
159 break;
160 default:
161 one_min_x2 = 1.0f-x2;
162 /* last term m=n */
163 k = 2*n-1;
164 dfact_k = 1.0f;
165 if ((k % 2) == 0)
166 for (kk=1; kk<k/2+1; kk++)
167 dfact_k *= 2.0f*(float)kk;
168 else
169 for (kk=1; kk<(k+1)/2+1; kk++)
170 dfact_k *= (2.0f*(float)kk-1.0f);
171
172 Pnm[n*lenX+i] = dfact_k * powf(one_min_x2, (float)n/2.0f);
173 /* before last term */
174 /* P_{n(n-1)} = (2*n-1)*x*P_{(n-1)(n-1)} */
175 Pnm[(n-1)*lenX+i] = (float)k * (x[i]) *Pnm_minus1[(n-1)*lenX+i];
176 /* three term recurence for the rest */
177 for (m=0; m<n-1; m++)
178 /* P_l = ( (2l-1)xP_(l-1) - (l+m-1)P_(l-2) )/(l-m) */
179 Pnm[m*lenX+i] = ( ((float)k * (x[i]) *Pnm_minus1[m*lenX+i]) - ((float)(n+m-1) * Pnm_minus2[m*lenX+i])) / (float)(n-m);
180 break;
181 }
182 }
183}
184
185
186/* ========================================================================== */
187/* SH and Beamforming related Functions */
188/* ========================================================================== */
189
191(
192 int order,
193 float* dirs_rad,
194 int nDirs,
195 float* Y /* the SH weights: (order+1)^2 x nDirs */
196)
197{
198 int dir, j, n, m, idx_Y;
199 double* Lnm;
200 double *p_nm, *cos_incl;
201 double *norm_real;
202
203 if(nDirs<1)
204 return;
205
206 Lnm = malloc1d((2*order+1)*nDirs*sizeof(double));
207 norm_real = malloc1d((2*order+1)*sizeof(double));
208 cos_incl = malloc1d(nDirs*sizeof(double));
209 p_nm = malloc1d((order+1)*nDirs * sizeof(double));
210 for (dir = 0; dir<nDirs; dir++)
211 cos_incl[dir] = cos((double)dirs_rad[dir*2+1]);
212
213 idx_Y = 0;
214 for(n=0; n<=order; n++){
215 /* vector of unnormalised associated Legendre functions of current order */
216 unnorm_legendreP(n, cos_incl, nDirs, p_nm); /* includes Condon-Shortley phase term */
217
218 for(dir=0; dir<nDirs; dir++){
219 /* cancel the Condon-Shortley phase from the definition of the Legendre functions to result in signless real SH */
220 if (n != 0)
221 for(m=-n, j=0; m<=n; m++, j++)
222 Lnm[j*nDirs+dir] = pow(-1.0, (double)abs(m)) * p_nm[abs(m)*nDirs+dir];
223 else
224 Lnm[dir] = p_nm[dir];
225 }
226
227 /* normalisation */
228 for(m=-n, j=0; m<=n; m++, j++)
229 norm_real[j] = sqrt( (2.0*(double)n+1.0) * (double)factorial(n-abs(m)) / (4.0*SAF_PId*(double)factorial(n+abs(m))) );
230
231 /* norm_real * Lnm_real .* CosSin; */
232 for(dir=0; dir<nDirs; dir++){
233 for(m=-n, j=0; m<=n; m++, j++){
234 if(j<n)
235 Y[(j+idx_Y)*nDirs+dir] = (float)(norm_real[j] * Lnm[j*nDirs+dir] * sqrt(2.0)*sin((double)(n-j)*(double)dirs_rad[dir*2]));
236 else if(j==n)
237 Y[(j+idx_Y)*nDirs+dir] = (float)(norm_real[j] * Lnm[j*nDirs+dir]);
238 else /* (j>n) */
239 Y[(j+idx_Y)*nDirs+dir] = (float)(norm_real[j] * Lnm[j*nDirs+dir] * sqrt(2.0)*cos((double)(abs(m))*(double)dirs_rad[dir*2]));
240 }
241 }
242
243 /* increment */
244 idx_Y = idx_Y + (2*n+1);
245 }
246
247 free(p_nm);
248 free(Lnm);
249 free(norm_real);
250 free(cos_incl);
251}
252
254(
255 int N,
256 float* dirs_rad,
257 int nDirs,
258 float* Y
259)
260{
261 int n, m, i, dir, index_n;
262 float Nn0, Nnm;
263 float sleg_n[11], sleg_n_1[11], sleg_n_2[11], scos_incl, sfactorials_n[21];
264 float* leg_n, *leg_n_1, *leg_n_2, *cos_incl, *factorials_n;
265
266 if(nDirs<1)
267 return;
268
269 if(N<=10 && nDirs == 1){
270 /* Single direction optimisation for up to 7th order */
271 leg_n = sleg_n;
272 leg_n_1 = sleg_n_1;
273 leg_n_2 = sleg_n_2;
274 cos_incl = &scos_incl;
275 factorials_n = sfactorials_n;
276 }
277 else{
278 factorials_n = malloc1d((2*N+1)*sizeof(float));
279 leg_n = malloc1d((N+1)*nDirs * sizeof(float));
280 leg_n_1 = malloc1d((N+1)*nDirs * sizeof(float));
281 leg_n_2 = malloc1d((N+1)*nDirs * sizeof(float));
282 cos_incl = malloc1d(nDirs * sizeof(float));
283 }
284 index_n = 0;
285
286 /* precompute factorials */
287 for (i = 0; i < 2*N+1; i++)
288 factorials_n[i] = (float)factorial(i);
289
290 /* cos(inclination) = sin(elevation) */
291 for (dir = 0; dir<nDirs; dir++)
292 cos_incl[dir] = cosf(dirs_rad[dir*2+1]);
293
294 /* compute SHs with the recursive Legendre function */
295 for (n = 0; n<N+1; n++) {
296 if (n==0) {
297 for (dir = 0; dir<nDirs; dir++)
298 Y[n*nDirs+dir] = 1.0f/SQRT4PI;
299 index_n = 1;
300 }
301 else {
302 unnorm_legendreP_recur(n, cos_incl, nDirs, leg_n_1, leg_n_2, leg_n); /* does NOT include Condon-Shortley phase term */
303
304 Nn0 = sqrtf(2.0f*(float)n+1.0f);
305 for (dir = 0; dir<nDirs; dir++){
306 for (m = 0; m<n+1; m++) {
307 if (m==0)
308 Y[(index_n+n)*nDirs+dir] = Nn0/SQRT4PI * leg_n[m*nDirs+dir];
309 else {
310 Nnm = Nn0* sqrtf( 2.0f * factorials_n[n-m]/factorials_n[n+m] );
311 Y[(index_n+n-m)*nDirs+dir] = Nnm/SQRT4PI * leg_n[m*nDirs+dir] * sinf((float)m * (dirs_rad[dir*2]));
312 Y[(index_n+n+m)*nDirs+dir] = Nnm/SQRT4PI * leg_n[m*nDirs+dir] * cosf((float)m * (dirs_rad[dir*2]));
313 }
314 }
315 }
316 index_n += 2*n+1;
317 }
318 utility_svvcopy(leg_n_1, (N+1)*nDirs, leg_n_2);
319 utility_svvcopy(leg_n, (N+1)*nDirs, leg_n_1);
320 }
321
322 if(N>10 || nDirs > 1){
323 free(factorials_n);
324 free(leg_n);
325 free(leg_n_1);
326 free(leg_n_2);
327 free(cos_incl);
328 }
329}
330
331void getSHreal_part
332(
333 int order_start,
334 int order_end,
335 float* dirs_rad,
336 int nDirs,
337 float* Y /* the SH weights: (order_end+1)^2 x nDirs */
338)
339{
340 int dir, i, n, m, index_n, powm;
341 float Nn0, Nnm;
342 double* Lnm;
343 double *p_nm;
344 double *norm_real;
345 double *cos_incl;
346 float *fcos_incl;
347 float* leg_n, *leg_n_1, *leg_n_2, *factorials_n;
348
349 if(nDirs<1)
350 return;
351
352 assert((order_end - order_start) >= 0);
353 Lnm = malloc1d((2*order_end+1)*nDirs*sizeof(double));
354 norm_real = malloc1d((2*order_end+1)*sizeof(double));
355 p_nm = malloc1d((order_end+1)*nDirs * sizeof(double));
356 cos_incl = malloc1d(nDirs * sizeof(double));
357 fcos_incl = malloc1d(nDirs * sizeof(float));
358 factorials_n = malloc1d((2*order_end+1)*sizeof(float));
359 leg_n = malloc1d((order_end+1)*nDirs * sizeof(float));
360 leg_n_1 = malloc1d((order_end+1)*nDirs * sizeof(float));
361 leg_n_2 = malloc1d((order_end+1)*nDirs * sizeof(float));
362
363 index_n = 0;
364
365 /* precompute factorials */
366 for (i = 0; i < 2*order_end+1; i++)
367 factorials_n[i] = (float)factorial(i);
368
369 for(n=0; n<=order_end; n++){
370 if(n < order_start){
371 for(m=0; m<=2*n+1; m++)
372 for(dir=0; dir<nDirs; dir++)
373 Y[(index_n+m)*nDirs+dir] = 0.f;
374 }
375 else {
376 if (n==0) {
377 for (dir = 0; dir<nDirs; dir++)
378 Y[dir] = 1.0f/SQRT4PI;
379 }
380 else {
381 /* cos(inclination) = sin(elevation) */
382 for (dir = 0; dir<nDirs; dir++){
383 cos_incl[dir] = cos(dirs_rad[dir*2+1]);
384 fcos_incl[dir] = (float) cos_incl[dir];
385 }
386
387 if (n==order_start || n==order_start+1) {
388 /* vector of unnormalised associated Legendre functions of current order */
389 unnorm_legendreP(n, cos_incl, nDirs, p_nm); /* includes Condon-Shortley phase term */
390
391 for(dir=0; dir<nDirs; dir++){
392 /* cancel the Condon-Shortley phase from the definition of the Legendre functions to result in signless real SH */
393 if (n != 0){
394 powm = -1;
395 for(m = 0; m<n+1; m++){
396 powm = -powm;
397 leg_n[m*nDirs+dir] = powm * (float)p_nm[abs(m)*nDirs+dir];
398 }
399 }
400 else
401 leg_n[dir] = (float)p_nm[dir];
402 }
403 }
404 else
405 unnorm_legendreP_recur(n, fcos_incl, nDirs, leg_n_1, leg_n_2, leg_n); /* does NOT include Condon-Shortley phase term */
406 utility_svvcopy(leg_n_1, (order_end+1)*nDirs, leg_n_2); // should be n copies only
407 utility_svvcopy(leg_n, (order_end+1)*nDirs, leg_n_1);
408
409 Nn0 = sqrtf(2.0f*(float)n+1.0f);
410 for (dir = 0; dir<nDirs; dir++){
411 for (m = 0; m<n+1; m++) {
412 if (m==0)
413 Y[(index_n+n)*nDirs+dir] = Nn0/SQRT4PI * leg_n[m*nDirs+dir];
414 else {
415 Nnm = Nn0* sqrtf( 2.0f * factorials_n[n-m]/factorials_n[n+m] );
416 Y[(index_n+n-m)*nDirs+dir] = Nnm/SQRT4PI * leg_n[m*nDirs+dir] * sinf((float)m * (dirs_rad[dir*2]));
417 Y[(index_n+n+m)*nDirs+dir] = Nnm/SQRT4PI * leg_n[m*nDirs+dir] * cosf((float)m * (dirs_rad[dir*2]));
418 }
419 }
420 }
421 }
422 }
423
424 /* increment */
425 index_n += 2*n+1;
426 }
427
428 free(p_nm);
429 free(Lnm);
430 free(norm_real);
431 free(cos_incl);
432 free(fcos_incl);
433 free(factorials_n);
434 free(leg_n);
435 free(leg_n_1);
436 free(leg_n_2);
437}
438
440(
441 int order,
442 float* dirs_rad,
443 int nDirs,
444 float_complex* Y
445)
446{
447 int dir, j, n, m, idx_Y;
448 double *norm_real;
449 double *Lnm, *cos_incl;
450 double_complex Ynm;
451
452 Lnm = malloc1d((order+1)*nDirs*sizeof(double));
453 norm_real = malloc1d((order+1)*sizeof(double));
454 cos_incl = malloc1d(nDirs*sizeof(double));
455 for (dir = 0; dir<nDirs; dir++)
456 cos_incl[dir] = cos((double)dirs_rad[dir*2+1]);
457
458 idx_Y = 0;
459 for(n=0; n<=order; n++){
460 /* vector of unnormalised associated Legendre functions of current order */
461 unnorm_legendreP(n, cos_incl, nDirs, Lnm); /* includes Condon-Shortley phase term */
462
463 /* normalisation */
464 for(m=0; m<=n; m++)
465 norm_real[m] = sqrt( (2.0*(double)n+1.0)*(double)factorial(n-m) / (4.0*SAF_PId*(double)factorial(n+m)) );
466
467 /* norm_real .* Lnm_real .* CosSin; */
468 for(dir=0; dir<nDirs; dir++){
469 for(m=-n, j=0; m<=n; m++, j++){
470 if(m<0){
471 Ynm = crmul(conj(crmul(cexp(cmplx(0.0, (double)abs(m)*(double)dirs_rad[dir*2])), norm_real[abs(m)] * Lnm[abs(m)*nDirs+dir])), pow(-1.0, (double)abs(m)));
472 Y[(j+idx_Y)*nDirs+dir] = cmplxf((float)creal(Ynm), (float)cimag(Ynm));
473 }
474 else {/* (m>=0) */
475 Ynm = crmul(cexp(cmplx(0.0, (double)abs(m)*(double)dirs_rad[dir*2])), norm_real[m] * Lnm[m*nDirs+dir]);
476 Y[(j+idx_Y)*nDirs+dir] = cmplxf((float)creal(Ynm), (float)cimag(Ynm));
477 }
478 }
479 }
480
481 /* increment */
482 idx_Y = idx_Y + (2*n+1);
483 }
484
485 free(Lnm);
486 free(norm_real);
487 free(cos_incl);
488}
489
491(
492 int order,
493 float_complex* T_c2r
494)
495{
496 int n, m, q, p, idx, nSH;
497
498 nSH = ORDER2NSH(order);
499 memset(T_c2r, 0, nSH*nSH*sizeof(float_complex));
500 T_c2r[0] = cmplxf(1.0f, 0.0f);
501 if(order == 0)
502 return;
503
504 idx = 1;
505 for(n=1, q = 1; n<=order; n++){
506 idx += (2*n+1);
507 for(m=-n, p=0; m<=n; m++, q++, p++){
508 if(m<0){
509 T_c2r[(q)*nSH+(q)] = cmplxf(0.0f, 1.0f/sqrtf(2.0f));
510 T_c2r[(idx-p-1)*nSH+(q)] = cmplxf(1.0f/sqrtf(2.0f), 0.0f);
511 }
512 else if(m==0)
513 T_c2r[(q)*nSH+(q)] = cmplxf(1.0f, 0.0f);
514 else{
515 T_c2r[(q)*nSH+(q)] = cmplxf(powf(-1.0f,(float)m)/sqrtf(2.0f), 0.0f);
516 T_c2r[(idx-p-1)*nSH+(q)] = cmplxf(0.0f, -powf(-1.0f, (float)abs(m))/sqrtf(2.0f));
517 }
518 }
519 }
520}
521
523(
524 int order,
525 float_complex* T_r2c
526)
527{
528 int n, m, q, p, idx, nSH;
529
530 nSH = ORDER2NSH(order);
531 memset(T_r2c, 0, nSH*nSH*sizeof(float_complex));
532 T_r2c[0] = cmplxf(1.0f, 0.0f);
533 if(order == 0)
534 return;
535
536 idx = 1;
537 for(n=1, q = 1; n<=order; n++){
538 idx += (2*n+1);
539 for(m=-n, p=0; m<=n; m++, q++, p++){
540 if(m<0){
541 T_r2c[(q)*nSH+(q)] = cmplxf(0.0f, -1.0f/sqrtf(2.0f));
542 T_r2c[(idx-p-1)*nSH+(q)] = cmplxf(0.0f, powf(-1.0f, (float)abs(m))/sqrtf(2.0f)); //cmplxf(1.0f/sqrtf(2.0f), 0.0f);
543 }
544 else if(m==0)
545 T_r2c[(q)*nSH+(q)] = cmplxf(1.0f, 0.0f);
546 else{
547 T_r2c[(q)*nSH+(q)] = cmplxf(powf(-1.0f,(float)m)/sqrtf(2.0f), 0.0f);
548 T_r2c[(idx-p-1)*nSH+(q)] = cmplxf(1.0f/sqrtf(2.0f), 0.0f); //cmplxf(0.0f, -powf(-1.0f, (float)abs(m))/sqrtf(2.0f));
549 }
550 }
551 }
552}
553
555(
556 int order,
557 float_complex* C_N,
558 int K,
559 float* R_N
560)
561{
562 int i, nSH;
563 float_complex* T_c2r, *R_N_c;
564 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
565
566 nSH = ORDER2NSH(order);
567 T_c2r = malloc1d(nSH*nSH*sizeof(float_complex));
568 R_N_c = malloc1d(nSH*K*sizeof(float_complex));
569 complex2realSHMtx(order, T_c2r);
570 for(i=0; i<nSH*nSH; i++)
571 T_c2r[i] = conjf(T_c2r[i]);
572 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, K, nSH, &calpha,
573 T_c2r, nSH,
574 C_N, K, &cbeta,
575 R_N_c, K);
576 for(i=0; i<nSH*K; i++)
577 R_N[i] = crealf(R_N_c[i]);
578
579 free(T_c2r);
580 free(R_N_c);
581}
582
583/* Ivanic, J., Ruedenberg, K. (1998). Rotation Matrices for Real Spherical Harmonics. Direct Determination
584 * by Recursion Page: Additions and Corrections. Journal of Physical Chemistry A, 102(45), 9099?9100. */
586(
587 float Rxyz[3][3],
588 float* RotMtx/*(L+1)^2 x (L+1)^2 */,
589 int L
590)
591{
592 int i, j, M, l, m, n, d, bandIdx, denom;
593 float u, v, w;
594 float R_1[3][3], _R_lm1[121*121], _R_l[121*121];
595 float* R_lm1, *R_l;
596
597 /* Prep */
598 M = (L+1) * (L+1);
599 if(L<=10){
600 R_lm1 = _R_lm1;
601 R_l = _R_l;
602 }
603 else{
604 R_lm1 = malloc1d(M*M*sizeof(float));
605 R_l = malloc1d(M*M*sizeof(float));
606 }
607 memset(RotMtx, 0, M*M*sizeof(float));
608
609 /* zeroth-band (l=0) is invariant to rotation */
610 RotMtx[0] = 1;
611
612 /* the first band (l=1) is directly related to the rotation matrix */
613 R_1[0][0] = Rxyz[1][1];
614 R_1[0][1] = Rxyz[1][2];
615 R_1[0][2] = Rxyz[1][0];
616 R_1[1][0] = Rxyz[2][1];
617 R_1[1][1] = Rxyz[2][2];
618 R_1[1][2] = Rxyz[2][0];
619 R_1[2][0] = Rxyz[0][1];
620 R_1[2][1] = Rxyz[0][2];
621 R_1[2][2] = Rxyz[0][0];
622 for (i=1; i<4; i++){
623 R_lm1[(i-1)*M+0] = R_1[i-1][0];
624 R_lm1[(i-1)*M+1] = R_1[i-1][1];
625 R_lm1[(i-1)*M+2] = R_1[i-1][2];
626 for (j=1; j<4; j++)
627 RotMtx[i*M+j] = R_1[i-1][j-1];
628 }
629
630 /* compute rotation matrix of each subsequent band recursively */
631 bandIdx = 4;
632 for(l = 2; l<=L; l++){
633 for(i=0; i<2*l+1; i++)
634 memset(R_l + i*M, 0, (2*l+1) * sizeof(float));
635 for(m=-l; m<=l; m++){
636 for(n=-l; n<=l; n++){
637 /* compute u,v,w terms of Eq.8.1 (Table I) */
638 d = m == 0 ? 1 : 0; /* the delta function d_m0 */
639 denom = abs(n) == l ? (2*l)*(2*l-1) : (l*l-n*n);
640 u = sqrtf( (float)((l*l-m*m)) / (float)denom);
641 v = sqrtf( (float)((1+d)*(l+abs(m)-1)*(l+abs(m))) / (float)denom) * (float)(1-2*d)*0.5f;
642 w = sqrtf( (float)((l-abs(m)-1)*(l-abs(m))) / (float)denom) * (float)(1-d)*(-0.5f);
643
644 /* computes Eq.8.1 */
645 if (u!=0)
646 u = u* getU(M,l,m,n,R_1,R_lm1);
647 if (v!=0)
648 v = v* getV(M,l,m,n,R_1,R_lm1);
649 if (w!=0)
650 w = w* getW(M,l,m,n,R_1,R_lm1);
651
652 R_l[(m+l)*M+(n+l)] = u+v+w;
653 }
654 }
655
656 for(i=0; i<2*l+1; i++)
657 for(j=0; j<2*l+1; j++)
658 RotMtx[(bandIdx + i)*M + (bandIdx + j)] = R_l[i*M+j];
659 for(i=0; i<2*l+1; i++)
660 memcpy(R_lm1+i*M, R_l + i*M, (2*l+1) * sizeof(float));
661 bandIdx += 2*l+1;
662 }
663
664 /* clean-up */
665 if(L>10){
666 free(R_lm1);
667 free(R_l);
668 }
669}
670
672(
673 int sectorOrder,
674 float_complex* A_xyz
675)
676{
677 int i, j, Nxyz, Ns, nC_xyz, nC_s;
678 float x1, x3, z2, y1, y3;
679 float* G_mtx;
680
681 Ns = sectorOrder;
682 Nxyz = Ns+1;
683 nC_xyz = (Nxyz+1)*(Nxyz+1);
684 nC_s = (Ns+1)*(Ns+1);
685 x1 = sqrtf(2.0f*SAF_PI/3.0f);
686 x3 = -x1;
687 y1 = y3 = sqrtf(2.0f*SAF_PI/3.0f);
688 z2 = sqrtf(4.0f*SAF_PI/3.0f);
689 G_mtx = malloc1d(nC_s*4*nC_xyz*sizeof(float));
690 gaunt_mtx(Ns, 1, Nxyz, G_mtx);
691
692 for (i=0; i<nC_xyz; i++){
693 for (j=0; j<nC_s; j++){
694 A_xyz[i*nC_s*3 + j*3 + 0] = cmplxf(x1*G_mtx[j*4*nC_xyz + 1*nC_xyz + i] + x3*G_mtx[j*4*nC_xyz + 3*nC_xyz + i], 0.0f);
695 A_xyz[i*nC_s*3 + j*3 + 1] = cmplxf(0.0f, y1*G_mtx[j*4*nC_xyz + 1*nC_xyz + i] + y3*G_mtx[j*4*nC_xyz + 3*nC_xyz + i]);
696 A_xyz[i*nC_s*3 + j*3 + 2] = cmplxf(z2*G_mtx[j*4*nC_xyz + 2*nC_xyz + i], 0.0f);
697 }
698 }
699
700 free(G_mtx);
701}
702
704(
705 int orderSec,
706 float_complex* A_xyz, /* FLAT: (sectorOrder+2)^2 x (sectorOrder+1)^2 x 3 */
707 SECTOR_PATTERNS pattern,
708 float* sec_dirs_deg,
709 int nSecDirs,
710 float* sectorCoeffs /* FLAT: (nSecDirs*4) x (orderSec+2)^2 */
711)
712{
713 int i, j, ns, orderVel, nSH;
714 float normSec, azi_sec, elev_sec, Q;
715 float* b_n, *c_nm, *xyz_nm;
716
717 if(orderSec==0){
718 memcpy(sectorCoeffs, wxyzCoeffs, 16*sizeof(float)); /* ACN/N3D to WXYZ */
719 normSec = 1.0f;
720 }
721 else{
722 orderVel = orderSec+1;
723 nSH = (orderSec+2)*(orderSec+2);
724 b_n = malloc1d((orderSec+1)*sizeof(float));
725 c_nm = calloc1d((orderVel+1)*(orderVel+1), sizeof(float)); /* pad with zeros */
726 xyz_nm = malloc1d((orderVel+1)*(orderVel+1)*3*sizeof(float));
727 switch(pattern){
730 Q = (float)((orderSec+1) * (orderSec+1));
731 break;
733 beamWeightsMaxEV(orderSec, b_n);
734 cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans, 1, 1, orderSec+1, 1.0f,
735 b_n, 1,
736 b_n, 1, 0.0f,
737 &Q, 1);
738 Q = 4.0f*SAF_PI/(Q);
739 break;
741 beamWeightsCardioid2Spherical(orderSec, b_n);
742 Q = 2.0f*(float)orderSec + 1.0f;
743 break;
744 }
745 normSec = Q/(float)nSecDirs; /* directivity factor / number of sectors */
746
747 for(ns=0; ns<nSecDirs; ns++){
748 /* rotate the pattern by rotating the coefficients */
749 azi_sec = sec_dirs_deg[ns*2] * SAF_PI/180.0f;
750 elev_sec = sec_dirs_deg[ns*2+1] * SAF_PI/180.0f; /* from elevation to inclination */
751 rotateAxisCoeffsReal(orderSec, b_n, SAF_PI/2.0f-elev_sec, azi_sec, c_nm);
752 beamWeightsVelocityPatternsReal(orderSec, b_n, azi_sec, elev_sec, A_xyz, xyz_nm);
753
754 /* store coefficients */
755 for(j=0; j<nSH; j++){
756 sectorCoeffs[ns*4*nSH + 0*nSH +j] = sqrtf(normSec) * c_nm[j];
757 for(i=0; i<3; i++)
758 sectorCoeffs[ns*4*nSH + (i+1)*nSH +j] = sqrtf(normSec) * xyz_nm[j*3+i];
759 }
760 }
761
762 free(b_n);
763 free(c_nm);
764 free(xyz_nm);
765 }
766 return normSec;
767}
768
770(
771 int orderSec,
772 float_complex* A_xyz,
773 SECTOR_PATTERNS pattern,
774 float* sec_dirs_deg,
775 int nSecDirs,
776 float* sectorCoeffs
777)
778{
779 int i, j, ns, orderVel, nSH;
780 float normSec, azi_sec, elev_sec;
781 float* b_n, *c_nm, *xyz_nm;
782
783 if(orderSec==0){
784 memcpy(sectorCoeffs, wxyzCoeffs, 16*sizeof(float)); /* ACN/N3D to WXYZ */
785 normSec = 1.0f;
786 }
787 else{
788 orderVel = orderSec+1;
789 nSH = (orderSec+2)*(orderSec+2);
790 b_n = malloc1d((orderSec+1)*sizeof(float));
791 c_nm = calloc1d((orderVel+1)*(orderVel+1), sizeof(float)); /* pad with zeros */
792 xyz_nm = malloc1d((orderVel+1)*(orderVel+1)*3*sizeof(float));
793 switch(pattern){
794 case SECTOR_PATTERN_PWD: beamWeightsHypercardioid2Spherical(orderSec, b_n); break;
795 case SECTOR_PATTERN_MAXRE: beamWeightsMaxEV(orderSec, b_n); break;
796 case SECTOR_PATTERN_CARDIOID: beamWeightsCardioid2Spherical(orderSec, b_n); break;
797 }
798 normSec = (float)(orderSec+1)/(float)nSecDirs;
799
800 for(ns=0; ns<nSecDirs; ns++){
801 /* rotate the pattern by rotating the coefficients */
802 azi_sec = sec_dirs_deg[ns*2] * SAF_PI/180.0f;
803 elev_sec = sec_dirs_deg[ns*2+1] * SAF_PI/180.0f;
804 rotateAxisCoeffsReal(orderSec, b_n, SAF_PI/2.0f-elev_sec, azi_sec, c_nm);
805 beamWeightsVelocityPatternsReal(orderSec, b_n, azi_sec, elev_sec, A_xyz, xyz_nm);
806
807 /* store coefficients */
808 for(j=0; j<nSH; j++){
809 sectorCoeffs[ns*4*nSH + 0*nSH +j] = normSec * c_nm[j];
810 for(i=0; i<3; i++)
811 sectorCoeffs[ns*4*nSH + (i+1)*nSH +j] = normSec * xyz_nm[j*3+i];
812 }
813 }
814
815 free(b_n);
816 free(c_nm);
817 free(xyz_nm);
818 }
819 return normSec;
820}
821
823(
824 int N,
825 float* b_n
826)
827{
828 int n;
829
830 /* The coefficients can be derived by the binomial expansion of the cardioid function */
831 for(n=0; n<N+1; n++) {
832 b_n[n] = sqrtf(4.0f*SAF_PI*(2.0f*(float)n+1.0f)) *
833 (float)factorial(N)* (float)factorial(N+1)/
834 ((float)factorial(N+n+1)*(float)factorial(N-n))/
835 ((float)N+1.0f);
836 }
837}
838
840(
841 int N,
842 float* b_n
843)
844{
845 int n;
846 float* c_n;
847 float dirs_rad[2] = {0.0f, 0.0f};
848
849 c_n = malloc1d((N+1)*(N+1)*sizeof(float));
850 getSHreal(N, dirs_rad, 1, c_n);
851 for(n=0; n<N+1; n++)
852 b_n[n] = c_n[(n+1)*(n+1)-n-1] * 4.0f * SAF_PI/(powf((float)N+1.0f, 2.0f));
853
854 free(c_n);
855}
856
858(
859 int N,
860 float* b_n
861)
862{
863 int n;
864 float norm;
865 double temp_i;
866 double* temp_o;
867
868 temp_o = malloc1d( (N+1)*sizeof(double));
869 norm = 0.0f;
870 for (n=0; n<=N; n++) {
871 temp_i = cos(2.4068f/((double)N+1.51));
872 unnorm_legendreP(n, &temp_i, 1, temp_o);
873 b_n[n] = sqrtf((2.0f*(float)n+1.0f)/(4.0f*SAF_PI))*(float)temp_o[0];
874 norm += sqrtf((2.0f*(float)n+1.0f)/(4.0f*SAF_PI))*b_n[n];
875 }
876
877 /* normalise to unity response on look-direction */
878 for (n=0; n<=N; n++)
879 b_n[n] /= norm;
880
881 free(temp_o);
882}
883
885(
886 int order,
887 float* b_n,
888 float azi_rad,
889 float elev_rad,
890 float_complex* A_xyz,
891 float* velCoeffs
892)
893{
894 int nSH;
895 float_complex* velCoeffs_c;
896
897 nSH = ORDER2NSH(order+1);
898 velCoeffs_c = malloc1d(nSH*3*sizeof(float_complex));
899 beamWeightsVelocityPatternsComplex(order, b_n, azi_rad, elev_rad, A_xyz, velCoeffs_c);
900 complex2realCoeffs(order+1, velCoeffs_c, 3, velCoeffs);
901
902 free(velCoeffs_c);
903}
904
906(
907 int order,
908 float* b_n,
909 float azi_rad,
910 float elev_rad,
911 float_complex* A_xyz,
912 float_complex* velCoeffs
913)
914{
915 int i, j, d3, nSH_l, nSH;
916 float_complex* c_nm, *A_1, *velCoeffs_T;
917 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
918
919 nSH_l = ORDER2NSH(order);
920 nSH = ORDER2NSH(order+1);
921 c_nm = malloc1d(nSH_l*sizeof(float_complex));
922 A_1 = malloc1d(nSH*nSH_l*sizeof(float_complex));
923 velCoeffs_T = malloc1d(3*nSH*sizeof(float_complex));
924 rotateAxisCoeffsComplex(order, b_n, SAF_PI/2.0f-elev_rad, azi_rad, c_nm);
925
926 /* x_nm, y_nm, z_nm */
927 for(d3 = 0; d3<3; d3++){
928 for(i=0; i<nSH; i++)
929 for(j=0; j<nSH_l; j++)
930 A_1[i*nSH_l+j] = A_xyz[i*nSH_l*3 + j*3 + d3];
931 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, 1, nSH_l, &calpha,
932 A_1, nSH_l,
933 c_nm, 1, &cbeta,
934 &velCoeffs_T[d3*nSH], 1);
935 }
936 for(d3 = 0; d3<3; d3++)
937 for(i=0; i<nSH; i++)
938 velCoeffs[i*3+d3] = velCoeffs_T[d3*nSH+i]; /* transpose */
939
940 free(c_nm);
941 free(A_1);
942 free(velCoeffs_T);
943}
944
946(
947 int order,
948 float* c_n,
949 float theta_0, /* inclination*/
950 float phi_0, /* azimuth */
951 float* c_nm
952)
953{
954 int nSH;
955 float_complex* c_nm_c;
956
957 nSH = ORDER2NSH(order);
958 c_nm_c = malloc1d(nSH*sizeof(float_complex));
959 rotateAxisCoeffsComplex(order, c_n, theta_0, phi_0, c_nm_c);
960 complex2realCoeffs(order, c_nm_c, 1, c_nm);
961
962 free(c_nm_c);
963}
964
966(
967 int order,
968 float* c_n,
969 float theta_0, /* inclination*/
970 float phi_0, /* azimuth */
971 float_complex* c_nm
972)
973{
974 int n, m, q, nSH;
975 float phi_theta[2];
976 float_complex* Y_N;
977
978 phi_theta[0] = phi_0;
979 phi_theta[1] = theta_0;
980 nSH = ORDER2NSH(order);
981 Y_N = malloc1d(nSH*sizeof(float_complex));
982 getSHcomplex(order, (float*)phi_theta, 1, Y_N);
983 for(n=0, q = 0; n<=order; n++)
984 for(m=-n; m<=n; m++, q++)
985 c_nm[q] = crmulf(conjf(Y_N[q]), sqrtf(4.0f*SAF_PI/(2.0f*(float)n+1.0f)) * c_n[n]);
986
987 free(Y_N);
988}
989
991(
992 int order,
993 float* dirs_rad,
994 int nDirs,
995 float* w,
996 float* cond_N
997)
998{
999 int n, i, j, nSH, nSH_n, ind;
1000 float minVal, maxVal;
1001 float *YY_n, *W, *W_Yn, *s;
1002 float** Y_N, **Y_n;
1003
1004 /* get SH */
1005 nSH = ORDER2NSH(order);
1006 Y_N = (float**)malloc2d(nSH, nDirs, sizeof(float));
1007 Y_n = (float**)malloc2d(nDirs, nSH, sizeof(float));
1008 YY_n = malloc1d(nSH*nSH*sizeof(float));
1009 getSHreal(order, dirs_rad, nDirs, FLATTEN2D(Y_N));
1010
1011 /* diagonalise the integration weights, if available */
1012 if(w!=NULL){
1013 W = calloc1d(nDirs*nDirs, sizeof(float));
1014 W_Yn = malloc1d(nDirs*nSH*sizeof(float));
1015 for(i=0; i<nDirs; i++)
1016 W[i*nDirs+i] = w[i];
1017 }
1018 else{
1019 W = NULL;
1020 W_Yn = NULL;
1021 }
1022
1023 /* compute the condition number for each order up to N */
1024 s = malloc1d(nSH*sizeof(float));
1025 for(n=0; n<=order; n++){
1026 nSH_n = (n+1)*(n+1);
1027 for(i=0; i<nDirs; i++)
1028 for(j=0; j<nSH_n; j++)
1029 Y_n[i][j] = Y_N[j][i]; /* truncate to current order and transpose */
1030 if(w==NULL){
1031 cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans, nSH_n, nSH_n, nDirs, 1.0f,
1032 FLATTEN2D(Y_n), nSH,
1033 FLATTEN2D(Y_n), nSH, 0.0f,
1034 YY_n, nSH_n);
1035 }
1036 else{
1037 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nDirs, nSH_n, nDirs, 1.0f,
1038 W, nDirs,
1039 FLATTEN2D(Y_n), nSH, 0.0f,
1040 W_Yn, nSH_n);
1041 cblas_sgemm(CblasRowMajor, CblasTrans, CblasNoTrans, nSH_n, nSH_n, nDirs, 1.0f,
1042 FLATTEN2D(Y_n), nSH,
1043 W_Yn, nSH_n, 0.0f,
1044 YY_n, nSH_n);
1045 }
1046
1047 /* condition number = max(singularValues)/min(singularValues) */
1048 utility_ssvd(NULL, YY_n, nSH_n, nSH_n, NULL, NULL, NULL, s);
1049 utility_simaxv(s, nSH_n, &ind);
1050 maxVal = s[ind];
1051 utility_siminv(s, nSH_n, &ind);
1052 minVal = s[ind];
1053 cond_N[n] = maxVal/(minVal+2.23e-7f);
1054 }
1055
1056 free(Y_N);
1057 free(Y_n);
1058 free(YY_n);
1059 free(W);
1060 free(W_Yn);
1061 free(s);
1062}
1063
1064
1066(
1067 float* dirs_rad,
1068 int nDirs,
1069 int order,
1070 float* w
1071)
1072{
1073 int i, j, nSH;
1074 float sumW;
1075 float** Y_N, **Y_N_T, **Y_leftinv;
1076
1077 if(order<0){
1078 int nSH, ind;
1079 float minVal, maxVal, cond_N;
1080
1081 float *YY_N, *s;
1082
1083 /* get SH */
1084 s = NULL;
1085 Y_N = NULL;
1086 YY_N = NULL;
1087
1088 for(int n=1; n<100; n++){
1089 /* compute the condition number for order N */
1090 nSH = ORDER2NSH(n);
1091 Y_N = (float**)realloc2d((void**)Y_N, nSH, nDirs, sizeof(float));
1092 YY_N = (float*)realloc1d(YY_N, nSH*nSH*sizeof(float));
1093 s = (float*) realloc1d(s, nSH*sizeof(float));
1094 getSHreal(n, dirs_rad, nDirs, FLATTEN2D(Y_N));
1095
1096 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, nSH, nDirs, 1.0f,
1097 FLATTEN2D(Y_N), nDirs,
1098 FLATTEN2D(Y_N), nDirs, 0.0f,
1099 YY_N, nSH);
1100
1101 /* condition number = max(singularValues)/min(singularValues) */
1102 utility_ssvd(NULL, YY_N, nSH, nSH, NULL, NULL, NULL, s);
1103 utility_simaxv(s, nSH, &ind);
1104 maxVal = s[ind];
1105 utility_siminv(s, nSH, &ind);
1106 minVal = s[ind];
1107 cond_N = maxVal/(minVal+2.23e-7f);
1108
1109 if(cond_N > 2 * (n + 1)){ // experimental condition
1110 order = n-1;
1111 break;
1112 }
1113
1114 /* Hard limit */
1115 if(n>30){
1116 order = n-1;
1117 break;
1118 }
1119 }
1120 }
1121 if(order<1) // could not find order
1122 order=0;
1123
1124 nSH = ORDER2NSH(order);
1125 Y_N = (float**)malloc2d(nSH, nDirs, sizeof(float));
1126 Y_N_T = (float**)malloc2d(nDirs, nSH, sizeof(float));
1127 Y_leftinv = (float**)malloc2d(nSH, nDirs, sizeof(float));
1128
1129 getSHreal(order, dirs_rad, nDirs, FLATTEN2D(Y_N));
1130
1131 for(i=0; i<nDirs; i++)
1132 for(j=0; j<nSH; j++)
1133 Y_N_T[i][j] = Y_N[j][i]; /* truncate to current order and transpose */
1134
1135 utility_spinv(NULL, FLATTEN2D(Y_N_T), nDirs, nSH, FLATTEN2D(Y_leftinv));
1136
1137 sumW=0.f;
1138 for(int idx=0; idx<nDirs; idx++){
1139 w[idx] = sqrtf(FOURPI)*Y_leftinv[0][idx];
1140 sumW += w[idx];
1141 }
1142
1143 if(fabs(sumW - FOURPI) > 0.001){
1144 order=0;
1145 saf_print_warning("Grid weights no bueno!");
1146 }
1147 return order;
1148}
1149
1150/* ========================================================================== */
1151/* Localisation Functions in the SHD */
1152/* ========================================================================== */
1153
1155(
1156 void ** const phPWD,
1157 int order,
1158 float* grid_dirs_deg,
1159 int nDirs
1160)
1161{
1162 *phPWD = malloc1d(sizeof(sphPWD_data));
1163 sphPWD_data *h = (sphPWD_data*)(*phPWD);
1164 int i, j;
1165 float** grid_dirs_rad, **grid_svecs_tmp;
1166
1167 h->order = order;
1168 h->nSH = ORDER2NSH(h->order);
1169 h->nDirs = nDirs;
1170
1171 /* steering vectors for each grid direction */
1172 h->grid_svecs = malloc1d(h->nSH * (h->nDirs) * sizeof(float_complex));
1173 grid_dirs_rad = (float**)malloc2d(h->nDirs, 2, sizeof(float));
1174 grid_svecs_tmp = (float**)malloc2d(h->nSH, h->nDirs, sizeof(float));
1175 for(i=0; i<h->nDirs; i++){
1176 grid_dirs_rad[i][0] = grid_dirs_deg[i*2]*SAF_PI/180.0f;
1177 grid_dirs_rad[i][1] = SAF_PI/2.0f - grid_dirs_deg[i*2+1]*SAF_PI/180.0f;
1178 }
1179 getSHreal(h->order, FLATTEN2D(grid_dirs_rad), h->nDirs, FLATTEN2D(grid_svecs_tmp));
1180 for(i=0; i<h->nSH; i++)
1181 for(j=0; j<h->nDirs; j++)
1182 h->grid_svecs[j*(h->nSH)+i] = cmplxf(grid_svecs_tmp[i][j], 0.0f);
1183
1184 /* store cartesian coords of scanning directions (for optional peak finding) */
1185 h->grid_dirs_xyz = malloc1d(h->nDirs * 3 * sizeof(float));
1186 unitSph2cart(grid_dirs_deg, h->nDirs, 1, h->grid_dirs_xyz);
1187
1188 /* for run-time */
1189 h->A_Cx = malloc1d((h->nSH) * sizeof(float_complex));
1190 h->pSpec = malloc1d(h->nDirs*sizeof(float));
1191 h->P_minus_peak = malloc1d(h->nDirs*sizeof(float));
1192 h->VM_mask = malloc1d(h->nDirs*sizeof(float));
1193 h->P_tmp = malloc1d(h->nDirs*sizeof(float));
1194
1195 /* clean-up */
1196 free(grid_dirs_rad);
1197 free(grid_svecs_tmp);
1198}
1199
1201(
1202 void ** const phPWD
1203)
1204{
1205 sphPWD_data *h = (sphPWD_data*)(*phPWD);
1206
1207 if (h != NULL) {
1208 free(h->grid_dirs_xyz);
1209 free(h->grid_svecs);
1210 free(h->A_Cx);
1211 free(h->pSpec);
1212 free(h->P_minus_peak);
1213 free(h->P_tmp);
1214 free(h->VM_mask);
1215 free(h);
1216 h = NULL;
1217 *phPWD = NULL;
1218 }
1219}
1220
1222(
1223 void* const hPWD,
1224 float_complex* Cx,
1225 int nSrcs,
1226 float* P_map,
1227 int* peak_inds
1228)
1229{
1230 sphPWD_data *h = (sphPWD_data*)(hPWD);
1231 int i, k, peak_idx;
1232 float kappa, scale;
1233 float VM_mean[3];
1234 float_complex A_Cx_A;
1235 const float_complex calpha = cmplxf(1.0f, 0.0f); const float_complex cbeta = cmplxf(0.0f, 0.0f);
1236
1237 /* derive the power-map value for each grid direction */
1238 for (i = 0; i < (h->nDirs); i++){
1239 cblas_cgemv(CblasRowMajor, CblasNoTrans, h->nSH, h->nSH, &calpha,
1240 Cx, h->nSH,
1241 &(h->grid_svecs[i*(h->nSH)]), 1, &cbeta,
1242 h->A_Cx, 1);
1243 cblas_cdotu_sub(h->nSH, h->A_Cx, 1, &(h->grid_svecs[i*(h->nSH)]), 1, &A_Cx_A);
1244 h->pSpec[i] = crealf(A_Cx_A);
1245 }
1246
1247 /* Output power-map */
1248 if(P_map!=NULL)
1249 cblas_scopy(h->nDirs, h->pSpec, 1, P_map, 1);
1250
1251 /* Peak-finding */
1252 if(peak_inds!=NULL){
1253 kappa = 50.0f;
1254 scale = kappa/(2.0f*SAF_PI*expf(kappa)-expf(-kappa));
1255 cblas_scopy(h->nDirs, h->pSpec, 1, h->P_minus_peak, 1);
1256
1257 /* Loop over the number of sources */
1258 for(k=0; k<nSrcs; k++){
1259 utility_simaxv(h->P_minus_peak, h->nDirs, &peak_idx);
1260 peak_inds[k] = peak_idx;
1261 if(k==nSrcs-1)
1262 break;
1263 VM_mean[0] = h->grid_dirs_xyz[peak_idx*3];
1264 VM_mean[1] = h->grid_dirs_xyz[peak_idx*3+1];
1265 VM_mean[2] = h->grid_dirs_xyz[peak_idx*3+2];
1266
1267 /* Apply mask for next iteration */
1268 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, h->nDirs, 1, 3, 1.0f,
1269 h->grid_dirs_xyz, 3,
1270 (const float*)VM_mean, 3, 0.0f,
1271 h->VM_mask, 1);
1272 cblas_sscal(h->nDirs, kappa, h->VM_mask, 1);
1273 for(i=0; i<h->nDirs; i++)
1274 h->VM_mask[i] = expf(h->VM_mask[i]); /* VM distribution */
1275 cblas_sscal(h->nDirs, scale, h->VM_mask, 1);
1276 for(i=0; i<h->nDirs; i++)
1277 h->VM_mask[i] = 1.0f/(0.00001f+(h->VM_mask[i])); /* inverse VM distribution */
1278 utility_svvmul(h->P_minus_peak, h->VM_mask, h->nDirs, h->P_tmp);
1279 cblas_scopy(h->nDirs, h->P_tmp, 1, h->P_minus_peak, 1);
1280 }
1281 }
1282}
1283
1285(
1286 void ** const phMUSIC,
1287 int order,
1288 float* grid_dirs_deg,
1289 int nDirs
1290)
1291{
1292 *phMUSIC = malloc1d(sizeof(sphMUSIC_data));
1293 sphMUSIC_data *h = (sphMUSIC_data*)(*phMUSIC);
1294 int i, j;
1295 float** grid_dirs_rad, **grid_svecs_tmp;
1296
1297 h->order = order;
1298 h->nSH = ORDER2NSH(h->order);
1299 h->nDirs = nDirs;
1300
1301 /* steering vectors for each grid direction */
1302 h->grid_svecs = malloc1d(h->nSH * (h->nDirs) * sizeof(float_complex));
1303 grid_dirs_rad = (float**)malloc2d(h->nDirs, 2, sizeof(float));
1304 grid_svecs_tmp = (float**)malloc2d(h->nSH, h->nDirs, sizeof(float));
1305 for(i=0; i<h->nDirs; i++){
1306 grid_dirs_rad[i][0] = grid_dirs_deg[i*2]*SAF_PI/180.0f;
1307 grid_dirs_rad[i][1] = SAF_PI/2.0f - grid_dirs_deg[i*2+1]*SAF_PI/180.0f;
1308 }
1309 getSHreal(h->order, FLATTEN2D(grid_dirs_rad), h->nDirs, FLATTEN2D(grid_svecs_tmp));
1310 for(i=0; i<h->nSH; i++)
1311 for(j=0; j<h->nDirs; j++)
1312 h->grid_svecs[i*(h->nDirs)+j] = cmplxf(grid_svecs_tmp[i][j], 0.0f);
1313
1314 /* store cartesian coords of scanning directions (for optional peak finding) */
1315 h->grid_dirs_xyz = malloc1d(h->nDirs * 3 * sizeof(float));
1316 unitSph2cart(grid_dirs_deg, h->nDirs, 1, h->grid_dirs_xyz);
1317
1318 /* for run-time */
1319 h->VnA = malloc1d(h->nSH * (h->nDirs) * sizeof(float_complex));
1320 h->abs_VnA = malloc1d(h->nSH * (h->nDirs) * sizeof(float));
1321 h->pSpec = malloc1d(h->nDirs*sizeof(float));
1322 h->pSpecInv = malloc1d(h->nDirs*sizeof(float));
1323 h->P_minus_peak = malloc1d(h->nDirs*sizeof(float));
1324 h->P_tmp = malloc1d(h->nDirs*sizeof(float));
1325 h->VM_mask = malloc1d(h->nDirs*sizeof(float));
1326
1327 /* clean-up */
1328 free(grid_dirs_rad);
1329 free(grid_svecs_tmp);
1330}
1331
1333(
1334 void ** const phMUSIC
1335)
1336{
1337 sphMUSIC_data *h = (sphMUSIC_data*)(*phMUSIC);
1338
1339 if (h != NULL) {
1340 free(h->grid_dirs_xyz);
1341 free(h->grid_svecs);
1342 free(h->VnA);
1343 free(h->abs_VnA);
1344 free(h->pSpec);
1345 free(h->pSpecInv);
1346 free(h->P_minus_peak);
1347 free(h->P_tmp);
1348 free(h->VM_mask);
1349 free(h);
1350 h = NULL;
1351 *phMUSIC = NULL;
1352 }
1353}
1354
1356(
1357 void* const hMUSIC,
1358 float_complex *Vn, /* nSH x (nSH - nSrcs) */
1359 int nSrcs,
1360 float* P_music,
1361 int* peak_inds
1362)
1363{
1364 sphMUSIC_data *h = (sphMUSIC_data*)(hMUSIC);
1365 int i, k, VnD2, peak_idx;
1366 float kappa, scale;
1367 float VM_mean[3];
1368 const float_complex calpha = cmplxf(1.0f, 0.0f); const float_complex cbeta = cmplxf(0.0f, 0.0f);
1369
1370 VnD2 = h->nSH - nSrcs; /* noise subspace second dimension length */
1371
1372 /* derive the pseudo-spectrum value for each grid direction */
1373 cblas_cgemm(CblasRowMajor, CblasTrans, CblasNoTrans, h->nDirs, VnD2, h->nSH, &calpha,
1374 h->grid_svecs, h->nDirs,
1375 Vn, VnD2, &cbeta,
1376 h->VnA, VnD2);
1377 utility_cvabs(h->VnA, (h->nDirs)*VnD2, h->abs_VnA);
1378 for (i = 0; i < (h->nDirs); i++)
1379 h->pSpecInv[i] = cblas_sdot(VnD2, &(h->abs_VnA[i*VnD2]), 1, &(h->abs_VnA[i*VnD2]), 1);
1380 //h->pSpec[i] = 1.0f / (h->pSpecInv[i] + 2.23e-10f);
1381 utility_svrecip(h->pSpecInv, h->nDirs, h->pSpec);
1382
1383 /* Output pseudo-spectrum */
1384 if(P_music!=NULL)
1385 cblas_scopy(h->nDirs, h->pSpec, 1, P_music, 1);
1386
1387 /* Peak-finding */
1388 if(peak_inds!=NULL){
1389 kappa = 50.0f;
1390 scale = kappa/(2.0f*SAF_PI*expf(kappa)-expf(-kappa));
1391 cblas_scopy(h->nDirs, h->pSpec, 1, h->P_minus_peak, 1);
1392
1393 /* Loop over the number of sources */
1394 for(k=0; k<nSrcs; k++){
1395 utility_simaxv(h->P_minus_peak, h->nDirs, &peak_idx);
1396 peak_inds[k] = peak_idx;
1397 if(k==nSrcs-1)
1398 break;
1399 VM_mean[0] = h->grid_dirs_xyz[peak_idx*3];
1400 VM_mean[1] = h->grid_dirs_xyz[peak_idx*3+1];
1401 VM_mean[2] = h->grid_dirs_xyz[peak_idx*3+2];
1402
1403 /* Apply mask for next iteration */
1404 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, h->nDirs, 1, 3, 1.0f,
1405 h->grid_dirs_xyz, 3,
1406 (const float*)VM_mean, 3, 0.0f,
1407 h->VM_mask, 1);
1408 cblas_sscal(h->nDirs, kappa, h->VM_mask, 1);
1409 for(i=0; i<h->nDirs; i++)
1410 h->VM_mask[i] = expf(h->VM_mask[i]); /* VM distribution */
1411 cblas_sscal(h->nDirs, scale, h->VM_mask, 1);
1412 for(i=0; i<h->nDirs; i++)
1413 h->VM_mask[i] = 1.0f/(0.00001f+(h->VM_mask[i])); /* inverse VM distribution */
1414 utility_svvmul(h->P_minus_peak, h->VM_mask, h->nDirs, h->P_tmp);
1415 cblas_scopy(h->nDirs, h->P_tmp, 1, h->P_minus_peak, 1);
1416 }
1417 }
1418}
1419
1421(
1422 void ** const phESPRIT,
1423 int order
1424)
1425{
1426 *phESPRIT = malloc1d(sizeof(sphESPRIT_data));
1427 sphESPRIT_data *h = (sphESPRIT_data*)(*phESPRIT);
1428 int i, j;
1429
1430 h->N = order;
1431 h->NN = order*order;
1432 h->maxK = h->NN;
1433
1434 /* pre-compute indices and matrices */
1435 for(i=0; i<6; i++){
1436 h->rWVnimu[i] = malloc1d((order*order) * (order*order) * sizeof(double));
1437 h->WVnimu[i] = malloc1d((order*order) * (order*order) * sizeof(double_complex));
1438 }
1439 h->nIdx[0] = h->nIdx[1] = h->nIdx[4] = h->nIdx[5] = h->nIdx[10] = h->nIdx[11] = (order*order);
1440 h->nIdx[2] = h->nIdx[3] = h->nIdx[6] = h->nIdx[7] = h->nIdx[8] = h->nIdx[9] = ((order-1)*(order-1));
1441 for(i=0; i<12; i++){
1442 if(h->nIdx[i] == 0)
1443 h->idx_from_Ynm2Ynimu[i] = NULL;
1444 else
1445 h->idx_from_Ynm2Ynimu[i] = calloc1d(h->nIdx[i], sizeof(int));
1446 }
1447 getWnimu(order, 1, 1,-1, h->rWVnimu[0]);
1448 getWnimu(order,-1, 0, 0, h->rWVnimu[1]);
1449 getWnimu(order,-1, 1,-1, h->rWVnimu[2]);
1450 getWnimu(order, 1, 0, 0, h->rWVnimu[3]);
1451 getVnimu(order, 0, 0, h->rWVnimu[4]);
1452 getVnimu(order, 1, 0, h->rWVnimu[5]);
1453 for(i=0; i<6; i++){
1454 for(j=0; j<(order*order) * (order*order); j++)
1455 h->WVnimu[i][j] = cmplx(h->rWVnimu[i][j], 0.0);
1456 }
1457 muni2q(order, 1,-1, h->idx_from_Ynm2Ynimu[0], h->idx_from_Ynm2Ynimu[1]);
1458 muni2q(order,-1,-1, h->idx_from_Ynm2Ynimu[2], h->idx_from_Ynm2Ynimu[3]);
1459 muni2q(order, 1, 1, h->idx_from_Ynm2Ynimu[4], h->idx_from_Ynm2Ynimu[5]);
1460 muni2q(order,-1, 1, h->idx_from_Ynm2Ynimu[6], h->idx_from_Ynm2Ynimu[7]);
1461 muni2q(order,-1, 0, h->idx_from_Ynm2Ynimu[8], h->idx_from_Ynm2Ynimu[9]);
1462 muni2q(order, 1, 0, h->idx_from_Ynm2Ynimu[10], h->idx_from_Ynm2Ynimu[11]);
1463
1464 /* memory allocations for run-time matrices */
1465 utility_zpinv_create(&(h->hZpinv), h->maxK, h->maxK);
1466 utility_zeigmp_create(&(h->hZeigmp), h->maxK);
1467 utility_zglslv_create(&(h->hZglslv), h->maxK, h->maxK);
1468 h->Us_1m1 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1469 h->Us_m1m1 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1470 h->Us_11 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1471 h->Us_m11 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1472 h->Us_m10 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1473 h->Us_10 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1474 h->Us_00 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1475 h->WVnimu0_Us1m1 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1476 h->WVnimu1_Usm1m1 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1477 h->WVnimu2_Us11 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1478 h->WVnimu3_Usm11 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1479 h->WVnimu4_Usm10 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1480 h->WVnimu5_Us10 = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1481 h->LambdaXYp = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1482 h->LambdaXYm = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1483 h->LambdaZ = malloc1d((h->NN) * (h->maxK) * sizeof(double_complex));
1484 h->pinvUs = malloc1d((h->maxK) * (h->NN) * sizeof(double_complex));
1485 h->PsiXYp = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1486 h->PsiXYm = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1487 h->PsiZ = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1488 h->tmp_KK = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1489 h->V = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1490 h->PhiXYp = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1491 h->PhiXYm = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1492 h->PhiZ = malloc1d((h->maxK) * (h->maxK) * sizeof(double_complex));
1493}
1494
1496(
1497 void ** const phESPRIT
1498)
1499{
1500 sphESPRIT_data *h = (sphESPRIT_data*)(*phESPRIT);
1501 int i;
1502
1503 if (h != NULL) {
1504 for(i=0; i<6; i++){
1505 free(h->rWVnimu[i]);
1506 free(h->WVnimu[i]);
1507 }
1508 for(i=0; i<12; i++)
1509 free(h->idx_from_Ynm2Ynimu[i]);
1510 utility_zpinv_destroy(&(h->hZpinv));
1511 utility_zeigmp_destroy(&(h->hZeigmp));
1512 utility_zglslv_destroy(&(h->hZglslv));
1513 free(h->Us_1m1);
1514 free(h->Us_m1m1);
1515 free(h->Us_11);
1516 free(h->Us_m11);
1517 free(h->Us_m10);
1518 free(h->Us_10);
1519 free(h->Us_00);
1520 free(h->WVnimu0_Us1m1);
1521 free(h->WVnimu1_Usm1m1);
1522 free(h->WVnimu2_Us11);
1523 free(h->WVnimu3_Usm11);
1524 free(h->WVnimu4_Usm10);
1525 free(h->WVnimu5_Us10);
1526 free(h->LambdaXYp);
1527 free(h->LambdaXYm);
1528 free(h->LambdaZ);
1529 free(h->pinvUs);
1530 free(h->PsiXYp);
1531 free(h->PsiXYm);
1532 free(h->PsiZ);
1533 free(h->tmp_KK);
1534 free(h->V);
1535 free(h->PhiXYp);
1536 free(h->PhiXYm);
1537 free(h->PhiZ);
1538
1539 free(h);
1540 h = NULL;
1541 *phESPRIT = NULL;
1542 }
1543}
1544
1546(
1547 void * const hESPRIT,
1548 float_complex* Us, /* nSH * K */
1549 int K,
1550 float* src_dirs_rad /* K x 2 */
1551)
1552{
1553 sphESPRIT_data *h = (sphESPRIT_data*)(hESPRIT);
1554 int i, j;
1555 const double_complex i2_ = cmplx(0.0, 2.0);
1556 const double_complex calpha = cmplx(1.0, 0.0); const double_complex cbeta = cmplx(0.0, 0.0); /* blas */
1557 double phiX, phiY;
1558
1559 /* Fill matrices */
1560 memset(h->Us_1m1, 0, (h->NN) * K * sizeof(double_complex));
1561 memset(h->Us_m1m1, 0, (h->NN) * K * sizeof(double_complex));
1562 memset(h->Us_11, 0, (h->NN) * K * sizeof(double_complex));
1563 memset(h->Us_m11, 0, (h->NN) * K * sizeof(double_complex));
1564 memset(h->Us_m10, 0, (h->NN) * K * sizeof(double_complex));
1565 memset(h->Us_10, 0, (h->NN) * K * sizeof(double_complex));
1566 memset(h->Us_00, 0, (h->NN) * K * sizeof(double_complex));
1567 for (i = 0; i < K; i++) {
1568 for (j = 0; j < h->nIdx[0]; j++)
1569 h->Us_1m1[h->idx_from_Ynm2Ynimu[0][j] * K + i] = cmplx(crealf(Us[h->idx_from_Ynm2Ynimu[1][j] * K + i]), cimagf(Us[h->idx_from_Ynm2Ynimu[1][j] * K + i]));
1570 for (j = 0; j < h->nIdx[2]; j++)
1571 h->Us_m1m1[h->idx_from_Ynm2Ynimu[2][j] * K + i] = cmplx(crealf(Us[h->idx_from_Ynm2Ynimu[3][j] * K + i]), cimagf(Us[h->idx_from_Ynm2Ynimu[3][j] * K + i]));
1572 for (j = 0; j < h->nIdx[4]; j++)
1573 h->Us_11[h->idx_from_Ynm2Ynimu[4][j] * K + i] = cmplx(crealf(Us[h->idx_from_Ynm2Ynimu[5][j] * K + i]), cimagf(Us[h->idx_from_Ynm2Ynimu[5][j] * K + i]));
1574 for (j = 0; j < h->nIdx[6]; j++)
1575 h->Us_m11[h->idx_from_Ynm2Ynimu[6][j] * K + i] = cmplx(crealf(Us[h->idx_from_Ynm2Ynimu[7][j] * K + i]), cimagf(Us[h->idx_from_Ynm2Ynimu[7][j] * K + i]));
1576 for (j = 0; j < h->nIdx[8]; j++)
1577 h->Us_m10[h->idx_from_Ynm2Ynimu[8][j] * K + i] = cmplx(crealf(Us[h->idx_from_Ynm2Ynimu[9][j] * K + i]), cimagf(Us[h->idx_from_Ynm2Ynimu[9][j] * K + i]));
1578 for (j = 0; j < h->nIdx[10]; j++)
1579 h->Us_10[h->idx_from_Ynm2Ynimu[10][j] * K + i] = cmplx(crealf(Us[h->idx_from_Ynm2Ynimu[11][j] * K + i]), cimagf(Us[h->idx_from_Ynm2Ynimu[11][j] * K + i]));
1580 for (j = 0; j < (h->NN); j++)
1581 h->Us_00[j*K + i] = cmplx(crealf(Us[j*K + i]), cimagf(Us[j*K + i]));
1582 }
1583
1584 /* */
1585 cblas_zgemm(CblasRowMajor, CblasTrans, CblasNoTrans, (h->NN), K, (h->NN), &calpha,
1586 h->WVnimu[0], (h->NN),
1587 h->Us_1m1, K, &cbeta,
1588 h->WVnimu0_Us1m1, K);
1589 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, (h->NN), K, (h->NN), &calpha,
1590 h->WVnimu[1], (h->NN),
1591 h->Us_m1m1, K, &cbeta,
1592 h->WVnimu1_Usm1m1, K);
1593 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, (h->NN), K, (h->NN), &calpha,
1594 h->WVnimu[2], (h->NN),
1595 h->Us_11, K, &cbeta,
1596 h->WVnimu2_Us11, K);
1597 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, (h->NN), K, (h->NN), &calpha,
1598 h->WVnimu[3], (h->NN),
1599 h->Us_m11, K, &cbeta,
1600 h->WVnimu3_Usm11, K);
1601 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, (h->NN), K, (h->NN), &calpha,
1602 h->WVnimu[4], (h->NN),
1603 h->Us_m10, K, &cbeta,
1604 h->WVnimu4_Usm10, K);
1605 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, (h->NN), K, (h->NN), &calpha,
1606 h->WVnimu[5], (h->NN),
1607 h->Us_10, K, &cbeta,
1608 h->WVnimu5_Us10, K);
1609 utility_zvvsub(h->WVnimu0_Us1m1, h->WVnimu1_Usm1m1, h->NN*K, h->LambdaXYp);
1610 cblas_dscal(/*re+im*/2*h->NN*K, -1.0, (double*)h->WVnimu2_Us11, 1);
1611 utility_zvvadd(h->WVnimu2_Us11, h->WVnimu3_Usm11, h->NN*K, h->LambdaXYm);
1612 utility_zvvadd(h->WVnimu4_Usm10, h->WVnimu5_Us10, h->NN*K, h->LambdaZ);
1613
1614 /* */
1615 utility_zpinv(h->hZpinv, h->Us_00, h->NN, K, h->pinvUs);
1616 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, K, K, (h->NN), &calpha,
1617 h->pinvUs, (h->NN),
1618 h->LambdaXYp, K, &cbeta,
1619 h->PsiXYp, K);
1620 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, K, K, (h->NN), &calpha,
1621 h->pinvUs, (h->NN),
1622 h->LambdaXYm, K, &cbeta,
1623 h->PsiXYm, K);
1624 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, K, K, (h->NN), &calpha,
1625 h->pinvUs, (h->NN),
1626 h->LambdaZ, K, &cbeta,
1627 h->PsiZ, K);
1628
1629 /* */
1630 utility_zeigmp(h->hZeigmp, h->PsiXYp, h->PsiZ, K, NULL, h->V, NULL);
1631 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, K, K, K, &calpha,
1632 h->PsiXYp, K,
1633 h->V, K, &cbeta,
1634 h->tmp_KK, K);
1635 utility_zglslv(h->hZglslv, h->V, K, h->tmp_KK, K, h->PhiXYp);
1636 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, K, K, K, &calpha,
1637 h->PsiXYm, K,
1638 h->V, K, &cbeta,
1639 h->tmp_KK, K);
1640 utility_zglslv(h->hZglslv, h->V, K, h->tmp_KK, K, h->PhiXYm);
1641 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, K, K, K, &calpha,
1642 h->PsiZ, K,
1643 h->V, K, &cbeta,
1644 h->tmp_KK, K);
1645 utility_zglslv(h->hZglslv, h->V, K, h->tmp_KK, K, h->PhiZ);
1646
1647 /* extract DoAs */
1648 for(i=0; i<K; i++){
1649 phiX = (creal(h->PhiXYp[i*K+i])+creal(h->PhiXYm[i*K+i]))/2.0;
1650 phiY = creal(ccdiv(ccsub(h->PhiXYp[i*K+i], h->PhiXYm[i*K+i]), i2_));
1651 src_dirs_rad[i*2] = (float)atan2(phiY, phiX);
1652 src_dirs_rad[i*2+1] = (float)SAF_MIN(atan2(creal(h->PhiZ[i*K+i]), sqrt(phiX*phiX+phiY*phiY)), SAF_PI/2.0f);
1653 }
1654}
1655
1657(
1658 int order,
1659 float_complex* Cx,
1660 float_complex* Y_grid,
1661 int nGrid_dirs,
1662 float* pmap
1663)
1664{
1665 int i, j, nSH;
1666 float_complex* Cx_Y, *Y_Cx_Y, *Cx_Y_s, *Y_grid_s;
1667 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
1668
1669 nSH = ORDER2NSH(order);
1670 Cx_Y = malloc1d(nSH * nGrid_dirs * sizeof(float_complex));
1671 Y_Cx_Y = malloc1d(nGrid_dirs*sizeof(float_complex));
1672 Cx_Y_s = malloc1d(nSH*sizeof(float_complex));
1673 Y_grid_s = malloc1d(nSH*sizeof(float_complex));
1674
1675 /* Calculate PWD powermap: real(diag(Y_grid.'*C_x*Y_grid)) */
1676 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nGrid_dirs, nSH, &calpha,
1677 Cx, nSH,
1678 Y_grid, nGrid_dirs, &cbeta,
1679 Cx_Y, nGrid_dirs);
1680 for(i=0; i<nGrid_dirs; i++){
1681 for(j=0; j<nSH; j++){
1682 Cx_Y_s[j] = Cx_Y[j*nGrid_dirs+i];
1683 Y_grid_s[j] = Y_grid[j*nGrid_dirs+i];
1684 }
1685 /* faster to perform the dot-product for each vector seperately */
1686 utility_cvvdot(Y_grid_s, Cx_Y_s, nSH, NO_CONJ, &Y_Cx_Y[i]);
1687 }
1688
1689 for(i=0; i<nGrid_dirs; i++)
1690 pmap[i] = crealf(Y_Cx_Y[i]);
1691
1692 free(Cx_Y);
1693 free(Y_Cx_Y);
1694 free(Cx_Y_s);
1695 free(Y_grid_s);
1696}
1697
1699(
1700 int order,
1701 float_complex* Cx,
1702 float_complex* Y_grid,
1703 int nGrid_dirs,
1704 float regPar,
1705 float* pmap,
1706 float_complex* w_MVDR_out
1707)
1708{
1709 int i, j, nSH;
1710 float Cx_trace;
1711 float_complex *Cx_d, *invCx_Ygrid, *w_MVDR, *invCx_Ygrid_s, *Y_grid_s;
1712 float_complex denum;
1713
1714 nSH = ORDER2NSH(order);
1715 w_MVDR = malloc1d(nSH * nGrid_dirs*sizeof(float_complex));
1716 Cx_d = malloc1d(nSH*nSH*sizeof(float_complex));
1717 invCx_Ygrid = malloc1d(nSH*nGrid_dirs*sizeof(float_complex));
1718 invCx_Ygrid_s = malloc1d(nSH*sizeof(float_complex));
1719 Y_grid_s = malloc1d(nSH*sizeof(float_complex));
1720
1721 /* apply diagonal loading */
1722 Cx_trace = 0.0f;
1723 for(i=0; i<nSH; i++)
1724 Cx_trace += crealf(Cx[i*nSH+i]);
1725 Cx_trace /= (float)nSH;
1726 memcpy(Cx_d, Cx, nSH*nSH*sizeof(float_complex));
1727 for(i=0; i<nSH; i++)
1728 Cx_d[i*nSH+i] = craddf(Cx_d[i*nSH+i], regPar*Cx_trace);
1729
1730 /* solve the numerator part of the MVDR weights for all grid directions: Cx^-1 * Y */
1731 utility_cslslv(NULL, Cx_d, nSH, Y_grid, nGrid_dirs, invCx_Ygrid);
1732 for(i=0; i<nGrid_dirs; i++){
1733 /* solve the denumerator part of the MVDR weights for each grid direction: Y^T * Cx^-1 * Y */
1734 for(j=0; j<nSH; j++){
1735 invCx_Ygrid_s[j] = conjf(invCx_Ygrid[j*nGrid_dirs+i]);
1736 Y_grid_s[j] = Y_grid[j*nGrid_dirs+i];
1737 }
1738 /* faster to perform the dot-product for each vector seperately */
1739 utility_cvvdot(Y_grid_s, invCx_Ygrid_s, nSH, NO_CONJ, &denum);
1740
1741 /* calculate the MVDR weights per grid direction: (Cx^-1 * Y) * (Y^T * Cx^-1 * Y)^-1 */
1742 for(j=0; j<nSH; j++)
1743 w_MVDR[j*nGrid_dirs +i] = ccdivf(invCx_Ygrid[j*nGrid_dirs +i], denum);
1744 }
1745
1746 /* generate MVDR powermap, by using the generatePWDmap function with the MVDR weights instead */
1747 generatePWDmap(order, Cx, w_MVDR, nGrid_dirs, pmap);
1748
1749 /* optional output of the beamforming weights */
1750 if (w_MVDR_out!=NULL)
1751 memcpy(w_MVDR_out, w_MVDR, nSH * nGrid_dirs*sizeof(float_complex));
1752
1753 free(w_MVDR);
1754 free(Cx_d);
1755 free(invCx_Ygrid);
1756 free(invCx_Ygrid_s);
1757 free(Y_grid_s);
1758}
1759
1760/* EXPERIMENTAL
1761 * Delikaris-Manias, S., Vilkamo, J., & Pulkki, V. (2016). Signal-dependent spatial filtering based on
1762 * weighted-orthogonal beamformers in the spherical harmonic domain. IEEE/ACM Transactions on Audio,
1763 * Speech and Language Processing (TASLP), 24(9), 1507-1519. */
1765(
1766 int order,
1767 float_complex* Cx,
1768 float_complex* Y_grid,
1769 int nGrid_dirs,
1770 float regPar,
1771 float lambda,
1772 float* pmap
1773)
1774{
1775 int i, j, k, nSH;
1776 float Cx_trace, S, G;
1777 float* mvdr_map;
1778 float_complex* Cx_d, *A, *invCxd_A, *invCxd_A_tmp, *w_LCMV_s, *w_CroPaC, *wo, *Cx_Y, *Cx_Y_s;
1779 float_complex b[2];
1780 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
1781 float_complex A_invCxd_A[2][2];
1782 float_complex Y_wo_xspec;
1783
1784 b[0] = cmplxf(1.0f, 0.0f);
1785 b[1] = cmplxf(0.0f, 0.0f);
1786 nSH = ORDER2NSH(order);
1787 Cx_Y = malloc1d(nSH * nGrid_dirs * sizeof(float_complex));
1788 Cx_d = malloc1d(nSH*nSH*sizeof(float_complex));
1789 A = malloc1d(nSH*2*sizeof(float_complex));
1790 invCxd_A = malloc1d(nSH*2*sizeof(float_complex));
1791 invCxd_A_tmp = malloc1d(nSH*2*sizeof(float_complex));
1792 w_LCMV_s = malloc1d(2*nGrid_dirs*sizeof(float_complex));
1793 w_CroPaC = malloc1d(nSH*nGrid_dirs*sizeof(float_complex));
1794 wo = malloc1d(nSH*sizeof(float_complex));
1795 mvdr_map = malloc1d(nGrid_dirs*sizeof(float));
1796 Cx_Y_s = malloc1d(nSH*sizeof(float_complex));
1797
1798 /* generate MVDR map and weights to use as a basis */
1799 generateMVDRmap(order, Cx, Y_grid, nGrid_dirs, regPar, mvdr_map, w_CroPaC);
1800
1801 /* first half of the cross-spectrum */
1802 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nGrid_dirs, nSH, &calpha,
1803 Cx, nSH,
1804 Y_grid, nGrid_dirs, &cbeta,
1805 Cx_Y, nGrid_dirs);
1806
1807 /* apply diagonal loading to cov matrix */
1808 Cx_trace = 0.0f;
1809 for(i=0; i<nSH; i++)
1810 Cx_trace += crealf(Cx[i*nSH+i]);
1811 Cx_trace /= (float)nSH;
1812 memcpy(Cx_d, Cx, nSH*nSH*sizeof(float_complex));
1813 for(i=0; i<nSH; i++)
1814 Cx_d[i*nSH+i] = craddf(Cx_d[i*nSH+i], regPar*Cx_trace);
1815
1816 /* calculate CroPaC beamforming weights for each grid direction */
1817 for(i=0; i<nGrid_dirs; i++){
1818 for(j=0; j<nSH; j++){
1819 A[j*2] = Y_grid[j*nGrid_dirs+i];
1820 A[j*2+1] = ccmulf(A[j*2], Cx[j*nSH+j]);
1821 }
1822
1823 /* solve for minimisation problem for LCMV weights: (Cx^-1 * A) * (A^H * Cx^-1 * A)^-1 * b */
1824 utility_cslslv(NULL, Cx_d, nSH, A, 2, invCxd_A);
1825 for(j=0; j<nSH*2; j++)
1826 invCxd_A_tmp[j] = conjf(invCxd_A[j]);
1827 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans, 2, 2, nSH, &calpha,
1828 A, 2,
1829 invCxd_A_tmp, 2, &cbeta,
1830 A_invCxd_A, 2);
1831 for(j=0; j<nSH; j++)
1832 for(k=0; k<2; k++)
1833 invCxd_A_tmp[k*nSH+j] = invCxd_A[j*2+k];
1834 utility_cglslv(NULL, (float_complex*)A_invCxd_A, 2, invCxd_A_tmp, nSH, w_LCMV_s);
1835 cblas_cgemm(CblasRowMajor, CblasTrans, CblasNoTrans, nSH, 1, 2, &calpha,
1836 w_LCMV_s, nSH,
1837 b, 1, &cbeta,
1838 wo, 1);
1839
1840 /* calculate the cross-spectrum between static beam Y, and adaptive beam wo (LCMV) */
1841 for(j=0; j<nSH; j++)
1842 Cx_Y_s[j] = Cx_Y[j*nGrid_dirs+i];
1843 utility_cvvdot(wo, Cx_Y_s, nSH, NO_CONJ, &Y_wo_xspec);
1844
1845 /* derive CroPaC weights */
1846 S = SAF_MIN(cabsf(Y_wo_xspec), mvdr_map[i]); /* ensures distortionless response */
1847 G = sqrtf(S/(mvdr_map[i]+2.23e-10f));
1848 G = SAF_MAX(lambda, G); /* optional spectral floor parameter, to control harshness of attenuation (good for demos) */
1849 for(j=0; j<nSH; j++)
1850 w_CroPaC[j*nGrid_dirs + i] = crmulf(w_CroPaC[j*nGrid_dirs + i], G);
1851 }
1852
1853 /* generate CroPaC powermap, by using the generatePWDmap function with the CroPaC weights instead */
1854 generatePWDmap(order, Cx, w_CroPaC, nGrid_dirs, pmap);
1855
1856 free(mvdr_map);
1857 free(Cx_d);
1858 free(A);
1859 free(invCxd_A);
1860 free(invCxd_A_tmp);
1861 free(w_LCMV_s);
1862 free(w_CroPaC);
1863 free(wo);
1864 free(Cx_Y);
1865 free(Cx_Y_s);
1866}
1867
1869(
1870 int order,
1871 float_complex* Cx,
1872 float_complex* Y_grid,
1873 int nSources,
1874 int nGrid_dirs,
1875 int logScaleFlag,
1876 float* pmap
1877)
1878{
1879 int i, j, nSH;
1880 float_complex* V, *Vn, *Vn_Y;
1881 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
1882 float_complex tmp;
1883
1884 nSH = ORDER2NSH(order);
1885 nSources = SAF_MIN(nSources, nSH/2);
1886 V = malloc1d(nSH*nSH*sizeof(float_complex));
1887 Vn = malloc1d(nSH*(nSH-nSources)*sizeof(float_complex));
1888 Vn_Y = malloc1d((nSH-nSources)*nGrid_dirs*sizeof(float_complex));
1889
1890 /* obtain eigenvectors */
1891 //utility_ceig(Cx, nSH, 1, NULL, V, NULL, NULL);
1892 utility_cseig(NULL, Cx, nSH, 1, V, NULL, NULL);
1893
1894 /* truncate, to obtain noise sub-space */
1895 for (i = 0; i < nSH; i++)
1896 for (j = 0; j < nSH - nSources; j++)
1897 Vn[i*(nSH - nSources) + j] = V[i*nSH + j + nSources];
1898
1899 /* derive the pseudo-spectrum value for each grid direction */
1900 cblas_cgemm(CblasRowMajor, CblasTrans, CblasNoTrans, nSH-nSources, nGrid_dirs, nSH, &calpha,
1901 Vn, nSH-nSources,
1902 Y_grid, nGrid_dirs, &cbeta,
1903 Vn_Y, nGrid_dirs);
1904 for(i=0; i<nGrid_dirs; i++){
1905 tmp = cmplxf(0.0f,0.0f);
1906 for(j=0; j<nSH-nSources; j++)
1907 tmp = ccaddf(tmp, ccmulf(conjf(Vn_Y[j*nGrid_dirs+i]),Vn_Y[j*nGrid_dirs+i]));
1908 pmap[i] = logScaleFlag ? logf(1.0f/(crealf(tmp)+2.23e-10f)) : 1.0f/(crealf(tmp)+2.23e-10f);
1909 }
1910
1911 free(V);
1912 free(Vn);
1913 free(Vn_Y);
1914}
1915
1917(
1918 int order,
1919 float_complex* Cx,
1920 float_complex* Y_grid,
1921 int nSources,
1922 int nGrid_dirs,
1923 int logScaleFlag,
1924 float* pmap
1925)
1926{
1927 int i, j, nSH;
1928 float_complex* V, *Vn, *Vn1, *Un, *Un_Y;
1929 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
1930 float_complex Vn1_Vn1H;
1931
1932 nSH = ORDER2NSH(order);
1933 nSources = SAF_MIN(nSources, nSH/2);
1934 V = malloc1d(nSH*nSH*sizeof(float_complex));
1935 Vn = malloc1d(nSH*(nSH-nSources)*sizeof(float_complex));
1936 Vn1 = malloc1d((nSH-nSources)*sizeof(float_complex));
1937 Un = malloc1d(nSH*sizeof(float_complex));
1938 Un_Y = malloc1d(nGrid_dirs*sizeof(float_complex));
1939
1940 /* obtain eigenvectors */
1941 utility_ceig(NULL, Cx, nSH, NULL, V, NULL, NULL);
1942
1943 /* truncate, to obtain noise sub-space */
1944 for(i=0; i<nSH; i++)
1945 for(j=0; j<nSH-nSources; j++)
1946 Vn[i*(nSH-nSources)+j] = V[i*nSH + j + nSources];
1947 for(j=0; j<nSH-nSources; j++)
1948 Vn1[j] = V[j + nSources];
1949
1950 /* derive the pseudo-spectrum value for each grid direction */
1951 utility_cvvdot(Vn1, Vn1, nSH-nSources, NO_CONJ, &Vn1_Vn1H);
1952 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, 1, nSH-nSources, &calpha,
1953 Vn, nSH-nSources,
1954 Vn1, nSH-nSources, &cbeta,
1955 Un, 1);
1956 for(i=0; i<nSH; i++)
1957 Un[i] = ccdivf(Un[i], craddf(Vn1_Vn1H, 2.23e-9f));
1958 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans, 1, nGrid_dirs, nSH, &calpha,
1959 Un, 1,
1960 Y_grid, nGrid_dirs, &cbeta,
1961 Un_Y, nGrid_dirs);
1962 for(i=0; i<nGrid_dirs; i++)
1963 pmap[i] = logScaleFlag ? logf(1.0f/(powf(cabsf(Un_Y[i]),2.0f) + 2.23e-9f)) : 1.0f/(powf(cabsf(Un_Y[i]),2.0f) + 2.23e-9f);
1964
1965 free(V);
1966 free(Vn);
1967 free(Vn1);
1968 free(Un);
1969 free(Un_Y);
1970}
1971
1972
1973/* ========================================================================== */
1974/* Microphone/Hydrophone array processing functions */
1975/* ========================================================================== */
1976
1978(
1979 ARRAY_SHT_OPTIONS method,
1980 int order,
1981 float amp_thresh_dB,
1982 float_complex* H_array, //nBins x nMics x nGrid
1983 float* grid_dirs_deg,
1984 int nBins,
1985 int nMics,
1986 int nGrid,
1987 float* w_grid,
1988 float_complex* H_sht //nBins x nSH x nMics
1989)
1990{
1991 int i, kk, nSH, order_grid, nSH_grid;
1992 float alpha, beta;
1993 float* grid_dirs_rad, *Ytmp;
1994 float_complex* W, *Y_grid, *HW, *YW;
1995 float_complex *YWH_H, *HWH_H, *inv_HWH_H; /* LS */
1996 float_complex* HWY_T, *YWY_T, *inv_YWY_T, *Q, *QQ_H, *inv_QQ_H; /* LSHD */
1997 void* hInv;
1998 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
1999
2000 /* Checks */
2001 saf_assert(order<=sqrt(nMics)-1, "Order exceeds maximum possible for this number of sensors.");
2002
2003 /* Grid weights */
2004 W = calloc1d(nGrid*nGrid,sizeof(float_complex));
2005 for(i=0; i<nGrid; i++)
2006 W[i*nGrid+i] = w_grid==NULL ? calpha : cmplxf(w_grid[i], 0.0f);
2007
2008 /* Grid order and allocations */
2009 nSH = ORDER2NSH(order);
2010 HWY_T = YWY_T = inv_YWY_T = Q = QQ_H = inv_QQ_H = NULL;
2011 YW = YWH_H = HW = HWH_H = inv_HWH_H = NULL;
2012 switch (method){
2013 case ARRAY_SHT_DEFAULT: /* fall through */
2014 case ARRAY_SHT_REG_LS:
2015 order_grid = order;
2016 nSH_grid = ORDER2NSH(order_grid);
2017
2018 /* Intermediaries */
2019 YWH_H = malloc1d(nSH*nMics*sizeof(float_complex));
2020 HWH_H = malloc1d(nMics*nMics*sizeof(float_complex));
2021 inv_HWH_H = malloc1d(nMics*nMics*sizeof(float_complex));
2022 break;
2023
2024 case ARRAY_SHT_REG_LSHD:
2025 order_grid = (int)(sqrtf((float)nGrid)/2.0f-1.0f);
2026 nSH_grid = ORDER2NSH(order_grid);
2027
2028 /* Intermediaries */
2029 HWY_T = malloc1d(nMics*nSH_grid*sizeof(float_complex));
2030 YWY_T = malloc1d(nSH_grid*nSH_grid*sizeof(float_complex));
2031 inv_YWY_T = malloc1d(nSH_grid*nSH_grid*sizeof(float_complex));
2032 Q = malloc1d(nMics*nSH_grid*sizeof(float_complex));
2033 QQ_H = malloc1d(nMics*nMics*sizeof(float_complex));
2034 inv_QQ_H = malloc1d(nMics*nMics*sizeof(float_complex));
2035 break;
2036 }
2037 HW = malloc1d(nMics*nGrid*sizeof(float_complex));
2038 YW = malloc1d(nSH_grid*nGrid*sizeof(float_complex));
2039
2040 /* SH basis */
2041 grid_dirs_rad = malloc1d(nGrid*2*sizeof(float));
2042 for(i=0; i<nGrid; i++){
2043 grid_dirs_rad[i*2+0] = SAF_PI/180.0f * grid_dirs_deg[i*2+0];
2044 grid_dirs_rad[i*2+1] = SAF_PI/2.0f - SAF_PI/180.0f * grid_dirs_deg[i*2+1];
2045 }
2046 Ytmp = malloc1d(nSH_grid*nGrid*sizeof(float));
2047 getSHreal(order_grid, grid_dirs_rad, nGrid, Ytmp);
2048 cblas_sscal(nSH_grid*nGrid, SQRT4PI, Ytmp, 1);
2049 Y_grid = calloc1d(nSH_grid*nGrid, sizeof(float_complex));
2050 cblas_scopy(nSH_grid*nGrid, Ytmp, 1, (float*)Y_grid, 2);
2051
2052 /* Regularisation parameters */
2053 alpha = powf(10.0f, amp_thresh_dB/20.0f);
2054 beta = 1.0f/(2.0f*alpha);
2055
2056 /* Loop over frequency */
2057 utility_cinv_create(&hInv, SAF_MAX(nSH_grid, nMics));
2058 for (kk=0; kk<nBins; kk++){
2059 switch (method){
2060 case ARRAY_SHT_DEFAULT: /* fall through */
2061 case ARRAY_SHT_REG_LS:
2062 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH_grid, nGrid, nGrid, &calpha,
2063 Y_grid, nGrid,
2064 W, nGrid, &cbeta,
2065 YW, nGrid);
2066 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH_grid, nMics, nGrid, &calpha,
2067 YW, nGrid,
2068 &H_array[kk*nMics*nGrid], nGrid, &cbeta,
2069 YWH_H, nMics);
2070 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nMics, nGrid, nGrid, &calpha,
2071 &H_array[kk*nMics*nGrid], nGrid,
2072 W, nGrid, &cbeta,
2073 HW, nGrid);
2074 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nMics, nMics, nGrid, &calpha,
2075 HW, nGrid,
2076 &H_array[kk*nMics*nGrid], nGrid, &cbeta,
2077 HWH_H, nMics);
2078
2079 /* Tikhonov regularised inversion */
2080 for(i=0; i<nMics; i++)
2081 HWH_H[i*nMics+i] = craddf(HWH_H[i*nMics+i], beta*beta);
2082 utility_cinv(hInv, HWH_H, inv_HWH_H, nMics);
2083 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nMics, nMics, &calpha,
2084 YWH_H, nMics,
2085 inv_HWH_H, nMics, &cbeta,
2086 &H_sht[kk*nSH*nMics], nMics);
2087 break;
2088
2089 case ARRAY_SHT_REG_LSHD:
2090 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nMics, nGrid, nGrid, &calpha,
2091 &H_array[kk*nMics*nGrid], nGrid,
2092 W, nGrid, &cbeta,
2093 HW, nGrid);
2094 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nMics, nSH_grid, nGrid, &calpha,
2095 HW, nGrid,
2096 Y_grid, nGrid, &cbeta,
2097 HWY_T, nSH_grid);
2098 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH_grid, nGrid, nGrid, &calpha,
2099 Y_grid, nGrid,
2100 W, nGrid, &cbeta,
2101 YW, nGrid);
2102 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH_grid, nSH_grid, nGrid, &calpha,
2103 YW, nGrid,
2104 Y_grid, nGrid, &cbeta,
2105 YWY_T, nSH_grid);
2106 utility_cinv(hInv, YWY_T, inv_YWY_T, nSH_grid);
2107 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nMics, nSH_grid, nSH_grid, &calpha,
2108 HWY_T, nSH_grid,
2109 inv_YWY_T, nSH_grid, &cbeta,
2110 Q, nSH_grid);
2111
2112 /* Apply Tikhonov regularised inversion in the SHD */
2113 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nMics, nMics, nSH_grid, &calpha,
2114 Q, nSH_grid,
2115 Q, nSH_grid, &cbeta,
2116 QQ_H, nMics);
2117 for(i=0; i<nMics; i++)
2118 QQ_H[i*nMics+i] = craddf(QQ_H[i*nMics+i], beta*beta);
2119 utility_cinv(hInv, QQ_H, inv_QQ_H, nMics);
2120 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans, nSH /*truncated here*/, nMics, nMics, &calpha,
2121 Q, nSH_grid,
2122 inv_QQ_H, nMics, &cbeta,
2123 &H_sht[kk*nSH*nMics], nMics);
2124 break;
2125 }
2126 }
2127
2128 /* clean-up */
2129 free(W);
2130 free(grid_dirs_rad);
2131 free(Ytmp);
2132 free(Y_grid);
2133 switch (method){
2134 case ARRAY_SHT_DEFAULT: /* fall through */
2135 case ARRAY_SHT_REG_LS:
2136 free(YWH_H);
2137 free(HWH_H);
2138 free(inv_HWH_H);
2139 break;
2140 case ARRAY_SHT_REG_LSHD:
2141 free(HWY_T);
2142 free(YWY_T);
2143 free(inv_YWY_T);
2144 free(Q);
2145 free(QQ_H);
2146 free(inv_QQ_H);
2147 break;
2148 }
2149 free(YW);
2150 free(HW);
2151 utility_cinv_destroy(&hInv);
2152}
2153
2155(
2156 ARRAY_SHT_OPTIONS method,
2157 int order,
2158 float amp_thresh_dB,
2159 float_complex* H_array,
2160 float* grid_dirs_deg,
2161 int nFFT,
2162 int nMics,
2163 int nGrid,
2164 float* w_grid,
2165 float* h_sht
2166)
2167{
2168 int i, j, k, nBins, nSH;
2169 float_complex* H_sht, *H_sht_bins;
2170 void* hSafFFT;
2171
2172 /* compute decoding matrix per bin */
2173 nBins = nFFT/2 + 1;
2174 nSH = ORDER2NSH(order);
2175 H_sht = malloc1d(nBins*nSH*nMics*sizeof(float_complex));
2176 arraySHTmatrices(method, order, amp_thresh_dB, H_array, grid_dirs_deg, nBins, nMics, nGrid, w_grid, H_sht);
2177
2178 /* ifft, to obtain time-domain filters */
2179 H_sht_bins = malloc1d(nBins*sizeof(float_complex));
2180 saf_rfft_create(&hSafFFT, nFFT);
2181 for(i=0; i<nSH; i++){
2182 for(j=0; j<nMics; j++){
2183 for(k=0; k<nBins; k++)
2184 H_sht_bins[k] = H_sht[k*nSH*nMics + i*nMics + j];
2185 saf_rfft_backward(hSafFFT, H_sht_bins, &h_sht[i*nMics*nFFT + j*nFFT]);
2186 }
2187 }
2188
2189 /* clean-up */
2190 saf_rfft_destroy(&hSafFFT);
2191 free(H_sht);
2192 free(H_sht_bins);
2193}
2194
2196(
2197 float_complex* H_sht,
2198 float_complex* DCM,
2199 float* freqVector,
2200 float alias_freq_hz,
2201 int nBins,
2202 int order,
2203 int nMics,
2204 float_complex* H_sht_eq
2205)
2206{
2207 int i, kk, idxf_alias, nSH;
2208 float_complex* HD, *HDH_H, *EQ;
2209 float* L_diff_fal;
2210 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
2211
2212 /* Prep */
2213 nSH = ORDER2NSH(order);
2214 HD = malloc1d(nSH*nMics*sizeof(float_complex));
2215 HDH_H = malloc1d(nSH*nSH*sizeof(float_complex));
2216 L_diff_fal = malloc1d(nSH*sizeof(float));
2217 EQ = calloc1d(nSH*nSH, sizeof(float_complex));
2218
2219 /* Channel energies under diffuse conditions for the aliasing frequency limit */
2220 idxf_alias = 0;
2221 while(freqVector[idxf_alias]<alias_freq_hz)
2222 idxf_alias++;
2223 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nMics, nMics, &calpha,
2224 &H_sht[idxf_alias*nSH*nMics], nMics,
2225 &DCM[idxf_alias*nMics*nMics], nMics, &cbeta,
2226 HD, nMics);
2227 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, nSH, nMics, &calpha,
2228 HD, nMics,
2229 &H_sht[idxf_alias*nSH*nMics], nMics, &cbeta,
2230 HDH_H, nSH);
2231 for(i=0; i<nSH; i++)
2232 L_diff_fal[i] = crealf(HDH_H[i*nSH+i]);
2233
2234 /* Loop over frequency */
2235 for(kk=0; kk<nBins; kk++){
2236 if(kk<=idxf_alias)
2237 cblas_ccopy(nSH*nMics, &H_sht[kk*nSH*nMics], 1, &H_sht_eq[kk*nSH*nMics], 1);
2238 else{
2239 /* Channel energies under diffuse conditions for this frequency */
2240 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nMics, nMics, &calpha,
2241 &H_sht[kk*nSH*nMics], nMics,
2242 &DCM[kk*nMics*nMics], nMics, &cbeta,
2243 HD, nMics);
2244 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nSH, nSH, nMics, &calpha,
2245 HD, nMics,
2246 &H_sht[kk*nSH*nMics], nMics, &cbeta,
2247 HDH_H, nSH);
2248
2249 /* Compute and apply EQ matrix */
2250 for(i=0; i<nSH; i++)
2251 EQ[i*nSH+i] = cmplxf(sqrtf(L_diff_fal[i]/crealf(HDH_H[i*nSH+i])), 0.0f);
2252 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nMics, nSH, &calpha,
2253 EQ, nSH,
2254 &H_sht[kk*nSH*nMics], nMics, &cbeta,
2255 &H_sht_eq[kk*nSH*nMics], nMics);
2256 }
2257 }
2258
2259 /* clean-up */
2260 free(HD);
2261 free(HDH_H);
2262 free(L_diff_fal);
2263 free(EQ);
2264}
2265
2267(
2268 int order,
2269 double* kr,
2270 int nBands,
2271 ARRAY_CONSTRUCTION_TYPES arrayType,
2272 double_complex* b_N
2273)
2274{
2275 int i, n;
2276 double* Jn, *Jnprime;
2277 double_complex* Hn2, *Hn2prime;
2278
2279 memset(b_N, 0, nBands*(order+1)*sizeof(double_complex));
2280 switch(arrayType){
2281 default:
2283 /* compute spherical Bessels of the first kind */
2284 Jn = malloc1d(nBands*(order+1)*sizeof(double));
2285 bessel_Jn_ALL(order, kr, nBands, Jn, NULL);
2286
2287 /* modal coefficients for open spherical array (omni sensors): 1i^n * jn; */
2288 for(n=0; n<order+1; n++)
2289 for(i=0; i<nBands; i++)
2290 b_N[i*(order+1)+n] = crmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), Jn[i*(order+1)+n]);
2291
2292 free(Jn);
2293 break;
2294
2296 /* compute spherical Bessels/Hankels and their derivatives */
2297 Jn = malloc1d(nBands*(order+1)*sizeof(double));
2298 Jnprime = malloc1d(nBands*(order+1)*sizeof(double));
2299 Hn2 = malloc1d(nBands*(order+1)*sizeof(double_complex));
2300 Hn2prime = malloc1d(nBands*(order+1)*sizeof(double_complex));
2301 bessel_Jn_ALL(order, kr, nBands, Jn, Jnprime);
2302 hankel_Hn2_ALL(order, kr, nBands, Hn2, Hn2prime);
2303
2304 /* modal coefficients for rigid spherical array: 1i^n * (jn-(jnprime./hn2prime).*hn2); */
2305 for(i=0; i<nBands; i++){
2306 for(n=0; n<order+1; n++){
2307 if(n==0 && kr[i]<=1e-20)
2308 b_N[i*(order+1)+n] = cmplx(1.0, 0.0);
2309 else if(kr[i] <= 1e-20)
2310 b_N[i*(order+1)+n] = cmplx(0.0, 0.0);
2311 else{
2312 b_N[i*(order+1)+n] = ccmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), ( ccsub(cmplx(Jn[i*(order+1)+n], 0.0),
2313 ccmul(ccdiv(cmplx(Jnprime[i*(order+1)+n],0.0), Hn2prime[i*(order+1)+n]), Hn2[i*(order+1)+n]))));
2314 }
2315 }
2316 }
2317
2318 free(Jn);
2319 free(Jnprime);
2320 free(Hn2);
2321 free(Hn2prime);
2322 break;
2323
2326 saf_print_error("Unsupported array type");
2327 break;
2328 }
2329}
2330
2332(
2333 float r,
2334 float c,
2335 int maxN
2336)
2337{
2338 return c*(float)maxN/(2.0f*SAF_PI*r);
2339}
2340
2342(
2343 int maxN,
2344 int Nsensors,
2345 float r,
2346 float c,
2347 ARRAY_CONSTRUCTION_TYPES arrayType,
2348 double dirCoeff,
2349 float maxG_db,
2350 float* f_lim
2351)
2352{
2353 int n;
2354 float kR_lim, maxG;
2355 double kr;
2356 double_complex* b_N;
2357
2358 maxG = powf(10.0f, maxG_db/10.0f);
2359 kr = 1.0f;
2360 for (n=1; n<maxN+1; n++){
2361 b_N = malloc1d((n+1) * sizeof(double_complex));
2362 sphModalCoeffs(n, &kr, 1, arrayType, dirCoeff, b_N);
2363 kR_lim = powf(maxG*(float)Nsensors* powf((float)cabs(b_N[n])/(4.0f*SAF_PI), 2.0f), (-10.0f*log10f(2.0f)/(6.0f*n)));
2364 f_lim[n-1] = kR_lim*c/(2.0f*SAF_PI*r);
2365 free(b_N);
2366 }
2367}
2368
2370(
2371 int order,
2372 double* kr,
2373 int nBands,
2374 ARRAY_CONSTRUCTION_TYPES arrayType,
2375 double dirCoeff,
2376 double_complex* b_N
2377)
2378{
2379 int i, n, maxN, maxN_tmp;
2380 double* jn, *jnprime;
2381 double_complex* hn2, *hn2prime;
2382
2383 memset(b_N, 0, nBands*(order+1)*sizeof(double_complex));
2384 switch(arrayType){
2385 default:
2387 /* compute spherical Bessels of the first kind */
2388 jn = malloc1d(nBands*(order+1)*sizeof(double));
2389 bessel_jn_ALL(order, kr, nBands, &maxN, jn, NULL);
2390
2391 /* modal coefficients for open spherical array (omni sensors): 4*pi*1i^n * jn; */
2392 for(n=0; n<maxN+1; n++)
2393 for(i=0; i<nBands; i++)
2394 b_N[i*(order+1)+n] = crmul(crmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), 4.0*SAF_PId), jn[i*(order+1)+n]);
2395
2396 free(jn);
2397 break;
2398
2400 /* compute spherical Bessels of the first kind + derivatives */
2401 jn = malloc1d(nBands*(order+1)*sizeof(double));
2402 jnprime = malloc1d(nBands*(order+1)*sizeof(double));
2403 bessel_jn_ALL(order, kr, nBands, &maxN, jn, jnprime);
2404
2405 /* modal coefficients for open spherical array (directional sensors): 4*pi*1i^n * (dirCoeff*jn - 1i*(1-dirCoeff)*jnprime); */
2406 for(n=0; n<maxN+1; n++)
2407 for(i=0; i<nBands; i++)
2408 b_N[i*(order+1)+n] = ccmul(crmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), 4.0*SAF_PId), ccsub(cmplx(dirCoeff*jn[i*(order+1)+n], 0.0),
2409 cmplx(0.0, (1.0-dirCoeff)*jnprime[i*(order+1)+n])) );
2410
2411 free(jn);
2412 free(jnprime);
2413 break;
2414
2416 /* equivalent to "ARRAY_CONSTRUCTION_RIGID", as long as the sensor radius is the same as the scatterer radius. Call
2417 "sphScattererModalCoeffs" or "sphScattererDirModalCoeffs", if sensors protrude from the rigid baffle. */
2418
2420 /* compute spherical Bessels/Hankels and their derivatives */
2421 jn = malloc1d(nBands*(order+1)*sizeof(double));
2422 jnprime = malloc1d(nBands*(order+1)*sizeof(double));
2423 hn2 = malloc1d(nBands*(order+1)*sizeof(double_complex));
2424 hn2prime = malloc1d(nBands*(order+1)*sizeof(double_complex));
2425 maxN = 1000000000;
2426 bessel_jn_ALL(order, kr, nBands, &maxN_tmp, jn, jnprime);
2427 maxN = SAF_MIN(maxN_tmp, maxN);
2428 hankel_hn2_ALL(order, kr, nBands, &maxN_tmp, hn2, hn2prime);
2429 maxN = SAF_MIN(maxN_tmp, maxN); /* maxN being the minimum highest order that was computed for all values in kr */
2430
2431 /* modal coefficients for rigid spherical array: 4*pi*1i^n * (jn-(jnprime./hn2prime).*hn2); */
2432 for(i=0; i<nBands; i++){
2433 for(n=0; n<maxN+1; n++){
2434 if(n==0 && kr[i]<=1e-20)
2435 b_N[i*(order+1)+n] = cmplx(4.0*SAF_PId, 0.0);
2436 else if(kr[i] <= 1e-20)
2437 b_N[i*(order+1)+n] = cmplx(0.0, 0.0);
2438 else{
2439 b_N[i*(order+1)+n] = ccmul(crmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), 4.0*SAF_PId), ( ccsub(cmplx(jn[i*(order+1)+n], 0.0),
2440 ccmul(ccdiv(cmplx(jnprime[i*(order+1)+n],0.0), hn2prime[i*(order+1)+n]), hn2[i*(order+1)+n]))));
2441 }
2442 }
2443 }
2444
2445 free(jn);
2446 free(jnprime);
2447 free(hn2);
2448 free(hn2prime);
2449 break;
2450 }
2451}
2452
2454(
2455 int order,
2456 double* kr,
2457 double* kR,
2458 int nBands,
2459 double_complex* b_N
2460)
2461{
2462 int i, n, maxN, maxN_tmp;
2463 double* jn, *jnprime;
2464 double_complex* hn2, *hn2prime;
2465
2466 /* compute spherical Bessels/Hankels and their derivatives */
2467 jn = malloc1d(nBands*(order+1)*sizeof(double));
2468 jnprime = malloc1d(nBands*(order+1)*sizeof(double));
2469 hn2 = malloc1d(nBands*(order+1)*sizeof(double_complex));
2470 hn2prime = malloc1d(nBands*(order+1)*sizeof(double_complex));
2471 maxN = 1000000000;
2472 bessel_jn_ALL(order, kr, nBands, &maxN_tmp, jn, NULL);
2473 maxN = SAF_MIN(maxN_tmp, maxN);
2474 bessel_jn_ALL(order, kR, nBands, &maxN_tmp, NULL, jnprime);
2475 maxN = SAF_MIN(maxN_tmp, maxN);
2476 hankel_hn2_ALL(order, kr, nBands, &maxN_tmp, hn2, NULL);
2477 maxN = SAF_MIN(maxN_tmp, maxN);
2478 hankel_hn2_ALL(order, kR, nBands, &maxN_tmp, NULL, hn2prime);
2479 maxN = SAF_MIN(maxN_tmp, maxN); /* maxN being the minimum highest order that was computed for all values in kr */
2480
2481 /* modal coefficients for rigid spherical array (OMNI): 4*pi*1i^n * (jn_kr-(jnprime_kr./hn2prime_kr).*hn2_kr); */
2482 /* modal coefficients for rigid spherical scatterer (OMNI): 4*pi*1i^n * (jn_kr-(jnprime_kR./hn2prime_kR).*hn2_kr); */
2483 for(i=0; i<nBands; i++){
2484 for(n=0; n<maxN+1; n++){
2485 if(n==0 && kr[i]<=1e-20)
2486 b_N[i*(order+1)+n] = cmplx(4.0*SAF_PId, 0.0);
2487 else if(kr[i] <= 1e-20)
2488 b_N[i*(order+1)+n] = cmplx(0.0, 0.0);
2489 else{
2490 b_N[i*(order+1)+n] = ccmul(crmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), 4.0*SAF_PId), ( ccsub(cmplx(jn[i*(order+1)+n], 0.0),
2491 ccmul(ccdiv(cmplx(jnprime[i*(order+1)+n],0.0), hn2prime[i*(order+1)+n]), hn2[i*(order+1)+n]))));
2492 }
2493 }
2494 }
2495
2496 free(jn);
2497 free(jnprime);
2498 free(hn2);
2499 free(hn2prime);
2500}
2501
2503(
2504 int order,
2505 double* kr,
2506 double* kR,
2507 int nBands,
2508 double dirCoeff, /* 0.0 gives NaNs */
2509 double_complex* b_N
2510)
2511{
2512 int i, n, maxN, maxN_tmp;
2513 double* jn_kr, *jnprime_kr, *jnprime_kR;
2514 double_complex* hn2_kr, *hn2prime_kr, *hn2prime_kR;
2515
2516 /* compute spherical Bessels/Hankels and their derivatives */
2517 jn_kr = malloc1d(nBands*(order+1)*sizeof(double));
2518 jnprime_kr = malloc1d(nBands*(order+1)*sizeof(double));
2519 jnprime_kR = malloc1d(nBands*(order+1)*sizeof(double));
2520 hn2_kr = malloc1d(nBands*(order+1)*sizeof(double_complex));
2521 hn2prime_kr = malloc1d(nBands*(order+1)*sizeof(double_complex));
2522 hn2prime_kR = malloc1d(nBands*(order+1)*sizeof(double_complex));
2523 maxN = 1000000000;
2524 bessel_jn_ALL(order, kr, nBands, &maxN_tmp, jn_kr, jnprime_kr);
2525 maxN = SAF_MIN(maxN_tmp, maxN);
2526 bessel_jn_ALL(order, kR, nBands, &maxN_tmp, NULL, jnprime_kR);
2527 maxN = SAF_MIN(maxN_tmp, maxN);
2528 hankel_hn2_ALL(order, kr, nBands, &maxN_tmp, hn2_kr, hn2prime_kr);
2529 maxN = SAF_MIN(maxN_tmp, maxN);
2530 hankel_hn2_ALL(order, kR, nBands, &maxN_tmp, NULL, hn2prime_kR);
2531 maxN = SAF_MIN(maxN_tmp, maxN); /* maxN being the minimum highest order that was computed for all values in kr */
2532
2533 /* modal coefficients for rigid spherical array (OMNI): 4*pi*1i^n * (jn_kr-(jnprime_kr./hn2prime_kr).*hn2_kr); */
2534 /* modal coefficients for rigid spherical scatterer (OMNI): 4*pi*1i^n * (jn_kr-(jnprime_kR./hn2prime_kR).*hn2_kr); */
2535 /* modal coefficients for rigid spherical scatterer (DIRECTIONAL):
2536 4*pi*1i^n * [ (beta*jn_kr - i(1-beta)*jnprime_kr) - (jnprime_kR/hn2prime_kR) * (beta*hn2_kr - i(1-beta)hn2prime_kr) ] */
2537 for(i=0; i<nBands; i++){
2538 for(n=0; n<maxN+1; n++){
2539 if(n==0 && kr[i]<=1e-20)
2540 b_N[i*(order+1)+n] = cmplx(4.0*SAF_PId, 0.0);
2541 else if(kr[i] <= 1e-20)
2542 b_N[i*(order+1)+n] = cmplx(0.0, 0.0);
2543 else{ // Dear god, what happened here?!...
2544//#if __STDC_VERSION__ >= 199901L
2545// b_N[i*(order+1)+n] = 4.0f*PI*cpowf(I,(float)n) * ( (dirCoeff*jn_kr[i*(order+1)+n] - I*(1.0f-dirCoeff)*jnprime_kr[i*(order+1)+n]) -
2546// (jnprime_kR[i*(order+1)+n]/hn2prime_kR[i*(order+1)+n]) * (dirCoeff*hn2_kr[i*(order+1)+n] -
2547// I*(1.0f-dirCoeff)*hn2prime_kr[i*(order+1)+n]) );
2548//#else
2549 b_N[i*(order+1)+n] = cmplx(dirCoeff * jn_kr[i*(order+1)+n], -(1.0-dirCoeff)* jnprime_kr[i*(order+1)+n]);
2550 b_N[i*(order+1)+n] = ccsub(b_N[i*(order+1)+n], ccmul(ccdiv(cmplx(jnprime_kR[i*(order+1)+n], 0.0), hn2prime_kR[i*(order+1)+n]),
2551 (ccsub(crmul(hn2_kr[i*(order+1)+n], dirCoeff), ccmul(cmplx(0.0f,1.0-dirCoeff), hn2prime_kr[i*(order+1)+n])))));
2552 b_N[i*(order+1)+n] = crmul(ccmul(cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)), b_N[i*(order+1)+n]), 4.0*SAF_PId/dirCoeff); /* had to scale by directivity to preserve amplitude? */
2553// b_N[i*(order+1)+n] = dirCoeff * jn_kr[i*(order+1)+n] - I*(1.0-dirCoeff)* jnprime_kr[i*(order+1)+n];
2554// b_N[i*(order+1)+n] = b_N[i*(order+1)+n] - (jnprime_kR[i*(order+1)+n]/hn2prime_kR[i*(order+1)+n])*(dirCoeff*hn2_kr[i*(order+1)+n] - I*(1.0-dirCoeff)*hn2prime_kr[i*(order+1)+n]);
2555// b_N[i*(order+1)+n] = cpow(cmplx(0.0,1.0), cmplx((double)n,0.0)) * b_N[i*(order+1)+n] * 4.0*SAF_PId/dirCoeff; /* had to scale by directivity to preserve amplitude? */
2556//#endif
2557 }
2558 }
2559 }
2560
2561 free(jn_kr);
2562 free(jnprime_kr);
2563 free(jnprime_kR);
2564 free(hn2_kr);
2565 free(hn2prime_kr);
2566 free(hn2prime_kR);
2567}
2568
2570(
2571 int order,
2572 float* sensor_dirs_rad,
2573 int N_sensors,
2574 ARRAY_CONSTRUCTION_TYPES arrayType,
2575 double dirCoeff,
2576 double* kr,
2577 int nBands,
2578 double* M_diffcoh
2579)
2580{
2581 int i, j, k, n;
2582 float cosangle;
2583 float* sensor_dirs_xyz, *ppm, *ppm_z1, *ppm_z2;
2584 double* b_N2, *Pn;
2585 double_complex* b_N;
2586
2587 /* sph->cart */
2588 sensor_dirs_xyz = malloc1d(N_sensors*3*sizeof(float));
2589 for(i=0; i<N_sensors; i++){
2590 sensor_dirs_xyz[i*3] = cosf(sensor_dirs_rad[i*2+1]) * cosf(sensor_dirs_rad[i*2]);
2591 sensor_dirs_xyz[i*3+1] = cosf(sensor_dirs_rad[i*2+1]) * sinf(sensor_dirs_rad[i*2]);
2592 sensor_dirs_xyz[i*3+2] = sinf(sensor_dirs_rad[i*2+1]);
2593 }
2594
2595 /* calculate modal coefficients */
2596 b_N = malloc1d(nBands * (order+1) * sizeof(double_complex));
2597 b_N2 = malloc1d(nBands * (order+1) * sizeof(double));
2598 switch (arrayType){
2600 sphModalCoeffs(order, kr, nBands, ARRAY_CONSTRUCTION_OPEN, 1.0, b_N); break;
2602 sphModalCoeffs(order, kr, nBands, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, dirCoeff, b_N); break;
2603 case ARRAY_CONSTRUCTION_RIGID: /* fall through */
2605 sphModalCoeffs(order, kr, nBands, ARRAY_CONSTRUCTION_RIGID, 1.0, b_N);
2606 break;
2607 }
2608 for(i=0; i<nBands * (order+1); i++)
2609 b_N2[i] = pow(cabs(ccdiv(b_N[i], cmplx(4.0*SAF_PId, 0.0))), 2.0);
2610
2611 /* determine theoretical diffuse-coherence matrix for sensor array */
2612 ppm = malloc1d((order+1)*sizeof(float));
2613 ppm_z1 = malloc1d((order+1)*sizeof(float));
2614 ppm_z2 = malloc1d((order+1)*sizeof(float));
2615 Pn = malloc1d((order+1)*sizeof(double));
2616 for(i=0; i<N_sensors; i++){
2617 for(j=i; j<N_sensors; j++){
2618 cosangle = 0.0f;
2619 for(k=0; k<3; k++)
2620 cosangle += sensor_dirs_xyz[j*3+k] * sensor_dirs_xyz[i*3+k];
2621 cosangle = cosangle>1.0f ? 1.0f : (cosangle<-1.0f ? -1.0f : cosangle);
2622 for(n=0; n<order+1; n++){
2623 unnorm_legendreP_recur(n, &cosangle, 1, ppm_z1, ppm_z2, ppm);
2624 Pn[n] = (2.0*(double)n+1.0) * 4.0f*SAF_PI * (double)ppm[0];
2625 memcpy(ppm_z2, ppm_z1, (order+1)*sizeof(float));
2626 memcpy(ppm_z1, ppm, (order+1)*sizeof(float));
2627 }
2628 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBands, 1, order+1, 1.0,
2629 b_N2, order+1,
2630 Pn, 1, 0.0,
2631 &M_diffcoh[j*N_sensors*nBands + i*nBands], 1);
2632
2633 memcpy(&M_diffcoh[i*N_sensors*nBands + j*nBands], &M_diffcoh[j*N_sensors*nBands + i*nBands], nBands*sizeof(double));
2634 }
2635 }
2636
2637 free(b_N);
2638 free(b_N2);
2639 free(sensor_dirs_xyz);
2640 free(ppm);
2641 free(ppm_z1);
2642 free(ppm_z2);
2643 free(Pn);
2644}
2645
2647(
2648 float_complex* H_array,
2649 int nBins,
2650 int nCH,
2651 int nGrid,
2652 float* w_grid,
2653 float_complex* M_diffcoh
2654)
2655{
2656 int kk, i;
2657 float_complex* W, *HW;
2658 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
2659
2660 /* Grid weights */
2661 W = calloc1d(nGrid*nGrid,sizeof(float_complex));
2662 for(i=0; i<nGrid; i++)
2663 W[i*nGrid+i] = w_grid==NULL ? calpha : cmplxf(w_grid[i], 0.0f);
2664
2665 /* Loop over frequency */
2666 HW = malloc1d(nCH*nGrid*sizeof(float_complex));
2667 for (kk=0; kk<nBins; kk++){
2668 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCH, nGrid, nGrid, &calpha,
2669 &H_array[kk*nCH*nGrid], nGrid,
2670 W, nGrid, &cbeta,
2671 HW, nGrid);
2672 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans, nCH, nCH, nGrid, &calpha,
2673 HW, nGrid,
2674 &H_array[kk*nCH*nGrid], nGrid, &cbeta,
2675 &M_diffcoh[kk*nCH*nCH], nCH);
2676 }
2677
2678 /* Clean-up */
2679 free(W);
2680 free(HW);
2681}
2682
2684(
2685 float* H_array,
2686 int nCH,
2687 int nGrid,
2688 float* w_grid,
2689 float* M_diffcoh
2690)
2691{
2692 int i;
2693 float* W, *HW;
2694
2695 /* Grid weights */
2696 W = calloc1d(nGrid*nGrid,sizeof(float));
2697 for(i=0; i<nGrid; i++)
2698 W[i*nGrid+i] = w_grid==NULL ? 1.0f : w_grid[i];
2699
2700 /* Loop over frequency */
2701 HW = malloc1d(nCH*nGrid*sizeof(float_complex));
2702 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nCH, nGrid, nGrid, 1.0f,
2703 H_array, nGrid,
2704 W, nGrid, 0.0f,
2705 HW, nGrid);
2706 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nCH, nCH, nGrid, 1.0f,
2707 HW, nGrid,
2708 H_array, nGrid, 0.0f,
2709 M_diffcoh, nCH);
2710
2711 /* Clean-up */
2712 free(W);
2713 free(HW);
2714}
2715
2716void simulateCylArray /*untested*/
2717(
2718 int order,
2719 double* kr,
2720 int nBands,
2721 float* sensor_dirs_rad,
2722 int N_sensors,
2723 float* src_dirs_deg,
2724 int N_srcs,
2725 ARRAY_CONSTRUCTION_TYPES arrayType,
2726 float_complex* H_array
2727)
2728{
2729 int i, j, n, band;
2730 double angle;
2731 double_complex* b_N, *C, *b_NC;
2732 const double_complex calpha = cmplx(1.0, 0.0), cbeta = cmplx(0.0, 0.0);
2733
2734 /* calculate modal coefficients */
2735 b_N = malloc1d(nBands * (order+1) * sizeof(double_complex));
2736 cylModalCoeffs(order, kr, nBands, arrayType, b_N);
2737
2738 /* Compute angular-dependent part of the array responses */
2739 C = malloc1d((order+1)*N_sensors*sizeof(double_complex));
2740 b_NC = malloc1d(nBands*N_sensors*sizeof(double_complex));
2741 for(i=0; i<N_srcs; i++){
2742 for(j=0; j<N_sensors; j++){
2743 angle = sensor_dirs_rad[i*2] - src_dirs_deg[i*2]*SAF_PId/180.0;
2744 for(n=0; n<order+1; n++){
2745 /* Jacobi-Anger expansion */
2746 if(n==0)
2747 C[n*N_sensors+j] = cmplx(1.0, 0.0);
2748 else
2749 C[n*N_sensors+j] = cmplx(2.0*cos((double)n*angle), 0.0);
2750 }
2751 }
2752 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBands, N_sensors, order+1, &calpha,
2753 b_N, order+1,
2754 C, N_sensors, &cbeta,
2755 b_NC, N_sensors);
2756
2757 /* store array response per frequency, sensors and plane wave dirs */
2758 for(band=0; band<nBands; band++)
2759 for(j=0; j<N_sensors; j++)
2760 H_array[band*N_sensors*N_srcs + j*N_srcs + i] = cmplxf((float)creal(b_NC[band*N_sensors+j]), (float)cimag(b_NC[band*N_sensors+j]));
2761 }
2762
2763 free(b_N);
2764 free(C);
2765 free(b_NC);
2766}
2767
2769(
2770 int order,
2771 double* kr,
2772 double* kR,
2773 int nBands,
2774 float* sensor_dirs_rad,
2775 int N_sensors,
2776 float* src_dirs_deg,
2777 int N_srcs,
2778 ARRAY_CONSTRUCTION_TYPES arrayType,
2779 double dirCoeff,
2780 float_complex* H_array
2781)
2782{
2783 int i, j, n, band;
2784 double dcosangle;
2785 double *ppm;
2786 float cosangle;
2787 float* U_sensors, *U_srcs;
2788 double_complex* b_N, *P, *b_NP;
2789 const double_complex calpha = cmplx(1.0, 0.0), cbeta = cmplx(0.0, 0.0);
2790
2791 /* calculate modal coefficients */
2792 b_N = malloc1d(nBands * (order+1) * sizeof(double_complex));
2793 switch (arrayType){
2795 sphModalCoeffs(order, kr, nBands, ARRAY_CONSTRUCTION_OPEN, 1.0, b_N); break;
2797 sphModalCoeffs(order, kr, nBands, ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL, dirCoeff, b_N); break;
2798 case ARRAY_CONSTRUCTION_RIGID: /* fall through */
2800 if(kR==NULL)
2801 sphModalCoeffs(order, kr, nBands, ARRAY_CONSTRUCTION_RIGID, 1.0, b_N); /* if kr==kR, dirCoeff is irrelevant */
2802 else
2803 sphScattererDirModalCoeffs(order, kr, kR, nBands, dirCoeff, b_N);
2804 break;
2805 }
2806
2807 /* calculate (unit) cartesian coords for sensors and plane waves */
2808 U_sensors = malloc1d(N_sensors*3*sizeof(float));
2809 U_srcs = malloc1d(N_srcs*3*sizeof(float));
2810 unitSph2cart(sensor_dirs_rad, N_sensors, 0, U_sensors);
2811 unitSph2cart(src_dirs_deg, N_srcs, 1, U_srcs);
2812
2813 /* Compute angular-dependent part of the array responses */
2814 ppm = malloc1d((order+1)*sizeof(double));
2815 P = malloc1d((order+1)*N_sensors*sizeof(double_complex));
2816 b_NP = malloc1d(nBands*N_sensors*sizeof(double_complex));
2817 for(i=0; i<N_srcs; i++){
2818 for(j=0; j<N_sensors; j++){
2819 utility_svvdot((const float*)&U_sensors[j*3], (const float*)&U_srcs[i*3], 3, &cosangle);
2820 for(n=0; n<order+1; n++){
2821 /* Legendre polynomials correspond to the angular dependency */
2822 dcosangle = (double)cosangle;
2823 unnorm_legendreP(n, &dcosangle, 1, ppm);
2824 P[n*N_sensors+j] = cmplx((2.0*(double)n+1.0)/(4.0*SAF_PId) * ppm[0], 0.0);
2825 }
2826 }
2827 cblas_zgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBands, N_sensors, order+1, &calpha,
2828 b_N, order+1,
2829 P, N_sensors, &cbeta,
2830 b_NP, N_sensors);
2831
2832 /* store array response per frequency, sensors and plane wave dirs */
2833 for(band=0; band<nBands; band++)
2834 for(j=0; j<N_sensors; j++)
2835 H_array[band*N_sensors*N_srcs + j*N_srcs + i] = cmplxf((float)creal(b_NP[band*N_sensors+j]), (float)cimag(b_NP[band*N_sensors+j]));
2836 }
2837
2838 free(U_sensors);
2839 free(U_srcs);
2840 free(b_N);
2841 free(ppm);
2842 free(P);
2843 free(b_NP);
2844}
2845
2847(
2848 int order,
2849 float_complex* M_array2SH,
2850 int nSensors,
2851 int nBands,
2852 float_complex* H_array,
2853 int nDirs,
2854 float_complex* Y_grid,
2855 float* cSH,
2856 float* lSH
2857#if 0
2858 , float* WNG
2859#endif
2860)
2861{
2862 int band, i, n, m, nSH, q;
2863 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
2864 float w_uni_grid, lSH_n, lSH_nm;
2865 float_complex cSH_n, cSH_nm, yre_yre_dot, yre_yid_dot;
2866 float_complex *y_recon_kk, *y_recon_nm, *w_y_recon_nm, *y_ideal_nm, *MH_M, *EigV;
2867
2868 nSH = ORDER2NSH(order);
2869 w_uni_grid = 1.0f/(float)nDirs;
2870 y_recon_kk = malloc1d(nSH*nDirs*sizeof(float_complex));
2871 y_recon_nm = malloc1d(nDirs*sizeof(float_complex));
2872 w_y_recon_nm = malloc1d(nDirs*sizeof(float_complex));
2873 y_ideal_nm = malloc1d(nDirs*sizeof(float_complex));
2874 MH_M = malloc1d(nSensors*nSensors*sizeof(float_complex));
2875 EigV = malloc1d(nSensors*nSensors*sizeof(float_complex));
2876 for(band=0; band<nBands; band++){
2877 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, nDirs, nSensors, &calpha,
2878 &M_array2SH[band*nSH*nSensors], nSensors,
2879 &H_array[band*nSensors*nDirs], nDirs, &cbeta,
2880 y_recon_kk, nDirs);
2881 for(n=0; n<order+1; n++){
2882 cSH_n = cmplxf(0.0f, 0.0f);
2883 lSH_n = 0.0f;
2884 for(m=-n; m<=n; m++){
2885 q = n*n+n+m;
2886 for(i=0; i<nDirs; i++){
2887 y_recon_nm[i] = y_recon_kk[q*nDirs+i];
2888 w_y_recon_nm[i] = crmulf(y_recon_nm[i], w_uni_grid);
2889 y_ideal_nm[i] = Y_grid[q*nDirs+i];
2890 }
2891 utility_cvvdot(w_y_recon_nm, y_recon_nm, nDirs, CONJ, &yre_yre_dot);
2892 utility_cvvdot(w_y_recon_nm, y_ideal_nm, nDirs, CONJ, &yre_yid_dot);
2893 cSH_nm = ccdivf(yre_yid_dot, ccaddf(csqrtf(yre_yre_dot), cmplxf(2.23e-9f, 0.0f)));
2894 cSH_n = ccaddf(cSH_n, cSH_nm);
2895 lSH_nm = crealf(yre_yre_dot);
2896 lSH_n += lSH_nm;
2897 }
2898 cSH[band*(order+1)+n] = SAF_MAX(SAF_MIN(cabsf(cSH_n)/(2.0f*(float)n+1.0f),1.0f),0.0f);
2899 lSH[band*(order+1)+n] = 10.0f*log10f(lSH_n/(2.0f*(float)n+1.0f)+2.23e-9f);
2900 }
2901 }
2902
2903#if 0
2904 /* find maximum noise amplification of all filters in matrix */
2905 for(band=0; band<nBands; band++){
2906 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans, nSensors, nSensors, nSH, &calpha,
2907 &M_array2SH[band*nSH*nSensors], nSensors,
2908 &M_array2SH[band*nSH*nSensors], nSensors, &cbeta,
2909 MH_M, nSensors);
2910 utility_ceig(NULL, MH_M, nSensors, 1, NULL, NULL, EigV); /* eigenvalues in decending order */
2911 WNG[band] = 10.0f*log10f(crealf(EigV[0])+2.23e-9f);
2912 }
2913#endif
2914
2915 free(y_recon_kk);
2916 free(y_recon_nm);
2917 free(w_y_recon_nm);
2918 free(y_ideal_nm);
2919 free(MH_M);
2920 free(EigV);
2921}
2922
void generateMinNormMap(int order, float_complex *Cx, float_complex *Y_grid, int nSources, int nGrid_dirs, int logScaleFlag, float *pmap)
Generates an activity-map based on the sub-space minimum-norm (MinNorm) method.
Definition saf_sh.c:1917
void generateMVDRmap(int order, float_complex *Cx, float_complex *Y_grid, int nGrid_dirs, float regPar, float *pmap, float_complex *w_MVDR_out)
Generates a powermap based on the energy of adaptive Minimum-Variance Distortion-less Response (MVDR)...
Definition saf_sh.c:1699
void arraySHTmatricesDiffEQ(float_complex *H_sht, float_complex *DCM, float *freqVector, float alias_freq_hz, int nBins, int order, int nMics, float_complex *H_sht_eq)
Diffuse-field equalisation of SHT matrices above the spatial aliasing frequency.
Definition saf_sh.c:2196
void sphMUSIC_destroy(void **const phMUSIC)
Destroys an instance of the spherical harmonic domain MUSIC implementation.
Definition saf_sh.c:1333
void unnorm_legendreP(int n, double *x, int lenX, double *y)
Calculates unnormalised Legendre polynomials up to order N, for all values in vector x [1].
Definition saf_sh.c:54
void rotateAxisCoeffsComplex(int order, float *c_n, float theta_0, float phi_0, float_complex *c_nm)
Generates spherical coefficients for a rotated axisymmetric pattern (COMPLEX)
Definition saf_sh.c:966
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
Definition saf_sh.h:51
void sphPWD_create(void **const phPWD, int order, float *grid_dirs_deg, int nDirs)
Creates an instance of a spherical harmonic domain implementation of the steer-response power (SRP) a...
Definition saf_sh.c:1155
void cylModalCoeffs(int order, double *kr, int nBands, ARRAY_CONSTRUCTION_TYPES arrayType, double_complex *b_N)
Calculates the modal coefficients for open/rigid cylindrical arrays.
Definition saf_sh.c:2267
void evaluateSHTfilters(int order, float_complex *M_array2SH, int nSensors, int nBands, float_complex *H_array, int nDirs, float_complex *Y_grid, float *cSH, float *lSH)
Generates some objective measures, which evaluate the performance of spatial encoding filters.
Definition saf_sh.c:2847
void sphMUSIC_compute(void *const hMUSIC, float_complex *Vn, int nSrcs, float *P_music, int *peak_inds)
Computes a pseudo-spectrum based on the MUSIC algorithm in the spherical harmonic domain; optionally ...
Definition saf_sh.c:1356
void sphESPRIT_create(void **const phESPRIT, int order)
Creates an instance of the spherical harmonic domain ESPRIT-based direction of arrival estimator.
Definition saf_sh.c:1421
float computeSectorCoeffsEP(int orderSec, float_complex *A_xyz, SECTOR_PATTERNS pattern, float *sec_dirs_deg, int nSecDirs, float *sectorCoeffs)
Computes beamforming matrices (sector coefficients) which, when applied to input SH signals,...
Definition saf_sh.c:704
void unnorm_legendreP_recur(int n, float *x, int lenX, float *Pnm_minus1, float *Pnm_minus2, float *Pnm)
Calculates unnormalised Legendre polynomials up to order N, for all values in vector x.
Definition saf_sh.c:130
int calculateGridWeights(float *dirs_rad, int nDirs, int order, float *w)
Computes approximation of quadrature/integration weights for a given grid.
Definition saf_sh.c:1066
void diffCohMtxMeas(float_complex *H_array, int nBins, int nCH, int nGrid, float *w_grid, float_complex *M_diffcoh)
Calculates the diffuse coherence matrices for an arbitrary array.
Definition saf_sh.c:2647
void getSHcomplex(int order, float *dirs_rad, int nDirs, float_complex *Y)
Computes complex-valued spherical harmonics [1] for each given direction on the unit sphere.
Definition saf_sh.c:440
void beamWeightsVelocityPatternsReal(int order, float *b_n, float azi_rad, float elev_rad, float_complex *A_xyz, float *velCoeffs)
Generates beamforming coefficients for velocity patterns (REAL)
Definition saf_sh.c:885
void sphMUSIC_create(void **const phMUSIC, int order, float *grid_dirs_deg, int nDirs)
Creates an instance of the spherical harmonic domain MUSIC implementation, which may be used for comp...
Definition saf_sh.c:1285
void beamWeightsVelocityPatternsComplex(int order, float *b_n, float azi_rad, float elev_rad, float_complex *A_xyz, float_complex *velCoeffs)
Generates beamforming coefficients for velocity patterns (COMPLEX)
Definition saf_sh.c:906
void sphESPRIT_estimateDirs(void *const hESPRIT, float_complex *Us, int K, float *src_dirs_rad)
Estimates the direction-of-arrival (DoA) based on the ESPRIT-based estimator, in the spherical harmon...
Definition saf_sh.c:1546
void sphModalCoeffs(int order, double *kr, int nBands, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, double_complex *b_N)
Calculates the modal coefficients for open/rigid spherical arrays.
Definition saf_sh.c:2370
void beamWeightsCardioid2Spherical(int N, float *b_n)
Generates spherical coefficients for generating cardioid beampatterns.
Definition saf_sh.c:823
void beamWeightsMaxEV(int N, float *b_n)
Generates beamweights in the SHD for maximum energy-vector beampatterns.
Definition saf_sh.c:858
void sphPWD_destroy(void **const phPWD)
Destroys an instance of the spherical harmonic domain PWD implementation.
Definition saf_sh.c:1201
void rotateAxisCoeffsReal(int order, float *c_n, float theta_0, float phi_0, float *c_nm)
Generates spherical coefficients for a rotated axisymmetric pattern (REAL)
Definition saf_sh.c:946
float sphArrayAliasLim(float r, float c, int maxN)
Returns a simple estimate of the spatial aliasing limit (the kR = maxN rule)
Definition saf_sh.c:2332
void complex2realSHMtx(int order, float_complex *T_c2r)
Computes a complex to real spherical harmonic transform matrix.
Definition saf_sh.c:491
void sphPWD_compute(void *const hPWD, float_complex *Cx, int nSrcs, float *P_map, int *peak_inds)
Computes a power-map based on determining the energy of hyper-cardioid beamformers; optionally,...
Definition saf_sh.c:1222
void diffCohMtxMeasReal(float *H_array, int nCH, int nGrid, float *w_grid, float *M_diffcoh)
Calculates the diffuse coherence matrices for an array that uses a broad-band real-valued basis.
Definition saf_sh.c:2684
void sphArrayNoiseThreshold(int maxN, int Nsensors, float r, float c, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, float maxG_db, float *f_lim)
Computes the frequencies (per order), at which the noise of a SHT of a SMA exceeds a specified maximu...
Definition saf_sh.c:2342
void generateMUSICmap(int order, float_complex *Cx, float_complex *Y_grid, int nSources, int nGrid_dirs, int logScaleFlag, float *pmap)
Generates an activity-map based on the sub-space multiple-signal classification (MUSIC) method.
Definition saf_sh.c:1869
void beamWeightsHypercardioid2Spherical(int N, float *b_n)
Generates beamweights in the SHD for hypercardioid beampatterns.
Definition saf_sh.c:840
void complex2realCoeffs(int order, float_complex *C_N, int K, float *R_N)
Converts SH coefficients from the complex to real basis.
Definition saf_sh.c:555
ARRAY_CONSTRUCTION_TYPES
Microphone/Hydrophone array construction types.
Definition saf_sh.h:64
void simulateCylArray(int order, double *kr, int nBands, float *sensor_dirs_rad, int N_sensors, float *src_dirs_deg, int N_srcs, ARRAY_CONSTRUCTION_TYPES arrayType, float_complex *H_array)
Simulates a cylindrical microphone array, returning the transfer functions for each (plane wave) sour...
Definition saf_sh.c:2717
void arraySHTmatrices(ARRAY_SHT_OPTIONS method, int order, float amp_thresh_dB, float_complex *H_array, float *grid_dirs_deg, int nBins, int nMics, int nGrid, float *w_grid, float_complex *H_sht)
Computes matrices required to transform array signals into spherical harmonic signals (frequency-doma...
Definition saf_sh.c:1978
void sphESPRIT_destroy(void **const phESPRIT)
Destroys an instance of the spherical harmonic domain ESPRIT-based direction of arrival estimator.
Definition saf_sh.c:1496
SECTOR_PATTERNS
Sector pattern designs for directionally-constraining sound-fields [1].
Definition saf_sh.h:81
void simulateSphArray(int order, double *kr, double *kR, int nBands, float *sensor_dirs_rad, int N_sensors, float *src_dirs_deg, int N_srcs, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, float_complex *H_array)
Simulates a spherical microphone array, returning the transfer functions for each (plane wave) source...
Definition saf_sh.c:2769
void getSHrotMtxReal(float Rxyz[3][3], float *RotMtx, int L)
Generates a real-valued spherical harmonic rotation matrix [1] based on a 3x3 rotation matrix (see qu...
Definition saf_sh.c:586
void getSHreal_recur(int N, float *dirs_rad, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
Definition saf_sh.c:254
void arraySHTfilters(ARRAY_SHT_OPTIONS method, int order, float amp_thresh_dB, float_complex *H_array, float *grid_dirs_deg, int nFFT, int nMics, int nGrid, float *w_grid, float *h_sht)
Computes filters required to transform array signals into spherical harmonic signals (time-domain)
Definition saf_sh.c:2155
void generatePWDmap(int order, float_complex *Cx, float_complex *Y_grid, int nGrid_dirs, float *pmap)
Generates a powermap based on the energy of a plane-wave decomposition (PWD) (i.e.
Definition saf_sh.c:1657
void computeVelCoeffsMtx(int sectorOrder, float_complex *A_xyz)
Computes the matrices which generate the coefficients of a beampattern of order (sectorOrder+1) that ...
Definition saf_sh.c:672
float computeSectorCoeffsAP(int orderSec, float_complex *A_xyz, SECTOR_PATTERNS pattern, float *sec_dirs_deg, int nSecDirs, float *sectorCoeffs)
Computes beamforming matrices (sector coefficients) which, when applied to input SH signals,...
Definition saf_sh.c:770
void getSHreal(int order, float *dirs_rad, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
Definition saf_sh.c:191
void sphDiffCohMtxTheory(int order, float *sensor_dirs_rad, int N_sensors, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, double *kr, int nBands, double *M_diffcoh)
Calculates the theoretical diffuse coherence matrix for a spherical array.
Definition saf_sh.c:2570
void sphScattererModalCoeffs(int order, double *kr, double *kR, int nBands, double_complex *b_N)
Calculates the modal coefficients for a rigid spherical scatterer with omni-directional sensors.
Definition saf_sh.c:2454
void checkCondNumberSHTReal(int order, float *dirs_rad, int nDirs, float *w, float *cond_N)
Computes the condition numbers for a least-squares SHT.
Definition saf_sh.c:991
void real2complexSHMtx(int order, float_complex *T_r2c)
Computes a real to complex spherical harmonic transform matrix.
Definition saf_sh.c:523
void sphScattererDirModalCoeffs(int order, double *kr, double *kR, int nBands, double dirCoeff, double_complex *b_N)
Calculates the modal coefficients for a rigid spherical scatterer with directional sensors.
Definition saf_sh.c:2503
ARRAY_SHT_OPTIONS
Microphone array to spherical harmonic domain conversion options.
Definition saf_sh.h:98
void generateCroPaCLCMVmap(int order, float_complex *Cx, float_complex *Y_grid, int nGrid_dirs, float regPar, float lambda, float *pmap)
(EXPERIMENTAL) Generates a powermap utilising the CroPaC LCMV post-filter described in [1]
Definition saf_sh.c:1765
@ ARRAY_CONSTRUCTION_RIGID_DIRECTIONAL
Rigid baffle, directional sensors.
Definition saf_sh.h:70
@ ARRAY_CONSTRUCTION_RIGID
Rigid baffle, omni-directional sensors.
Definition saf_sh.h:68
@ ARRAY_CONSTRUCTION_OPEN_DIRECTIONAL
Open array, directional sensors.
Definition saf_sh.h:67
@ ARRAY_CONSTRUCTION_OPEN
Open array, omni-directional sensors.
Definition saf_sh.h:65
@ SECTOR_PATTERN_PWD
Plane-wave decomposition/Hyper-cardioid.
Definition saf_sh.h:82
@ SECTOR_PATTERN_MAXRE
Spatially tapered hyper-cardioid, such that it has maximum energy concentrated in the look- direction...
Definition saf_sh.h:83
@ SECTOR_PATTERN_CARDIOID
Cardioid pattern.
Definition saf_sh.h:86
@ ARRAY_SHT_DEFAULT
The default SHT filters are ARRAY_SHT_REG_LS.
Definition saf_sh.h:99
@ ARRAY_SHT_REG_LS
Regularised least-squares (LS)
Definition saf_sh.h:100
@ ARRAY_SHT_REG_LSHD
Regularised least-squares (LS) in the SH domain (similar as in [1])
Definition saf_sh.h:101
#define saf_print_error(message)
Macro to print a error message along with the filename and line number.
void bessel_jn_ALL(int N, double *z, int nZ, int *maxN, double *j_n, double *dj_n)
Computes the spherical Bessel function of the first kind (jn) and their derivatives (djn) for ALL ord...
void utility_zvvsub(const double_complex *a, const double_complex *b, const int len, double_complex *c)
Double-precision, complex, vector-vector subtraction, i.e.
#define saf_assert(x, message)
Macro to make an assertion, along with a string explaining its purpose.
#define SAF_PI
pi constant (single precision)
void hankel_hn2_ALL(int N, double *z, int nZ, int *maxN, double_complex *h_n2, double_complex *dh_n2)
Computes the spherical Hankel function of the second kind (hn2) and their derivatives (dhn2) for ALL ...
void utility_cglslv(void *const hWork, const float_complex *A, const int dim, float_complex *B, int nCol, float_complex *X)
General linear solver: single precision complex, i.e.
void utility_svvmul(const float *a, const float *b, const int len, float *c)
Single-precision, element-wise vector-vector multiplication i.e.
void utility_cinv(void *const hWork, float_complex *A, float_complex *B, const int N)
Matrix inversion: double precision complex, i.e.
#define SAF_MAX(a, b)
Returns the maximum of the two values.
#define SAF_PId
pi constant (double precision)
void utility_ssvd(void *const hWork, const float *A, const int dim1, const int dim2, float *U, float *S, float *V, float *sing)
Singular value decomposition: single precision, i.e.
#define FOURPI
4pi (single precision)
void utility_zeigmp(void *const hWork, const double_complex *A, const double_complex *B, const int dim, double_complex *VL, double_complex *VR, double_complex *D)
Computes eigenvalues of a matrix pair using the QZ method, double precision complex,...
void utility_cinv_destroy(void **const phWork)
De-allocate the working struct used by utility_cinv()
void utility_ceig(void *const hWork, const float_complex *A, const int dim, float_complex *VL, float_complex *VR, float_complex *D, float_complex *eig)
Eigenvalue decomposition of a NON-SYMMETRIC matrix: single precision complex, i.e.
void bessel_Jn_ALL(int N, double *z, int nZ, double *J_n, double *dJ_n)
Computes the (cylindrical) Bessel function of the first kind (Jn) and their derivatives (dJn) for ALL...
void utility_zglslv(void *const hWork, const double_complex *A, const int dim, double_complex *B, int nCol, double_complex *X)
General linear solver: double precision complex, i.e.
void utility_svvcopy(const float *a, const int len, float *c)
Single-precision, vector-vector copy, i.e.
void saf_rfft_create(void **const phFFT, int N)
Creates an instance of saf_rfft; real<->half-complex (conjugate-symmetric) FFT.
void utility_spinv(void *const hWork, const float *inM, const int dim1, const int dim2, float *outM)
General matrix pseudo-inverse (the svd way): single precision, i.e.
void utility_zglslv_destroy(void **const phWork)
De-allocate the working struct used by utility_zglslv()
void utility_cseig(void *const hWork, const float_complex *A, const int dim, int sortDecFLAG, float_complex *V, float_complex *D, float *eig)
Eigenvalue decomposition of a SYMMETRIC/HERMITION matrix: single precision complex,...
#define SAF_MIN(a, b)
Returns the minimum of the two values.
void utility_cvabs(const float_complex *a, const int len, float *c)
Single-precision, complex, absolute values of vector elements, i.e.
void utility_cvvdot(const float_complex *a, const float_complex *b, const int len, CONJ_FLAG flag, float_complex *c)
Single-precision, complex, vector-vector dot product, i.e.
void utility_cinv_create(void **const phWork, int maxN)
(Optional) Pre-allocate the working struct used by utility_cinv()
long double factorial(int n)
Factorial, accurate up to n<=25.
void utility_zpinv(void *const hWork, const double_complex *inM, const int dim1, const int dim2, double_complex *outM)
General matrix pseudo-inverse (the svd way): double precision complex, i.e.
void saf_rfft_backward(void *const hFFT, float_complex *inputFD, float *outputTD)
Performs the backward-FFT operation; use for complex (conjugate symmetric) to real transformations.
void saf_rfft_destroy(void **const phFFT)
Destroys an instance of saf_rfft.
void utility_zpinv_create(void **const phWork, int maxDim1, int maxDim2)
(Optional) Pre-allocate the working struct used by utility_zpinv()
void unitSph2cart(float *dirs, int nDirs, int anglesInDegreesFLAG, float *dirs_xyz)
Converts spherical coordinates to Cartesian coordinates of unit length.
#define saf_print_warning(message)
Macro to print a warning message along with the filename and line number.
void utility_zeigmp_destroy(void **const phWork)
De-allocate the working struct used by utility_zeigmp()
void hankel_Hn2_ALL(int N, double *z, int nZ, double_complex *H_n2, double_complex *dH_n2)
Computes the (cylindrical) Hankel function of the second kind (Hn2) and their derivatives (dHn2) for ...
void utility_zvvadd(const double_complex *a, const double_complex *b, const int len, double_complex *c)
Double-precision, complex, vector-vector addition, i.e.
void utility_svrecip(const float *a, const int len, float *c)
Single-precision, vector-reciprocal/inversion, i.e.
void utility_svvdot(const float *a, const float *b, const int len, float *c)
Single-precision, vector-vector dot product, i.e.
#define SQRT4PI
sqrt(4pi) (single precision)
void utility_zglslv_create(void **const phWork, int maxDim, int maxNCol)
(Optional) Pre-allocate the working struct used by utility_zglslv()
void utility_siminv(const float *a, const int len, int *index)
Single-precision, index of minimum absolute value in a vector, i.e.
void utility_zeigmp_create(void **const phWork, int maxDim)
(Optional) Pre-allocate the working struct used by utility_zeigmp()
void utility_simaxv(const float *a, const int len, int *index)
Single-precision, index of maximum absolute value in a vector, i.e.
void utility_zpinv_destroy(void **const phWork)
De-allocate the working struct used by utility_zpinv()
void utility_cslslv(void *const hWork, const float_complex *A, const int dim, float_complex *B, int nCol, float_complex *X)
Linear solver for HERMITIAN positive-definate 'A': single precision complex, i.e.
@ NO_CONJ
Do not take the conjugate.
@ CONJ
Take the conjugate.
void ** malloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:89
void ** realloc2d(void **ptr, size_t dim1, size_t dim2, size_t data_size)
2-D realloc which does NOT retain previous data order
Definition md_malloc.c:115
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void * calloc1d(size_t dim1, size_t data_size)
1-D calloc (same as calloc, but with error checking)
Definition md_malloc.c:69
void * realloc1d(void *ptr, size_t dim1_data_size)
1-D realloc (same as realloc, but with error checking)
Definition md_malloc.c:79
#define FLATTEN2D(A)
Use this macro when passing a 2-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:65
const float wxyzCoeffs[4][4]
First-order ACN/N3D to FuMa [without sqrt(4pi) term] conversion matrix.
Definition saf_sh.c:42
Main header for the Spherical Harmonic Transform and Spherical Array Processing module (SAF_SH_MODULE...
void getVnimu(int order, int ni, int mu, double *Vnimu)
Helper function for sphESPRIT_create()
float getV(int M, int l, int m, int n, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
void gaunt_mtx(int N1, int N2, int N, float *A)
Constructs a matrix of Guant coefficients.
float getU(int M, int l, int m, int n, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
float getW(int M, int l, int m, int n, float R_1[3][3], float *R_lm1)
Helper function for getSHrotMtxReal()
void getWnimu(int order, int mm, int ni, int mu, double *Wnimu)
Helper function for sphESPRIT_create()
void muni2q(int order, int ni, int mu, int *idx_nimu, int *idx_nm)
Helper function for sphESPRIT_create()
Internal header for the Spherical Harmonic Transform and Spherical Array Processing module (SAF_SH_MO...
static double Jn(int n, double z)
Wrapper for the cylindrical Bessel function of the first kind.
Internal data structure for sphESPRIT.
Internal data structure for sphMUSIC.
Internal data structure for sphPWD.