Скользящая средняя на языке Си
Я пытаюсь сделать фильтр скользящего среднего на языке C, я адаптировал программу на matlab, которая работает правильно, вход моего фильтра - архив.pcm (звуковой сигнал развертки), проблема для меня - выходной архив скользящее среднее на языке C, выходной сигнал идет не так, сигнал только уменьшается с течением времени (не фильтруйте).
Ниже моего кода C:
#include <stdio.h>
#include <stdlib.h>
#define m 16 // MEDIA LENGTH
int main()
{
short *x; // X VECTOR = 0
double coef[m]; // COEF VECTOR = 0
int i; // FOR COUNT VARIABLE
int n; // FOR COUNT VARIABLE
int itera; // LENGTH OF THE INPUT FILE
double aux; // AUX VARIABLE TO MAKE THE SUM
double *y; // AUX VECTOR TO RECEIVE X VECTOR
double *saida; // OUTPUT VECTOR
FILE *arq; // POINTER TO THE FILE
for (i = 0; i < m; i++) {
coef[i] = 1.0/m;
}
arq = fopen("sweep_100_3400.pcm", "rb");
if (arq == NULL)
{
printf("ERROR WHILE OPENING THE FILE\n");
return;
}
// compute size of file
fseek(arq,0,SEEK_END);
itera = ftell(arq)/sizeof(short);
rewind(arq);
// alloc mem for x, read the vector from input file
x = malloc(itera*sizeof(short));
fread(x,sizeof(short),itera,arq);
fclose(arq);
// alloc mem for y
y = malloc(itera*sizeof(double));
saida = malloc(itera*sizeof(double));
for (i=0; i < itera; i++) {
y[0] = x[i];
aux=0;
for (n=0; n<m; n++){
aux += coef[n] * y[n];
}
saida[i]=aux;
for (n=m; n <2; n--){
x[n] = x[n-1];
}
}
arq=fopen("saida_medial_movel_c.pcm","wb");
fwrite(saida, sizeof(double),itera,arq);
fclose(arq);
free(saida);
free(y);
free(x);
}
На рисунке ниже показан вывод программы Matlab на скользящую среднюю с длиной 16:
Это изображение является выводом на языке C со скользящей средней длиной 16:
Кто-то знает, что может быть?
Ниже кода в matlab, который я адаптировал:
%MOVING AVERAGE EXAMPLE SCRIPT
clear all;
close all;
clc;
% DEFINES MEDIA LENGTH
m = 16;
%VECTOS EQUAL ZERO
x = zeros (m,1);
coef = zeros (m,1);
%INITIALIZE VECTOR
for j = 1 : m,
coef (j,1) = 1/m;
end
%READ INPUT FILE
fid = fopen ('sweep_100_3400.pcm','rb');
s = fread (fid, 'int16');
fclose(fid);
subplot(2,1,1);
plot(s);
grid on;
title('ENTRADA DO FILTRO');
%PROCESS
itera = length(s);
sav_y = zeros (itera, 1);
%EXECUTE PROCESS
for j = 1 : itera,
x(1,1) = s (j,1);
%PRODUCTS SUM
y=0;
for n = 1 : m,
y = y + coef(n,1) * x(n,1);
end
sav_y(j,1) = y;
%SHIFT THE VECTOR
for n = m: -1 : 2,
x (n,1) = x(n-1,1);
end
end
%PLOT OUTPUT
subplot (2,1,2);
plot (sav_y);
grid on;
title('SAÍDA DO FLITRO');
%SAVE THE OUTPUT IN ANOTHER FILE
fid = fopen('saida_mm_manual.pcm','wb');
fwrite(fid,sav_y,'int16');
fclose(fid);
Обновление 1 (используя ответ выше):
Начало сигнала все еще с помехами, но от середины до конца сигнала правильно.
1 ответ
Есть несколько проблем с вашим основным циклом обработки сигналов.
for (i=0; i < itera; i++) {
y[0] = x[i];
aux=0;
for (n=0; n<m; n++){
aux += coef[n] * y[n];
}
saida[i]=aux;
for (n=m; n <2; n--){
x[n] = x[n-1];
}
}
Каждый элемент вашего массива coef одинаков, поэтому вы можете также сделать его одним постоянным значением 1.0 / m. Для массива y вы не устанавливаете ни один из его элементов, кроме первого. Таким образом, ваш цикл добавляет константу, умноженную на неинициализированное значение, в 16 раз на aux. Это то, что производит вывод мусора. Я не уверен, что должен делать ваш (n=m; n <2; n--) цикл, но он никогда не будет работать, так как m > 2.
(Неэффективное) простое скользящее среднее будет выглядеть примерно так:
for (i = 0; i < itera - m; i++) {
aux = 0;
for (n = 0; n < m; n++){
aux += x[i+n];
}
saida[i] = aux * (1.0 / m);
}
Более эффективная версия могла бы избежать повторной обработки промежуточных элементов, и вместо этого просто добавьте новый номер, входящий в скользящее окно, и вычитайте старый номер, покидая окно каждую итерацию. Если вы работаете с числами с плавающей запятой, нужно соблюдать осторожность с числовой стабильностью при работе с плохо обусловленными числами и т. Д., Но это совсем другая проблема, о которой вам сейчас не нужно беспокоиться.