├── Beamforming ├── 2d.png ├── 3d.png ├── array.png └── beamfoming.m ├── Comparison of Direction of Arrival (DOA) Estimation Techniques for Closely Spaced Targets.pdf ├── GCC-PHAT └── README.md ├── MUSIC+ESPRIT+MVDR+MinNorm+Beamforming ├── DOA.m ├── MUSIC.png ├── MVDR.png ├── Min-Norm.png ├── beamforming.png └── music、esprit、mvdr算法的谱估计.pdf ├── MUSIC-DOA-Estimation ├── 1_simulate_source_signal.py ├── 2_simulate_recieved_signal.py ├── 3_DOA_estimation_MUSIC.py └── MUSIC.ipynb ├── MUSIC_implement1 ├── implement-2.m ├── music-1.png └── music-2.png ├── MUSIC_implement2 ├── doa_music.m └── result.png ├── MUSIC算法讲解 ├── 1.jpeg ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png └── README.md ├── README.md ├── SRP-PHAT └── README.md └── root-MUSIC ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png └── rootMUSIC.m /Beamforming/2d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/Beamforming/2d.png -------------------------------------------------------------------------------- /Beamforming/3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/Beamforming/3d.png -------------------------------------------------------------------------------- /Beamforming/array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/Beamforming/array.png -------------------------------------------------------------------------------- /Beamforming/beamfoming.m: -------------------------------------------------------------------------------- 1 | clc 2 | clear all 3 | close all 4 | 5 | %% ------------------------------初始化常量-------------------------------% 6 | c = 334; % 声速c 7 | fs = 1000; % 抽样频率fs 8 | T = 0.1; % ?? 9 | t = 0:1/fs:T; % 时间 [0, 0.1] 10 | L = length(t); % 时间长度:101 11 | f = 500; % 感兴趣的频率 12 | w = 2*pi*f; % 角频率 13 | k = w/c; % 波数 k 14 | 15 | %% ------------------------------各阵元坐标-------------------------------% 16 | M = 17; % 阵元个数 17 | % Nmid = 12; % 参考点 18 | % d = 3; % 阵元间距 19 | % m = (0:1:M-1) 20 | yi = zeros(M,1); % 生成一个M*1维的零矩阵 21 | zi = [ 0; 3; 6; 9;12;15;18;21;24;12;12;12;12;12;12;12;12]; 22 | xi = [12;12;12;12;12;12;12;12;12; 0; 3; 6; 9;15;18;21;24]; 23 | %xi = xi.' % 列向量 m*d 阵元数*阵元间距 24 | 25 | 26 | figure(1) 27 | plot(xi,zi,'r*'); 28 | title('十字形麦克风阵列') 29 | 30 | 31 | %% ---------------------------- 声源位置------------------------------------% 32 | x1 = 12; 33 | y1 = 10; 34 | z1 = 12; %声源位置 (12,10,12) x,z为水平面 35 | 36 | x2 = 12; % array center 37 | y2 = 0; 38 | z2 = 12; 39 | 40 | Ric1 = sqrt((x1-xi).^2+(y1-yi).^2+(z1-zi).^2); % 声源到各阵元的距离 41 | Ric2 = sqrt((x1-x2).^2+(y1-y2).^2+(z1-z2).^2); %sound to array center:10 42 | Rn1 = Ric1 - Ric2; %声源至各阵元与参考阵元的声程差矢量 43 | 44 | s1 = cos(2*w*t); % 参考阵元接收到的矢量 45 | Am = 10^(-1); % 振幅 46 | n1 = Am * (randn(M, L) + j*randn(M, L)); % 各阵元高斯白噪声 47 | p1 = zeros(M,L); 48 | 49 | 50 | %% ----------------------------各阵元的延迟求和----------------------------------% 51 | % 整个程序最关键的部分,延迟求和,同时得到各阵元接收的声压信号矩阵,以及协方差矩阵 52 | for k1 = 1:M 53 | p1(k1,:) = Ric2/Ric1(k1) * s1.*exp(-j*w*Rn1(k1)/c); 54 | % 接收到的信号 55 | end 56 | 57 | p = p1+n1; % 各阵元接收的声压信号矩阵 58 | R = p*p'/L; % 接收数据的自协方差矩阵 A.'是一般转置,A'是共轭转置 59 | 60 | 61 | %% ----------------------------------扫描范围----------------------------------% 62 | % 我们设置步长为0.1,扫描范围是20x20的平面,双重for循环得到M*1矢量矩阵,最后得到交叉谱矩阵(cross spectrum matrix) 63 | % 由DSP理论,这个就是声音的功率。 64 | step_x = 0.1; % 步长设置为0.1 65 | step_z = 0.1; 66 | y = y1; 67 | x = (9:step_x:15); % 扫描范围 9-15 68 | z = (9:step_z:15); 69 | 70 | for k1=1:length(z) 71 | for k2=1:length(x) 72 | Ri = sqrt((x(k2)-xi).^2+(y-yi).^2+(z(k1)-zi).^2); % 该扫描点到各阵元的聚焦距离矢量 73 | Ri2 = sqrt((x(k2)-x2).^2+(y-y2).^2+(z(k1)-z2).^2); % 10.8628 74 | Rn = Ri-Ri2; % 扫描点到各阵元与参考阵元的程差矢量 75 | b = exp(-j*w*Rn/c); % 声压聚焦方向矢量 76 | Pcbf(k1,k2) = abs(b'*R*b); % CSM,最关键,(1,18)*(18,18)*(18,1) 77 | end 78 | end 79 | 80 | 81 | %% -------------------------------------归一化-------------------------------------% 82 | for k1 = 1:length(z) 83 | pp(k1) = max(Pcbf(k1,:)); % Pcbf 的第k1行的最大元素的值 84 | end 85 | 86 | Pcbf = Pcbf/max(pp); % 所有元素除以其最大值 归一化幅度, (61,61) 87 | 88 | 89 | %% -------------------------------------作图展示------------------------------------% 90 | figure(2) 91 | surf(x,z,Pcbf); 92 | xlabel('x(m)'),ylabel('z(m)') 93 | title('三维单声源图') 94 | colorbar 95 | 96 | figure(3) 97 | pcolor(x,z,Pcbf); 98 | shading interp; 99 | xlabel('x(m)'); 100 | ylabel('z(m)'); 101 | title('单声源图') 102 | colorbar 103 | -------------------------------------------------------------------------------- /Comparison of Direction of Arrival (DOA) Estimation Techniques for Closely Spaced Targets.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/Comparison of Direction of Arrival (DOA) Estimation Techniques for Closely Spaced Targets.pdf -------------------------------------------------------------------------------- /GCC-PHAT/README.md: -------------------------------------------------------------------------------- 1 | # GCC-PHAT 2 | Generalized Cross Correlation Phase Transform (GCC-PHAT) algorithm for Time Delay of Arrival (TDOA) estimation. 3 | 4 | 5 | ## References 6 | * [xiongyihui/tdoa](https://github.com/xiongyihui/tdoa) 7 | * [TDOA-GCC-PHAT方法](http://www.funcwj.cn/2018/05/10/gcc-phat-for-tdoa-estimate/) 8 | * [麦克风阵列声源定位 GCC-PHAT](https://blog.csdn.net/u010592995/article/details/79735198) 9 | * [基于麦克风阵列的声源定位算法之GCC-PHAT](https://www.cnblogs.com/ytxwzqin/p/9004603.html) 10 | -------------------------------------------------------------------------------- /MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/DOA.m: -------------------------------------------------------------------------------- 1 | % Simulation of MUSIC, ESPRIT, MVDR, Min-Norm and Classical DOA 2 | % algorithms for a uniform linear array. 3 | clc 4 | clear all 5 | 6 | doas=[-30 -5 40 70]*pi/180; %DOA's of signals in rad. 7 | P=[1 1 1 1]; %Power of incoming signals 8 | N=10; %Number of array elements 9 | K=1024; %Number of data snapshots 10 | d=0.5; %Distance between elements in wavelengths 11 | noise_var=1; %Variance of noise 12 | r=length(doas); %Total number of signals 13 | 14 | % Steering vector matrix. Columns will contain the steering vectors of the r signals 15 | A=exp(-1i*2*pi*d*(0:N-1)'*sin([doas(:).'])); 16 | % Signal and noise generation 17 | sig=round(rand(r,K))*2-1; % Generate random BPSK symbols for each of the % r signals 18 | noise=sqrt(noise_var/2)*(randn(N,K)+1i*randn(N,K)); %Uncorrelated noise 19 | 20 | X=A*diag(sqrt(P))*sig+noise; %Generate data matrix 21 | R=X*X'/K; %Spatial covariance matrix 22 | [Q ,D]=eig(R); %Compute eigendecomposition of covariance matrix 23 | [D,I]=sort(diag(D),1,'descend'); %Find r largest eigenvalues 24 | Q=Q(:,I); %Sort the eigenvectors to put signal eigenvectors first 25 | Qs=Q(:,1:r); %Get the signal eigenvectors 26 | Qn=Q(:,r+1:N); %Get the noise eigenvectors 27 | 28 | % MUSIC algorithm 29 | % Define angles at which MUSIC "spectrum?will be computed 30 | angles=(-90:0.1:90); 31 | %Compute steering vectors corresponding values in angles 32 | a1=exp(-1i*2*pi*d*(0:N-1)'*sin([angles(:).']*pi/180)); 33 | 34 | for k=1:length(angles) %Compute MUSIC "spectrum? 35 | music_spectrum(k)=(a1(:,k)'*a1(:,k))/(a1(:,k)'*Qn*Qn'*a1(:,k)); 36 | end 37 | 38 | figure(1) 39 | plot(angles,abs(music_spectrum)) 40 | title('MUSIC Spectrum') 41 | xlabel('Angle in degrees') 42 | 43 | %ESPRIT Algorithm 44 | phi= linsolve(Qs(1:N-1,:),Qs(2:N,:)); 45 | ESPRIT_doas=asin(-angle(eig(phi))/(2*pi*d))*180/pi; 46 | 47 | %MVDR 48 | IR=inv(R); %Inverse of covariance matrix 49 | for k=1:length(angles) 50 | mvdr(k)=1/(a1(:,k)'*IR*a1(:,k)); 51 | end 52 | figure(2) 53 | plot(angles,abs(mvdr)) 54 | xlabel('Angle in degrees') 55 | title('MVDR') 56 | 57 | %Min norm method 58 | alpha=Qs(1,:); 59 | Shat=Qs(2:N,:); 60 | ghat=-Shat*alpha'/(1-alpha*alpha'); 61 | g=[1;ghat]; 62 | for k=1:length(angles) 63 | minnorm_spectrum(k)=1/(abs(a1(:,k)'*g)); 64 | end 65 | figure(3) 66 | plot(angles,abs(minnorm_spectrum)) 67 | xlabel('Angle in degrees') 68 | title('Min-Norm') 69 | 70 | %Estimate DOA's using the classical beamformer 71 | for k=1:length(angles) 72 | Classical(k)=(a1(:,k)'*R*a1(:,k)); 73 | end 74 | figure(4) 75 | plot(angles,abs(Classical)) 76 | xlabel('Angle in degrees') 77 | title('Classical Beamformer') 78 | -------------------------------------------------------------------------------- /MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/MUSIC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/MUSIC.png -------------------------------------------------------------------------------- /MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/MVDR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/MVDR.png -------------------------------------------------------------------------------- /MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/Min-Norm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/Min-Norm.png -------------------------------------------------------------------------------- /MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/beamforming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/beamforming.png -------------------------------------------------------------------------------- /MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/music、esprit、mvdr算法的谱估计.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/music、esprit、mvdr算法的谱估计.pdf -------------------------------------------------------------------------------- /MUSIC-DOA-Estimation/1_simulate_source_signal.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from cmath import e, pi, sin, cos 4 | 5 | N = 5 6 | M = 10 7 | p = 100 8 | fc = 1e6 9 | fs = 1e7 10 | 11 | # Generating Source Signal : Nxp 12 | 13 | s = np.zeros((N, p), dtype=complex) 14 | for t in np.arange(start=1, stop=p + 1): 15 | t_val = t / fs 16 | amp = np.random.multivariate_normal(mean=np.zeros(N), cov=1 * np.diag(np.ones(N))) 17 | s[:, t - 1] = np.exp(1j * 2 * pi * fc * t_val) * amp 18 | print("Source Signal s : ", s.shape) 19 | 20 | np.save('source_signal_data.npy', s) 21 | -------------------------------------------------------------------------------- /MUSIC-DOA-Estimation/2_simulate_recieved_signal.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from cmath import e, pi, sin, cos 4 | 5 | N = 5 6 | M = 10 7 | p = 100 8 | fc = 1e6 9 | fs = 1e7 10 | 11 | # source data 12 | s = np.load('source_signal_data.npy') 13 | print("Source Signal s : ", s.shape) 14 | 15 | # storing DOAs in radians 16 | doa = np.array([20, 50, 85, 110, 145]) * pi / 180 17 | print("Original Directions of Arrival (degrees): \n", doa * 180 / pi) 18 | 19 | c = 3e8 20 | d = 150 21 | 22 | 23 | # Steering Vector as a function of theta 24 | def a(theta): 25 | a1 = np.exp(-1j * 2 * pi * fc * d * (np.cos(theta) / c) * np.arange(M)) 26 | return a1.reshape((M, 1)) 27 | 28 | 29 | A = np.zeros((M, N), dtype=complex) 30 | for i in range(N): 31 | A[:, i] = a(doa[i])[:, 0] 32 | print("Steering Matrix A: ", A.shape) 33 | 34 | # Generating Recieved Signal 35 | noise = np.random.multivariate_normal(mean=np.zeros(M), cov=np.diag(np.ones(M)), size=p).T 36 | X = (A @ s + noise) 37 | print("Recieved Signal X: ", X.shape) 38 | 39 | np.save('recieved_signal_data.npy', X) 40 | -------------------------------------------------------------------------------- /MUSIC-DOA-Estimation/3_DOA_estimation_MUSIC.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from cmath import e, pi, sin, cos 4 | 5 | N = 5 6 | M = 10 7 | p = 100 8 | fc = 1e6 9 | fs = 1e7 10 | c = 3e8 11 | d = 150 12 | 13 | 14 | # Steering Vector as a function of theta 15 | def a(theta): 16 | a1 = np.exp(-1j * 2 * pi * fc * d * (np.cos(theta) / c) * np.arange(M)) 17 | return a1.reshape((M, 1)) 18 | 19 | 20 | # recieved signal data 21 | X = np.load('recieved_signal_data.npy') 22 | print("Recieved Signal X: ", X.shape) 23 | 24 | # empirical covariance matrix of X 25 | S = X @ X.conj().T / p 26 | print("Empirical Covariance Matrix S : ", S.shape) 27 | 28 | # finding eigen values and eigen vectors 29 | eigvals, eigvecs = np.linalg.eig(S) 30 | # eigen values are real as S is Hermitian matrix 31 | eigvals = eigvals.real 32 | 33 | # sorting eig vals and eig vecs in decreasing order of eig vals 34 | idx = eigvals.argsort()[::-1] 35 | eigvals = eigvals[idx] 36 | eigvecs = eigvecs[:, idx] 37 | 38 | # Plotting Eigen Values 39 | fig, ax = plt.subplots(figsize=(10, 4)) 40 | ax.scatter(np.arange(N), eigvals[:N], label="N EigVals from Source") 41 | ax.scatter(np.arange(N, M), eigvals[N:], label="M-N EigVals from Noise") 42 | plt.title('Visualize Source and Noise Eigenvalues') 43 | plt.legend() 44 | 45 | # separating source and noise eigvectors 46 | Us, Un = eigvecs[:, :N], eigvecs[:, N:] 47 | print("Source Eigen Values : Us: ", Us.shape) 48 | print("Noise Eigen Values : Un: ", Un.shape) 49 | 50 | # plotting original DOAs for comparison with peaks 51 | fig, ax = plt.subplots(figsize=(10, 4)) 52 | doa = np.array([20, 50, 85, 110, 145]) 53 | print("Original Directions of Arrival (degrees): \n", doa) 54 | for k in range(len(doa)): 55 | plt.axvline(x=doa[k], color='red', linestyle='--') 56 | 57 | 58 | def P(theta): 59 | return (1 / (a(theta).conj().T @ Un @ Un.conj().T @ a(theta)))[0, 0] 60 | 61 | 62 | # searching for all possible theta 63 | theta_vals = np.arange(0, 181, 1) 64 | P_vals = np.array([P(val * pi / 180.0) for val in theta_vals]).real 65 | 66 | # Plotting P_vals vs theta to find peaks 67 | plt.plot(np.abs(theta_vals), P_vals) 68 | plt.xticks(np.arange(0, 181, 10)) 69 | plt.xlabel('theta') 70 | plt.title('Dotted Lines = Actual DOA Peaks = Estimated DOA') 71 | 72 | plt.legend() 73 | plt.grid() 74 | plt.show() 75 | -------------------------------------------------------------------------------- /MUSIC-DOA-Estimation/MUSIC.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import scipy.signal\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "import seaborn as sns\n", 13 | "import pandas as pd\n", 14 | "from cmath import e,pi,sin,cos" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Simulating Narrowband Sources\n", 22 | "\n", 23 | "Let :\n", 24 | "\n", 25 | "No of Sources = N = 5 \n", 26 | "No of Recievers = M = 10 \n", 27 | "No of Time Snapshots of Recieved Signal = p = 100 \n", 28 | "Frequency of Source = fc = 10^6 Hz \n", 29 | "Sampling Frequency at Sensors = fs = 10^7 Hz \n", 30 | "(=> Time Values for Snapshots are 1/fs, 2/fs, ... , p/fs) \n", 31 | "\n", 32 | "Source Signal : A * exp(j * 2 * pi * fc * t) where A ~ Gaussian(0,1) \n", 33 | "\n", 34 | "\n" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 2, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "N=5\n", 44 | "M=10\n", 45 | "p=100\n", 46 | "fc=1e6\n", 47 | "fs=1e7" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "Generating Source Signals" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 3, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "s=\n", 67 | " (5, 100)\n" 68 | ] 69 | } 70 | ], 71 | "source": [ 72 | "#Generating Source Signal : Nxp\n", 73 | "s = np.zeros((N,p),dtype=complex)\n", 74 | "for t in np.arange(start=1,stop=p+1):\n", 75 | " t_val = t/fs\n", 76 | " amp = np.random.multivariate_normal(mean=np.zeros(N),cov=1*np.diag(np.ones(N)))\n", 77 | " s[:,t-1] = np.exp(1j*2*pi*fc*t_val)*amp\n", 78 | "print(\"s=\\n\",s.shape)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "Getting the Reciever Signals after Effect of Delay (using Steering Matrix) and Mixing with Noise \n", 86 | "\n", 87 | "Assume Incident DOAs are 20,50,85,110,145 degrees respectively for the sources, we will find these values using MUSIC algorithm.\n", 88 | "\n", 89 | "Assume signal travels as speed of light = c = 3* 10^8 m/s\n", 90 | "\n", 91 | "Assume Reciever Antenas are spaced at d=150 m in a straight line" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 12, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "A=\n", 104 | " (10, 5)\n" 105 | ] 106 | } 107 | ], 108 | "source": [ 109 | "#storing DOAs in radians\n", 110 | "doa = np.array([20,50,85,110,145])*pi/180\n", 111 | "\n", 112 | "c = 3e8\n", 113 | "d = 150\n", 114 | "\n", 115 | "#Steering Vector as a function of theta\n", 116 | "def a(theta):\n", 117 | " a1 = np.exp(-1j*2*pi*fc*d*(np.cos(theta)/c) * np.arange(M) )\n", 118 | " return a1.reshape((M,1))\n", 119 | "#print(a(2))\n", 120 | "\n", 121 | "A = np.zeros((M,N),dtype=complex)\n", 122 | "for i in range(N):\n", 123 | " A[:,i] = a(doa[i])[:,0]\n", 124 | "\n", 125 | "print(\"A=\\n\",A.shape)" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "Recieved Signal x(t) = A(theta)*s(t) + n(t), where n(t) is White Gaussian Noise for each reciever" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 13, 138 | "metadata": {}, 139 | "outputs": [ 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "X=\n", 145 | " (10, 100)\n" 146 | ] 147 | } 148 | ], 149 | "source": [ 150 | "# X1 = np.zeros((p,M),dtype=complex)\n", 151 | "# for t in range(p):\n", 152 | "# X1[t] = ( A@(s[t].reshape((N,1))) + np.random.normal(loc=0,scale=1,size=(M,1)) ).reshape((M))\n", 153 | " \n", 154 | "# print(\"X1=\\n\",X1.shape)\n", 155 | "\n", 156 | "# noise = np.random.normal(loc=0,scale=1,size=(M,1))\n", 157 | "noise = np.random.multivariate_normal(mean=np.zeros(M),cov=np.diag(np.ones(M)),size=p).T\n", 158 | "X = (A@s + noise)\n", 159 | "print(\"X=\\n\",X.shape)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Application of MUSIC Algorithm: \n", 167 | " \n", 168 | " When recieved signal data (X), source frequency (fc), no of sources(M), is known, we can find the DOA for each source signal using MUltiple Signal Classification Algorithm. " 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 14, 174 | "metadata": {}, 175 | "outputs": [ 176 | { 177 | "name": "stdout", 178 | "output_type": "stream", 179 | "text": [ 180 | "emp cov = (10, 10)\n", 181 | "eigvals= [14.64255066 12.07169297 9.32253135 8.3474172 6.66493894 1.28216741\n", 182 | " 0.60300562 1.06407958 0.86838992 0.80960545]\n", 183 | "[9.32253135 9.32253135 9.32253135 9.32253135 9.32253135 9.32253135\n", 184 | " 9.32253135 9.32253135 9.32253135 9.32253135]\n" 185 | ] 186 | } 187 | ], 188 | "source": [ 189 | "#finding covariance matrix of X\n", 190 | "S = X@X.conj().T/p\n", 191 | "\n", 192 | "print(\"emp cov = \",S.shape)\n", 193 | "\n", 194 | "#finding eigen values and eigen vectors\n", 195 | "eigvals, eigvecs = np.linalg.eig(S)\n", 196 | "eigvals = eigvals.real\n", 197 | "\n", 198 | "#finding norm of eigvals so that they can be sorted in decreasing order\n", 199 | "eignorms = np.abs(eigvals)\n", 200 | "\n", 201 | "print(\"eigvals=\",eigvals)\n", 202 | "\n", 203 | "#sorting eig vals and eig vecs in decreasing order of eig vals\n", 204 | "\n", 205 | "idx = eignorms.argsort()[::-1] \n", 206 | "eignorms = eignorms[idx]\n", 207 | "eigvals = eigvals[idx]\n", 208 | "eigvecs = eigvecs[:,idx]\n", 209 | "\n", 210 | "# idx = eigvals.argsort()[::-1] \n", 211 | "# eignorms = eignorms[idx]\n", 212 | "# eigvals = eigvals[idx]\n", 213 | "# eigvecs = eigvecs[:,idx]\n", 214 | "\n", 215 | "# print(\"eigvals=\",eigvals)\n", 216 | "# print(\"eigvecs=\\n\",eigvecs)\n", 217 | "\n", 218 | "print(np.abs(S@eigvecs[:,2]/eigvecs[:,2]))" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 15, 224 | "metadata": {}, 225 | "outputs": [ 226 | { 227 | "data": { 228 | "text/plain": [ 229 | "" 230 | ] 231 | }, 232 | "execution_count": 15, 233 | "metadata": {}, 234 | "output_type": "execute_result" 235 | }, 236 | { 237 | "data": { 238 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABA4AAAFlCAYAAABr1ap+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nO3de7SXZZ03/vcloIApOLnHUWmFuRp+IiAapuaUJjqoeaDJTNaYx8nlKh8nl5H6PKwO1kzO2NIe56km09LmcTxEyE8bizRt7GSBgKAoHkaLg5NIQanQIF7PHxzGDdwc9t7w/Qqv11p77f297sP1+bK5l37f3NfnLrXWAAAAAGzITq0uAAAAAGhfggMAAACgkeAAAAAAaCQ4AAAAABoJDgAAAIBGggMAAACgUe9tOdmee+5ZBw8evC2nBAAAADbh4YcffrHW2rGhbds0OBg8eHCmTZu2LacEAAAANqGU8qumbZYqAAAAAI0EBwAAAEAjwQEAAADQaJv2OAAAAGDjVqxYkfnz52f58uWtLoXtUN++fTNo0KD06dNns48RHAAAALSR+fPnZ7fddsvgwYNTSml1OWxHaq1ZvHhx5s+fn/3222+zj7NUAQAAoI0sX748b37zm4UG9LhSSt785jdv8d0sggMAAIA2IzRga+nK3y3BAQAAAJ2UUnLppZeuff3FL34xn/nMZ9bb76abbkpHR0dGjhy59mvOnDlZuHBhTjvttI3Oce655+ZrX/tap7HJkyfnhBNO2OhxgwcPzosvvrhZ7+OJJ57IyJEjc/DBB+eZZ57ZrGO64rXXXsvFF1+cYcOGZfjw4Tn00EPz7LPPbrX5tjXBAQAAAJ3ssssumTRp0mZ9QP/Qhz6UmTNnrv0aOnRo9tlnn0ycOHGjx40bNy633XZbp7Hbbrst48aN61btrzd58uScdtppmTFjRvbff/+147XWvPbaaz02z+23356FCxdm1qxZmT17du68884MHDiw2+d99dVXe6C67hMcAAAAvIFNnrEgR151f/a7/N9y5FX3Z/KMBd0+Z+/evXPBBRfk2muv7dLxzz33XIYNG5YkeeWVV3L66adn6NChef/735/DDjss06ZNy+jRo/PEE0/k+eefT5K8/PLLue+++zJ27NgkydixY/OOd7wjBx54YK6//vr15nj55Zfzvve9LwcddFCGDRuW22+/vdP2e+65J1/60pfy1a9+Ne9973vz3HPPZciQITnrrLMybNiwzJs3L7feemuGDx+eYcOG5bLLLlt77Jve9KaMHz8+Bx54YI499tj88pe/zNFHH523ve1tueuuu9ar5fnnn8/ee++dnXZa9RF70KBB2WOPPZJko3OsMXHixJxzzjlJknPOOScXXnhhDjvssHzyk5/MSy+9lHPPPTfDhw/PiBEj8p3vfCdJ8oMf/CBHHHFEDjnkkHzwgx/MSy+9tGW/pC0gONiIrXEBAgAA9JTJMxbkikmzs2DJstQkC5YsyxWTZvfIZ5ePfexjueWWW7J06dKN7nf77bd3WqqwbNmyTtu/8pWvZI899sicOXPyuc99Lg8//HCSpFevXvnABz6QO+64I0ly99135+ijj87uu++eJPnGN76Rhx9+ONOmTct1112XxYsXdzrv97///eyzzz555JFH8uijj+b444/vtP3EE0/MhRdemEsuuSQPPPBAkuSpp57KRz/60Tz22GPp06dPLrvsstx///2ZOXNmpk6dmsmTJydZFUocc8wxeeyxx7LbbrtlwoQJuffee3PnnXfmU5/61Hp/BqeffnruvvvujBw5MpdeemlmzJiRJFm4cGHjHBszf/78/OxnP8s111yTz33ucxkwYEBmz56dWbNm5ZhjjsmLL76Yz3/+87nvvvsyffr0jBo1Ktdcc80mz9tVgoMGW/MCBAAA6AlXT5mbZStWdhpbtmJlrp4yt9vn3n333XPWWWfluuuu2+h+6y5V6NevX6ftP/nJT3LGGWckSYYNG5YRI0as3fb65QrrLlO47rrrctBBB+Xwww/PvHnz8tRTT3U67/Dhw3Pvvffmsssuy49//OMMGDBgk+/prW99aw4//PAkydSpU3P00Ueno6MjvXv3zl//9V/nwQcfTJLsvPPOa4OI4cOH56ijjkqfPn0yfPjwPPfcc+udd9CgQZk7d26+8IUvZKeddsro0aPzwx/+cKNzbMwHP/jB9OrVK0ly33335WMf+9jabXvssUceeuihzJkzJ0ceeWRGjhyZm2++Ob/61a82ed6u6r3VzvwGt7ELcOzB+7aoKgAAgP+2cMmyLRrfUh//+MdzyCGH5Nxzz+2R863rXe96V55//vk88sgj+dnPfrY2RPjRj36U++67Lz//+c/Tv3//HH300es9QvDP//zPM3369Nxzzz2ZMGFCRo8evcG7AV5v11133ay6+vTps/bpAzvttFN22WWXtT839R3YZZddcsIJJ+SEE07IXnvtlcmTJ+fYY49tnOP1TzdY971tqs5aa4477rjceuutm/V+ussdBw229gUIAADQXfsM7LdF41vqT/7kT3L66afnxhtv7PI5jjzyyLXLEebMmZPZs2ev3VZKyYc+9KGcffbZOeGEE9K3b98kydKlS7PHHnukf//+eeKJJ/LQQw+td96FCxemf//+OfPMMzN+/PhMnz59i+p65zvfmX//93/Piy++mJUrV+bWW2/NUUcd1aX3OH369CxcuDDJqicszJo1K29961s3Osdee+2Vxx9/PK+99lruvPPOxnMfd9xx+fKXv7z29e9+97scfvjh+elPf5qnn346yaqlFU8++WSXat8cgoMGW/sCBAAA6K7xY4akX59encb69emV8WOG9Ngcl1566UafrrBuj4Of/exnnbZ/9KMfzaJFizJ06NBMmDAhBx54YKdlBePGjcsjjzzSaZnC8ccfn1dffTUHHHBALr/88rXLC15v9uzZeec735mRI0fms5/9bCZMmLBF72vvvffOVVddlfe+97056KCD8o53vCOnnnrqFp1jjRdeeCEnn3zy2qUYvXv3zkUXXbTROa666qqcdNJJede73pW999678dwTJkzI7373uwwbNiwHHXRQHnjggXR0dOSmm27KuHHjMmLEiBxxxBF54oknulT75ii11q128nWNGjWqTps2bZvN1x1rehy8frlCvz698oW/Gm6pAgAAsNU8/vjjOeCAAzZ7/8kzFuTqKXOzcMmy7DOwX8aPGdJWn1lWrlyZFStWpG/fvnnmmWdy7LHHZu7cudl5551bXdoOa0N/x0opD9daR21ofz0OGqy50Nr5AgQAABh78L5t/TnllVdeyXvf+96sWLEitdZ85StfERq8wQgONqLdL0AAAIB2t9tuu+WNcuc5G6bHAQAAANBIcAAAAAA0EhwAAAAAjQQHAAAAQCPBAQAAAJ2UUnLmmWeuff3qq6+mo6MjJ5100gb3P/roozNq1H8/yW/atGk5+uij19vvueeeS79+/TJy5Mi1X9/61reSJCeeeGKWLFnSWNPNN9+ccePGdRp78cUX09HRkT/+8Y+Nx51zzjmZOHFi4/bX++Mf/5hjjz02I0eOzO23375Zx3TV4MGD84EPfGDt64kTJ+acc87Z6DF33XVXrrrqqq1a14Zs8qkKpZRvJDkpyQu11mHrbLs0yReTdNRaX9w6JQIAALAt7brrrnn00UezbNmy9OvXL/fee2/23XfjT5x74YUX8r3vfS8nnHDCRvfbf//9M3PmzPXG77nnno0e9/73vz+XXnppXnnllfTv3z/Jqg/bJ598cnbZZZdNvKPNM2PGjCTZYH0rV65Mr169emSeNR5++OHMmTMnQ4cO3az9TznllJxyyik9WsPm2Jw7Dm5Kcvy6g6WUtyT5yyS/7uGaAAAA2Fyz7kiuHZZ8ZuCq77Pu6JHTnnjiifm3f/u3JMmtt9663r/2r2v8+PH5u7/7uy7PN3jw4Lz44qp/j/7c5z6XIUOG5C/+4i8ybty4fPGLX8zuu++eo446KnfffffaY2677ba1dV155ZU59NBDM2zYsFxwwQWpta43x+WXX56hQ4dmxIgR+cQnPtFp2wsvvJAzzzwzU6dOzciRI/PMM89k8ODBueyyy3LIIYfk29/+dmbOnJnDDz88I0aMyPvf//787ne/S7LqjotLLrkko0aNygEHHJCpU6fmr/7qr/L2t789EyZMaHzPl1566Qb/zH77299m7NixGTFiRA4//PDMmjUrSXLTTTfloosuSpJ8+9vfzrBhw3LQQQflPe95T5JV4cb48eNz6KGHZsSIEfna17622X/+G7PJ4KDW+mCS325g07VJPplk/d8GAAAAW9+sO5K7L06WzktSV32/++IeCQ/OOOOM3HbbbVm+fHlmzZqVww47bKP7H3HEEdl5553zwAMPbHS/Z555ptNShR//+Medtk+dOjXf+c538sgjj+R73/tepk2btnbbuHHjcttttyVJFi5cmCeffDLHHHNMkuSiiy7K1KlT194p8d3vfrfTeRcvXpw777wzjz32WGbNmrXeB/o//dM/zQ033JB3v/vdmTlzZvbff/8kyZvf/OZMnz49Z5xxRs4666z8wz/8Q2bNmpXhw4fns5/97Nrjd95550ybNi0XXnhhTj311Hz5y1/Oo48+mptuuimLFy/e4J/F6aefnunTp+fpp5/uNP7pT386Bx98cGbNmpW///u/z1lnnbXesVdeeWWmTJmSRx55JHfddVeS5MYbb8yAAQMyderUTJ06NV//+tfz7LPPNv8yNlOXehyUUk5NsqDW+shm7HtBKWVaKWXaokWLujIdAAAAG/LDK5MVyzqPrVi2arybRowYkeeeey633nprTjzxxM06ZsKECfn85z+/0X3WLFVY8/Xud7+70/af/vSnOfXUU9O3b9/stttuOfnkk9due9/73pef/vSn+f3vf5877rgjH/jAB9YuH3jggQdy2GGHZfjw4bn//vvz2GOPdTrvgAED0rdv35x//vmZNGnS2uUOm/KhD30oSbJ06dIsWbIkRx11VJLk7LPPzoMPPrh2vzVLCIYPH54DDzwwe++9d3bZZZe87W1vy7x58zZ47l69emX8+PH5whe+0Gn8Jz/5ST784Q8nSY455pgsXrw4v//97zvtc+SRR+acc87J17/+9axcuTJJ8oMf/CDf+ta3MnLkyBx22GFZvHhxnnrqqc16nxuzxcFBKaV/kv+Z5FObs3+t9fpa66ha66iOjo4tnQ4AAIAmS+dv2fgWOuWUU/KJT3xivWUKY8aMyciRI/M3f/M3ncaPOeaYLFu2LA899FCPzL+ufv365fjjj8+dd97ZaZnC8uXL89GPfjQTJ07M7Nmz85GPfCTLly/vdGzv3r3zy1/+Mqeddlq++93v5vjj11uRv0G77rrrZu23ps/CTjvt1Knnwk477ZRXX3218bgPf/jDefDBBxvDhSb//M//nM9//vOZN29e3vGOd2Tx4sWpteaf/umf1oYyzz77bP7yL/9yi867IV2542D/JPsleaSU8lySQUmml1L+rNvVAAAAsPkGDNqy8S103nnn5dOf/nSGDx/eaXzKlCmZOXNmbrjhhvWOmTBhQv7xH/+xy3MeeeSRufvuu7N8+fK89NJL6y05GDduXK655pr85je/yRFHHJEka0OCPffcMy+99NIGn6Lw0ksvZenSpTnxxBNz7bXX5pFHNnkDfScDBgzIHnvssXZpxb/8y7+svfugO/r06ZNLLrkk11577dqxd7/73bnllluSJD/60Y+y5557Zvfdd+903DPPPJPDDjssV155ZTo6OjJv3ryMGTMmX/3qV7NixYokyZNPPpmXX3652zVu8qkK66q1zk7yp2terw4PRnmqAgAAwDY2+lOrehq8frlCn36rxnvAoEGDcvHFF2/RMSeeeGI2drf5mh4Ha5x33nmd5jj00ENzyimnZMSIEdlrr70yfPjwDBgwYO324447LmeddVbO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\n", 239 | "text/plain": [ 240 | "
" 241 | ] 242 | }, 243 | "metadata": { 244 | "needs_background": "light" 245 | }, 246 | "output_type": "display_data" 247 | } 248 | ], 249 | "source": [ 250 | "#Plotting Eigen Values\n", 251 | "\n", 252 | "fig, ax = plt.subplots(figsize=(18,6))\n", 253 | "ax.scatter(np.arange(N),eigvals[:N],label=\"N EigVals from Source\")\n", 254 | "ax.scatter(np.arange(N,M),eigvals[N:],label=\"M-N EigVals from Noise\")\n", 255 | "plt.legend()" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "See that lower M-N eigen values coming due to noise are almost equal (=lb0), and top N eigen values lbi>lb0" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 16, 268 | "metadata": {}, 269 | "outputs": [ 270 | { 271 | "name": "stdout", 272 | "output_type": "stream", 273 | "text": [ 274 | "(10, 5)\n", 275 | "(10, 5)\n" 276 | ] 277 | } 278 | ], 279 | "source": [ 280 | "#separating source and noise eigvectors\n", 281 | "Us, Un = eigvecs[:,:N], eigvecs[:,N:]\n", 282 | "print(Us.shape)\n", 283 | "print(Un.shape)" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": 17, 289 | "metadata": {}, 290 | "outputs": [ 291 | { 292 | "name": "stdout", 293 | "output_type": "stream", 294 | "text": [ 295 | "(0.31105344205684204+2.1483757842812687e-17j)\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "def P_MU(theta):\n", 301 | "# print(a(theta).conj().T.shape)\n", 302 | "# print(Us.shape)\n", 303 | "# print(np.linalg.norm(a(theta).conj().T@Un))\n", 304 | " return(1/(a(theta).conj().T@Un@Un.conj().T@a(theta)))[0,0]\n", 305 | " \n", 306 | "print(P_MU(2))" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": 18, 312 | "metadata": {}, 313 | "outputs": [ 314 | { 315 | "name": "stdout", 316 | "output_type": "stream", 317 | "text": [ 318 | "(181,)\n", 319 | "doas= [ 20. 50. 85. 110. 145.]\n", 320 | "amps= [ 0.91485181 -0.34802287 0.70692032 -1.24343434 -0.27870137]\n" 321 | ] 322 | } 323 | ], 324 | "source": [ 325 | "#searching for all possible theta\n", 326 | "theta_vals = np.arange(0,181,1)\n", 327 | "P_MU_vals = np.array([P_MU(val*pi/180.0) for val in theta_vals]).real\n", 328 | "print(P_MU_vals.shape)\n", 329 | "\n", 330 | "print(\"doas=\",doa*180/pi)\n", 331 | "print(\"amps=\",amp)" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 19, 337 | "metadata": {}, 338 | "outputs": [ 339 | { 340 | "data": { 341 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABA4AAAFlCAYAAABr1ap+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzdeZCk933f98/v6X56eu7FYk8sSQC7OAiAoEASpCiSpga8RFF2SbRix6pERZcVQ5WSbCmlVKyknFiWopQUJTqt2CEtmVRiCU4kUgel6CK1IimCFImDAAECBHaxJLEndndmdqbP5/jlj+6np3e2u6ePp7uf5+n3qwq1uzOzO89T3eh+5vN8D2OtFQAAAAAAQCfOtA8AAAAAAAAkF8EBAAAAAADoiuAAAAAAAAB0RXAAAAAAAAC6IjgAAAAAAABdERwAAAAAAICu8pP8ZgcOHLC33XbbJL9lLEqlkhYXF6d9GCPLynlInEtSZeVcsnIeEueSRFk5D4lzSaqsnEtWzkPiXJIqK+eSlfOQOJdpe+yxxy5baw92+txEg4PbbrtNX/7ylyf5LWNx8uRJra2tTfswRpaV85A4l6TKyrlk5TwkziWJsnIeEueSVFk5l6ych8S5JFVWziUr5yFxLtNmjPlGt8/RqgAAAAAAALoiOAAAAAAAAF0RHAAAAAAAgK4IDgAAAAAAQFcEBwAAAAAAoCuCAwAAAAAA0BXBAQAAAAAA6IrgAAAAAAAAdEVwAAAAAAAAuiI4AAAAAAAAXREcAAAAAACArggOAADImJfXyyrX/WkfBgAAyAiCAwAAMub7fv1v9Jufe2nahwEAADKC4AAAgIy5WqprvexN+zAAAEBGEBwAAJAhYWgVWskLwmkfCgAAyAiCAwAAMsQLG4GBF9gpHwkAAMgKggMAADLEbwYGPhUHAAAgJgQHAABkSNSi4IdUHAAAgHgQHAAAkCFRiwIzDgAAQFwIDgAAyBC/OePAZ8YBAACICcEBAAAZ0ppxEFJxAAAA4kFwAABAhkQtCnUqDgAAQEwIDgAAyJBoKCJbFQAAQFwIDgAAyJDWVgUqDgAAQEwIDgAAyJAoMPCYcQAAAGJCcAAAQIawVQEAAMSN4AAAgAzxoooDZhwAAICYEBwAAJAhPsEBAACIGcEBAAAZEs02iLYrAAAAjIrgAACADIkqDphxAAAA4kJwAABAhvjNFgVaFQAAQFz2DA6MMa82xvyVMeZZY8wzxpgfa378p4wxZ40xTzb/+8D4DxcAAPTiNVsUaFUAAABxyffxNb6kn7DWPm6MWZb0mDHmL5qf+yVr7f82vsMDAACDoOIAAADEbc/gwFp7XtL55u+3jDFfk3Rs3AcGAAAGx1YFAAAQt4FmHBhjbpP0BklfbH7oR40xTxljftMYc1PMxwYAAAbU2qrAcEQAABATY21/FxbGmCVJfy3pZ621HzfGHJZ0WZKV9DOSjlpr/0mHv/ewpIcl6fDhw2965JFH4jr2idne3tbS0tK0D2NkWTkPiXNJqqycS1bOQ+Jckmjc5/GX3/D0f3+tLkn6D9+1IGPM2L5XVh4TiXNJoqych8S5JFVWziUr5yFxLtP20EMPPWatfbDT5/qZcSBjjCvp9yT9R2vtxyXJWnux7fMfkfTJTn/XWvthSR+WpAcffNCura0NdPBJcPLkSaXxuHfLynlInEtSZeVcsnIeEueSROM+jxc/e1r62tckSe9453fKzY1vgVJWHhOJc0mirJyHxLkkVVbOJSvnIXEuSdbPVgUj6Tckfc1a+4ttHz/a9mUflPTV+A8PAAAMwmtrUaBdAQAAxKGfioO3S/pBSU8bY55sfux/kPQDxpgH1GhVOCPph8dyhAAAoG9+21BELww1r9wUjwYAAGRBP1sVPiepU4Pkn8R/OMDefvqPntVTL1aVocofAIiNF+5UGXg+mxUAAMDoxtf4CIzJM+c29fI2F8MA0El7xYEf0qoAAABGR3CA1NmseKoH0z4KAEim9rDACwhZAQDA6AgOkDobZU91Bn4BQEftYQHDEQEAQBz6WscIJMlGpa6AigMA6Kg9LPBDKg4AAMDoCA6QKlUvUNVrXAiHoZXjdJrbCQCzqz0s8Kg4AAAAMaBVAamyWfFav694lB0AwG7tYQEzDgAAQBwIDpAqG2WCAwDopX2rAhUHAAAgDgQHSJWNcr31+wqrFQDgBl7bVgWfigMAABADggOkygatCgDQU3tY0L6aEQAAYFgEB0iVzbZWhTIVBwBwA58ZBwAAIGYEB0iV64YjEhwAwA280CrX3DjjM+MAAADEgOAAqbJRaZtx4PlTPBIASCY/CDXv5hq/D6k4AAAAoyM4QKpct1WhzgUxAOzmB1bFZnBQp+IAAADEgOAAqbJR8VTIN562DEcEgBt5YaiFQrPigBkHAAAgBgQHSJXNsqejq0VJUqVOqwIA7Oa1typQcQAAAGJAcIBU2ajUdWSlGRxQcQAAN/ADq2Kz4sBjxgEAAIgBwQFSZaPs6Uiz4oB1jABwIy8ItUDFAQAAiBHBAVJls+xp/2JBrkPFAQB04odW81HFATMOAABADAgOkBpeEGqr5mvffEGFnFSh4gAAbuAHtjXjwKPiAAAAxIDgAKlxrdJYxbhvwdVczhAcAEAHXhC2Kg7YqgAAAOJAcIDU2GgLDgo5qUyrAgDcwA+tim7j7d0LqTgAAACjIzhAamyUG8HB6nyj4qBKxQEA3MALQuUdR3nHUHEwg756dlOhJTACAMSL4ACpsVmpS5L2LRQ0l2OrAgB04gdWbs4onzPyqTiYKec3K/q7v/Y5PXGJ90cAQLwIDpAaUcXBvnlXhZxhqwIAdOCHofI5R67jsFVhxlyr+JKkbY/ACAAQL4IDpEYrOFhwNZeTqgQHAHAda628wMp1jNw8wcGsiR5vn4cdABAzggOkxmbFkzHScrE5HJFWBQC4TtBsTcjnohkH3HmeJTWf4AAAMB4EB0iNzYqn5bm8co7RnEOrAgDs5reCAyM358gjOJgpUcUB2zQAAHEjOEBqbJTr2rdQkCQVclKFigMAuE70g6PrOM3hiNx6niW0KgAAxoXgAKmxUfG0b8GVJM01hyNaVk4BQEvUmpDPGVoVZhDBAQBgXAgOkBobZU+r843goJBr9PLWGfwFAC1es8Ign3OarQq8Rs6Sut8IimhVAADEjeAAqbFZ8VqtCnM5I0mq1rkoBoBIVGHgOobgYAZRcQAAGBeCA6TGRrmufW0VB5JU9vwpHhGy6IWLW/rzM960DwMYyk6rQjTjgDvPs6TuR8MRp3wgAIDMIThAKoShbVYc7Mw4kBiQiPh94omz+u3n6gr5gQspFLUquDkj16HiYNZQcQAAGBeCA6TCVs1XaHXdjANJKhMcIGbRmk+PafRIoegHx3y0VYHhiDNlJzjgcQcAxIvgAKmwWW6Ujt8w48AjOEC8qs0a3zq37JBCrRkHOaN8zmFI3oypB9FwxCkfCAAgcwgOkAoblboktWYczDUrDioEB4hZzW88pwgOkEbRHWc358h1jHxaFWYKrQoAgHEhOEAqbLQqDmhVwHjVoooDfuBCCkUl6vkcWxVmkefTqgAAGA+CA6TCRuX64IBWBYxL9JzyfC68kT7MOJhtUeBJqwIAIG4EB0iFzXKjVWF1vjHjgIoDjEs1alUIeG4hfdpnHLg5hyGfM6ZOqwIAYEwIDpAKUavC6jzrGDFe0XDEGlfeSCG/GRTkc47yDhUHsyaqlKJVAQAQN4IDpMJmxdNiIadCvvGULTAcEWMStSowHBFp5DWDgrzT3KpAcDBTPFoVAABjQnCAVNioeK1VjJKUN1LOMVQcIHatGQf8wIUU2mlVcOTmTKsCAbOBrQoAgHEhOEAqbJS9VpuCJBljNO/mmHGA2EWtClQcII12WhWM8o7TmrKP2cBwRADAuBAcIBU2K/XrggNJmi/kaFVA7GoMR0SKRZUyruPIzRt59LrPlDrrGAEAY0JwgFTYKHutVYyReTenSt2f0hEhq3YqDrjwRvr4wU7Fges4rT9jNtCqAAAYF4IDpEJjxsH1wcECFQcYg9ZwRH7gQgpFFQb5nFE+ZxRaKeTu88yIKk5oVQAAxG3P4MAY82pjzF8ZY541xjxjjPmx5sf3G2P+whjzQvPXm8Z/uJhF1lptlj2tzheu+3jRzanC1RFi5Adhq8SXGQdIo6jCwHUcubnGW7zHgMSZEVUcWIlqEwBArPqpOPAl/YS19l5Jb5X0I8aYeyX9pKRPWWvvlPSp5p+B2FW8QPUg7FxxQKsCYlRrCwsIDpBG0VaFxnBEc93HkH3tr1tUTQEA4rRncGCtPW+tfbz5+y1JX5N0TNL3SvpY88s+Jun7xnWQmG0bZU+StG/3cESXVgXEq9r2fPK46EYKRdUFbs5RvllxQHAwO9pftwg/AQBxGmjGgTHmNklvkPRFSYetteebn7og6XCsRwY0tYKDXRUHxQLrGBGvKhUHSLlWxYFjVMg1Kg648zw76gQHAIAxMdb2dyfCGLMk6a8l/ay19uPGmA1r7b62z69ba2+Yc2CMeVjSw5J0+PDhNz3yyCPxHPkEbW9va2lpadqHMbK0nsfXrgT6+S9V9S/eXNQ9N+ckNc7lP73k6pkrgX5xbWHKRziatD4unaT9XM5vh/rvP1eRJH3/na7+3onCHn8j+dL+mLTLyrmM8zw+/kJdf3jK03/4rgX99cu+PvpMXb+4Nq/9xfHMQs7KYyJl41z+5efKenm7cV33C++c18GFdM/AzsJjEuFckikr55KV85A4l2l76KGHHrPWPtjpc/l+/gFjjCvp9yT9R2vtx5sfvmiMOWqtPW+MOSrpUqe/a639sKQPS9KDDz5o19bWBj3+qTt58qTSeNy7pfU8Kk+fl770uNbe9mbdc3RFUuNcbn/NAT29fi6V59QurY9LJ2k/l2fObUqf+5wk6dhrbtPa2l1TPqLRpf0xaZeVcxnneXyh8pzcM6f10EMP6fKXvyU985Te/Ja36tX7xxOwZuUxkbJxLoUvn5S2S5KkN775LTpxMF0XrLtl4TGJcC7JlJVzycp5SJxLkvWzVcFI+g1JX7PW/mLbp/5Q0oeav/+QpD+I//CAxipG6cZWhflCThVaFRCjatuWDmYcII38IFTeaby1t7Yq8FyeGfUg1EKhUZlHqwIAIE791LC9XdIPSnqXMebJ5n8fkPRzkt5rjHlB0nuafwZitzMc8fqy8Xk3p5ofKmBHOWJSaxuOyEU30sgPrdzmbIN881ef18iZ4QWhFucaxaS8hgEA4rRnq4K19nOSTJdPvzvewwFutFGpq5B3VHSvz7miuypVL2hdKAGjqPoEB0g3LwhblQZR5QEVB7PDC6z2zbt6ZavGUEwAQKzSPTUHM+FaxdO+eVeNrpkd824jOGAlI+LS3qpAcIA08gPbqjQo5Bu/eqxjnBl1n4oDAMB4EBwg8TbK3g3zDSRpvtC4OGLOAeJSbQuhuEuLNPLCnRkH0a8+z+WZUQ9CLc4x4wAAED+CAyTeRtm7Yb6BRMUB4ldrXmjP5aQaP2whhfzgxhkHVBzMBmutvCDUUrPioEZwAACIEcEBEm+j4mm1Q8VBNOOgTMUBYhJVHCzkDXfrkEp+GCqfu36rgh/yXJ4FQWhlrXZaFQg/AQAxIjhA4m2W61qdvzE4KEYVBwQHiEk042DBpVUB6eQFVnmnWXHQ/NWn4mAmRJUlzDgAAIwDwQESb6M5HHG3+ULUquBP+pCQUVHFwTwVB0gpv22rQvQrIdhsiCoMlgkOAABjQHCARKv5gcr1oONwxKhVoVLn4gjxqPqBCnlHrsNFN9LJD3e2KuwEB1QczILoNWun4oBqPABAfAgOkGibFU+StLrQfThiuU7FAeJR80IV847yjqE/GKnkBaHcaKtCM0BgxsFsiCpLmHEAABgHggMk2ma5ERz0alWoslUBMal6gYpuTnkqDpBSftBWceBQcTBLouBgiXWMAIAxIDhAom00Kw46tSrsVBwQHCAeUXDgOtytQzp5oW1tVWhVHPBcnglRcDBfYMYBACB+BAdItI1WxUH3VoUKFQeISdULVXSbrQpcdCOF/CCUG21VaAYHXkjFwSyo+43HuZBzlHekGoERACBG+WkfANDLRrkuqXPFgeMYzeUdggPEpuo3WxXE3TqkU6dWBSoOZkNUcVDIGwa8AgBiR8UBEm1nOOKNwYHU2KxQoVUBMal6gYr5xowDVtghjbwwvKFVgefybIjaqwo55rQAAOJHcIBE2yh7yjmmtZd6t3mX4ADxqfmh5lxHruGiG+nkB7bVqsA6xtniNV+z3JyRS7sVACBmBAdItI1KXavzrowxHT8/X8ipTKsCYlL1Qs3lc6xjRGp5wU7FQRQc+AQHMyF6zXLzjRkHvIYBAOJEcIBE26z4HVcxRuYLOVWpOEBMal7QHI7YuEsbMlQOKeMFVm6zRSHnGBkj+SE/QM6CqLIkGo5IxQEAIE4EB0i0jXK963wDqdGqwDpGxCVax5hvvjJ6/MCFlPHDUHln563ddRxaFWZENMvCzTm0KgAAYkdwgES7VvG0XOxVcZBnqwJiU/V31jFK3LFD+viBbbUoSI0BiWxVmA07wYFR3tCqAACIF8EBEm275ncdjChJ867DcETEpn2rgkRwgPTxgrDVqiBJecewVWFG1PxoHWOjVaHG6xcAIEbdfyIDEqBUC7Q4l+v6+QUqDhATa22rVcGPWhUo8UbK+KFtrWGUGj9EeszqmAleax0jrQoAgPhRcYBEK9V8LfaoOCgy4wAx8QKr0Ko1HFGi4gDpYq1VENrrZhzkHYdWhRmxs46R4YgAgPgRHCCxrLUq1X0t9QgOFgo5Vak4QAyqfuN51BiO2JxxEPDcQnpEFTLXtSrkDOsYZ0Tr8WcdIwBgDAgOkFgVL1Bo1bPiYN7NqeIFspYLY4wmCqDm3JzyzZ+76BFGmkRrF/NtwxHdHK0Ks6LeNhzRpeIAABAzggMk1nbNl7RHcFDIKQgtd1YwsprXeA7N5XdaFZhxgDSJnq9RxUz0e1oVZkNrq4LT2AxDcAAAiBPBARKrVGvcAV7qMRxx3m18rlrnAgmjqbW1KrisY0QK+cFOj3skn3PYqjAj6n5jo4bjNCsOeNwBADEiOEBilaKKg0LvGQeSVPb8iRwTsqvarDgo5hmOiHTymy0J121VyBkqZ2ZEYxVn48WL4YgAgLgRHCCxtqqNMKDXcMT5ZnBQYbMCRhTNOGgMR2x8jOGISJP2UvVIPue0Zh8g27zAtgUHtCoAAOJFcIDEiioOloq91zFKYiUjRtaqOGgPDnzu1CI9ou0J7RUHeYeKg1lRb6s4iFoVGBwMAIgLwQESq1Tfezhi1KrASkaMaqfiwNmZcUCPMFKk21YFhiPOBs8PVWiGRjtVUzz2AIB4EBwgsaKtCj1bFag4QEyqfodWBUp9kSJRZYHbvlUhZ1qzD5BtXhDKze+0Kki8hgEA4kNwgMQq9bmOUZIqVBxgRDvDEQkOkE47rQptMw4ch1aFGeEFVoW2VgWJ1zAAQHwIDpBY2811jAvu3usYGY6IUbW3KkR361hjhzTxWq0KbVsV8obn8Yyo+ddvVZBoVQAAxIfgAIlVqvlaLOTktJXd7rbQXNVIxQFGFQUHc26Ou3VIJb/VqnB9xQEzDmZDe6sCr2EAgLgRHCCxSjW/Z5uCRMUB4lNrXmDP5R1FN2y5W4c0iSoLrtuqkGOrwqzwgvbhiMw4AADEi+AAibVd83sORpSYcYD41LxAxjSCA2YcII2i4MBtCw5cx2ltW0C2ecGNrQo1XsMAADEhOEBibfdRceDmjHKOoeIAI6v6oebyjowxcoyRmzNUHCBVWsMR21sVcqb1cWRbPbCt4MAlOAAAxIzgAIlV6qPiwBijeTfHOkaMrOoFKrYN4izkHCoOkCpRZYHbtlXBzTkMR5wRnh+qwDpGAMCYEBwgsbZrwZ4VB1KjXYFWBYyq6gUq5neCAzdPcIB0iWYZXNeqwIyDmVEPwhvXMRIaAQBiQnCAxGpUHHRfxRiZd3Oq1P0JHBGyrOqFKro7L4kF7tQiZfzWOsb2VgVmHMyKxoyDaDhi42OEnwCAuBAcILH62aogSQtUHCAGN7QqUHGAlPFaMw7ahyM2Kg6speog6zy/fTgirQoAgHgRHCCx+tmqIElFZhwgBlU/1Nyu4KBGxQFSxG+1KlxfcSBJQUhwkHX1wMrN725V4L0RABAPggMkkh+Eqvlh3xUHVSoOMKLGjIPrWxW4W4c02WlV2Kk4iH7vExxkntc244BWBQBA3AgOkEilWiMI6Gs4IhUHiEGtQ6sCMw6QJq3hiG3rGKPf81zOPi9o36rQ+BjBAQAgLgQHSKTt5rDDvoYjMuMAMah6oeaoOECK+UH3igM2K2Rf3d8Zjug2ZxzUeA0DAMSE4ACJtF1tBAf9VhxUqTjAiKo+wxGRblE7Qv66dYyNt3mfioNMC0MrP7RtwxEbH2cdIwAgLgQHSKTtWv/BwUIhpzIVBxhRbdc6RjfncNGNVInaEa5rVYgqDphxkGlec75FFBy4tCoAAGJGcIBEKjWDg+V+tioUcqpQcYARUXGAtPMDK8dITts6xrxDxcEsiFpRouGIjjHKOYbXMKTS1y9u6c+fuTDtwwCwy57BgTHmN40xl4wxX2372E8ZY84aY55s/veB8R4mZk1pgIqDeTenmh+ybgwjqXYYjkjFAdLEC8PW+sUIMw5mg+dHFQc7oRFzWpBWv/HZl/Tj/+lJWcvrFpAk/VQcfFTS+zt8/JestQ80//uTeA8Lsy5qVVjqs1VBEisZMTRrrapeyDpGpJof2NZQvEhrxkHIcznLojaVQp7wE+m3XfNVrge6cK067UMB0GbP4MBa+xlJVydwLEDLoBUHkljJiKFFk8fn2isOcqxjRLr4QYeKg2aQ4PncucuyWqeKA9qtkFKl5matU5dKUz4SAO1MP2VAxpjbJH3SWvu65p9/StI/lnRN0pcl/YS1dr3L331Y0sOSdPjw4Tc98sgjMRz2ZG1vb2tpaWnahzGyNJ3HH52q6/de8PSR9y3ccAdNuv5cPnfW079/uq5feOe8Di6kb2xHmh6XvaT1XEqe1Y98qqwfeG1B33Wbq+3tbX38m67+9ryvf/PuxWkf3kjS+ph0kpVzGdd5fPSZmh6/6OtX37XznH3qFV+/+FhN//KtRd2xb+/1toPKymMipftcLpRC/eRnK/rh18/pO27Ja3t7W//qy47u3p/Tw6+fm/bhDS3Nj8lunEv//pcvVvT19VD/5T0FvedWd2zfR8rO45KV85A4l2l76KGHHrPWPtjpc3vfzu3s30r6GUm2+ev/LumfdPpCa+2HJX1Ykh588EG7trY25LecnpMnTyqNx71bms7ji9Xn5J4+rfe+66GOn28/l9JT56WnH9fr3/hm3X1keYJHGY80PS57Seu5XLxWlT71Kb3unru09u236uTJk7r9NYf0xQvfTOX5tEvrY9JJVs5lXOfxx698RQubl6/7t/MvXJYe+6Je/21v0Ftu3x/798zKYyKl+1yev7AlffYz+rb779Pa/Ud18uRJrS5JNx1Y0draG6d9eENL82OyG+fSv1946rPS+jXl9h3V2trrxvZ9pOw8Llk5D4lzSbKhbs9aay9aawNrbSjpI5LeEu9hYdaVan5fbQrSzoyDCjMOMKRoPkaxrT+YdYxIGz+0rWGIkejPbFXIttYqzrZWFVoVkFZR6+mpV2hVAJJkqODAGHO07Y8flPTVbl8LDGO76mux0F9wUGzNOPDHeUjIsKoXzTi4/qLbCyxTnZEaXhBe94OjtNPz7rF1JtPqwY0zDuYIDpBS0ZyrU69sT/lIALTb8yczY8zvSFqTdMAY87KkfyVpzRjzgBqtCmck/fAYjxEzaLvm97VRQWKrAkbXqeJgrrlhoR6EmsvH3xsOxM0PbGsYYiTvNLcqUHGQadE6xkKeigOkX7keyDHS+c3qQBWoAMZrz/8TrbU/0OHDvzGGYwFaSnVfS8X+3ijmo1aFOhdIGE4rONi1VUGS6j7BAdLBD2+sOIhaFdgQkm1RxUFhV6tCVE0FpIW1VqW6rzsPLenrF7f10uWSXndsddqHBUBDtioA47ZdC/pOmOdpVcCIolVmxbZWhajklzt2SAsvsNeVqks7P0h6Aa0KWdZxxkGOigOkT9ULZa10/7F9kmhXAJKE4ACJVKr5Wprr7y7vPK0KGFHHioNmlQE/cCEt/DBU/oaKA6f1OWRX3W+8TjEcEWlXat4EuufoshwjnbpEcAAkBcEBEqlU63844k7FAcEBhlPtUHEQ9Qpz4Y208DrOODCtzyG7ooqDQn7n8S/kc2yGQeqUa41ruZsWCnrN/gU2KwAJQnCARNoeYBhOFBywjhHDiioO5q5bx9hsVQh4XiEd/I5bFaLhiAQHWUarArIiqjhYnMvpxMElWhWABCE4QOJYa5utCv0FB45jNJd3VKHiAEOqdWhViLYq1LjwRkr4oW0NQ4xEf6ZVIdt2Kg6ur5ri9QtpE82rWijkdeLQkl66XFLAOlkgEQgOkDhVL1RoNdD6nYVCjooDDC2aPN6pVYESb6RFo1VhV8WBw/N4FkSVBe0VB3N5R3Wf90WkS6nZqrA4l9PxA4uq+aHObVSmfFQAJIIDJNBWzZOkvocjSo12BWYcYFid1zE2fk+pL9Ki0apwfcWBm2cd4yyoB12GI/K4I2V2VxxI0ou0KwCJQHCAxNlJm/uvOJin4gAjqPqBco657qKbdYxIm0arwq6tCk4044DncZa1WhWYcYCUa10DFvI6cbARHLBZAUgGggMkTqkWDcYZLDioUnGAIVW9sDXTILLTqsCFN9LBC0K5u7YqRAEYrQrZ5rVaFdq3KjgKLaER0qVVcTCX0/7Fgm5acHX6MpsVgCQgOEDibDeDg+VBZhy4eVoVMLSqF1zXpiDtBAcMF0Na+MGNwxGNMco5huGIGecFoYyRcs71wYEk2hWQKqX6TsWBJB0/uETFAZAQBAdInGEqDoq0KmAEVS9UcVfFwRwX3UgZPwxvaFWQpLxjWMeYcfXAqpBzZExbcNB8LtCugDQp13wZszOs+MTBRZ16hYoDIAkIDpA42wffxWYAACAASURBVEMEBwtujnWMGFrNv7HiwOWiGynjBfaGVgWp8VymVSHb6n543XwDqa3igNcwpEipHmixkG+FYCcOLunydk2bFW/KRwaA4ACJEw3GWRpwxkHZ88d1SMi4qhdqrkurAjMOkBZ+0LniwM0ZnscZ5wWh3C5zWmi3QpqU674WCjvvx9GAxNNsVgCmjuAAibPTqtD/OsaluXwrcAAG1ag42HXRTcUBUsbrMONAkvI5hxkHGed1WMVJuxXSqFQLrqs4PX5wUZJoVwASgOAAidNqVSj0X3GwMp/XZsWTtZTjYnBVL1Ax37nigOAAaeGFoVynQ8WBY2hVyLh6EF63TlYi/EQ67a44ePX+Bbk5o1NUHABTR3CAxNmuNd40nA69ut2sFF0FoWWzAoZS9cIbKg5aMw64W4cUCEIra9W94oDncaZ5zeGI7Qg/kUalWnDdjSM35+jWmxfZrAAkAMEBEqdU8wcajChJK/OuJOlaleE5GFzHdYzcrUOKRDMMdt91lhphghdScZBlnh+2goII6xiRRuW6r4VdraonDi7q9GVaFYBpIzhA4mzX/IEGI0qNigNJulZhQCIGV+2wVcFxjNyc4aIbqeA3g4F8p60KDhUHWUerArIi2qrQ7vjBJX3jSokhr8CUERwgcRoVB/0PRpSkVSoOMIKqF7YGibVzcw4X3UgFf6+KA2YcZFqn4Yi0KiCNyrXrZxxIjc0KXmD1ravlKR0VAIngAAlUqgWDVxzMN77+Gnt+MYROrQpS48Kbi26kQRQM7P7hsfExhzt1GVf3O1QcsI4RKVSqBze0q55gswKQCAQHSJyRWhWoOMAQal6oOffGl8MCP3AhJaJ1i/kOFQduzsin4iDTvODGGQesY0Qa7d6qIDVaFSSxWQGYMoIDJE6pPsJwRGYcYEBBaFUPwhvWMUpUHCA9omCg04yDvOO0ggVkkxfYDjMOGq9pvIYhLep+KC+wN1wDrs67OrA0p9MEB8BUERwgcYbZqrBcpFUBw4kuqju2KuQc1bhbhxTYc6sCFQeZxowDZEG53rj5s7viQGq0K9CqAEwXwQESZ5hWBTfnaKGQ0ybBAQZU9QJJUrFTq0LekcdFN1KgtVWhy4wDKg6yre6HKuyqmtoJDoJpHBIwsFK98VzdvVVBkk4cWqJVAZgyggMkih+EqnphxzeNvawUXWYcYGBVPwoOurQqUHGAFIgqDvJOh4oDx8jzqTjIsnqvigNew5AS5Vqz4qDDZq3Dy0VtlD1WywJTRHCARCnVmmnzgOsYpcZmBWYcYFBVL2pV6DwckTJfpIHfa6tC3pFHxUGmeUGowg0zDmhVQLr0qjiIWlK3qlznAdNCcIBE2W72tw3aqiA1hudQcYBBtVoVOgxHdAkOkBI9tyo4bFXIuk7DEaMQidcwpEWr4qDDjINoCDbBATA9BAdIlFLzTWPQ4YgSrQoYzs6Mg86tCqxjRBpEww/dTlsVcg7lvRnn+eENwYExRoU8A16RHq2Kgw7XgK0h2FznAVNDcIBE2W4GB0vFIYKDeZdWBQwsalWYy3cejljjbh1SoLWOsVPFQc7IC6k4yLJ6EMrN3xgazVE1hRTptVVhpdhcu01wAEwNwQESJao4GKZVYaWY5w0FA4uGI84xHBEp5rVaFTpUHDhUHGSZtVb1INRch9CokCc4QHrszLnqUXHADSJgaggOkCitVoVhtirMu7pW8WQtd9bQv1qvdYzcrUNKtIYjdtqqkDOtVgZkTxBaWasbWhUkggOkS6+Kg9XWjANuEAHTQnCARNlups3DVRy4Cu1OjxzQj52tCh0qDnLMOEA6RBUFnSoOeB5nW2u+RZd2K6qmkBZRxcFCj60K1xiOCEwNwQESZWc44nDrGCVps0Iajf7tNRyRu3VIg+iHw07rGPM5I58ZB5m189hTNYV0K9d9FV1HuQ5DXqMbSlQcANNDcIBE2R5xq4IkXSM4wACi4YfFDnfrWMeItGgNR+zUquA4zXJ2woMsiqpJCp2qTQg/kSKlut+1VTWfc7RYyDHjAJgiggMkynbNV94xHSfc7yXqfyM4wCD2rDigzBcp4PcYjhhVITDnIJu8XhUHvIYhRcq1QAs9Kk5X5l0qDoApIjhAopRqvhbn8jLmxovfvaxEwQH9bxhAzxkHeUdewJ1aJF+rz73DD4/RisYoXEC2eH7jsS90mnGQY6Us0mO71r3iQGrMOWB7FjA9BAdIlO2aP9RgRIlWBQyn6gdyc6ZjT2VU+cIdOyRdazhih+dx9LHoB0xkSz1oVE2xVQFpV64HHTcqRFaKrra4OQRMDcEBEqVRcTD4YERpZzgiaTQGUfUCFfOdn3NRiTcX3ki6aPhht8n6kuRRcZBJdb97tckcwQFSpFT3e864ouIAmC6CAyRKqRYMXXEQ/T0G52AQVS/UnNv5pbDQvBCnNxxJ12pV6DIcUdoZoIhsaQ1HzHcZjkjFFFKiXNuj4mCeigNgmggOkCjbtd5pcy/5nKOlOdJoDKbmBZrrUnFQaH6cO3ZIularQpd1jNLOD5jIlp7DEdkMgxTptVVBalYc0I4KTA3BARKlNMKMA0laKea1yZsKBlD1AxW7VRxEMw648EbCeWG0jrH7VoWonQHZUt9rqwKvX0iJcr33VoXl5owDBhYD00FwgEQpjVBxIDXK2EijMYiqF3bcqCC1zThoDh8DksoPQuUd03EjzU6rAj9AZlHUptJxqwKtCkiR0h5bFVaKrvzQtrYhAZgsggMkyihbFaTGmwqtChhE1Qu6BgetrQpMo0fC+aHt2KYg7QRgzOrIpqiioNCxVSFHxQFSwQ9C1fxQC3u0KkgMwQamheAAiWGtbc44GG6rghRVHDA4B/2r+eHerQrcsUPCeUHYcTCitFNxwIyDbOo544BWBaRE2WtU9vW6BlyZb6zd3iI4AKaC4ACJUfVChVYjtiowHBGD6bWOsZBjOCLSwQ96VBw0AzCfdYyZtBMcdN+qQE84kq5cawQH/VQcbHKDCJgKggMkxnat8UYwcqsCMw4wgF6tCq0ZBwQHSDg/DJXvcMdZklyHVoUsi16fOlUczFE1hZQo1RvXgD0rDopUHADTtGdwYIz5TWPMJWPMV9s+tt8Y8xfGmBeav9403sPELCg1g4Neg3H2sjLvaqvmK2R6OPpU9ULN7dGqQIk3ks4LbCsg2C0KFHyCg0zqORwxx2YYpEM/FQcrrRkHVBwA09BPxcFHJb1/18d+UtKnrLV3SvpU88/ASFoVB8XR1jFaK23XeVNBf2p+94qD6EK8xkU3Es4PulccRC0MHq0KmRQFmx2HI7JSFinRqjgoMOMASKo9gwNr7WckXd314e+V9LHm7z8m6ftiPi7MoFIcrQrNNxXaFdCvqhf2mHFAmS/Sweu1VcGh4iDLWq0KXdYxSryGIfnKzeBgocc1YGurAjMOgKkw/QzMMcbcJumT1trXNf+8Ya3d1/y9kbQe/bnD331Y0sOSdPjw4Tc98sgj8Rz5BG1vb2tpaWnahzGypJ/Hk5d8/fLjNf1Pby3q+L7emxW6nctjF3392hM1/eu3FXXryvDbGSYp6Y/LINJ4Lj/0ZyW9/zZX/+DuQutj0XlcKof67z5T0T+9v6C3H3OneJTDS+Nj0k1WzmUc5/FrT1R1sRTqf37Hwg2f+9ZWqP/xbyr6kQfm9OYjwweznWTlMZHSey6fPFXX777g6SPvW2i1q0Tn8jdnPX3k6br+13fO69BC+sZapfUx6YRz6e2L533926/U9LPvmNexpc7PVWut/qs/L+u7b3f1n91V6Pg1g8rK45KV85A4l2l76KGHHrPWPtjpcyNfQVhrrTGma/pgrf2wpA9L0oMPPmjX1tZG/ZYTd/LkSaXxuHdL+nlc+8o56fEn9M63vUV3HFru+bXdzqVw6rJ+7Ykv6q77HtB3nLh5TEcar6Q/LoNI27n4QajgT/8/3XXidq2t3dn6eHQe5zcr0mc+reN33q21t7xmikc6vLQ9Jr1k5VzGcR6/deZLquaqWlv7Ozd87sVL29Lf/LXueu09WnvgWKzfNyuPiZTec3nS/7r0wgt699qanGZwEJ3L1lfOSU8/oTe86c2683Dv99UkSutj0gnn0tvFL31T+srTWnvHd+jYvvmuX7fy2T/XvkNHtbZ2fyzfNyuPS1bOQ+JckmzY+PmiMeaoJDV/vRTfIWFWtYYjjtCqsBq1KtD/hj5UmyW+xW7DERkshpTwesw4iLaD0KqQTV4QKu+YVmjQjjktSItSczhirxkHUnMINsMRgakYNjj4Q0kfav7+Q5L+IJ7DwSzbro4eHESrephxgH5UvcaFStd1jAwWQ0r4/WxVYDhiJnmB7biKUWLGAdKjNeNgj81ay8U813jAlPSzjvF3JD0q6W5jzMvGmB+S9HOS3muMeUHSe5p/BkayHdM6RolVPejPTnCwR8UBF91IOD8MewxHbG5VoOIgk+p+2HEVoyTNUTWFlCjVA7k50/W5HFkpUnEATMueP6FZa3+gy6feHfOxYMaVar7m3ZxyXe6a9WN5Li9jqDhAf2qtVoU9tipw0Y2E8wLb9XncqjggAMukehB2rTiYc3kNQzqUa/6e1QZSo+LgpculCRwRgN3SN2IXmVWq+yO1KUiS4xgtzeWZcYC+RBUHc13WMTqOkZszVBwg8fyw0efeSVSJQMVBNnl+qEKXapNCrvHaRnCApCvVgz3nG0hUHADTRHCAxNiuBVqaG32F4krRZccv+lL1eg9HlCQ353DRjcTze/W5Nz/uMeMgk7wgbM1j2Y0ZB0iLct3XQh83j5aLLlWlwJQQHCAxSjVfS8XRd4yvzLva5E0FfajtMRxRalx4e1x0I+G8HuXqUSUCWxWyqa/hiISfSLhSrc+Kg/m8SvWA1itgCggOkBjbNX+kwYiRlSKtCuhP1e8jOKDiACngh7brcMRcKzjgeZxFvWYcEBwgLcr1fmccNIZgRwO1AUwOwQESo1TztTTijANJWp2njA39iVoV5npMcaZVAWngB1Z5p/Pz2JjGrA4vpOIgi7yg+1aFqE2lRmiEhCvVAi320a663KxMZc4BMHkEB0iMjbKn1eY6xVGszDM4B/2p9tGqMJd3uOhG4jVaFbpvpMk7DhUHGVXvNRyRigOkRL8VByvNigNaUoHJIzhAIgSh1YVrVR3dVxz531phcA761M9wxELekcdFNxKuV6uC1NiswFaFbOo13yKqpqo127KApCrV+6s4WKHiAJgaggMkwqWtqoLQ6pZ98yP/WyvzeW3VfAWU5WIPrYqDLusYpUZwwERyJJ0XhF1bFaRGyTpDPrOp3sdGDSoOkHTlWp8VB83K1C1mWQETR3CARDi3UZGkeIKDaHAOaTT20M9wRGYcIA0a6xh7VxywVSGbPL97xYHjGOUdw2sYEi0Mrcpef1sVohkH17jGAyaO4ACJcHajKkk6FkvFQSM4YLMC9tLPcETu1CIN/DBUvssPj1JjxoEX8jzOosZwxO6hUSFP+Ilkq/qBrJUW+hiQHd0couIAmDyCAyTC+WbFwdHVOGYcNN54GJyDvdT8QIW8I8fhohvpZa2VF1i5PZ7HLhUHmeUFYasloRParZB0pVqj+q+fioOlqOKgQsUBMGkEB0iEcxsVLRfzrf28o2hVHBAcYA81L1SxR7WB1LjorhEcIMGieS49Kw5yjnwqDjKp3qNVQWpUTRF+IsnK9UYI0M+MAzfnaKGQo+IAmAKCAyTC2Y1qLG0K0k4ZG60K2Mu1qrdnWFXIcbcOyea3goNe6xiN6j4VB1lUD6zcXu1WVE0h4VoVB31sVZAacw64xgMmj+AAiXBuoxLLYERJWl2IKg4oY0Nv66W69i3sERzkmXGAZIuCLbfXVoU8FQdZ1U+rQo3XMCTYIBUHUuMGEesYgckjOEAinN+sxDLfQNqZcUAajb2slz3tXyz0/BrKfJF00eyCvSoOmHGQTV4Q9tyowWsYkq5Up+IASAOCA0xdue5rvezFVnGwWMjLMcw4wN42ynXtW9gjOKDMFwnnN+8m7zXjgMqZbGoEB90f+zlew5Bw5dqAFQfzVBwA00BwgKk7F+MqRqmxt3q56LLjF3taL3u6aY9WBZe7dUg4rznjYM+tCiEVB1kTbdQoMOMAKdaqOOgzOFguutwcAqaA4ABTd34zvlWMkZX5PG8q6MkPQm1WvL4qDjxKvJFgfVUcOE7r65AdrfkWrGNEirVmHPTZqrBSzFNxAEwBwQGm7txGIziIq1VBagzOof8NvWw2g6X9fQxHrAehrCU8QDJFwVavPnc3Z1QnAMuc6LHvORyRqikkXGurwiAVB1WP92VgwggOMHVnN6oyRjoSZ8VB0W39YAh0sl5uPD9u2nM4YuOHMe7YIamibQm97jpTcZBNnh899j2GI9KqgIQr130ZIxXd/n4sWZnPywusajyvgYkiOMDUnduo6PBysedF76AarQqUsaG7jXJdkvpqVZDEhTcSq7VVodeMg7zDjIMMigZeuj1nHOQIPpFopVqgxUJexnR/DWu3XIzWbnODCJgkggNM3fnNio7ui6/aQJJW52lVQG9XS43gYK/hiFEJMHMOkFReH33urmPYqpBBfc04oFUBCVeu+1oo9DffQGpfu80NImCSCA4wdec2qrHON5CaMw5IotHDRtSqsGfFQeNihgtvJFVUSZDvUa6ez5lWZQKyIwo05/bYqkBJN5KsVA+0ONfffAOpcY0niRtEwIQRHGCqrLU6t1GJbRVjZGXeVake0NOLrtabrQp7zTiIeocJDpBUUSVB3ukx4yDntGYhIDvq/t4VB3N5R3U/mNQhAQMr1wasOJhvhAxsVgAmi+AAU3W1VFfND2NdxSjtlLHxpoJu1sue3JzR4h4XK60ZB4RQSCi/n60KjqHdJoP6aVNhHSOSrlT3+96oIDHjAJgWggNM1bmNqqR4VzFKjYoDiTI2dLdRrmvfQmHPYUxzDEdEwkWVBPleWxVyDjMOMmhnxkGPrQrMOEDCleuBFub6rzhY5uYQMBUEB5iqsxsVSYq/VaGVRvOmgs6ulup7DkaUqDhA8nn9bFXIOcw4yKBoHWNhj4qD0IrWPSRWqTZYxQEzDoDpIDjAVJ1rBgdUHGDSNsrenoMRpZ0SYO7YIal2WhV6bFXIGXnMOMicKDTqvY6R8BPJVq4HA804WCjklHOMtrjGAyaK4ABTdX6zorm809ed30FEg3M26X9DF+vlel/Bwc46Ri66kUw7rQo9tio4jqyVgpCqgyyJXpd6VhwQfiLhtmv+QFsVjDFaLuZpVQAmjOAAU3Vuo6pj++b37DMf1AqDc7CH9bKnmxYHaFXgohsJ1brr3HOrgml+Lc/jLKn1sVWB1zAkmbV24IoDqTHngGs8YLIIDjBVZzcqsbcpSNIqrQrowVrbGo64l+iimz3oSKqod71XxUE0PM+n4iBTWhUH+R7DEXkNQ4LV/FBBaAeqOJAaN4ioOAAmi+AAU3V+s6Jb9sW7ilHa6X9jOCI62ar58kOr/QO0KtAfjKTymmHAXq0K0s4wPWRDP+sY55hxgAQr1wNJGq7igJtDwEQRHGBq6n6oS1s1HV2Nv+LAGKMV3lTQxUap8bzYN8BWBX7gQlJFz81erQrR8DwGJGZLP8EBMw6QZKVa4wbPIFsVJCoOgGkgOMDUXLxWlbXxr2KMrMy79L+ho/VyXZL6G47I3TokXD/DEd3mqkZWMmZLvY+NGsw4QJK1Kg7mBq044BoPmDSCA0zN2TGtYoysFF1dI41GB1ej4KCP4YisY0TSeX388Jhvfo7gIFuiapMC6xiRUqX6kBUH82xVACaN4ABTc36zERwcHcOMA6nxpkIajU42hqk4IDhAQkVhQN7ZezgirQrZUmcdI1KuXBt2xoGrrZrPillggggOMDXnNqqSpFvGMONAalQcbBIcoIP15oyDvoIDhiMi4aJWhVyP4CAajkjFQba05lv0aFMh/ESStSoOBt6q0Pj67RpVB8CkEBxgas5uVLR/saD5AVPmfjVaFQgOcKONcl3GNOZg7IW7dUg6L7Byc0bG9AgOoooDArBM8YJQxvQOjVjHiCQrN4ODQSsOVorNtdvcIAImhuAAU3NuYzyrGCP7Fl2tlzyFlLFhl6vlulbn3Z4X2xHHMco7hooDJJYfhD3nG0g7ARjBQbbUAys35/QMjVjHiCQrNVsVlgatOJhvfD1zDoDJITjA1JzfqI5lFWPk9psXVQ/C1hBGILJe9rS/jzaFSCHvsI4RieWHtud8A2mn4sAnSM0ULwh7zjeQpEKucSeXqikkUaviYMDgYDmqOKCyFJgYggNMzbmNythWMUrSHYeWJEkvXtoe2/dAOm2U69q3sHebQqSQd7hbh8Ty+qg4iGYcUHGQLV4Q9tyoIDHjAMkWVRzMu8O1KlBxAEwOwQGm4lrV01bNH2urAsEBulkveX0NRowUcg4X3UgsP7CtioJuouF5DEfMlkZo1PuxL7qNS72KF0zikICBlOu+5t1cX62D7ZabwxGZcQBMDsEBpuJ8tFFhjBUH+xYKOrBUIDjADdbLde0bIDhwCQ6QYF4YtioKusk3KxJ81jFmSs3fu9pkpeiqkHP0ylZtQkcF9K9UD7Q4N/iQ7Gi48RatCsDEEBxgKs415w6Mc8aBJB0/uKQXXyE4wPXWy3XtX+y/VWGOVgUkmN/cqtBLNAOh7lNxkCVeYPecceA4RkdWizq/ybwfJE+55muhMNh8A6mt4oBWBWBiCA4wFdHAwnHOOJAa7QovXtqWtVwso6HqBap64UAVB4U8FQdILj8MWxUF3bhUHGSS10fFgaRmcFCdwBEBg7la9lobEgbh5hwVXYeKA2CCCA4wFec2Kso7RgeX58b6fe44uKTNiqcrpfpYvw/SY73ceC4MNOOAigMkmBfsvVWBGQfZ5AWh3PzeveFHV4u6QHCABPrGlZJuvXlxqL+7UnR1rULFATApIwUHxpgzxpinjTFPGmO+HNdBIfvOb1Z1eKU48DCcQTEgEbutlxp3J24aYKsCMw6QZH4fWxWiz7NVIVvqfaxjlBoVBxc2q1TfIVG8INTL6xXddvPCUH9/uZjXVo2KA2BS4qg4eMha+4C19sEY/i3MiLNjXsUYITjAbq2Kg8XBtirwAxeSyg/33qoQfd4P+cExS/pZxSlJR1eKqgehrlJ9hwT51tWygtDqtmErDuapOAAmiVYFTMXZ9YqOjnEVY+ToalELhRzBAVqGblWg4gAJ5QWh3L22KjQ/7xOAZUrdD1XI91Nx0AjqmXOAJDlzpSRJuv3AcMHBctFlxgEwQWaUsjVjzEuS1iVZSf+ntfbDHb7mYUkPS9Lhw4ff9Mgjjwz9/aZle3tbS0tL0z6MkSXlPDZqoX78ryr6h3e5+sDx/n94azfIufzU5ytaco3+2zePP6gYRlIelzik4Vw+/U1Pv/VsXb+8Nq99xc4X3LvP41cer+pyxepn3j7+Kpm4peEx6VdWziXu8/jZL1SUd6R/8Zbuz8/tutWPfrqsH3htQd91W/9tOnvJymMipfNcfurzFa3OGf03b7r+/W33uZzeDPTTj1b1Y2+c0xsODT6IblrS+Jh0w7nc6M/PePrt5+r61YcWtDI3eOvq//FkVd+8Furn3jlcq4OUncclK+chcS7T9tBDDz3WrZNg1HePd1hrzxpjDkn6C2PMc9baz7R/QTNM+LAkPfjgg3ZtbW3Ebzl5J0+eVBqPe7eknMcffuWcpCf0X7z3Lfq2V+8b6t8Y5FzecPFJffH0lUSceydJeVzikIZzefpTL0jPfl0feM9a1zt1u8/j/z37uDYvXEv8uXWShsekX1k5l7jP45e++jmtLhS0tvaWrl+zXfOlT/+Zbrv9uNa+80Rs3zsrj4mUznMpPvkZHbl5QWtr11/j7T6Xe69V9dOPfkoHXnOn1t5664SPcnhpfEy64Vxu9Ok/+KqW587q771vTcYMHhz82dWndfrZCyMdS1Yel6ych8S5JNlIrQrW2rPNXy9J+oSk7lctQNOjp65ouZjXfbesTOT73XFoSec2qyrV6IODdLVc19Jcvq/y3kghz4wDJJcXWLn9blVgxkGm1PuccXDz0pzyjtGFzcoEjgroz0uXS7rtwOJQoYEkrcznda3KtR0wKUMHB8aYRWPMcvR7Se+T9NW4DgzZ9YXTV/Ttt+/fc+94XE4cbPTOnXqFOQeQNsqe9g2wUUFqDEdkxgGSyg/DPYcjRjMQCMCyxetzq0LOMTq8UmTGARLlzJVGcDCslaKruh+qUg9iPCoA3Yzyk9thSZ8zxnxF0t9K+mNr7Z/Gc1jIqvObFb10uaS3Hr95Yt+TzQpot16uDzQYUZLcvCE4QGL5gd0ziHUcI8c0vhbZ4fm27+qpaCUjkAR1P9TZ9YpuH3IVo6TWNgau74DJGHrGgbX2tKRvi/FYMAMePXVFkvQdJyYXHNx686LyjuGNBZKk9aEqDnLy+IELCeWF4Z6tCpKUzznyQgKwLOm3VUFqBAfPnrs25iMC+vPNq2WFtnGNNqyo5fWZc5u6/1WrcR0agC5Yx4iJevTUFe1bcHXPkcnMN5AkN+fo1psXaFWAJGm9VNf+xcEqDljHiCTrp+JAklzHUHGQMZ7ff3BwdKWo85sVjbJNC4jLN5qrGEdpVXjN/gUtzeX1DIEYMBEEB5ioR09f0Vtvv1lOH3fH4nTHoSUqDiBpuFaFQt5RPQi54EYieYFtDT/sJZ9jyGfW1INQbr6/99Oj++ZV9UJtVth7j+l76XIjOLh9hODAcYzuPbqiZ85txnVYAHogOMDEfOtqWS+vVybaphA5cXBJ37hS5qJ5xvlBqK2qP0SrQuPCvM7zBwnkh/3ddXZzDi03GdPvcERJOrpalCQGJCIRzlwpaaWY100Dvh/vdu8tK/ra+S0FbIwBxo7gABMzjfkGkTsOLckPbas0DrNpo3mnbZiKA0m0KyCR/MAq7+z9tSQcNAAAIABJREFUdr5SzGujXJ/AEWESgtAqtBpoxoEkBiQiEc5cLuv2EVYxRu67ZUUVL2hVMAAYH4IDTMyjp6/owFJBdza3HEwSmxUgNeYbSBq44uDQMnfqkFxeEPbVqnD7gUWdfoWL66yIKuj63apAxQGS5KXLo61ijNx3S2MoIu0KwPgRHGAirLV69NQVvfX4zSOny8M4cbARHJzionmmrZcbFQeDDke86/CyJOn5C1uxHxMwKj+0yvcRHJw4tKSXrpQo6c2IWrMCqt+Kg4NLc3KMdGGzMs7DAvZU9QKd26y01imO4s7DSyrkHDaGABNAcICJeOlySReuVafSpiBJi3N5HV0tUnEw49abZdqDtiocP7ionGP09YsEB0gWa62CsL9WheMHFlu705F+rYqDPkIjqTEc89BykYoDTN23rpZl7WiDESNuztFdR5bYrABMAMEBJuLR0835BsenExxIbFaAWv3dg7YqFN2cbrt5gYoDJE407LCfVoUTzZatU5d5HcyCKDjot+JAasw5uHCN4ADTdeZKWdJoqxjb3Xd0Vc+c22TzETBmBAeYiEdPXdHhlblY0uVhnTi4pFOvbCukTHdmXS0NNxxRku4+sqwXCJ6QMNeqjef0QiG/59ceb77+nuJ5nAmeH4VG/V/KHV2l4gDTd6Y5yPC2mxdi+ffuO7ai9bLHcxsYM4IDjJ21Vl84fVXfMaX5BpE7Di2pXA90nrstM2ujXFch72ihkBv47955aFlnrpRU9YIxHBkwnK83q2DuPLz30Nn9iwXtW3B1munjmRCth3X7HI4oNSoOzm9UuDOLqXrpSkn7FlztGyLE7+S+W1YkiXYFYMwIDjB2L17a1uXtmt524sBUj4PNClgv13XTgjtUgHX3kWVZy/MHyfJcMzi4+8jynl9rjNHxA4tUHGTEzoyDwSoOSvVAWzV/XIcF7OnM5VIsgxEjrz2yImPYrACMG8EBxu7zp5rzDaY0GDHS2qzARfPMWi97Q7UpSGxWQDI9f2FL+xcLOrg019fXnzi4RMVBRtT9aB1j/0HokdV5SdIFSroxRWcul2JtXV2cy+v2A4tUHABjRnCAsXv01BUd2zevV++Pp5dtWAeWClqdd/XiKwQHs2qjXB94MGLktpsXVMg5bFZAojx3cUt3H17uu4rm+MElvbJVa81GQHqdb65VvHmxv9BIalQcNP4uwQGmo7GKsRprxYEk3XfLKisZgTEjOMBY1fxAX3jpytSrDaRGmS6bFWbb1VJd+xeHqzjI5xydOLREcIDECEOrFy5u9dWmEDlxsHGxfvoVqg7S7qmXN+XmjF57tP/H/8hKIzi4sMlKTkzHN1obFeK9mXTfLSs6u1HReqke678LYAfBAcbq//nyy9ooe/rgG45N+1AkSXccXKJVYYZtlL2RhjHddXhJX7/I8wfJ8K31ssr1QK8dIDg4TstWZjx9dlN3H1nWXL7/Ya+HV4oyhooDTM+ZK43QMu4tW9GAxGfPU3UAjAvBAcam7of6dydP6U233qS3JaDiQJLuvWVFV0p1vXiJu8azxlqrjYqnm4ZsVZAacw7OblS0RZk3EmCQwYiRW29eUN4xOn2Z4CDNrLV66uVN3X9s30B/r5B3dGBpjhkHmJrWKsbYg4NVSQxIBMaJ4ABj8/HHX9bZjYr+2bvumOoaxnbf/bojcoz0iSfOTvtQMGHXqr6C0A49HFGS7m4OSKTqAEkQDeqMBnf2w805es3+BZ26RKtCmn3zalmbFU+vf9XqwH/36GqRigNMzZkrJd28WNBKcfgQv5P9iwUdXS0yIBEYI4IDjIUXhPr1ky/q2161qu+86+C0D6fl0EpR77jzoH7/iXMKQ/ZYz5Ko73Gk4OBIFBxQsYLpe/7Cll6zf0GLc/mB/t7xg0tUHKTcUy837qref2zw4ODISpGKA0zNS5dLuvXm8QzLvu+WFYIDYIwIDjAWf/DkOX3rakX/7F13JqbaIPL333BMZzcq+tszV6d9KJig9XIzOFgc/i7HsX3zWijkCA6QCM9duDZQm0LkxMFFnblcVkB4mlpPn91UIe8MVG0SaVQcMBwR03Hmcjn2NoXIvbes6vQr26rUg7H8+8CsIzhA7ILQ6tf/6kXde3RF777n0LQP5wbvu++wFgo5/T7tCjNlo9yYSzDKcETHMbrz8DLBAaau6gU6c6U80GDEyImDS6oHoV5eL4/hyDAJT728oXuOrqiQH/wy7sjqvK5VfZVq/hiODOiuUg904VpVt8e8ijFy3y0rCq30tQtUHQDjQHCA2H3yqXN66XJJ//zdyZlt0G6hkNf7X3dEf/z0eVU9UulZ0ao4GCE4kKS7Di3p+QuUeWO6Xry0rSC0Q1UcHG+uZDz1Cs/jNApDq6+evabXD9GmIDUqDiQ2K2DyvnF1PIMRI9FmBdoVgPEgOECswtDq33z6Rd19eFnvu/fItA+nqw++4Zi2qr4+9bVL0z4UTMjV5oyD/SMGB3cfWdbl7Vrr3wOmIRqMOGzFgSSdfoUBiWn00pWStmu+7h9iMKIkHWkGB8w5wKRFGxXiXsUYObZvXqvzrp5lswIwFgQHiNWfPnNBL1za1o++6w45TvKqDSJvO3FAh1fm9IknXp72oWBCNsqeHCMtFwcbJLfbXYcZkIjpe/7ilgp5R7cNUfJ702JBNy24VByk1NPNwYjDbFSQ2isOmHOAyXrpcqM9alwVB8YYBiQCY0RwgNj4Qahf/dQLOn5wUR+4/+i0D6ennGP0vQ8c08nnX9GV7dq0DwcTsF6ua99CYeRAi80KSILnLmzpjoNLyueGexs/cXBJp6g4SKWnXt5U0XV0R7NyZFCHV6g4wHScuVzSgaU5LQ24CWYQ992youcubMkLwrF9D2BWERwgNj/9yWf13IUt/cR771YuwdUGkQ++4Zj80OqTT52f9qFgAk6/UtKh5bmR/51Dy3NanXdbpeLANHz9wtZQbQqR4wcXaVVIqafPbui+W1aHDo2Kbk77Fws6f43gAJNjrdVXXt5ozVgZlzfdul91P9RfPntxrN8HmEUEB4jFxz5/Rr/16Df0T//O7fqe1ye72iByz9EVvfbIsj7OdoXMO7tR0RdeuqL3v270uRvGGN3NZgVM0WbZ04Vr1aEGI0ZOHFzS5e2aNitejEeGcQuagxHvH3IwYuTISpGKA0zUl86s67kLW/q+B46N9fu8997DOn5wUb/8ly8oZOUsECuCA4zs5POX9K//6Bm9557D+snvvmfahzOQv//GY/rKtzZ0ml7fTPvE4y/LWun73/iqWP69Ow8v6fkLW7KWixJM3nPNVWOjBAfHWwMSee1Lk1OvbKviBUPPN4gcXS2yVQET9dHPv6TVeVcffMN4g4OcY/Rj775Tz1/c0p98lYpSIE4EBxjJ8xe29KO//YTuPrKiX/lHD6SiRaHd9z5wTI6Rfp+qg8yy1ur3Hj+rtx7fr1fvX4jl37z7yLKuVX1d2mI+Bibv+YvRRoWVof+NE62VjLQrpMlTIw5GjBxZLeoCwxExIWc3KvqzZy7qH73l1Zov5Mb+/f7u62/RHYeW9Ct/+YICqg6A2BAcYGiXt2v6oY99SQuFnH7jQw9qcYzDbsbl8EpRb7/jgD7x5FlK2jLq8W+u66XLpdiqDaSdzQrMOcA0PHdhS6vzrg6vDD+z49X7F5R3DBUHKfP0yxtaLOR0+4HhBiNGbtk3r/Wyp6oXxHRkQHf/16PfkLVWP/jWWyfy/XKO0Y+/5069cGlbf/w0VQdAXAgOMJTNiqeHf+vLurxd07//0IO6Zd/8tA9paP/5m1+tb12t6Df/5qVpHwrG4Hcfe1kLhVysmz5YyYhpev7Clu4+sixjhq/wcnOObr15gZWMKfPU2U3dd2x15Oq+IyvRSkbaFTBelXqgR770Tb3v3iN61U3xVP314wOvO6q7Dy/rV/7y61QdADEhOMDAnvjmur7nVz+rp17e1C/9wwf0+lftm/YhjeR77j+q9917WD//p8/pq2c3p304iFHVC/TJr5zXd7/uaKwVMfsXCzq4PEfFASbOWjvyRoXI8YNLbFZIES8I9ey5a3r9iIMRpcaMA0k6T7sCxuwPnjyrjbKnf/z22yb6fR3H6Mfec6dOvVLSH33l3ES/N5BVBAfom7VWH/nMaf2Df/eoJOl3/+u36btjvIs7LcYY/fz3v143L87pn//OEyrX/WkfEmLyZ89c0FbN1/e/Kf5hTGxWwDSc3ahoq+aPNBgxcuLgks5cKcln33kqvHBxWzU/1P0jzjeQGjMOJLFZAWNlrdVHP39G9xxd0bffvn/i3//99x3Ra48s61f+//buPD6q6m78+OfMnkz2lYQEwhZWAQERVBDEBZdCta3L0ypdrK3VttrFLj6/tj7VLk8Xl/o8tVqt1n2vVn1aRUFBBdkjqyAQSIAQIHsyme38/rh3kiFkkGWSmTt+36/XvObecy+Z82Xu3HvP95577ptbZT8nRBxI4kAck4Y2P9c+spI7XtvEnNFFvPqdGUwst3ZPg2i5Xhd/vGICOw62cdvLGxNdHREnz62qYWBOGtOG5Mf9b48ozmDr/lYZG0P0q0gvl/j0OPASCGlqGuSqsxVU1TQCxKWXXyRxILeqiL70/vaDbN7XwlfOqDipW6tOlM2muOncSnYcaOOltdLrQIiTJYkDcVTBUJhnV+7monuWsGTrAW6bN5b7vjSZ7DRnoqsWd2cMK+D6s4fx9MrdvFolg+lY3d6mDpZuO8DnJg3E1gdP+xg9IIt2f4jlOw7F/W8LEctmM3EQGWfjZAwzH8kojUdrqKptItPjYHAcng6T7nIwa2Qhf3+/mkNt/jjUTogjPfzuTnLTncybWJqwOlwwtpixpVnc89ZW/EHpdSDEyZDEgehVIBTmmZW7OecPb/PD56rI87p4/vozWJCgrHF/ufm8SiaU5/CTF6qobZSrcFb2wupatIbPTY7f0xSiXTy+hLLcNG598UMZmVz0my37WhiYk0am5+STt5FHMso4B9bwYU0T48uy45YIvfWi0bT7Q9z5xkdx+XtCRNt9qJ2Fm+r4j9MH4XH2/SMYY1FK8YPzR1J9sJ3rHl1Jh1+O10KcKEkciMN0+EM8s2I3c/7wNrc8V0VWmoMHrpnCK98+Ky73VSY7p93GPVdOJBTW3PTUGjqDcoCxIq01z6+uYWpFHoPzvX3yGV63g99cNp7tB9q4a+HWPvkMIXraEqeBEQFy0l3ke11dvRhE8uoMhti8r5lTBsbvFsERxZl86fRBPL68WgZ6FXH3yHs7UUrxpX56BOPRzB5VxK8vO4W3P6rn6geX09QRSHSVhLAkSRwI/MEwb22u46an1jDl9je45fkqstOcPLhgCv+88SzOG1Oc0r0Mehqc7+WOS09hxc4GLr/vfel5YEFrdjeyvb6tTwZFjHbWiAKumFLOA0u2d91/LERf6QyG+Li+NS4DI0acPbKQl9bWsnpXQ9z+poi/qpomAiHN+Dgn8G86t5IMt4PbX92I1jJei4iPF9fU8NC7O5g3oZSS7OR4XPdVUwdx71WTWFfTyJX3L6O+pTPRVRLCciRx8CnV0ObnX+v38ZMXPmTqrxby1YdXsmhLPfMmlvLk16fx8o1nMmf0pythEO2zpw7kL1dPZnt9G5fcs4R3PqpPdJXEMdJa89DSHXicNi7qh6d+/PTi0RRkuLjluSq5f1L0qV+/tplgWDN9WPwG+/z5Z8YyINvDt59YQ1O7XIVLRo3tfn747DryvS6mDY3vQK+5XhffPbeSJVsPsHiLHOfEyXtuVQ3fe2Ydpw/J545LxyW6Ooe5eHwJf11wGjsPtPGF+95j96H2RFdJCEuRxMGngNaaumYfq+uC3PbPDVx49xIm3f4G33xsFS+treXsykIeXDCFFbeey68vG8/0Yfmf2oRBtAvGDuClG8+kKNPDgr99wD1vbpUR9JNcMBTmlueqeKVqL187a0hc7gP/JNlpTu747Cls3tfCnxd/3OefJz6dnl6xi4ff28nXZwxhxojCuP3d7DQnf7rqVOqafdzy/Dq56pxkAqEw1z+2mj2NPv5y9WTyvK64f8bV0wYztMDLL1/dSEAeWSdOwjMrd/PD59Zx5rACHvryaaS7HImu0hHOrizksWtP51Cbny/c9z4r9wXl3E6IY5R8v2hxUpp9AaoPtPNxfSsb9zazaW8zG/c0c9AcNdnt2MWUily+d24l04blM6EsB5dD8kexDC3M4MUbzuCnL3zIH9/4iDW7GvjFvLF9dt+8OHG+QIgbn1jDwk11fHfOCG46d0S/ffa5Y4qZN6GUexdtZe64AXHtSi7EqupD/Oc/1jNjRAE/mjsq7n//1EG5/GjuKO54bROPLqvmmukVcf8Mcfy01vzspQ28v/0gf7x8AlMq8vrkc1wOG7dePJqvPbKSx5ZV85Uzh/TJ54jU9vSKXfz4hQ85a3gBD1wzJaEDIn6SyYNzefob07nh8dXcu7aNN+uWctO5Izj/U3ZrrhDHSxIHFhIOaxra/ext8lHX7Ot6r23oYOfBNqoPtnclCMA4GRhZnMmc0UWMKcnCv387Cz4zC7cjeXfmySjd5eDOKyYyaXAut7+yidm/X8wl40v55tnDGFOalejqCYyE2bWPrGTFzkPcNm8sC86o6Pc6/PwzY1i67QC3PLeOp78xPalPmoR17Gns4BuPrmZgThr3XjUJh71vEr1fO2sI728/yO2vbGLSoFzGDUz9wXCT3d/e3cmTH+ziW7OGcdmkvnk6TMQ5o4qYMaKAuxZu5bMTB5LbBz0bRGoKhTWPvLeT/3plIzMrC7n/6smWOP6NLsni9Ztn8tun3uSNPUG+8egqxg3M4qY5lcweVYS9Dx7jLITVSeKgHwVDYfyhMG2dIVo7g7T6grR0BmjxGdOtncarxRekxRegsSPAoVY/h9r8HGzz09DuJ9SjO5VNwYAsDxUFXs4fO4CK/HQG53sZUuBlaKEXZ9RJ5uLF1ZI0OEFKKa6ZXsEFYwfw0NIdPLasmpfX7WH2yEK+efYwpg7Jkyx1guxv8bHgoRVsrWvhrismMn9i3w6IGEt+hptfzh/HDU+s5rw73+bnl4zl3DHFCamLSA2+QIhvPLoKXyDEU9edTnZ63916Y7Mpfv+FCVx09xJufGI1r3xnBhluOUVIlEVb9nP7qxu5YGwxPzh/ZJ9/nlKK/7x4DBfe/Q5ffWQFv7r0FEaXSGJcxBYKa16p2sM9b27l4/o2Zo8s5M9fskbSIMJht3HmQCc/unIm/1i7hz+9tZVr/76SPK+L2SOLOG9METNGFOKVfaEQgCQOjqqu2cd/vbKRujofT+5eSVgbV/3DWhPSRjfCsNaEwpqwNu5F7AyE6QyG8JvT0e89G/2xuBw2Mt0OstOc5HldDM5PZ9LgHPK8Lgoy3JRkeyjO8lCSnUZBhqvPrkCJIxVnefjJRaP51qzhPLa8moeW7uCK+5cxMCeNOaOLmDO6mGlD8yRB0w+217fy+PJdPLtyN4GQ5sEvn8bZlfG79/tEXDy+hDzvNH7+8nqu/ftKzhlVxM8uGUNFgdzaIo6PPxjmR89XsX5PEw9cPYXhRX1/+0ue18XdV07kqgeW8dWHV/DjC0cxaVBun3+u6BYMhXn1w73c+uJ6RpdkcecVE7H105XPkQMyufOKidz2z41c8qelXDN9MDefV0lWP4wVI6wjFNYs2xPkl3e+zcf1bYwszuR/vziJuWMH9Nu2Gm8Ou43PTy5j/sRSXt9Qx+sb9/HGxn08v7oGl93GtGH5TBmcy9jSLMaWZlOc5ZaLRRYXDms6g2F8gRAdkZc/hC8QwhcId5X5/CF8QWNZpKwzEDbbfvqwtmFYY753T49wBJmV6GDjSBIHRxEIhdm8t5mOjjCtqh2lFDYFdptCKYVdgU0pbEqhFGS4HeR7bbgddlwOG26Hrce7Ue512cnwOMhwO8lwO8j0GK8Mt4MMj0ManRaQne7khtnD+dpZQ3h53R7e2FjHsytr+Pv71XhddmaMKOS0IXmcMjCbsaVZkq2Ok2AozFub9/PosmqWbD2Aw6aYO24A35o1PGluG5k+LJ9XvzODR97byV0Lt3L+ne9w3cyhXHX6IAbmJMdjqUTyamjz88QHu3jkvZ3sb+nkhxeM7NeeK6cPzec3nxvPr1/bxGX/+x5nDs/nhtnDmT5UBs3tS62dQZ5esZuHlu6gtrGDyuIM/rpgSr8PLjd/4kDOrizk969v4eH3dvLPdXv56UWjuPTUgfL9f4q1+4Ms336Itz+qZ+GmOmoaOhk1wMWfvziJCyycMOjJabdx8fgSLh5fQjAUZmV1A29uqmPRlnr+GPV0rYIMF2NKsxla4KUsN43yvHTKc9Mpz0vrl0GZU1korPEFNQdaO+nwh+gMhujwRzXkzVekIR9p5PuiGv5d85HlMcpPRKRd57CprvagTYFdmdM2YzrSNhxQnFoDb0pr5ijKctN58/uzWLx4MbNmzUx0dUQS8jjtXD6lnMunlOMLhHj/44Ms3FTHos37+deGfQAoBcMLMzhlYDbDizOoyPdSke9lcH66JBQ+QYsvQFVNE6urG1izu5E1uxpoaA9Qku3h++dVcsXUcooyPYmu5hGcdhvXzhjKvAml/Oq1Tdy7aBv3LtpGZXEGs0cVMXtkEZMH5x52K5H4dNu2v5W/vbuD51fX4AuEmVlZyO++MISZIwr6vS6XTynn4lNKeGL5Lu5fsp3/eGA5kwblsOCMCk6ryKNUEmBx0RkMsXFPM//eUMfjy6tp8QWZOiSP2+aN5ZxRRQlrjOWku7j9s6dw+ZRy/t9LG/jeM+u4c+FHzBhRyMwRBUwfVkB2mjSOUpXxJK5OttS1sGlvM+9uO8DyHYfwB8OkOe1MG5rH/MFhvn/5jJRJGPTGYbcxbWg+04bmc+vFRnJv095mNtQ2sX6PMfD4qp2HaPOHDvt3Xped/Aw3BRku891NVpqDDJcDr9u4SOh1O/C67V3Tkfc0px2HXeEwG6SJoLUmENIEw2HjPRQmGNYEQmGCIY0/ZFylj1ytN3pa9yiLeu8MGo30zqCxbsdhjf7uhrzRuDd6aAOwcOFx1dvjtJHmtJPmtONx2fE47KS5jPncdFf3crPMba6b5rSR5rLjcRqvyDrGv7d1lUWWH+/YF4sXLz6u9ZOdtFqEiBOP0240CkcVAbC/2ceHtU1U1TSxvraJpdsO8MKa2sP+TWGmmwxbgMd3raQ4y01xpoeiLDd5XjdZHgdZaU7j5XHgdTlS7iDd7g9yqM0Yx6O2oYPdDe3sPmS87zrUzo4DbUSeDjeiKIPzxhRzzqhizh1dZIlbdIqyPNx15al8e84IFm3ez1ub9/PQ0h385e3tZLodjCrJZHhRBsMKMxheZLwGZHksEZs4ca1+zZKt9VTVNFFV00hVTRN7m3y4HDYuO3UgXz1rCJXFiX0yh9ft4Oszh3L19ME8u3I39729ne8+tRYwxtWZPDiXUwflEDwUYnhDu2y3R6G1pqkjQE1DBx/Xt7J2dyNrdjWycU8z/lAYm4ILx5Xw9ZlDmViek+jqdhlflsOL15/BP9bW8tqH+3hpTS1PLN+FTcGE8hwmDcqlosDLEDMRXpqTJgPKWUSHP8S+Zh97GzvY0+RjX1MHtY0+tu1vYcu+Fpp9wa51RxRlsGD6YM6uLGJKRS4ep53Fixen3PnIJ8lwOzitIo/Top5uorWmoT1ATdS5S31LJwdaOznY6mf3oXbW7GqguSPY3SA+Rk67wmGz4bArXHabmVAwejD39l/f1t5O+qrFhxf2crE7rDXBsCbYIzkQCBvv8Xwypctuw+00ely7HbauRrvHaSPT46Ao0x3VSO9uuNfu3sG4UZWHNdqNBr3t8Aa+2ch3O2zSI6qfnFTiQCk1F7gbsAN/1Vr/Ji61EiIFFGV5mJPlYc7o7m7GrZ1Bqs0nYOw82MbOA21s3LmX3YfaWbnzEA3tgZh/z6Yg0+MkK81BlsdJlseJ1203dshRO+bItCeqzGW3YbMZWWybTWFXRhcr42XccuOwGQcjDWgNGuMeLa21cewxy7Sma0yPQCiMP2hkiAPBMJt2BNigt3WV+YNh2v1BWjtDtJmDf7b6gjS2+znU7u+1q1iWx0F5XjqVRZnMm1DKpEG5TCjPsfQVrmGFRnLg2hlDafEFeHfbQZZsreejuhb+tX7fYd+7UpBvjmdSlOWhKNNNlsdJhttOetfVCTteV+SqhTnvNm5z6jrJML9f0XcCoe77I33+MO2BIM0dQZo7AjT7AjR3BGhoD7CvycfeZuPkfG+TjxZfEPgAgCEFXk6ryGNieQ7zJpZSkOFObFA9eJx2rp5ewVVTB7FxbzOrqhtYvauR1dUNvPrhXgB+88GiroF6B+amUZzlITfdRU66k+w0JznpLrI8ju6rOub+ydN1FceYTtYeOOGwcZXN2OdpfIEQ7X7jClm7P0i7P0RLZ5Cmdj+N7cbAxo3tAQ60dlLb2MGexg7ao65Kepw2xg/M4StnVjCxPIfJg3Mpykq+nlNgDJp52aQyLptURiAUZs2uRpZureedrQd4bFk1ncHufbjLbqMkx0Oe10W+102+10VehoucNGdXgyHd5SDNZSPN6SDdZY8q776aZzePUZ+2hmmENsfOCka2u6Bxxdcf7L7yGzn2dvhDtJnbYWS6w9wm283yNn+IxnY/DW0BGtqNgbZ7O/bmpjsZXpTBZyaUMnJAJpXFxitPnrARk1KKPK+LPK+L8WVHT/r5g+Gu86A2f9Cc7j43ausM4guED2vER/Y5wXCYQFATCBtX/UNhDT1+HvX7fRQVHXnLZm8NaqdNGYkIu82cNhITTjNR4TTPIRx2W3cCw/w3Hqd5rhl9num04ek6FzX255HzzhOxeHEts+SxwEnphBMHSik78D/AeUANsEIp9bLWemO8KidEqslwOxhbms3Y0u5HnS1e3NB1K0xnMER9SyeN7YGQ2XY+AAAPVElEQVSoxkewqxHS7OtulDR1BNjbFDi8S1hX17ATu3crLrZsAYyTSJfDRrrL3tUtz+tyUJLtYXRJFvkZLnLTXeR7XeR6XZRkeyjPS7d0guBYZHqczB03gLnjBnSVHWztZNv+Vj6ub6Ou2cf+lk7qW3zUt3Syta6FFp9xoqFP4EqAXYH7rX/hsJknA3bVdS8eGIkKpUChzHfjREMB9JiPrBcPurdLIZ+gra0d75q3Yy6P1K23Cw/RJ0+qqyx6ufEeDmOetIe7Tt6NKzOaUDjcNR8wu29+EqXoGtS2It/L9KH5dBzaw/wZkxhXmt2nT0qIJ4fdxviyHMaX5fCVM42yumYfz76+lIJBldQ2dlDb0EFNYwfra5to7DD2UcezzdptCo/DONmMNB6VMpKbkYakzUx6RsYbiowzZDeTopiJTm0OTGUkQc0EaFTyU2MsJ2p5a3sH9vcWEghpAmby0/jej29b9brs5KQbjYnhhRnMHFFIaY6HgTlpDMpPp7I4M2mTJEfjtNuYOiSPqUPy+N75IwmHNXUtPnYcMJPhB9rY2+TjYFsnNQ3tVNU0cqjNf0y/k1jsCuwL/w+7ikp8R20f9qj5Y94zHeOKx/r3upLtUQOiRba/yCBpWms6/X4c77wecx3M+ZC57GQ4bIo0l5GQ8boc5KQ7u469uelOcr0uBmR5KMn2UJKTRkm2x1JPQ7Ail8OGy+Hqs0edGrdVT+qTvy1ExMn0OJgKbNNabwdQSj0FzAckcSDECXI77JTlplN2kgOZa627EgidwRD+YJhwGILhsPkkEKNxFAobJymR0WFDYd3VULSpSGPqyAalTSmcDuPKttNuZJiddhvL33+Xc2adjdOeuPvzrCg/w01+hpvTh+bHXCcc1nQEjKsTbf7Dr1JE5jsDoa6rVEGz++HHO6opLSszGkNmeSiqERXdcOrubdJ7T5PIv4lX8uB4N5F6OigszOh1WeREOzoh0V125Hr0sl6kTg6bDbt5n2nkSovdrnDaFPauKzKqq3ulO6rbZFaa8UScLI9xm1Gmx3FEI3Hx4gOcObz/xy6It+IsD+MKHMyaOqjX5eGwpsUXpLHDT3NHEF+we8TqyL2tvmDYGLU6YIxc3RkIE9KasLlvioxYHdlXGeVmgyt8+L4rpOlKcNmikl3QvT+LJMhs5k4ter92sN5P+cCirgRbZP/mtNtwOoyrcU67wm1eIU93dV81z3A7unpYfFoGOLbZFCXZaZRkp3HGsN7X0VobvTOi7muOXAmP7rkRud/ZODaFjXet2b5jJ2Xlg7rKjG7W4cPWi3S9Phb6GFvkx9xu15Hkq7GNRQZEsx02b5Tt27OHsrLSI9ZRvfybru3O3p3wPWzeZuvq+h29LUa2R5ddum4LIeJPHetO9Ih/qNTngbla62vN+auB07XWN/ZY7zrgOoDi4uLJTz311MnVOAFaW1vJyOj9ZNVKUiUOkFiSVarEkipxgMSSjFIlDpBYklWqxJIqcYDEkqxSJZZUiQMklkSbPXv2Kq31lN6W9fngiFrr+4H7AaZMmaJnzZrV1x8Zd0b3n1mJrsZJS5U4QGJJVqkSS6rEARJLMkqVOEBiSVapEkuqxAESS7JKlVhSJQ6QWJLZydxkVwuUR82XmWVCCCGEEEIIIYRIESeTOFgBjFBKDVFKuYArgZfjUy0hhBBCCCGEEEIkgxO+VUFrHVRK3Qj8G+NxjA9prTfErWZCCCGEEEIIIYRIuJMa40Br/RrwWpzqIoQQQgghhBBCiCRjvQcJCyGEEEIIIYQQot9I4kAIIYQQQgghhBAxSeJACCGEEEIIIYQQMUniQAghhBBCCCGEEDFJ4kAIIYQQQgghhBAxSeJACCGEEEIIIYQQMUniQAghhBBCCCGEEDFJ4kAIIYQQQgghhBAxKa11/32YUvVAdb99YPwUAAcSXYk4SJU4QGJJVqkSS6rEARJLMkqVOEBiSVapEkuqxAESS7JKlVhSJQ6QWBJtsNa6sLcF/Zo4sCql1Eqt9ZRE1+NkpUocILEkq1SJJVXiAIklGaVKHCCxJKtUiSVV4gCJJVmlSiypEgdILMlMblUQQgghhBBCCCFETJI4EEIIIYQQQgghREySODg29ye6AnGSKnGAxJKsUiWWVIkDJJZklCpxgMSSrFIlllSJAySWZJUqsaRKHCCxJC0Z40AIIYQQQgghhBAxSY8DIYQQQgghhBBCxCSJg6NQSs1VSm1RSm1TSv040fU5Hkqph5RS+5VS66PK8pRSbyiltprvuYms47FQSpUrpRYppTYqpTYopb5rllsxFo9S6gOl1DozltvM8iFKqeXmdva0UsqV6LoeK6WUXSm1Rin1ijlvyViUUjuVUh8qpdYqpVaaZVbcxnKUUs8ppTYrpTYppaZbNI6R5ncReTUrpW6yYiwASqmbzd/8eqXUk+a+wHK/FaXUd80YNiilbjLLLPOdHM9xURnuMb+fKqXUpMTV/HAx4viC+b2ElVJTeqz/EzOOLUqpC/q/xrHFiOV35j6sSin1olIqJ2qZ1WL5pRnHWqXU60qpUrPcUttX1LLvK6W0UqrAnE/aOCDmd/ILpVRt1PHloqhlltq+zPJvm7+XDUqp/44qt1Qs5nEw8p3sVEqtjVqWlLHEiGOiUmqZGcdKpdRUszypfyvHTGstr15egB34GBgKuIB1wJhE1+s46j8TmASsjyr7b+DH5vSPgd8mup7HEEcJMMmczgQ+AsZYNBYFZJjTTmA5MA14BrjSLL8PuD7RdT2OmL4HPAG8Ys5bMhZgJ1DQo8yK29gjwLXmtAvIsWIcPWKyA/uAwVaMBRgI7ADSzPlngC9b7bcCjAPWA+mAA1gIDLfSd3I8x0XgIuD/zP32NGB5ouv/CXGMBkYCi4EpUeVjMM5f3MAQjPMae6Jj+IRYzgcc5vRvo74TK8aSFTX9HeA+K25fZnk58G+gOnK8TOY4jvKd/AL4QS/rWnH7mm3ui93mfJFVY+mx/A/Az5I9lhjfyevAheb0RcDiqOmk/a0c60t6HMQ2Fdimtd6utfYDTwHzE1ynY6a1fgc41KN4PkbDAvP9s/1aqROgtd6rtV5tTrcAmzBOxK0Yi9Zat5qzTvOlgXOA58xyS8QCoJQqAy4G/mrOKywaSwyW2saUUtkYB7EHAbTWfq11IxaLoxdzgI+11tVYNxYHkKaUcmA0vPdivd/KaIwTnXatdRB4G7gMC30nx3lcnA/83dxvLwNylFIl/VPTo+stDq31Jq31ll5Wnw88pbXu1FrvALZhnN8khRixvG5uYwDLgDJz2oqxNEfNejGO+WCx7ct0J3AL3TFAEscBR42lN5bbvoDrgd9orTvNdfab5VaMBeg6l7wceNIsStpYYsShgSxzOhvYY04n9W/lWEniILaBwO6o+RqzzMqKtdZ7zel9QHEiK3O8lFIVwKkYV+otGYsyuvavBfYDb2BkThujTpKstJ3dhXESETbn87FuLBp4XSm1Sil1nVlmtW1sCFAP/E0Zt4/8VSnlxXpx9HQl3ScQlotFa10L/B7YhZEwaAJWYb3fynpghlIqXymVjnH1pBwLfic9xKp/qpwDWD2Or2JcpQOLxqKUukMptRv4IvAzs9hSsSil5gO1Wut1PRZZKo4oN5rdxR9S3bdXWTGWSoz98nKl1NtKqdPMcivGEjEDqNNabzXnrRbLTcDvzN/874GfmOVWi6NXkjj4lNJGvxnLPFJDKZUBPA/c1CODb6lYtNYhrfVEjCsoU4FRCa7SCVFKXQLs11qvSnRd4uQsrfUk4ELgBqXUzOiFFtnGHBhd5v6stT4VaMPoet3FInF0UcZ9//OAZ3sus0os5knpfIzETinGVce5Ca3UCdBab8LoNv468C9gLRDqsY4lvpNYrF7/VKOUuhUIAo8nui4nQ2t9q9a6HCOOGxNdn+NlJgp/SnfSw+r+DAwDJmIkc/+Q2OqcFAeQh9H1/YfAM+YVeyu7iu6LBVZ0PXCz+Zu/GbMXaKqQxEFstRhXUyLKzDIrq4t0izHf93/C+klBKeXESBo8rrV+wSy2ZCwRZhfyRcB0jO5KDnORVbazM4F5SqmdGLfxnAPcjTVjiVwVjnTzexEjqWO1bawGqNFaLzfnn8NIJFgtjmgXAqu11nXmvBVjORfYobWu11oHgBcwfj+W+61orR/UWk/WWs8EGjDGnLHidxItVv1T5RzAknEopb4MXAJ80UzogEVjifI48Dlz2kqxDMNIfK4zj/llwGql1ACsFQcAWus68yJOGHiA7m7vlosF47j/gtn9/QOMHqAFWDMWzGPiZcDTUcVWi2UBxnEejIseVt6+jiCJg9hWACOUMfK1C6O77MsJrtPJehljg8Z8fymBdTkmZub0QWCT1vqPUYusGEuhMkeHVkqlAedhjNmwCPi8uZolYtFa/0RrXaa1rsD4bbyltf4iFoxFKeVVSmVGpjEG5lqPxbYxrfU+YLdSaqRZNAfYiMXi6KHnlQcrxrILmKaUSjf3Z5HvxYq/lSLzfRDGyd0TWPM7iRar/i8D15gjYU8DmqJuabCSl4ErlVJupdQQYATwQYLrdFRKqbkYt8HN01q3Ry2yYiwjombnA5vNactsX1rrD7XWRVrrCvOYX4MxaPU+LBRHRI/7yi/FON6DBbcv4B8YAySilKrEGBT5ANaMBYxE+2atdU1UmdVi2QOcbU6fA0RuubDcb6VXOglGaEzWF8Y9nB9h3Id+a6Lrc5x1fxKjC1YAYyf/NYx70N/E2IgXAnmJrucxxHEWRtfRKoyusWvN78WKsYwH1pixrKd7xNihGDvBbRjZSXei63qccc2i+6kKlovFrPM687Uh8lu36DY2EVhpbmP/AHKtGIcZixc4CGRHlVk1ltswGgzrgUcxRoe24m9lCUbSYx0wx2rfyfEcFzFGvv4f8/j/IVFPKkj0K0Ycl5rTnUAd8O+o9W8149iCOdp3srxixLIN417gyDH/PgvH8rz5u68C/gkMtOL21WP5TrqfqpC0cRzlO3nUrGsVRmOuxMLblwt4zNzGVgPnWDUWs/xh4Ju9rJ+UscT4Ts7CGMdoHcaYbJPNdZP6t3KsL2UGI4QQQgghhBBCCHEEuVVBCCGEEEIIIYQQMUniQAghhBBCCCGEEDFJ4kAIIYQQQgghhBAxSeJACCGEEEIIIYQQMUniQAghhBBCCCGEEDFJ4kAIIYQQQgghhBAxSeJACCGEEEIIIYQQMUniQAghhBBCCCGEEDH9f62uKcrkOjnHAAAAAElFTkSuQmCC\n", 342 | "text/plain": [ 343 | "
" 344 | ] 345 | }, 346 | "metadata": { 347 | "needs_background": "light" 348 | }, 349 | "output_type": "display_data" 350 | } 351 | ], 352 | "source": [ 353 | "fig, ax = plt.subplots(figsize=(18,6))\n", 354 | "plt.plot(np.abs(theta_vals), P_MU_vals)\n", 355 | "plt.xticks(np.arange(0, 181, 10))\n", 356 | "plt.grid()" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 24, 362 | "metadata": {}, 363 | "outputs": [ 364 | { 365 | "name": "stdout", 366 | "output_type": "stream", 367 | "text": [ 368 | "[ 20 50 86 111 146]\n" 369 | ] 370 | } 371 | ], 372 | "source": [ 373 | "peak_indices = scipy.signal.find_peaks_cwt(vector=P_MU_vals, widths = 5*np.ones(len(P_MU_vals)), gap_thresh=None, min_length=None, min_snr=3, noise_perc=1)\n", 374 | "print(theta_vals[peak_indices])" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "metadata": {}, 381 | "outputs": [], 382 | "source": [] 383 | } 384 | ], 385 | "metadata": { 386 | "kernelspec": { 387 | "display_name": "Python 3", 388 | "language": "python", 389 | "name": "python3" 390 | }, 391 | "language_info": { 392 | "codemirror_mode": { 393 | "name": "ipython", 394 | "version": 3 395 | }, 396 | "file_extension": ".py", 397 | "mimetype": "text/x-python", 398 | "name": "python", 399 | "nbconvert_exporter": "python", 400 | "pygments_lexer": "ipython3", 401 | "version": "3.6.5" 402 | } 403 | }, 404 | "nbformat": 4, 405 | "nbformat_minor": 2 406 | } 407 | -------------------------------------------------------------------------------- /MUSIC_implement1/implement-2.m: -------------------------------------------------------------------------------- 1 | % 阵元间隔为半波长的均匀分布16元线阵,预成指向arcsin((2i-1)/16)(i=1,2,…,16)的16个均匀加权常规波束,远场有2个互 2 | % 不相关的目标源发射信号,每个源相对于基阵的方位为-45和-40,且每个信号源到达基阵的信噪比相同,快拍数为1000. 3 | % 进行波束域方法和阵元域方法的比较 4 | clc 5 | clear all 6 | close all 7 | 8 | C=340; %声速 9 | num=16; %阵元数 10 | k=1000; %快拍数 11 | Bearings=[-45 -40]; %入射信号方位角 12 | d=length(Bearings); %声源数 13 | D=0.1; %阵元间距 14 | fc=1000; %信号频率 15 | fs=2*fc; %采样频率 16 | snr=10; %信噪比 17 | e_position=[0:num-1]'; 18 | s_position=[0:d-1]'; 19 | 20 | %波束形成 21 | as=-13/16:1/8:-9/16; %波束指向角,正弦值[-0.8125, -0.6875, -0.5625] 22 | as=asin(as); %弧度数[-0.9484, -0.7580, -0.5974] 23 | vs=exp(j*2*pi*fc*e_position*D*sin(as)/C); %16*3 complex double 24 | w=1/num*vs'; %波束形成矩阵 25 | 26 | %入射信号 27 | aa=90*[-1:0.002:1]; %从-90到90采样1001个点 28 | s=exp(j*pi*[1:d].'*sin(aa/180*pi)); 29 | %s=exp(j*pi*sin(aa/180*pi)); 30 | %s=repmat(s,[d 1]); %声源信号 31 | 32 | %方向向量 33 | Bearings=Bearings*pi/180; 34 | TimeDelay=D*[0:num-1].'*sin(Bearings)/C; %线阵的延迟,(16,2) 35 | A=exp(sqrt(-1)*2*pi*fc*TimeDelay); %方向向量,(16,2)复数矩阵 36 | 37 | %基阵接收信号 38 | X=A*s; %阵列输出信号 39 | X=awgn(X,snr); %加噪声,(16,1001)复数矩阵 40 | 41 | %% 阵元域MUSIC算法 42 | Rxx=(X*X')/k; %协方差矩阵,(16,16)复数矩阵 43 | [EigenVectors,EigenValues]=eig(Rxx); 44 | Lemda=diag(EigenValues); %上面计算出的特征值为16*16的对角阵,该步提取对角阵上的值 45 | [SortedLemda,Index]=sort(Lemda); %将特征值降序排列 46 | Index=flipud(Index); %(16,1),上下翻转,也就是把Index倒序排列 47 | NoiseSubspace_Z(1:num,1:num-d)=EigenVectors(1:num,Index(d+1:num));%把噪声空间分成两部分,分别加入信号空间的一部分和噪声,(16,14) 48 | 49 | %%在-150到150之间进行搜索 50 | for i=-900:900 51 | az=exp(sqrt(-1)*2*pi*fc*D*[0:num-1]'*sin(i/10*pi/180)/C); %(16,1)复数矩阵 52 | MUSIC_Spec_Z(i+901)=az'*az/(az'*NoiseSubspace_Z*NoiseSubspace_Z'*az); %最后(1,1801)复数矩阵 53 | end 54 | 55 | %波束输出 56 | Y=w*X; %方位估计,(3,1001)复数矩阵 57 | 58 | %% 波束域MUSIC算法 59 | B=3; %波束数 60 | Ryy=(Y*Y')/k; %协方差矩阵 61 | [EigenVectors,EigenValues]=eig(Ryy); 62 | Lemda=diag(EigenValues); %计算矩阵特征值 63 | [SortedLemda,Index]=sort(Lemda); 64 | Index=flipud(Index); %将特征值降序排列 65 | NoiseSubspace(1:B,1:B-d)=EigenVectors(1:B,Index(d+1:B)); %把噪声空间分成两部分,分别为加入信号空间的一部分和噪声,(3,1) 66 | 67 | % 在20到50之间进行搜索 68 | for i=-600:1:-250 69 | a=exp(sqrt(-1)*2*pi*fc*D*[0:num-1]'*sin((i/10)*pi/180)/C); 70 | MUSIC_Spec_B(i+601)=a'*w'*w*a/(a'*w'*NoiseSubspace*(w'*NoiseSubspace)'*a); %(1,351)复数矩阵 71 | end 72 | 73 | %% 画波束 74 | figure(1); 75 | MUSIC_B=abs(MUSIC_Spec_B)/max(abs(MUSIC_Spec_B)); 76 | Delta1=[-60:0.1:-25]; 77 | plot(Delta1,10*log10(MUSIC_B),'r'); %NonNormative Spatial Spectrum 78 | xlabel('角度(度)'),ylabel('空间方位谱(dB)'); 79 | hold on 80 | Delta=[-90:0.1:90]; 81 | MUSIC_Z=abs(MUSIC_Spec_Z)/max(abs(MUSIC_Spec_Z)); 82 | plot(Delta,10*log10(MUSIC_Z)); 83 | 84 | figure(2); 85 | ss=exp(j*pi*e_position*sin(aa/180*pi)); %% 入射信号 86 | plot(aa,20*log10(abs(w*ss))),xlabel('角度(度)'),ylabel('波束(dB)'); 87 | axis([-90 90 -40 0]); -------------------------------------------------------------------------------- /MUSIC_implement1/music-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC_implement1/music-1.png -------------------------------------------------------------------------------- /MUSIC_implement1/music-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC_implement1/music-2.png -------------------------------------------------------------------------------- /MUSIC_implement2/doa_music.m: -------------------------------------------------------------------------------- 1 | % DOA MUSIC 2 | % 初始化参数 initial parameter 3 | clc 4 | close all 5 | clear all 6 | 7 | source=2; % 信源 signal number 期望信号 8 | sensor=7; % array number 9 | theta=[20 60]; % DOA of signal 10 | ss=1024; % snapshot 快拍数 11 | snr=[40 60]; % SNR 信噪比 12 | j=sqrt(-1); 13 | 14 | % 信号复包络 SIGNAL 15 | w=[pi/6 pi/5]'; 16 | for m=1:source 17 | S(m,:)=10.^(snr(m)/10)*exp(-j*w(m)*[0:ss-1]); %S:signal matrix, (2,1024) 18 | end 19 | 20 | % 阵列流形 STEERING VECTOR 21 | A=exp(-j*(0:sensor-1)'*pi*sin(theta/180*pi)); %A:(7,2) 22 | 23 | % 噪声 NOISE 24 | N=randn(sensor,ss)+j*randn(sensor,ss); %N:(7,1024) 25 | 26 | % 观测信号 SIGNAL RECEIVED 27 | Y=A*S+N; %(7,1024) 28 | 29 | % 阵列协方差矩阵 COVIARIANCE MATRIX 30 | R=Y*Y'/ss; %R:(7,7) complex matrix 31 | 32 | % 特征分解 eigen-decomposition 33 | [E,X,V]=svd(R); %E=V,7*7, E是特征向量; X是特征值,7*7,特征值在对角线上,其余为0 34 | %在不知道源的个数的情况下需要估计出p的值,方法是假设为某个值,出来的能量谱和真实的能量谱可能很接近 35 | p=length(theta); %已知源个数 36 | En=E(:,p+1:sensor); %X特征值由大到小降序排列,所以大特征值在前,即信号特征值在前,(7,5) 37 | 38 | % [V,D]=eig(R); %[特征向量,特征值] 39 | % D=diag(D); 40 | % Es=V(:,sensor-p+1:sensor); 41 | % En=V(:,1:sensor-p); %噪声特征向量,特征值由小到大,升序排列,所以大特征值在后,即信号特征值在后 42 | 43 | search_doa=[0:1:90]; 44 | for i=1:length(search_doa) 45 | a_theta=exp(-j*pi*(0:sensor-1)'*sin(search_doa(i)*pi/180)); %a_theta:(7,1), d = half-wavelength 46 | Q=(a_theta)'*En*En'*a_theta; % a complex 47 | Pmusic(i)=1./Q; 48 | end 49 | 50 | P_music=10*log10(Pmusic); 51 | plot(search_doa, P_music, 'linewidth', 2); 52 | title('MUSIC beamforming'); 53 | xlabel('angle/degree'); 54 | ylabel('magnitude/dB'); 55 | grid; 56 | % Pmusic(i)=1./abs(Q);%abs(1./((a_theta)'*En*En'*a_theta)); 57 | -------------------------------------------------------------------------------- /MUSIC_implement2/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC_implement2/result.png -------------------------------------------------------------------------------- /MUSIC算法讲解/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/1.jpeg -------------------------------------------------------------------------------- /MUSIC算法讲解/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/2.png -------------------------------------------------------------------------------- /MUSIC算法讲解/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/3.png -------------------------------------------------------------------------------- /MUSIC算法讲解/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/4.png -------------------------------------------------------------------------------- /MUSIC算法讲解/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/5.png -------------------------------------------------------------------------------- /MUSIC算法讲解/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/6.png -------------------------------------------------------------------------------- /MUSIC算法讲解/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/MUSIC算法讲解/7.png -------------------------------------------------------------------------------- /MUSIC算法讲解/README.md: -------------------------------------------------------------------------------- 1 | ## MUSIC算法讲解 2 | 3 | ### 1. 算法假设 4 | 为了分析推导的方便,现将波达方向估计问题中的数学模型作理想状态的假设如下: 5 | * 各待测信号源具有相同的极化、且互不相关的。一般考虑信号源为窄带的,且各信号源具有相同的中心频率。待测信号源的个数为D 6 | * 天线阵列是由M(M>D)个阵元组成的等间距直线阵,各阵元特性相同,各向同性,阵元间隔为d,并且阵元间隔不大于最高频率信号半波长 7 | * 天线阵列处于各信号源的远场中,即天线阵列接收从各信号源传来的信号为平面波 8 | * 各阵元上有互不相关,与各待测信号也不相关,方差为的零均值高斯白噪声 9 | * 各接收支路具有完全相同的特点 10 | 11 | ### 2. 阵列形状 12 | ![](1.jpeg) 13 | 14 | ### 3. 算法推导 15 | ![](2.png) 16 | 17 | ![](3.png) 18 | 19 | ![](4.png) 20 | 21 | ![](5.png) 22 | 23 | ![](6.png) 24 | 25 | ![](7.png) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sound Source Localization 2 | Classical algorithms of sound source localization with beamforming, TDOA and high-resolution spectral estimation. 3 | 4 | ## Usage 5 | ```bash 6 | matlab -nodesktop -nosplash –r matlabfile (name of .m) 7 | ``` 8 | 9 | ## Algorithm Interpretation 10 | * Beamforming: a spatial filtering method, is a signal processing technique used in sensor arrays for directional signal transmission or reception. 11 | * MUSIC: Multiple Signal Classification 12 | * ESPRIT: Estimation of Signal Parameters via Rotational Invariance Technique 13 | * MVDR: Minimum Variance Distortionless Response 14 | * GCC-PHAT: Generalized Cross Correlation - Phase Transform (TDOA estimation) 15 | * SRP-PHAT: Steered Response Power - Phase Transform 16 | 17 | ## Reference Paper 18 | * Paper 1 19 | * **Title**: *Comparison of Direction of Arrival (DOA) Estimation Techniques for Closely Spaced Targets* 20 | * **Authors**: Nauman Anwar Baig and Mohammad Bilal Malik 21 | * **Published**: International journal of future computer and communication 2, no. 6 (2013): 654 22 | * Paper 2 23 | * **Title**: [*Outdoor sound localization using a tetrahedral array*](https://projekter.aau.dk/projekter/files/281708108/Master_Thesis_Final.pdf) 24 | 25 | ## Results 26 | ### 1. Algorithm Summary 27 | #### 1.1 Classical Beamforming 28 | ![](MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/beamforming.png) 29 | 30 | #### 1.2 Min-Norm 31 | ![](MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/Min-Norm.png) 32 | 33 | #### 1.3 MUSIC 34 | ![](MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/MUSIC.png) 35 | 36 | #### 1.4 MVDR 37 | ![](MUSIC+ESPRIT+MVDR+MinNorm+Beamforming/MVDR.png) 38 | 39 | 40 | ### 2. Beamforming 41 | #### 2.1 microphone array 42 | ![](Beamforming/array.png) 43 | 44 | #### 2.2 Two-dimensional map of localization result 45 | ![](Beamforming/2d.png) 46 | 47 | #### 2.3 Three-dimensional map of localization result 48 | ![](Beamforming/3d.png) 49 | 50 | 51 | ### 3. MUSIC 52 | #### 3.1 matlab_implement2 (**BEST**) 53 | ![](MUSIC_implement2/result.png) 54 | 55 | #### 3.3 matlab_implement1 56 | ![](MUSIC_implement1/music-1.png) 57 | ![](MUSIC_implement1/music-2.png) 58 | 59 | 60 | ## Reference Book 61 | * [Microphone Array Beamforming](http://www.labbookpages.co.uk/audio/beamforming.html) 62 | 63 | ## Reference Website 64 | * [Matlab files for various types of beamforming](https://github.com/jorgengrythe/beamforming) 65 | * [空间谱专题02:波束形成(Beamforming)](https://www.cnblogs.com/xingshansi/p/7410846.html) 66 | * [空间谱专题10:MUSIC算法](https://www.cnblogs.com/xingshansi/p/7553746.html) 67 | * [MUSIC算法分析和实现](https://blog.csdn.net/zhuguorong11/article/details/70209070) 68 | * [MUSIC算法](https://blog.csdn.net/Wilder_ting/article/details/79122885) 69 | * [子空间分析方法](http://www.cnblogs.com/xingshansi/p/7554200.html) 70 | * [Lijiawei16/beamforming](https://github.com/Lijiawei16/beamforming) 71 | * [msamsami/doa-estimation-music](https://github.com/msamsami/doa-estimation-music) 72 | * [rkakshay/ssl](https://github.com/rkakshay/ssl) 73 | * [阵列信号基础之1:MUSIC算法](https://blog.csdn.net/qq_23947237/article/details/82318222) 74 | * [BSS Locate - A toolbox for source localization in stereo convolutive audio mixtures](http://bass-db.gforge.inria.fr/bss_locate/) 75 | -------------------------------------------------------------------------------- /SRP-PHAT/README.md: -------------------------------------------------------------------------------- 1 | # SRP-PHAT 2 | Steered Response Power - Phase Transform (SRP-PHAT) algorithm for DOA estimation. 3 | 4 | 5 | ## References 6 | * [基于麦克风阵列的声源定位算法之GCC-PHAT](https://www.cnblogs.com/ytxwzqin/p/9004603.html) 7 | * [TDOA - SRP-PHAT方法](http://www.funcwj.cn/2018/05/29/srp-phat-for-tdoa-estimate/) 8 | * [wangwei2009/DOA](https://github.com/wangwei2009/DOA) 9 | -------------------------------------------------------------------------------- /root-MUSIC/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/root-MUSIC/1.png -------------------------------------------------------------------------------- /root-MUSIC/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/root-MUSIC/2.png -------------------------------------------------------------------------------- /root-MUSIC/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/root-MUSIC/3.png -------------------------------------------------------------------------------- /root-MUSIC/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/root-MUSIC/4.png -------------------------------------------------------------------------------- /root-MUSIC/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aishoot/Sound_Localization_Algorithms/a2b02a9aecd13dbf617a6429fbdee46a871c20c5/root-MUSIC/5.png -------------------------------------------------------------------------------- /root-MUSIC/rootMUSIC.m: -------------------------------------------------------------------------------- 1 | %ROOT_MUSIC ALOGRITHM 2 | %DOA ESTIMATION BY ROOT_MUSIC 3 | clc; 4 | clear all; 5 | close all; 6 | 7 | K=2; %信源数 8 | M=8; %阵元数 9 | L=200; %信号长度 10 | w=[pi/4 pi/6].'; %信号频率 11 | lamda=((2*pi*3e8)/w(1)+(2*pi*3e8)/w(2))/2; %信号波长 12 | d_lamda=0.5; %阵元间距 13 | snr=20; %信噪比 14 | theta1=[45,60]; %信号入射角 15 | 16 | for k=1:K 17 | A(:,k)=exp(-1j*[0:M-1]'*d_lamda*2*pi*sin(theta1(k)*pi/180)); %阵列流型 18 | end 19 | 20 | for kk=1:L 21 | s(:,kk)=sqrt(10.^((snr/2)/10))*exp(1j*w*(kk-1)); %仿真信号 22 | end 23 | x=A*s+(1/sqrt(2))*(randn(M,L)+1j*randn(M,L)); %加入高斯白噪声 24 | R=(x*x')/L; %协方差矩阵 25 | 26 | %%%%%%第一种方法%%%%%%%%%% 27 | [V,D]=eig(R); %对协方差矩阵进行特征分解 28 | Un=V(:,1:M-K); %取噪声子空间 29 | Gn=Un*Un'; a = zeros(2*M-1,1)'; %找出多项式的系数,并按阶数从高至低排列 30 | for i=-(M-1):(M-1) 31 | a(i+M) = sum( diag(Gn,i) ); 32 | end 33 | a1=roots(a); %使用ROOTS函数求出多项式的根 34 | a2=a1(abs(a1)<1); %找出在单位圆里且最接近单位圆的N个根 35 | [lamda,I]=sort(abs(abs(a2)-1)); %挑选出最接近单位圆的N个根 36 | f=a2(I(1:K)); %计算信号到达方向角 37 | source_doa=[asin(angle(f(1))/pi)*180/pi asin(angle(f(2))/pi)*180/pi]; 38 | source_doa=sort(source_doa); 39 | disp('source_doa'); 40 | disp(source_doa); 41 | 42 | %%%%%%%第二种方法%%%%%%%%% 43 | % [V,D]=eig(R); 44 | % Un=V(:,1:M-K); %(4.5.7) 45 | % Un1=Un(1:M,:); 46 | % Un2=Un(K+1:M,:); 47 | % T=[1 0 0 0 0 0]'; %(阵元数-信源数)*1 48 | % c=Un1*inv(Un2)*T; % (K*(M-K))*((M-K)*(M-K))*((M-K)*1)=K*1 49 | % c=[1,c(2,1),c(1,1)]; %(4.5.8) 50 | % f=roots(c); 51 | % source_doa=[asin(angle(f(1))/pi)*180/pi asin(angle(f(2))/pi)*180/pi]; 52 | % source_doa=sort(source_doa); 53 | % disp('source_doa'); 54 | % disp(source_doa); 55 | --------------------------------------------------------------------------------