traitement du signal: l’ autocorrélation

Traitement de signal : L’autocorrélation en C sharp (c#)
L’autocorrélation est un outil mathématique souvent utilisé en traitement du signal. C’est la corrélation croisée d’un signal par lui-même. L’autocorrélation permet de détecter des régularités, des profils répétés dans un signal comme un signal périodique perturbé par beaucoup de bruit, ou bien une fréquence fondamentale d’un signal qui ne contient pas effectivement cette fondamentale, mais l’implique avec plusieurs de ses harmoniques.(Wikipédia).
Nous avons écrit deux routines en application de l’autocorrélation la première en C sharp la deuxième en assembleur que nous avons le plaisir de mettre à la disposition de nos lecteurs férus de technologie et d’informatique.
La routine en C sharp :
La routine reçoit en entrée deux vecteurs de type « float » nommés « buf » et « dst ».
Le premier vecteur contient les données du signal à analyser, le deuxième vecteur est un vecteur de sortie il sauvegarde les données traitées.
La routine comporte deux boucles imbriquées la première itération concerne la variable locale « o », la deuxième itération concerne la variable locale « i »;
A chaque itération de la première boucle le tableau ou le vecteur « dst » indexé par la variable « i » est rempli par la valeur « sum ». Cette dernière initialisée au début de la boucle à zéro totalise à la fin de la deuxième boucle le produit de chaque valeur du vecteur d’entrée « buf » indexée par la variable « i » par une autre donnée du même vecteur « buf » indexée par la valeur « (i+o) ».

 

public void AutoCorr(float []buf, float []dst)
{

int o;

for (o = 0; o < buf.Length; o++)
{
float sum;
int i;

sum = 0;

for (i = 0; i < buf.Length – o; i++)
{
sum += buf[i] * buf[i + o];

}

dst[o] += (float)sum;
}
}


Remarquez que nous prenons les précautions nécessaires pour éviter l’erreur « index out of range » ainsi « i » ne va pas au-delà de la longueur du vecteur « buf » moins la valeur « o ».
A la fin de la première boucle le vecteur « dst » est rempli de différentes valeurs. La première moitié du vecteur « dst » réfléchit les valeurs sauvegardées dans la deuxième moitié du même vecteur.
La première valeur dst[0] totalise le produit du vecteur par            lui-même soit dst[0]dst[0] + dst[1]dst[1]+ dst[2]dst[2]+ dst[3]dst[3]+ dst[4]dst[4]…la valeur de « o » étant égale à zéro lors de la première itération.
En divisant la valeur de dst[0] par la longueur du vecteur « buf » ou encore par le nombre des valeurs contenues dans le vecteur « buf » soit « buf.length » puis en cherchant la racine carrée du résultat l’on peut donc trouver la moyenne géométrique du vecteur « buf ».
le log10 de dst[0] multiplié par 10 nous indiquera utilement l’amplitude du signal en décibels. Ou encore le log10 de la racine carrée de la valeur dst[0] multiplié par 20 fera la même l’affaire.
L’index de la valeur maximale du vecteur « dst » nous indiquera le   « lag » ou le « décalage gagnant » et nous permettra par conséquent de mesurer la fréquence du signal audio.
Le calcul de la fréquence est en fait un peu plus compliqué puisque théoriquement il faut traquer les « harmoniques » qui nous indiqueraient la fondamentale. Nous reviendrons sur ce point avec plus de détails.
Si par exemple le signal capté est échantillonné à 11025 par seconde et que le maximum du vecteur de sortie « dst » est localisé à l’index mémoire 25 soit dst[25] l’on peut calculer la fréquence elle est égale à 440 hz soit 11025/25 = 441 hz.
En consultant les différentes fréquences des notes musicales l’on peut identifier aisément la note musicale captée par le signal qui s’avère être le « la » diapason 440hz.
La routine en assembleur :
Les deux boucles imbriquées de la routine auto corrélation peuvent ralentir le programme surtout si le vecteur d’entrée est volumineux suivant le taux d’échantillonnage et la durée du signal.
C’est ainsi que nous avons pensé écrire la même routine en assembleur.
Pour les initiés je soumets le code source de la routine :
La routine reçoit en entrée le vecteur « buf » et le vecteur « dst » en sortie tous les deux sont de type float (4 bytes).
Nous indiquons aussi à la routine en entrée la longueur du vecteur « buf » à l’aide du paramètre « l » que nous nous empressons de sauvegarder dans le registre «ecx ».
Les deux registres « esi » et « edi » indexent respectivement les deux vecteurs « buf » et « dst ».
On commence par sauvegarder les registres par l’instruction « pusha » que nous prenons la précaution de les restituer à la fin de la routine par l’instruction « popa ».
Nous utilisons le coprocesseur mathématique pour effectuer nos calculs en virgule flottante :
Les instructions « fldz », « fstp », « fadd » « fmul » sont des instructions du coprocesseur mathématiques.
VecAutoCorr proc STDCALL buf:DWORD, dst:DWORD, l:DWORD
local z:DWORD
pusha
mov esi,[buf]
mov ebx,0;
mov edi,[dst]
l1:
mov ecx,l
sub ecx,ebx
fldz
fstp DWORD PTR [edi+ebx
4]; lags[j]=0
mov edx,ebx; n=j
l2:
fld DWORD PTR [esi+edx4]; input [n]
push edx
sub edx,ebx
fmul DWORD PTR [esi+edx
4]; * input[n-j]
pop edx ; restore j
fadd DWORD PTR [edi+ebx4] ;ajouter au lag n
fstp DWORD PTR [edi+ebx
4] ; sauvegarder le resultat
inc edx
loop l2
fstp z
inc ebx
mov ecx,l
sub ecx,ebx
cmp ebx,ecx
jb l1
l3:
popa
ret
VecAutoCorr Endp

(55)