SAF
Loading...
Searching...
No Matches
test__tracker_module.c
Go to the documentation of this file.
1/*
2 * This file is part of the saf_tracker module unit tests.
3 * Copyright (c) 2020-2021 - Leo McCormack
4 *
5 * The saf_tracker module is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * The saf_tracker module is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * See <http://www.gnu.org/licenses/> for a copy of the GNU General Public
16 * License.
17 */
18
27#include "saf_test.h"
28
29#ifdef SAF_ENABLE_TRACKER_MODULE
30
31void test__tracker3d(void){
32 int hop, i, j, k, nSH, nGrid, rand_idx, dropouts;
33 int inds[2];
34 int* target_IDs;
35 void* hT3d, *hMUSIC;
36 float measNoiseSD_deg, noiseSpecDen_deg, scale, rand01;
37 float est_dirs_deg[2][2], est_dirs_xyz[2][3];
38 float* grid_dirs_deg;
39 float** insigs, **inputSH, **inputSH_noise, **inputSH_hop, **Y, **Cx, **V, **Vn;
40 float_complex** Vn_cmplx;
41 int nTargets;
42 float *target_dirs_xyz, *target_var_xyz;
43
44 /* Test configuration */
45 const float acceptedTolerance = 0.005f;
46 const int order = 2;
47 const float fs = 48e3;
48 const int hopsize = 128;
49 const int sigLen = (int)fs*5;
50 const int nSources = 2; /* cannot be changed, hard-coded for 2 */
51 const float src_dirs_deg[2][2] = { {-35.0f, 30.0f}, {120.0f, 0.0f} };
52
53 /* Configure the tracker */
54 tracker3d_config tpars;
55 /* Number of Monte Carlo samples/particles. The more complex the
56 * distribution is, the more particles required (but also, the more
57 * computationally expensive the tracker becomes). */
58 tpars.Np = 20;
59 tpars.ARE_UNIT_VECTORS = 1;
60 tpars.maxNactiveTargets = 2; /* about 2 higher than expected is good */
61 /* Likelihood of an estimate being noise/clutter */
62 tpars.noiseLikelihood = 0.2f; /* between [0..1] */
63 /* Measurement noise - e.g. to assume that estimates within the range +/-20
64 * degrees belong to the same target, set SDmnoise_deg = 20 */
65 measNoiseSD_deg = 20.0f;
66 tpars.measNoiseSD = 1.0f-cosf(measNoiseSD_deg*SAF_PI/180.0f); /* Measurement noise standard deviation */
67 /* Noise spectral density - not fully understood. But it influences the
68 * smoothness of the target tracks */
69 noiseSpecDen_deg = 1.0f;
70 tpars.noiseSpecDen = 1.0f-cosf(noiseSpecDen_deg*SAF_PI/180.0f); /* Noise spectral density */
71 /* FLAG - whether to allow for multiple target deaths in the same tracker
72 * prediction step */
73 tpars.ALLOW_MULTI_DEATH = 1;
74 /* Probability of birth and death */
75 tpars.init_birth = 0.5f; /* value between [0 1] - Prior probability of birth */
76 tpars.alpha_death = 20.0f; /* always >= 1; 1 is good */ /* 20-> means death is very unlikely... */
77 tpars.beta_death = 1.0f; /* always >= 1; 1 is good */
78 /* Elapsed time (in seconds) between observations */
79 tpars.dt = 1.0f/(fs/(float)hopsize); /* Hop length of frames */
80 /* Whether or not to allow multiple active sources for each update */
81 /* Real-time tracking is based on the particle with highest weight. A
82 * one-pole averaging filter is used to smooth the weights over time. */
83 tpars.W_avg_coeff = 0.5f;
84 /* Force kill targets that are close to another target. In these cases, the
85 * target that has been 'alive' for the least amount of time, is killed */
86 tpars.FORCE_KILL_TARGETS = 1;
87 tpars.forceKillDistance = 0.2f;
88 /* Mean position priors x,y,z (assuming directly in-front) */
89 tpars.M0[0] = 1.0f; tpars.M0[1] = 0.0f; tpars.M0[2] = 0.0f;
90 /* Mean Velocity priors x,y,z (assuming stationary) */
91 tpars.M0[3] = 0.0f; tpars.M0[4] = 0.0f; tpars.M0[5] = 0.0f;
92 /* Target velocity - e.g. to assume that a target can move 20 degrees in
93 * two seconds along the horizontal, set V_azi = 20/2 */
94 const float Vazi_deg = 3.0f; /* Velocity of target on azimuthal plane */
95 const float Vele_deg = 3.0f; /* Velocity of target on median plane */
96 memset(tpars.P0, 0, 6*6*sizeof(float));
97 /* Variance PRIORs of estimates along the x,y,z axes, respectively. Assuming
98 * coordinates will lay on the unit sphere +/- x,y,z, so a range of 2, and
99 * hence a variance of 2^2: */
100 tpars.P0[0][0] = 4.0f; tpars.P0[1][1] = 4.0f; tpars.P0[2][2] = 4.0f;
101 /* Velocity PRIORs of estimates the x,y,z axes */
102 tpars.P0[3][3] = 1.0f-cosf(Vazi_deg*SAF_PI/180.0f); /* x */
103 tpars.P0[4][4] = tpars.P0[3][3]; /* y */
104 tpars.P0[5][5] = 1.0f-cosf(Vele_deg*SAF_PI/180.0f); /* z */
105 /* PRIOR probabilities of noise. (Assuming the noise is uniformly
106 * distributed in the entire spatial grid). */
107 tpars.cd = 1.0f/(4.0f*SAF_PI);
108
109 /* Create tracker */
110 tracker3d_create(&hT3d, tpars);
111
112 /* Create spherical harmonic input signals */
113 insigs = (float**)malloc2d(nSources, sigLen, sizeof(float));
114 rand_m1_1(FLATTEN2D(insigs), nSources*sigLen);
115 nSH = ORDER2NSH(order);
116 Y = (float**)malloc2d(nSH, nSources, sizeof(float));
117 getRSH(order, (float*)src_dirs_deg, nSources, FLATTEN2D(Y));
118 scale = 1.0f/(float)nSources;
119 utility_svsmul(FLATTEN2D(Y), &scale, nSH*nSources, NULL);
120 inputSH = (float**)malloc2d(nSH, sigLen, sizeof(float));
121 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nSH, sigLen, nSources, 1.0f,
122 FLATTEN2D(Y), nSources,
123 FLATTEN2D(insigs), sigLen, 0.0f,
124 FLATTEN2D(inputSH), sigLen);
125
126 /* Add some noise */
127 inputSH_noise = (float**)malloc2d(nSH, sigLen, sizeof(float));
128 rand_m1_1(FLATTEN2D(inputSH_noise), nSH*sigLen);
129 scale = 0.05f;
130 utility_svsmul(FLATTEN2D(inputSH_noise), &scale, nSH*sigLen, NULL);
131 cblas_saxpy(nSH*sigLen, 1.0f, FLATTEN2D(inputSH_noise), 1, FLATTEN2D(inputSH), 1);
132
133 /* Create DoA estimator */
134 nGrid = 240; /* number of points in a t-design of degree 21 */
135 grid_dirs_deg = (float*)__Tdesign_degree_21_dirs_deg;
136 sphMUSIC_create(&hMUSIC, order, grid_dirs_deg, nGrid);
137
138 /* Memory allocations */
139 inputSH_hop = (float**)malloc2d(nSH, hopsize, sizeof(float));
140 Cx = (float**)malloc2d(nSH, nSH, sizeof(float));
141 V = (float**)malloc2d(nSH, nSH, sizeof(float));
142 Vn = (float**)malloc2d(nSH, (nSH-nSources), sizeof(float)); /* noise subspace */
143 Vn_cmplx = (float_complex**)malloc2d(nSH, (nSH-nSources), sizeof(float_complex));
144 target_dirs_xyz = NULL;
145 target_var_xyz = NULL;
146 target_IDs = NULL;
147
148 /* Loop over hops */
149 dropouts = 0;
150 for(hop=0; hop<(int)((float)sigLen/(float)hopsize); hop++){
151 /* Grab current hop */
152 for(i=0; i<nSH; i++)
153 memcpy(inputSH_hop[i], &inputSH[i][hop*hopsize], hopsize*sizeof(float));
154
155 /* Eigenvalue decomposition and truncation of eigen vectors to obtain
156 * noise subspace (based on source number) */
157 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, nSH, nSH, hopsize, 1.0f,
158 FLATTEN2D(inputSH_hop), hopsize,
159 FLATTEN2D(inputSH_hop), hopsize, 0.0f,
160 FLATTEN2D(Cx), nSH);
161 utility_sseig(NULL, FLATTEN2D(Cx), nSH, 1, FLATTEN2D(V), NULL, NULL);
162 for(i=0; i<nSH; i++)
163 for(j=0, k=nSources; j<nSH-nSources; j++, k++)
164 Vn[i][j] = V[i][k];
165 for(i=0; i<nSH; i++)
166 for(j=0; j<nSH-nSources; j++)
167 Vn_cmplx[i][j] = cmplxf(Vn[i][j], 0.0f);
168
169 /* DoA estimation */
170 sphMUSIC_compute(hMUSIC, FLATTEN2D(Vn_cmplx), nSources, NULL, (int*)inds);
171 est_dirs_deg[0][0] = grid_dirs_deg[inds[0]*2+0];
172 est_dirs_deg[0][1] = grid_dirs_deg[inds[0]*2+1];
173 est_dirs_deg[1][0] = grid_dirs_deg[inds[1]*2+0];
174 est_dirs_deg[1][1] = grid_dirs_deg[inds[1]*2+1];
175 unitSph2cart((float*)est_dirs_deg, nSources, 1, (float*)est_dirs_xyz);
176
177 /* Pick an estimate at random */
178 rand_0_1(&rand01, 1);
179 rand_idx = (int)(rand01*(float)nSources);
180
181 /* Feed tracker */
182 tracker3d_step(hT3d, (float*)&est_dirs_xyz[rand_idx], 1, &target_dirs_xyz, &target_var_xyz, &target_IDs, &nTargets);
183
184 /* Give the tracker a couple of steps, and then assert that it is keeping track of these two targets */
185 if(hop>10){
186 if(nTargets==nSources){
187 TEST_ASSERT_TRUE( fabsf(est_dirs_xyz[0][0] - target_dirs_xyz[0*3+0]) <= acceptedTolerance ||
188 fabsf(est_dirs_xyz[0][0] - target_dirs_xyz[1*3+0]) <= acceptedTolerance);
189 TEST_ASSERT_TRUE( fabsf(est_dirs_xyz[0][1] - target_dirs_xyz[0*3+1]) <= acceptedTolerance ||
190 fabsf(est_dirs_xyz[0][1] - target_dirs_xyz[1*3+1]) <= acceptedTolerance);
191 TEST_ASSERT_TRUE( fabsf(est_dirs_xyz[0][2] - target_dirs_xyz[0*3+2]) <= acceptedTolerance ||
192 fabsf(est_dirs_xyz[0][2] - target_dirs_xyz[1*3+2]) <= acceptedTolerance);
193 TEST_ASSERT_TRUE( fabsf(est_dirs_xyz[1][0] - target_dirs_xyz[0*3+0]) <= acceptedTolerance ||
194 fabsf(est_dirs_xyz[1][0] - target_dirs_xyz[1*3+0]) <= acceptedTolerance);
195 TEST_ASSERT_TRUE( fabsf(est_dirs_xyz[1][1] - target_dirs_xyz[0*3+1]) <= acceptedTolerance ||
196 fabsf(est_dirs_xyz[1][1] - target_dirs_xyz[1*3+1]) <= acceptedTolerance);
197 TEST_ASSERT_TRUE( fabsf(est_dirs_xyz[1][2] - target_dirs_xyz[0*3+2]) <= acceptedTolerance ||
198 fabsf(est_dirs_xyz[1][2] - target_dirs_xyz[1*3+2]) <= acceptedTolerance);
199 }
200 else
201 dropouts++; /* Should be very unlikely, (as the probably of death set to be so low), but it can still happen... */
202 }
203 }
204 TEST_ASSERT_TRUE(dropouts<12);
205
206 /* Clean-up */
207 tracker3d_destroy(&hT3d);
208 sphMUSIC_destroy(&hMUSIC);
209 free(target_dirs_xyz);
210 free(target_var_xyz);
211 free(target_IDs);
212 free(insigs);
213 free(inputSH);
214 free(inputSH_noise);
215 free(inputSH_hop);
216 free(Y);
217 free(Cx);
218 free(V);
219 free(Vn);
220 free(Vn_cmplx);
221}
222
223#endif /* SAF_ENABLE_TRACKER_MODULE */
void getRSH(int N, float *dirs_deg, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
Definition saf_hoa.c:119
void sphMUSIC_destroy(void **const phMUSIC)
Destroys an instance of the spherical harmonic domain MUSIC implementation.
Definition saf_sh.c:1333
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
Definition saf_sh.h:51
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 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 tracker3d_destroy(void **const phT3d)
Destroys an instance of the mighty tracker3d.
void tracker3d_step(void *const hT3d, float *newObs_xyz, int nObs, float **target_pos_xyz, float **target_var_xyz, int **target_IDs, int *nTargets)
Tracker time step to update & predict current target locations and to parse new measurements/observat...
void tracker3d_create(void **const phT3d, tracker3d_config tpars)
Creates an instance of the mighty tracker3d.
Definition saf_tracker.c:50
#define SAF_PI
pi constant (single precision)
const float __Tdesign_degree_21_dirs_deg[240][2]
Directions [azimuth, Elevation] in degrees, for minimum Tdesign degree: 21.
void rand_0_1(float *vector, int length)
Generates random numbers between 0 and 1 and stores them in the input vector.
void unitSph2cart(float *dirs, int nDirs, int anglesInDegreesFLAG, float *dirs_xyz)
Converts spherical coordinates to Cartesian coordinates of unit length.
void utility_sseig(void *const hWork, const float *A, const int dim, int sortDecFLAG, float *V, float *D, float *eig)
Eigenvalue decomposition of a SYMMETRIC matrix: single precision, i.e.
void utility_svsmul(float *a, const float *s, const int len, float *c)
Single-precision, multiplies each element in vector 'a' with a scalar 's', i.e.
void rand_m1_1(float *vector, int length)
Generates random numbers between -1 and 1 and stores them in the input vector.
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
#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
Unit test program for the Spatial_Audio_Framework.
User parameters for tracker3d.
Definition saf_tracker.h:59
float dt
Elapsed time (in seconds) between observations/measurements.
Definition saf_tracker.h:84
float M0[6]
[0,1,2] Position of sound source PRIORs (x,y,z), [3,4,5] Mean velocity PRIORs (x,y,...
Definition saf_tracker.h:97
float cd
PRIOR probability of noise.
float init_birth
PRIOR probability of birth [0 1].
Definition saf_tracker.h:79
float measNoiseSD
Measurement noise standard deviation.
Definition saf_tracker.h:71
int ALLOW_MULTI_DEATH
FLAG whether to allow for multiple target deaths in the same tracker prediction step.
Definition saf_tracker.h:76
int FORCE_KILL_TARGETS
FLAG force kill targets that are too close to one another.
Definition saf_tracker.h:90
float P0[6][6]
Diagonal matrix, [0,1,2] Variance PRIORs of estimates along the x,y,z axes; [3,4,5] Velocity PRIORs o...
int maxNactiveTargets
Maximum number of simultaneous targets permitted.
Definition saf_tracker.h:67
float forceKillDistance
Euclidian distance at which to kill targets that come too close to other (older) targets (<=).
Definition saf_tracker.h:94
int ARE_UNIT_VECTORS
1: if the Cartesian coordinates are given as unit vectors, 0: if not
Definition saf_tracker.h:65
float beta_death
Coefficient influencing the likelihood that a target will die; always >= 1.
Definition saf_tracker.h:82
float alpha_death
Coefficient influencing the likelihood that a target will die; always >= 1.
Definition saf_tracker.h:80
int Np
Number of Monte Carlo samples/particles.
Definition saf_tracker.h:60
float noiseLikelihood
Likelihood of an estimate being noise/clutter between [0..1].
Definition saf_tracker.h:69
float W_avg_coeff
Real-time tracking is based on the particle with highest weight.
Definition saf_tracker.h:86
float noiseSpecDen
Noise spectral density; influences the smoothness of the traget tracks.
Definition saf_tracker.h:74
void test__tracker3d(void)
Testing that the particle-filtering based tracker is able to correctly track two simultaneous targets...