写算法的实现,离不开各种矩阵以及线性代数的运算,包括矩阵求逆,矩阵分解,SVD以及特征值,特征向量等;
尤其是复高斯模型或者在频域里做处理时用到复数矩阵相关运算;APP公司官网的加速链接中就有BLAS相关的API。
这里gsl是开源的计算库,该库的最新文档链接如下:https://www.gnu.org/software/gsl/doc/html/index.html。
music方法使用特征空间法估计信号(自关矩阵)的成分。自相关矩阵的特征值对应的不同特征向量是正交的(现在的深度学习方法也是在找若干组正交基的过程)。特征值最大的对应于该信号在该特征向量上的投影值比较大,也就是包含的信息量要比其它特征向量空间中的大,这在数据压缩领域有应用。
使用gsl实现MUSIC算法的实现代码片段如下:
```
#include <stdio.h>
#include <math.h>
#include "m_pd.h"
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_blas.h>
#define NOOFITERATIONS 1000
/* music~ : music-code for MUSIC Algorithm
commands for compiling with gcc:
> gcc -Wall -c music~.c
> ld -export-dynamic -shared -o music~.pd_linux music.o -lc -lm -lgsl -lgslcblas
*/
/* ------------------------ music~ ----------------------------- */
static t_class *music_tilde_class;
typedef struct _music_tilde
{
t_object x_obj;
t_outlet *x_outlet_value;
t_outlet *x_outlet_index;
gsl_matrix_complex *x_cmin; //input signals
gsl_matrix_complex *x_cov; //covariance matrix
gsl_vector *x_eval; //eigenvalues
gsl_matrix_complex *x_evec; //eigenvectors
gsl_matrix_complex *x_a; //array response
gsl_matrix_complex *x_c; //
gsl_matrix_complex *x_c2;
gsl_eigen_hermv_workspace *x_w; //calculating eigenvalues and -vectors
int x_count; //sample counter
t_float x_msi; //dummy
} t_music_tilde;
//Function for calculating the array response, which is stored in x->x_a matrix
static void array_response(t_music_tilde *x, double angle){
double f = 1000;
double lambda = 343/f;
double d = 0.04;
int i, no = 8;
for (i=0; i<no; i++) {
if (angle < 0){
gsl_matrix_complex_set(x->x_a, (no-i-1), 0, gsl_complex_polar(1.0, (-(double)i*2*M_PI*d*sin(angle)/lambda)));
}
else {
gsl_matrix_complex_set(x->x_a, i, 0, gsl_complex_polar(1.0, (-(double)i*2*M_PI*d*sin(angle)/lambda)));
}
}
}
static t_int *music_tilde_perform(t_int *w)
{
t_music_tilde *x = (t_music_tilde *) w[1];
t_float *inarray[] = {(t_float *)w[2], (t_float *)w[3], (t_float *)w[4], (t_float *)w[5], (t_float *)w[6], (t_float *)w[7], (t_float *)w[8], (t_float *)w[9], (t_float *)w[10], (t_float *)w[11], (t_float *)w[12], (t_float *)w[13], (t_float *)w[14], (t_float *)w[15], (t_float *)w[16], (t_float *)w[17]};
int n = (int)(w[18]);
int i, j, p, nosources;
int m=0; //actual sample from 0 to n-1
double f1, f2, theta_rad;
gsl_matrix_complex_view nvec;
gsl_vector *mdl = gsl_vector_alloc(8);
const gsl_complex alpha = gsl_complex_rect((1/NOOFITERATIONS), (1/NOOFITERATIONS));
const gsl_complex beta = gsl_complex_rect(0, 0);
while (n--) {
if (x->x_count == NOOFITERATIONS) { // calculate eigenvalues and reset after a certain no of iterations
//normalize the covariance matrix
gsl_matrix_complex_scale(x->x_cov, alpha);
//gsl command for calculating eigenvalues
gsl_eigen_hermv(x->x_cov, x->x_eval, x->x_evec, x->x_w);
//print out the result
// post("cov[1][1]:%f cov[1][2]:%f cov[2][1]:%f cov[2][2]:%f eval[1]:%f eval[2]:%f", gsl_matrix_get(x->x_cov, 0, 0), gsl_matrix_get(x->x_cov, 0, 1), gsl_matrix_get(x->x_cov, 1, 0), gsl_matrix_get(x->x_cov, 1, 1), gsl_vector_get(x->x_eval, 0), gsl_vector_get(x->x_eval, 1));
//MDL Algorithm for estimating no of sources
gsl_vector_reverse(x->x_eval);
p = (int)x->x_eval->size;
for (i = 0; i < p; i++){
f1=1.0;
f2=0.0;
for (j = i; j < p; j++){
f1*=pow(gsl_vector_get(x->x_eval, j), (1/(double)(p-i)));
f2+=gsl_vector_get(x->x_eval, j);
}
f2=f2/(p-i);
gsl_vector_set(mdl, i, (-log(pow((f1/f2), ((p-i)*NOOFITERATIONS)))+0.5*i*(2*p-i)*NOOFITERATIONS));
}
nosources = gsl_vector_min_index(mdl);
//Building nvec matrix
nvec = gsl_matrix_complex_submatrix(x->x_evec, 0, 0, 8, (p-nosources));
x->x_c = gsl_matrix_complex_alloc(1, (p-nosources));
//estimating the pmu function
j=0;
for (i=-90; i<=90; i++) {
theta_rad=i*M_PI/180;
array_response(x, theta_rad);
gsl_blas_zgemm(CblasConjTrans, CblasNoTrans, alpha, x->x_a, &nvec.matrix, beta, x->x_c);
gsl_blas_zgemm(CblasNoTrans, CblasConjTrans, alpha, x->x_c, x->x_c, beta, x->x_c2);
outlet_float(x->x_outlet_value, GSL_REAL(gsl_matrix_complex_get(x->x_c2, 1, 1)));
outlet_float(x->x_outlet_index, j);
j++;
}
//reset the temporary variables
gsl_matrix_complex_set_zero(x->x_cov);
x->x_count = 0;
}
// aranging 16 inputs to a 8x1 complex matrix
for (i=0; i<8; i++) {
gsl_matrix_complex_set(x->x_cmin, i, 0, gsl_complex_rect((double)inarray[i*2][m], (double)inarray[i*2+1][m]));
}
// estimating the covariance matrix from 8 complex input signals
gsl_blas_zgemm(CblasNoTrans, CblasConjTrans, alpha, x->x_cmin, x->x_cmin, alpha, x->x_cov);
// increment counters
m++;
x->x_count++;
}
return (w+19);
}
static void music_tilde_dsp(t_music_tilde *x, t_signal **sp)
{
dsp_add(music_tilde_perform, 18, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec, sp[8]->s_vec, sp[9]->s_vec, sp[10]->s_vec, sp[11]->s_vec, sp[12]->s_vec, sp[13]->s_vec, sp[14]->s_vec, sp[15]->s_vec, sp[0]->s_n);
}
static void *music_tilde_new()
{
t_music_tilde *x = (t_music_tilde *)pd_new(music_tilde_class);
int k;
for (k=2; k<=16; k++) {
inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
}
x->x_outlet_value = outlet_new(&x->x_obj, &s_float);
x->x_outlet_index = outlet_new(&x->x_obj, &s_float);
//allocating memory
x->x_cmin = gsl_matrix_complex_alloc(8, 1);
x->x_cov = gsl_matrix_complex_calloc(8, 8);
x->x_eval = gsl_vector_alloc(8);
x->x_evec = gsl_matrix_complex_alloc(8, 8);
x->x_a = gsl_matrix_complex_alloc(8, 1);
x->x_c2 = gsl_matrix_complex_alloc(1, 1);
x->x_w = gsl_eigen_hermv_alloc(8);
x->x_count = 0;
return (x);
}
static void music_tilde_free(t_music_tilde *x) //destructor for deallocating memory
{
gsl_matrix_complex_free(x->x_cmin);
gsl_matrix_complex_free(x->x_cov);
gsl_vector_free(x->x_eval);
gsl_matrix_complex_free(x->x_evec);
gsl_matrix_complex_free(x->x_a);
gsl_matrix_complex_free(x->x_c);
gsl_matrix_complex_free(x->x_c2);
gsl_eigen_hermv_free(x->x_w);
}
void music_tilde_setup(void)
{
music_tilde_class = class_new(gensym("music~"), (t_newmethod)music_tilde_new, (t_method)music_tilde_free,
sizeof(t_music_tilde), 0, A_DEFFLOAT, 0);
CLASS_MAINSIGNALIN(music_tilde_class, t_music_tilde, x_msi);
class_addmethod(music_tilde_class, nullfn, gensym("signal"), 0);
class_addmethod(music_tilde_class, (t_method)music_tilde_dsp, gensym("dsp"), 0);
//class_sethelpsymbol(music_tilde_class, gensym("music/music~"));
}
```