├── A180705_Bayesian_DoA_Analysis.m ├── Beams.jpg ├── Exp_vs_Model.jpg ├── Exp_vs_Model_2D.jpg ├── README.md ├── Speaker_Mic.jpg ├── Sph_Mic_Angles.png └── Spherical_Harmonics.jpg /A180705_Bayesian_DoA_Analysis.m: -------------------------------------------------------------------------------- 1 | %% BAYESIAN DoA ANALYSIS 2 | clear all; close all; clc; 3 | % Add Spherical Harmonics Functions 4 | addpath('FUNCTIONS - SH and Beamforming'); 5 | % Add Experimental Data 6 | addpath('Generated Noise Sources'); 7 | 8 | %% USER INPUTS 9 | % ========================================================================= 10 | % ========================== INITIAL DEFINITIONS ========================== 11 | % ========================================================================= 12 | % Enter location to save output folder. This will create a new folder in 13 | % this location and save all figures and the workspace to this location 14 | Active_Folder = 'C:\Users\Chris\Documents\Matlab\RPI Matlab\Research\Chris Research\'; 15 | % ------------------------------------------------------------------------- 16 | % Enter name of folder to save output files. This will create a new folder 17 | % in this location and save all figures and the workspace to this location 18 | Save_Folder_Name = '180705 - TEST'; 19 | Save_Location = strcat(Active_Folder,Save_Folder_Name); 20 | mkdir(Save_Location) 21 | % ------------------------------------------------------------------------- 22 | % Experimentally measured sources vs. artificially generated sources. 23 | % - Enter 1 to load experimentally measured sources 24 | % - Enter 0 to load artificially generated sources 25 | Load_File_Yes_No = 0; % Enter 1 to load file, 0 to not load file 26 | % ------------------------------------------------------------------------- 27 | % Enter name of file with experimentally measured data to be loaded 28 | % - (Files found in 'Generated Noise Sources' folder) 29 | Load_Filename = 'WN_90_90_Windowed'; 30 | % ------------------------------------------------------------------------- 31 | % Nested sampling diffusion coefficient (Makes max less peaky and spreads 32 | % out 'volume'). Recommended: Beta = 1. 33 | Beta = 1; % 0 to 1 (0 = most diffuse, 1 = not diffuse) 34 | % ------------------------------------------------------------------------- 35 | % Define size of initial table (exploration table). 36 | % - Recommended: 1000 37 | Model_iterations = 1000; 38 | % ------------------------------------------------------------------------- 39 | % Amount of times delta likelihood must be below Converge_Limit in order to 40 | % break the loop (determines if delta converges) 41 | Threshold_Amount = Model_iterations; 42 | % ------------------------------------------------------------------------- 43 | % Define number of source models to run through. 44 | % - Recommended: 2 more than number of sources being tests. (e.g. If 45 | % 3 sources are being tested, test up to models with 5 sources) 46 | num_sources_guess = 5; 47 | % ------------------------------------------------------------------------- 48 | % Define sound energy beam grid, a.k.a. resolution of ourput plots. 49 | % - Recommended: num_look_az = 100; num_look_el = 50; 50 | num_look_az = 100; % Number of azimuthal look directions for grid 51 | num_look_el = 50; % Number of elevation look directions for grid 52 | % ------------------------------------------------------------------------- 53 | % Number of iterations before likelihood plot updates 54 | % *This does not affect any output, it just plots likelihood curve to 55 | % track progress* 56 | Likelihood_Update = 10000; 57 | % ------------------------------------------------------------------------- 58 | % Limit of times with no succesful step before alternative sampling is used 59 | Stuck_Lim = 10; %0; 60 | % ------------------------------------------------------------------------- 61 | % Number of times Likelihood increases before alternative sampling kicks in 62 | L_up_idx_lim = 10000; 63 | % ------------------------------------------------------------------------- 64 | % The threshold of delta each step must be below in order to "converge" 65 | Converge_Limit = 10; 66 | % ------------------------------------------------------------------------- 67 | % Threshold of the difference of max and min in the intial table in order 68 | % to break 69 | Exploration_Table_Threshold = 10; 70 | % ------------------------------------------------------------------------- 71 | 72 | 73 | % ========================================================================= 74 | % ======================== CONSISTENT DEFINITIONS ========================= 75 | % ========================================================================= 76 | % User Input (Typically remains constant) 77 | UI = struct; % Define User Input (UI) structure 78 | % ------------------------------------------------------------------------- 79 | % Assign look direction grid for experimental model calcs 80 | UI.num_look_az = num_look_az; 81 | UI.num_look_el = num_look_el; 82 | % ------------------------------------------------------------------------- 83 | % Choose which order SH beamformer is desired (order=2 for 16-channel mic) 84 | order = 2; 85 | UI.order = order; 86 | % ------------------------------------------------------------------------- 87 | % Define boundary limits of look directions 88 | phi_lim = [0 360]; 89 | UI.phi_lim = phi_lim; 90 | theta_lim = [0 180]; 91 | UI.theta_lim = theta_lim; 92 | % ------------------------------------------------------------------------- 93 | % Type of Beamformer 94 | % - Enter 1 for Plane Wave Decomposition Beamformer 95 | % - Enter 2 for Delay & Sum Beamformer 96 | % *All tests were run with plane wave decomp beamformer* 97 | UI.Beam_Type = 1; 98 | % ------------------------------------------------------------------------- 99 | % Calculate: 100 | % - Order index (ensures order (n) corresponds to appropriate degree (m)) 101 | % - Look directions in spherical harmonics 102 | % - Modal amplitude in spherica harmonics 103 | % - Weights in spherical harmonics 104 | [UI.order_idx,UI.ldir_harms,UI.b_n,UI.W_nm] = Beam_Model_Precalcs_4_7_18(UI); 105 | UI.plotflag = 0; % supress plots for now (don't change) 106 | 107 | % ========================================================================= 108 | % =============== ARTIFICIALLY GENERATED SOURCE ATTRIBUTES ================ 109 | % ***Ignore this section if testing experimentally measured data*** 110 | % ========================================================================= 111 | if Load_File_Yes_No == 0 112 | % Artificially generated DoA attributes. Define data to be tested against. 113 | UI.amplitudes = [1 1 1]; % 0 to 1 amplitude(s) per source(s) 114 | UI.S_angles_az = [5 135 270]; % 0 to 360 (degrees) azimuthal location(s) for source(s) 115 | UI.S_angles_el = [60 140 90]; % 0 to 180 (degrees) elevation location(s) for source(s) 116 | % ------------------------------------------------------------------------- 117 | % Amplitude of background noise. 118 | % - Recommended: between 0 and .3 119 | min_Noise_Level = .05; 120 | max_Noise_Level = .25; 121 | Noise_Level = max_Noise_Level - min_Noise_Level; 122 | % ------------------------------------------------------------------------- 123 | % Number of periods for noise (Changes behavior of noise) 124 | x_periods = 1; 125 | y_periods = 1; 126 | % ------------------------------------------------------------------------- 127 | % Generate Noise 128 | [Noise] = Beam_Noise(UI,min_Noise_Level,max_Noise_Level); 129 | % ------------------------------------------------------------------------- 130 | % Generate Artificial Sources 131 | [Beam_Exp_No_Noise] = Beam_Model_Iterate_4_7_18(UI); % Beamform artificial data 132 | % ------------------------------------------------------------------------- 133 | % Add noise to artificially generated signals. 134 | % Weight noise to affect low signals more & beam locations less. 135 | Inv_Beam_Exp_No_Noise = 1-Beam_Exp_No_Noise; % Inverse generated beam grid 136 | Beam_Exp = Beam_Exp_No_Noise + (Inv_Beam_Exp_No_Noise.^5.*Noise); % Add noise 137 | Beam_Exp = Beam_Exp/ max(max(Beam_Exp)); % Normalize 138 | % ------------------------------------------------------------------------- 139 | % Plot & save noise and artificially generated beam source data 140 | Plot_Beam_Grid(Noise,UI); 141 | savefig(strcat(Save_Location,'\Noise.fig')); 142 | Plot_Beam_Grid(Beam_Exp,UI); 143 | savefig(strcat(Save_Location,'\Beam_Exp.fig')); 144 | % ------------------------------------------------------------------------- 145 | % Calculate evidence of single source model against background noise (e.g. no sources) 146 | Background_Noise_Evidence = Beam_Noise_Evidence_New(UI,Noise,Model_iterations); 147 | 148 | 149 | % ========================================================================= 150 | % ============== EXPERIMENTALLY MEASURED SOURCE ATTRIBUTES ================ 151 | else %***Ignore this section if testing artificially generated data*** 152 | % ========================================================================= 153 | % Load experimental recorded data 154 | load(strcat(Load_Filename,'.mat')); % Input signal 155 | UI.fs = fs; % Sampling frequency 156 | UI.mic_signals = WN; % Data is saved as 'WN' in each file. 157 | % ------------------------------------------------------------------------- 158 | % Calculate, plot, & save experimentally measured beam source data 159 | UI.plotflag = 1; % plot experimental data in order to save 160 | [Beam_Exp] = Beam_Data_4_7_18(UI); % Beamform experimental data 161 | savefig(strcat(Save_Location,'\Beam_Exp.fig')); 162 | % ------------------------------------------------------------------------- 163 | % Evidence of single source model against background noise (e.g. no sources) 164 | % Table = 1000; phi x theta = 100x50; beta = 1; 165 | Background_Noise_Evidence = -6086.65229863351; 166 | end 167 | 168 | 169 | %% INITIAL DEFINITIONS 170 | % ============================ Initializations ============================ 171 | % These values will only be defined once before all iterations are run. 172 | Amps = zeros(num_sources_guess,num_sources_guess); 173 | Phis = Amps; 174 | Thetas = Amps; 175 | Sort_Amp_Std = Amps; 176 | Sort_Phi_Std = Amps; 177 | Sort_Theta_Std = Amps; 178 | 179 | Completely_Sorted_Table = zeros(Model_iterations,16,num_sources_guess); 180 | 181 | Shift_Final_L = zeros(1,num_sources_guess); 182 | Parameter_Break_VS_Likelihood_Break = Shift_Final_L; 183 | Z = Shift_Final_L; 184 | Evidence_log_noshift = Shift_Final_L; 185 | Evidence_log = Shift_Final_L; 186 | 187 | count_stuck = zeros(1,num_sources_guess); 188 | Direct_Slice_Sample_idx = zeros(1,num_sources_guess); 189 | Random_Gen_Slice_Samp_idx = zeros(1,num_sources_guess); 190 | B_i_j = zeros(1,num_sources_guess); 191 | deciban = zeros(1,num_sources_guess); 192 | 193 | Total_Time = 0; 194 | idx_total = 0; 195 | 196 | Converge_Limit_all = Converge_Limit; 197 | Initial_Table_Threshold_all = Exploration_Table_Threshold; 198 | 199 | 200 | %% MODEL SELECTION 201 | % ========================================================================= 202 | % ---------------------- Model Selection Iterations ----------------------- 203 | % ========================================================================= 204 | % Begin iterations through model selections. Begin with a single sources 205 | % model, then double source, and so on and so forth. 206 | for source_idx = 1:num_sources_guess 207 | 208 | if source_idx == 1 209 | Converge_Limit = 1; 210 | Exploration_Table_Threshold = 1; 211 | else 212 | Converge_Limit = Converge_Limit_all; 213 | Exploration_Table_Threshold = Initial_Table_Threshold_all; 214 | end 215 | 216 | %% --------------------------- Initializations ---------------------------- 217 | 218 | % Calculate randomized DoA attributes to populate exploration table 219 | theta = 180*rand(Model_iterations,source_idx); 220 | phi = 360*rand(Model_iterations,source_idx); 221 | amplitudes = [ones(Model_iterations,1) rand(Model_iterations,source_idx-1)]; 222 | 223 | % Initialize for Explorations Table and initial iterative calculations 224 | iteration = 0; % Initialize iteration count 225 | Beam_Model = zeros(UI.num_look_el,UI.num_look_az); % Initialize beam model grid 226 | error = zeros(Model_iterations,1); % Initialize error 227 | E = zeros(Model_iterations,1); % Initialize evidence 228 | Likelihood = zeros(Model_iterations,1); % Initialize likelihood 229 | K = UI.num_look_az * UI.num_look_el; % Calculate K (used in likelihood calculations, aka gridsize)) 230 | Exploration_Table = zeros(Model_iterations, 1+3*source_idx); % Initialize exploration table 231 | 232 | % Stop Matlab from breaking (ensure there aren't a bajillion plots) 233 | UI.plotflag = 0; % ***NEVER COMMENT OUT*** 234 | 235 | %% Calculate Initial Likelihoods and Populate Exploration Table 236 | for idx = 1:Model_iterations 237 | 238 | % DoA Attributes 239 | UI.amplitudes = amplitudes(idx,:); % assign amplitude per iteration 240 | UI.S_angles_az = phi(idx,:); % assign source phi per iteration 241 | UI.S_angles_el = theta(idx,:); % assign source theta per iteration 242 | 243 | % Beamform (create model) 244 | Beam_Model = Beam_Model_Iterate_4_7_18(UI); 245 | 246 | % Calculate Likelihood from model 247 | difference = Beam_Exp - Beam_Model; 248 | error = sum(sum(difference.*difference)); 249 | E = error/2; 250 | Likelihood(idx) = (-Beta*K/2)*log10(E); 251 | 252 | % Populate Exploration Table 253 | % Format: [Likelihood, Amplitudes, Phis, Thetas] 254 | Exploration_Table(idx,:) = [Likelihood(idx), UI.amplitudes, UI.S_angles_az, UI.S_angles_el]; 255 | end 256 | 257 | % ------------------------- Model Initializations ------------------------- 258 | % These values will be reset to their original definitions for each 259 | % different model iteration. They will not be reset for each parameter 260 | % iteration 261 | New_Likelihood = Likelihood; 262 | New_amplitudes = amplitudes; 263 | New_phi = phi; 264 | New_theta = theta; 265 | 266 | Sort_Amp = zeros(Model_iterations,source_idx); 267 | Sort_Phi = zeros(Model_iterations,source_idx); 268 | Sort_Theta = zeros(Model_iterations,source_idx); 269 | std_max = zeros(1,3); 270 | std_min = zeros(1,3); 271 | New_Table = zeros(1,size(Exploration_Table,2)); 272 | Loop_Break = ones(1,Threshold_Amount); 273 | 274 | Iteration_Time = 0; 275 | Iteration_Time_1000 = 0; 276 | idx = 0; 277 | L_up_idx = 0; 278 | Paramter_Dif_Break = 0; 279 | stuck = 0; 280 | check_stuck = 0; 281 | Random_Gen_Slice_Samp = 0; 282 | 283 | idk = 1; 284 | std_idx = 1; 285 | Run_plot_idx = 1; 286 | Area_Converge = 1; 287 | Use_Other_Sampling_Methods = 1; 288 | Int = 1; 289 | 290 | Delta_Like = inf; 291 | Loop_Sum = inf; 292 | Sort_Amp_Std_Avg = inf; 293 | Sort_Phi_Std_Avg = inf; 294 | Sort_Theta_Std_Avg = inf; 295 | Check_Threshold = inf; 296 | Max_L = max(Exploration_Table(:,1)); 297 | 298 | 299 | %% Loop until likelihood increase plateaus and converges 300 | while Area_Converge == 1 && Paramter_Dif_Break ~= 1 || Loop_Sum >= 1 && Paramter_Dif_Break ~= 1 301 | tic; 302 | 303 | % Maximum likelihood should never eclipse zero 304 | if Max_L > 0 305 | break 306 | end 307 | 308 | % Count the amount of times no successful step is made (for slice sampling) 309 | stuck = stuck + 1; 310 | 311 | % Count total iterations 312 | idx = idx + 1; 313 | 314 | % Find max/min likehoods 315 | [Max_L,~] = max(New_Likelihood); % Minimum likelihood 316 | [Min_L,Min_idx_L] = min(New_Likelihood); % Minimum likelihood 317 | 318 | % -------------------------- Random Perturbation -------------------------- 319 | % Select lowest likelihood in the initial table of paramters and 320 | % randomly perturb values until the likelihood improves. If it 321 | % improves, replace values in the initial table and append them in a 322 | % new, ever expnding, table as well. 323 | if L_up_idx < L_up_idx_lim && Use_Other_Sampling_Methods == 1 && stuck < Stuck_Lim 324 | % Randomly select parameter(s) 325 | if source_idx == 1 326 | random = round((7.49999-1.5)*rand(1,1)+1.5); 327 | else 328 | random = round((7.49999-.5)*rand(1,1)+.5); 329 | end 330 | 331 | % Perturb random parameter(s) 332 | switch random 333 | case 1 % Perturb Amplitude 334 | slot_a = round((source_idx-1)*rand(1,1)+1); 335 | slot = round(source_idx*rand(1,1)); 336 | if slot_a == 1 337 | slot_a = source_idx; 338 | end 339 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 340 | Int_Amp(:,slot_a) = rand(1,1); % insert perturbation 341 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 342 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 343 | case 2 % Perturb Phi 344 | slot_a = round((source_idx-1)*rand(1,1)+1); 345 | slot = round(source_idx*rand(1,1)); 346 | if slot == 0 347 | slot = source_idx; 348 | end 349 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 350 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 351 | Int_Phi(:,slot) = 360*rand(1,1); % insert perturbation 352 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 353 | case 3 % Perturb Theta 354 | slot_a = round((source_idx-1)*rand(1,1)+1); 355 | slot = round(source_idx*rand(1,1)); 356 | if slot == 0 357 | slot = source_idx; 358 | end 359 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 360 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 361 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 362 | Int_Theta(:,slot) = 180*rand(1,1); % insert perturbation 363 | case 4 % Perturb Amplitude & Phi 364 | slot_a = round((source_idx-1)*rand(1,1)+1); 365 | slot = round(source_idx*rand(1,1)); 366 | if slot_a == 1 367 | slot_a = source_idx; 368 | end 369 | if slot == 0 370 | slot = source_idx; 371 | end 372 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 373 | Int_Amp(:,slot_a) = rand(1,1); % insert perturbation 374 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 375 | Int_Phi(:,slot) = 360*rand(1,1); % insert perturbation 376 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 377 | case 5 % Perturb Amplitude & Theta 378 | slot_a = round((source_idx-1)*rand(1,1)+1); 379 | slot = round(source_idx*rand(1,1)); 380 | if slot_a == 1 381 | slot_a = source_idx; 382 | end 383 | if slot == 0 384 | slot = source_idx; 385 | end 386 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 387 | Int_Amp(:,slot_a) = rand(1,1); % insert perturbation 388 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 389 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 390 | Int_Theta(:,slot) = 180*rand(1,1); % insert perturbation 391 | case 6 % Perturb Phi & Theta 392 | slot_a = round((source_idx-1)*rand(1,1)+1); 393 | slot = round(source_idx*rand(1,1)); 394 | if slot_a == 1 395 | slot_a = source_idx; 396 | end 397 | if slot == 0 398 | slot = source_idx; 399 | end 400 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 401 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 402 | Int_Phi(:,slot) = 360*rand(1,1); % insert perturbation 403 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 404 | Int_Theta(:,slot) = 180*rand(1,1); % insert perturbation 405 | case 7 % Perturb Amplitude, Phi, & Theta 406 | slot_a = round((source_idx-1)*rand(1,1)+1); 407 | slot = round(source_idx*rand(1,1)); 408 | if slot_a == 1 409 | slot_a = source_idx; 410 | end 411 | if slot == 0 412 | slot = source_idx; 413 | end 414 | Int_Amp = New_amplitudes(Min_idx_L,:); % assign amplitude to rerun calcs 415 | Int_Amp(:,slot_a) = rand(1,1); % insert perturbation 416 | Int_Phi = New_phi(Min_idx_L,:); % assign source phi to rerun calcs 417 | Int_Phi(:,slot) = 360*rand(1,1); % insert perturbation 418 | Int_Theta = New_theta(Min_idx_L,:); % assign source theta to rerun calcs 419 | Int_Theta(:,slot) = 180*rand(1,1); % insert perturbation 420 | end % end switch random 421 | check_stuck = 0; % set stuck value count to 0 (don't need to go to slice sampling yet) 422 | 423 | 424 | % -------------------- Alternative Random Perturbation -------------------- 425 | elseif stuck >= Stuck_Lim && Use_Other_Sampling_Methods == 1 ||... 426 | Use_Other_Sampling_Methods == 1 && L_up_idx >= L_up_idx_lim 427 | 428 | % Randomly select parameter(s) 429 | if source_idx == 1 430 | random = round((7.49999-1.5)*rand(1,1)+1.5); 431 | else 432 | random = round((7.49999-.5)*rand(1,1)+.5); 433 | end 434 | 435 | % Sort exploration table by likelihood (least to greatest) 436 | Sort = sortrows(Exploration_Table,1); 437 | Sort_Parameters = Sort(:,2:end); 438 | 439 | % Select random model in exploration table for perturbation limit 440 | random_sort_idx = round((Model_iterations - 1)*rand(1) + 1); 441 | 442 | % Calculate the delta limits for perturbation limit per parameter type 443 | Sort_Delta_Amp = abs(sort(Sort_Parameters(1,1:source_idx)) -... 444 | sort(Sort_Parameters(random_sort_idx,1:source_idx))); 445 | Sort_Delta_Phi = abs(sort(Sort_Parameters(1,source_idx+1:2*source_idx)) -... 446 | sort(Sort_Parameters(random_sort_idx,source_idx+1:2*source_idx))); 447 | Sort_Delta_Theta = abs(sort(Sort_Parameters(1,2*source_idx+1:3*source_idx)) -... 448 | sort(Sort_Parameters(random_sort_idx,2*source_idx+1:3*source_idx))); 449 | 450 | % Calculate maximum per parameter type 451 | Max_Delta_Amp = max(Sort_Delta_Amp); 452 | Max_Delta_Phi = max(Sort_Delta_Phi); 453 | Max_Delta_Theta = max(Sort_Delta_Theta); 454 | 455 | % Perturb random parameter(s) 456 | switch random 457 | case 1 % Perturb Amplitude 458 | Delta_Amp = (2*Max_Delta_Amp)*rand(1,source_idx-1) - Max_Delta_Amp; % Delta Amp 459 | Int_Amp = [1 Sort_Parameters(1,2:source_idx)+Delta_Amp]; % Adjust value(s) by delta 460 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 461 | if Int_Amp(Int_idx) > 1 462 | Int_Amp(Int_idx) = 1 - (Int_Amp(Int_idx)-1); 463 | end 464 | end 465 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx); % Assign unaffected value(s) 466 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx); % Assign unaffected value(s) 467 | case 2 % Perturb Phi 468 | Delta_Phi = (2*Max_Delta_Phi)*rand(1,source_idx) - Max_Delta_Phi; % Delta Phi 469 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx) + Delta_Phi; % Adjust value(s) by delta 470 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 471 | if Int_Phi(Int_idx) > 360 472 | Int_Phi(Int_idx) = Int_Phi(Int_idx) - 360; 473 | end 474 | end 475 | Int_Amp = [1 Sort_Parameters(1,2:source_idx)]; % Assign unaffected value(s) 476 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx); % Assign unaffected value(s) 477 | case 3 % Perturb Theta 478 | Delta_Theta = (2*Max_Delta_Theta)*rand(1,source_idx) - Max_Delta_Theta; % Delta Theta 479 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx) + Delta_Theta; % Adjust value(s) by delta 480 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 481 | if Int_Theta(Int_idx) > 180 482 | Int_Theta(Int_idx) = Int_Theta(Int_idx) - 360; 483 | end 484 | end 485 | Int_Amp = [1 Sort_Parameters(1,2:source_idx)]; % Assign unaffected value(s) 486 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx); % Assign unaffected value(s) 487 | case 4 % Perturb Amplitude & Phi 488 | Delta_Amp = (2*Max_Delta_Amp)*rand(1,source_idx-1) - Max_Delta_Amp; % Delta Amp 489 | Int_Amp = [1 Sort_Parameters(1,2:source_idx) + Delta_Amp]; % Adjust value(s) by delta 490 | Delta_Phi = (2*Max_Delta_Phi)*rand(1,source_idx) - Max_Delta_Phi; % Delta Phi 491 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx) + Delta_Phi; % Adjust value(s) by delta 492 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 493 | if Int_Amp(Int_idx) > 1 494 | Int_Amp(Int_idx) = 1 - (Int_Amp(Int_idx)-1); 495 | end 496 | if Int_Phi(Int_idx) > 360 497 | Int_Phi(Int_idx) = Int_Phi(Int_idx) - 360; 498 | end 499 | end 500 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx); % Assign unaffected value(s) 501 | case 5 % Perturb Amplitude & Theta 502 | Delta_Amp = (2*Max_Delta_Amp)*rand(1,source_idx-1) - Max_Delta_Amp; % Delta Amp 503 | Int_Amp = [1 Sort_Parameters(1,2:source_idx) + Delta_Amp]; % Adjust value(s) by delta 504 | Delta_Theta = (2*Max_Delta_Theta)*rand(1,source_idx) - Max_Delta_Theta; % Delta Theta 505 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx) + Delta_Theta; % Adjust value(s) by delta 506 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 507 | if Int_Amp(Int_idx) > 1 508 | Int_Amp(Int_idx) = 1 - (Int_Amp(Int_idx)-1); 509 | end 510 | if Int_Theta(Int_idx) > 180 511 | Int_Theta(Int_idx) = Int_Theta(Int_idx) - 180; 512 | end 513 | end 514 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx); % Assign unaffected value(s) 515 | case 6 % Perturb Phi & Theta 516 | Delta_Phi = (2*Max_Delta_Phi)*rand(1,source_idx) - Max_Delta_Phi; % Delta Phi 517 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx) + Delta_Phi; % Adjust value(s) by delta 518 | Delta_Theta = (2*Max_Delta_Theta)*rand(1,source_idx) - Max_Delta_Theta; % Delta Theta 519 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx) + Delta_Theta; % Adjust value(s) by delta 520 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 521 | if Int_Phi(Int_idx) > 360 522 | Int_Phi(Int_idx) = Int_Phi(Int_idx) - 360; 523 | end 524 | if Int_Theta(Int_idx) > 180 525 | Int_Theta(Int_idx) = Int_Theta(Int_idx) - 180; 526 | end 527 | end 528 | Int_Amp = [1 Sort_Parameters(1,2:source_idx)]; % Assign unaffected value(s) 529 | case 7 % Perturb Amplitude, Phi, & Theta 530 | Delta_Amp = (2*Max_Delta_Amp)*rand(1,source_idx-1) - Max_Delta_Amp; % Delta Amp 531 | Int_Amp = [1 Sort_Parameters(1,2:source_idx) + Delta_Amp]; % Adjust value(s) by delta 532 | Delta_Phi = (2*Max_Delta_Phi)*rand(1,source_idx) - Max_Delta_Phi; % Delta Phi 533 | Int_Phi = Sort_Parameters(1,source_idx+1:2*source_idx) + Delta_Phi; % Adjust value(s) by delta 534 | Delta_Theta = (2*Max_Delta_Theta)*rand(1,source_idx) - Max_Delta_Theta; % Delta Theta 535 | Int_Theta = Sort_Parameters(1,2*source_idx+1:3*source_idx) + Delta_Theta; % Adjust value(s) by delta 536 | for Int_idx = 1:source_idx % Account for value(s) out of bounds 537 | if Int_Amp(Int_idx) > 1 538 | Int_Amp(Int_idx) = 1 - (Int_Amp(Int_idx)-1); 539 | end 540 | if Int_Phi(Int_idx) > 360 541 | Int_Phi(Int_idx) = Int_Phi(Int_idx) - 360; 542 | end 543 | if Int_Theta(Int_idx) > 180 544 | Int_Theta(Int_idx) = Int_Theta(Int_idx) - 180; 545 | end 546 | end 547 | end % end switch random 548 | 549 | % Prevent loop from getting stuck on one value. 550 | % Direct slice sampling (This should rarely happen in this context) 551 | if stuck > 20000 552 | rand_stuck = round((Model_iterations) * rand(1,1) + .5); 553 | if rand_stuck > Model_iterations 554 | rand_stuck = Model_iterations; 555 | end 556 | Int_Amp = Sort(rand_stuck,2:source_idx+1); 557 | Int_Phi = Sort(rand_stuck,source_idx+2:2*source_idx+1); 558 | Int_Theta = Sort(rand_stuck,2*source_idx+2:3*source_idx+1); 559 | Completely_Stuck_Check = 1; 560 | end 561 | 562 | check_stuck = 1; % notification of alternative sampling 563 | end 564 | 565 | % Assign Sampled Values 566 | UI.amplitudes = abs(Int_Amp); 567 | UI.S_angles_az = abs(Int_Phi); 568 | UI.S_angles_el = abs(Int_Theta); 569 | 570 | % -------------------- Beamforming & Likelihood Calcs --------------------- 571 | % Beamform (create model) 572 | Beam_Model = Beam_Model_Iterate_4_7_18(UI); 573 | 574 | % Calculate Likelihood 575 | difference = Beam_Exp - Beam_Model; 576 | error = sum(sum(difference.*difference)); 577 | E = error/2; 578 | Check_Likelihood = (-Beta*K/2)*log10(E); 579 | 580 | % Replace explorations table values 581 | % [Likelihood, Amplitudes, Phis, Thetas] 582 | % Dimensions: (Model_iterations) x (3*source_idx + 1) 583 | if Check_Likelihood > Min_L 584 | 585 | New_amplitudes(Min_idx_L,:) = UI.amplitudes; % assign new amplitude 586 | New_phi(Min_idx_L,:) = UI.S_angles_az; % assign new amplitude 587 | New_theta(Min_idx_L,:) = UI.S_angles_el; % assign new amplitude 588 | 589 | % Populate and Rewrite Parameter Tables 590 | L_up_idx = L_up_idx+1; 591 | New_Table(L_up_idx,:) = Exploration_Table(Min_idx_L,:); % Append unperturbed entry to to new table 592 | New_Likelihood(Min_idx_L) = Check_Likelihood; 593 | % Replace old entry in exploration table with higher likelihood entry 594 | Exploration_Table(Min_idx_L,:) =... 595 | [New_Likelihood(Min_idx_L), UI.amplitudes, UI.S_angles_az, UI.S_angles_el]; 596 | 597 | % Calculate the delta of likelihood 598 | if L_up_idx > 1 599 | Delta_Like(L_up_idx) = New_Table(L_up_idx,1) - New_Table(L_up_idx-1,1); 600 | end 601 | 602 | % --------------------------- Threshold Check ----------------------------- 603 | % Check to see if likelihood has plateaued. 1 for no, 0 for yes. 604 | if L_up_idx > Threshold_Amount % Needs to have minimum # of entries 605 | Check_Threshold = abs(sum(Delta_Like(L_up_idx-Threshold_Amount+1:L_up_idx))); 606 | if Check_Threshold > Converge_Limit % If > convergence criteria, add 1 607 | Loop_Break = circshift(Loop_Break,-1); 608 | Loop_Break(Threshold_Amount) = 1; 609 | else % If < convergence criteria, add 0 610 | Loop_Break = circshift(Loop_Break,-1); 611 | Loop_Break(Threshold_Amount) = 0; 612 | end 613 | 614 | % Keep track of amount of times various sampling methods have 615 | % been used 616 | if check_stuck == 1 617 | count_stuck(source_idx) = count_stuck(source_idx) + 1; 618 | end 619 | if Completely_Stuck_Check == 1 620 | Direct_Slice_Sample_idx(source_idx) = Direct_Slice_Sample_idx(source_idx) + 1; 621 | end 622 | if Random_Gen_Slice_Samp == 1 623 | Random_Gen_Slice_Samp_idx(source_idx) = Random_Gen_Slice_Samp_idx(source_idx) + 1; 624 | end 625 | 626 | Loop_Sum = sum(Loop_Break); % Add together to determine if convergence criteria is met 627 | end 628 | stuck = 0; % Reset amount of times no successful step was made 629 | end 630 | 631 | % ------------------------- Parameter Convergence ------------------------- 632 | % Check differences between Paramters within table (ensure convergence) 633 | if idx == 5000*std_idx && source_idx > 1 % only every 5000 iterations 634 | for Table_idx = 1:Model_iterations 635 | Sort_Amp(Table_idx,:) = sort(Exploration_Table(Table_idx,2:source_idx+1)); 636 | Sort_Phi(Table_idx,:) = sort(Exploration_Table(Table_idx,source_idx+2:2*source_idx+1)); 637 | Sort_Theta(Table_idx,:) = sort(Exploration_Table(Table_idx,2*source_idx+2:3*source_idx+1)); 638 | Sort_Table_Low_to_High = [New_Likelihood Sort_Amp Sort_Phi Sort_Theta]; 639 | 640 | % Organize Table for each model iteration for convenient 641 | % viewing if desired (not necessary for functionality of code) 642 | Completely_Sorted_Table(:,1:(1+(3*source_idx)),source_idx) = sortrows(Sort_Table_Low_to_High,1); 643 | end 644 | 645 | % Calculate 2*sigma to determine spread (95%) of parameters 646 | for Sort_Std_idx = 1:source_idx 647 | Sort_Amp_Std(source_idx,Sort_Std_idx) = 2.*std(Sort_Amp(:,Sort_Std_idx)); 648 | Sort_Phi_Std(source_idx,Sort_Std_idx) = 2.*std(Sort_Phi(:,Sort_Std_idx)); 649 | Sort_Theta_Std(source_idx,Sort_Std_idx) = 2.*std(Sort_Theta(:,Sort_Std_idx)); 650 | end 651 | 652 | % Average the 2*sigma (in case there are multiple sources) 653 | Sort_Amp_Std_Avg = sum(Sort_Amp_Std(source_idx,:))/source_idx; 654 | Sort_Phi_Std_Avg = sum(Sort_Phi_Std(source_idx,:))/source_idx; 655 | Sort_Theta_Std_Avg = sum(Sort_Theta_Std(source_idx,:))/source_idx; 656 | 657 | % If parameters meet the convergence criteria, then break 658 | if Sort_Amp_Std_Avg < .01 && Sort_Phi_Std_Avg < 1 && Sort_Theta_Std_Avg < .5 659 | Paramter_Dif_Break = 1; 660 | % If this is 0, then the likelihood converged. 661 | % If this is 1, then the parameter became very similar, so 662 | % there was no room for improvement, thus converging 663 | Parameter_Break_VS_Likelihood_Break(source_idx) = 1; 664 | break 665 | end 666 | 667 | std_idx = std_idx + 1; % count number of times parameter convergence checked 668 | end % End if check for parameter convergence 669 | 670 | % --------- Determine if there is still more area to be explored ---------- 671 | % Calculate the range of likelihood in the exploration table 672 | L_Dif_Initial_Table = max(Exploration_Table(:,1)) - min(Exploration_Table(:,1)); 673 | if L_Dif_Initial_Table >= Exploration_Table_Threshold 674 | Area_Converge = 1; 675 | else 676 | Area_Converge = 0; 677 | end 678 | 679 | % --------------------------- Slice Sampling 1 ---------------------------- 680 | % Completely repopulate exploration table & see if likelihood increases 681 | if Area_Converge == 1 && stuck == 200 && Direct_Slice_Sample_idx(source_idx) < 200 682 | 683 | Slice_amplitudes = [ones(Model_iterations,1) rand(Model_iterations,source_idx-1)]; 684 | Slice_phi = 360*rand(Model_iterations,source_idx); 685 | Slice_theta = 180*rand(Model_iterations,source_idx); 686 | 687 | % Initialize for exploration Table and Iterative Calculations 688 | Slice_Table = zeros(Model_iterations, 1+3*source_idx); 689 | 690 | for Slice_idx = 1:Model_iterations 691 | 692 | % Iterative Calculations 693 | iteration = iteration + 1; % count iteration 694 | Int_Amp = Slice_amplitudes(Slice_idx,:); % assign amplitude per iteration 695 | UI.amplitudes = Int_Amp; 696 | Int_Phi = Slice_phi(Slice_idx,:); % assign source phi per iteration 697 | UI.S_angles_az = Int_Phi; 698 | Int_Theta = Slice_theta(Slice_idx,:); % assign source theta per iteration 699 | UI.S_angles_el = Int_Theta; 700 | 701 | % Beamform (create model) 702 | Beam_Model = Beam_Model_Iterate_4_7_18(UI); 703 | 704 | % Calculate Likelihood 705 | difference = Beam_Exp - Beam_Model; 706 | error = sum(sum(difference.*difference)); 707 | E = error/2; 708 | Slice_Like = (-Beta*K/2)*log10(E); 709 | 710 | % [Likelihood, Amplitudes, Phis, Thetas] 711 | Slice_Table(Slice_idx,:) = [Slice_Like, Int_Amp, Int_Phi, Int_Theta]; 712 | end 713 | 714 | [~,Max_Slice_Like_idx] = max(Slice_Table(:,1)); 715 | Int_Amp = Slice_Table(Max_Slice_Like_idx,2:source_idx+1); 716 | Int_Phi = Slice_Table(Max_Slice_Like_idx,source_idx+2:2*source_idx+1); 717 | Int_Theta = Slice_Table(Max_Slice_Like_idx,2*source_idx+2:3*source_idx+1); 718 | Random_Gen_Slice_Samp = 1; 719 | Completely_Stuck_Check = 0; 720 | Use_Other_Sampling_Methods = 0; 721 | 722 | % --------------------------- Slice Sampling 2 ---------------------------- 723 | % Direct slice sampling 724 | elseif Area_Converge == 1 && stuck > 200 || Loop_Sum == 0 && Area_Converge == 1 725 | % Prevent loop from getting stuck on one value 726 | rand_stuck = round((Model_iterations) * rand(1,1) + .5); 727 | Sort = sortrows(Exploration_Table,1); % Sort initial table by likelihood (least to greatest) 728 | Int_Amp = Sort(rand_stuck,2:source_idx+1); 729 | Int_Phi = Sort(rand_stuck,source_idx+2:2*source_idx+1); 730 | Int_Theta = Sort(rand_stuck,2*source_idx+2:3*source_idx+1); 731 | Completely_Stuck_Check = 1; 732 | Random_Gen_Slice_Samp = 0; 733 | Use_Other_Sampling_Methods = 0; 734 | else 735 | %Otherwise use normal sampling methods 736 | Completely_Stuck_Check = 0; 737 | Random_Gen_Slice_Samp = 0; 738 | Use_Other_Sampling_Methods = 1; 739 | end 740 | % ----------------------- End Sampling Calculations ----------------------- 741 | 742 | % ---------------------------- Progress Display --------------------------- 743 | % Define time & iterations 744 | single_time = toc; 745 | Iteration_Time = single_time + Iteration_Time; 746 | Iteration_Time_1000 = single_time + Iteration_Time_1000; 747 | Total_Time = Total_Time + single_time; 748 | idx_total = idx_total + 1; % Total iterations 749 | 750 | % Progress Display (Updates every 1000 iterations) 751 | if idx == 1000*idk 752 | Avg_Iteration_Time = 1000*Iteration_Time/idx; 753 | idk = idk + 1; 754 | disp(['Sources: ', num2str(source_idx)]); 755 | disp(['Iteration: ', num2str(idx), ' / ', num2str(idx_total)]); 756 | disp(['1000 Iteration Time: ', num2str(Iteration_Time_1000), ' s']); 757 | disp(['Average 1000 Iteration Time: ', num2str(Avg_Iteration_Time), ' s']); 758 | if Iteration_Time < 60 759 | disp(['Running Model Time: ', num2str(Iteration_Time), ' s']); 760 | elseif Iteration_Time >= 60 && Iteration_Time < 3600 761 | disp(['Current Running Iteration Time: ', num2str(Iteration_Time/60), ' min']); 762 | else 763 | hour_single = floor(Iteration_Time/3600); 764 | disp(['Total Running Time: ', num2str(hour_single), ' hr, ',... 765 | num2str((Iteration_Time - (3600*hour_single))/60), ' min']); 766 | end 767 | if Total_Time < 3600 768 | disp(['Total Running Time: ', num2str(Total_Time/60), ' min']); 769 | else 770 | hour = floor(Total_Time/3600); 771 | disp(['Total Running Time: ', num2str(hour), ' hr, ',... 772 | num2str((Total_Time - (3600*hour))/60), ' min']); 773 | end 774 | disp(['# of Times Likelihood Increases: ',num2str(L_up_idx)]); %num2str(Loop_Sum)]); 775 | disp(['Similar Parameter Convergence: ', num2str(Parameter_Break_VS_Likelihood_Break),... 776 | ' Amp: ', num2str(Sort_Amp_Std_Avg), ' Phi: ',... 777 | num2str(Sort_Phi_Std_Avg), ' Theta: ',... 778 | num2str(Sort_Theta_Std_Avg) ]); 779 | disp(['# of Alternative Samples Used: ', num2str(count_stuck)]); 780 | disp(['# of Random Sliced Samples Used: ', num2str(Random_Gen_Slice_Samp_idx)]); 781 | disp(['# of Direct Sliced Samples Used: ', num2str(Direct_Slice_Sample_idx)]); 782 | disp(['Stuck: ', num2str(stuck)]); 783 | % Likelihood curve plateau / Likelihood range of exploration table 784 | disp(['Threshold Check: ', num2str(Check_Threshold), ' / ', num2str(L_Dif_Initial_Table)]); 785 | % # of times likelihood does not meet convergence criteria / Exploration table convergence (1/0) 786 | disp(['Break: ', num2str(Loop_Sum), ' / ', num2str(Use_Other_Sampling_Methods)]); 787 | Iteration_Time_1000 = 0; 788 | end % End if display 789 | 790 | % ----------------------------- Progress Plot ----------------------------- 791 | % Plot likelhood as it develops along with current likelihood of 792 | % exploration table. 793 | if idx == Likelihood_Update*Run_plot_idx 794 | XAXIS = size(New_Table,1) + (1:size(Exploration_Table,1))-1; 795 | if Check_Threshold >= 5*Converge_Limit || L_Dif_Initial_Table >= 5*Exploration_Table_Threshold 796 | figure(102); 797 | plot(New_Table(:,1), 'r', 'linewidth', 2); 798 | hold on 799 | plot(XAXIS, sort(Exploration_Table(:,1)), 'b', 'linewidth', 2); 800 | hold off 801 | legend('New Table', 'Initial Table', 'location', 'northwest'); 802 | title('Likelihood'); 803 | xlabel('Iterations'); 804 | ylabel('Likelihood'); 805 | else 806 | figure(102); 807 | plot(New_Table(:,1), 'g', 'linewidth', 2); 808 | hold on 809 | plot(XAXIS, sort(Exploration_Table(:,1)), 'b', 'linewidth', 2); 810 | hold off 811 | legend('New Table', 'Initial Table', 'location', 'northwest'); 812 | title('Likelihood'); 813 | xlabel('Iterations'); 814 | ylabel('Likelihood'); 815 | end % End red vs green plot 816 | Run_plot_idx = Run_plot_idx + 1; 817 | end % End if plot 818 | 819 | end % End While loop ------------------------------------------------------ 820 | 821 | 822 | %% CALCULATE EVIDENCE 823 | % ------------------------- Evidence Calculations ------------------------- 824 | % Sort exploration table (lowest likelihood to highest) 825 | Sorted_Table = sortrows(Exploration_Table,1); 826 | 827 | % Append exploration table to the end of new table. 828 | % Sorted from least to greatest by likelihood 829 | % (Separated to keep all tables) 830 | Final_Table = [New_Table; Sorted_Table]; 831 | switch source_idx 832 | case 1 833 | Final_Table_1 = Final_Table; 834 | case 2 835 | Final_Table_2 = Final_Table; 836 | case 3 837 | Final_Table_3 = Final_Table; 838 | case 4 839 | Final_Table_4 = Final_Table; 840 | case 5 841 | Final_Table_5 = Final_Table; 842 | end 843 | 844 | 845 | % For full likelihood curve (including the original table w/ replaced values) 846 | Total_Likelihood = [New_Table(:,1); Sorted_Table(:,1)]; 847 | 848 | % Shift & unlog to prep for evidence calc 849 | Final_L_log_noshift = New_Table(:,1); % Isolate likelihood 850 | 851 | % Shift and unlog for evidence calcs 852 | Shift_Final_L(source_idx) = max(Final_L_log_noshift); % Determine amount to shift lielihood 853 | Sub_Shift_Final_L = Final_L_log_noshift - Shift_Final_L(source_idx); 854 | Final_L = 10.^(Sub_Shift_Final_L); % Shift likelihood & unlog 855 | 856 | % Initialize Bayesian iterative values 857 | N = length(Final_L); 858 | Z_sum = 0; 859 | 860 | % Calculate Evidence (Eq. 20 in 'Bayesian Inference by Nested Sampling' - Tomislav Jasa and Ning Xiang) 861 | for Z_idx = 1:N 862 | mu_E(Z_idx,source_idx) = exp(-Z_idx/N) - exp(-(Z_idx+1)/N); 863 | Like(Z_idx,source_idx) = Final_L(Z_idx); 864 | Z_nosum = Final_L(Z_idx) * mu_E(Z_idx,source_idx); 865 | Z_sum = Z_sum + Z_nosum; 866 | end 867 | 868 | % Finish log evidence calc 869 | Z(source_idx) = Z_sum; 870 | Evidence_log_noshift(source_idx) = log10(Z_sum); 871 | Evidence_log(source_idx) = (Evidence_log_noshift(source_idx) + Shift_Final_L(source_idx)); 872 | 873 | % Calculate Bayes' Factor in log terms & decibans 874 | if source_idx > 1 875 | B_i_j(source_idx) = Evidence_log(source_idx-1)/Evidence_log(source_idx); 876 | deciban(source_idx) = 10*log10(B_i_j(source_idx)); 877 | else 878 | B_i_j(source_idx) = Background_Noise_Evidence/Evidence_log(source_idx); 879 | deciban(source_idx) = 10*log10(B_i_j(source_idx)); 880 | end 881 | 882 | 883 | %% DISPLAY SOURCE DoA ATTRIBUTES 884 | % ------------------------ DoA Attributes Display ------------------------- 885 | % Create amplitude & direction matrix for all sources 886 | par_idx_size = size(UI.amplitudes,2); 887 | Amps(source_idx,:) = [UI.amplitudes zeros(1,num_sources_guess-par_idx_size)]; 888 | Phis(source_idx,:) = [UI.S_angles_az zeros(1,num_sources_guess-par_idx_size)]; 889 | Thetas(source_idx,:) = [UI.S_angles_el zeros(1,num_sources_guess-par_idx_size)]; 890 | 891 | disp(['Max Likelihood: ', num2str(max(Likelihood))]); 892 | disp(['Max New Likelihood: ', num2str(max(New_Likelihood))]); 893 | disp(' '); 894 | disp(['Mod Amplitude: ', num2str(UI.amplitudes)]); 895 | disp(['Mod Phi: ', num2str(UI.S_angles_az)]); 896 | disp(['Mod Theta: ', num2str(UI.S_angles_el)]); 897 | 898 | %% PLOT CURRENT EVIDENCE 899 | % ---------------------------- Evidence Plots ----------------------------- 900 | % Plot Highest Likelihood Model 901 | [~,Max_idx_L] = max(Final_Table(:,1)); 902 | 903 | % Reassign to rerun final calcs 904 | UI.amplitudes = Final_Table(Max_idx_L,2:source_idx+1); % assign amplitude to rerun calcs 905 | UI.S_angles_az = Final_Table(Max_idx_L,source_idx+2:2*source_idx+1); % assign source phi to rerun calcs 906 | UI.S_angles_el = Final_Table(Max_idx_L,2*source_idx+2:3*source_idx+1); % assign source theta to rerun calcs 907 | 908 | UI.plotflag = 1; % plot outputs 909 | [Most_Likely_Model] = Beam_Model_Iterate_4_7_18(UI); % Beamform (create model) 910 | title(strcat(num2str(source_idx), ' Source Model' )); 911 | savefig(strcat(Save_Location,'\Beam', num2str(source_idx),'.fig')); 912 | 913 | % Plot Final Likelihood 914 | Like_XAXIS = size(New_Table,1) + (1:size(Sorted_Table,1))-1; 915 | figure; 916 | plot(New_Table(:,1), 'g', 'linewidth', 2); 917 | hold on 918 | plot(Like_XAXIS, Sorted_Table(:,1), 'r', 'linewidth', 2); 919 | hold off 920 | legend('Total Likelihood Curve', 'Exploration Table', 'location', 'northwest'); 921 | title(strcat(num2str(source_idx), ' Source Model Likelihood' )); 922 | xlabel('Iterations'); 923 | ylabel('Likelihood'); 924 | savefig(strcat(Save_Location,'\L_i_', num2str(source_idx),'.fig')); 925 | 926 | % Plot current log Evidence 927 | figure(103); 928 | bar(Evidence_log(1:source_idx)) 929 | title('Current log Evidence'); 930 | xlabel('Number of Sources'); 931 | ylabel('log Evidence'); 932 | 933 | % Plot current Evidence (in decibans) 934 | figure(104); 935 | bar(deciban(1:source_idx)) 936 | title('Current Deciban'); 937 | xlabel('Number of Sources'); 938 | ylabel('Decibans'); 939 | 940 | 941 | % ========================================================================= 942 | end % --------------------- End for Source Iterations --------------------- 943 | % ========================================================================= 944 | 945 | %% FINAL PLOTS & DISPLAY 946 | % ------------------------------ Final Plots ------------------------------ 947 | % Plot mu & Likliehood for each iteration 948 | figure; hold on 949 | for plot_idx = 1:num_sources_guess 950 | plot(1000*mu_E(:,plot_idx), 'linewidth', 1); 951 | plot(Like(:,plot_idx), 'linewidth', 1); 952 | end 953 | title('\mu(E(k)) & Likelihood'); 954 | legend('\mu_1', 'L_1', '\mu_2', 'L_2', '\mu_3', 'L_3', '\mu_4', 'L_4', ... 955 | '\mu_5', 'L_5', 'location', 'best'); 956 | savefig(strcat(Save_Location,'\mu(E(k)) & Likelihood.fig')); 957 | 958 | % Plot Final Evidence 959 | figure; 960 | bar(Evidence_log) 961 | title('log Evidence'); 962 | xlabel('Number of Sources'); 963 | ylabel('log Evidence'); 964 | savefig(strcat(Save_Location,'\log Evidence.fig')); 965 | 966 | % Plot Final Decibans 967 | figure; 968 | bar(deciban) 969 | title('Deciban Model Evidence'); 970 | xlabel('Number of Sources'); 971 | ylabel('Decibans'); 972 | savefig(strcat(Save_Location,'\Decibans.fig')); 973 | 974 | % ----------------------------- Final Display ----------------------------- 975 | % Display parameters for each iteration 976 | disp(' '); 977 | disp(' '); 978 | for a = 1:num_sources_guess 979 | disp([num2str(a),' -------------------------------------------------------------------------']); 980 | disp(['Mod Amplitude: ', num2str(Amps(a,:))]); 981 | disp(['Mod Phi: ', num2str(Phis(a,:))]); 982 | disp(['Mod Theta: ', num2str(Thetas(a,:))]); 983 | disp([num2str(a),' -------------------------------------------------------------------------']); 984 | disp(' '); 985 | end 986 | 987 | % Compile source attributes from lowest source number to highest for later viewing 988 | Source_Attributes = [Amps Phis Thetas]; 989 | 990 | % Display total running time 991 | if Total_Time < 60 992 | disp(['Total Running Time: ', num2str(Total_Time), ' s']); 993 | elseif Total_Time >= 60 && Total_Time < 3600 994 | disp(['Total Running Time: ', num2str(Total_Time/60), ' min']); 995 | else 996 | hour_single = floor(Total_Time/3600); 997 | disp(['Total Running Time: ', num2str(hour_single), ' hr, ',... 998 | num2str((Total_Time - (3600*hour_single))/60), ' min']); 999 | end 1000 | 1001 | % Save final workspace 1002 | save(strcat(Save_Location,'\',Load_Filename)); 1003 | -------------------------------------------------------------------------------- /Beams.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/5ff0b4be7c90be4a20a9d86aafe1e933934b2ab8/Beams.jpg -------------------------------------------------------------------------------- /Exp_vs_Model.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/5ff0b4be7c90be4a20a9d86aafe1e933934b2ab8/Exp_vs_Model.jpg -------------------------------------------------------------------------------- /Exp_vs_Model_2D.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/5ff0b4be7c90be4a20a9d86aafe1e933934b2ab8/Exp_vs_Model_2D.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MODEL-BASED BAYESIAN DIRECTION OF ARRIVAL ANALYSIS FOR SOUND SOURCES USING A SPHERICAL MICROPHONE ARRAY 2 | 3 | This algorithm estimates the directions of arrival (DoA) and relative levels of an arbitrary number of sound sources from sound data recorded on Rensselaer Polytechnic Institute’s (RPI) 16-channel spherical microphone array. This program is built upon a framework of Bayesian probability to estimate DoAs accurately. Nested and slice sampling methods were leveraged to execute this framework. Spherical harmonics and beamforming were utilized to spatially filter the incoming sound data as well as construct models. 4 | 5 | **NOTE**: Functions and other data files not included. Please contact me if you would like access to these files and/or the associated research papers/thesis. crlandschoot@gmail.com 6 | 7 | **NOTE**: This program was constructed for a specific use in the context of research and will take several changes to function within another ecosystem. For example, the main algorithm only works with RPI's 16-channel hand-built spherical microphone array. Its sampling constants are hardcoded and would need to be changed to use another sound source. The purpose of this repo is to provide the framework of the algorithm as a demonstration. 8 | 9 | This work is the intellectual property of Christopher Landschoot, Ning Xiang, and Rensselaer Polytechnic Institute. 10 | 11 | --- 12 | 13 | ### Patent Pending: https://patents.google.com/patent/US20220260665A1/en 14 | 15 | --- 16 | 17 | ## ABSTRACT 18 | http://dx.doi.org/10.1121/1.5138126 19 | 20 | In many room acoustics and noise control applications, it is often challenging to determine the directions of arrival (DoAs) of incoming sound sources. This work seeks to solve this problem reliably by beamforming, or spatially filtering, incoming sound data with a spherical microphone array via a probabilistic method. When estimating the DoA, the signal under consideration may contain one or multiple concurrent sound sources originating from different directions. This leads to a two-tiered challenge of first identifying the correct number of sources, followed by determining the directional information of each source. To this end, a probabilistic method of model-based Bayesian analysis is leveraged. This entails generating analytic models of the experimental data, individually defined by a specific number of sound sources and their locations in physical space, and evaluating each model to fit the measured data. Through this process, the number of sources is first estimated, and then the DoA information of those sources is extracted from the model that is the most concise to fit the experimental data. This paper will present the analytic models, the Bayesian formulation, and preliminary results to demonstrate the potential usefulness of this model-based Bayesian analysis for complex noise environments with potentially multiple concurrent sources. 21 | 22 | --- 23 | 24 | ### Spherical Harmonics 25 | ![alt text](https://github.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/blob/main/Spherical_Harmonics.jpg?raw=true) 26 | 27 | ### Spherical Beamforming Directivity by Order (*n*) 28 | ![alt text](https://github.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/blob/main/Beams.jpg?raw=true) 29 | 30 | 31 | ### Single Source Experimentally Measured Data vs. Modeled Data (Best Fit) - 360° Sound Energy "Heat Map" Plot 32 | ![alt text](https://github.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/blob/main/Exp_vs_Model.jpg?raw=true) 33 | 34 | 35 | ### Double Source Experimentally Measured Data vs. Modeled Data (Best Fit) - 180° Sound Energy Plot 36 | ![alt text](https://github.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/blob/main/Exp_vs_Model_2D.jpg?raw=true) 37 | 38 | ### RPI's Spherical Microphone Array 39 | ![alt text](https://github.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/blob/main/Sph_Mic_Angles.png?raw=true) 40 | -------------------------------------------------------------------------------- /Speaker_Mic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/5ff0b4be7c90be4a20a9d86aafe1e933934b2ab8/Speaker_Mic.jpg -------------------------------------------------------------------------------- /Sph_Mic_Angles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/5ff0b4be7c90be4a20a9d86aafe1e933934b2ab8/Sph_Mic_Angles.png -------------------------------------------------------------------------------- /Spherical_Harmonics.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlandsc/Model-based-Bayesian-DoA-Analysis-for-Sound-Sources-Using-a-Spherical-Microphone-Array/5ff0b4be7c90be4a20a9d86aafe1e933934b2ab8/Spherical_Harmonics.jpg --------------------------------------------------------------------------------