I am currently using the firls function in Matlab to create my FIR filter kernel. This works well but I typically have to play with the filter order to get the gain to 1 and the shape to be somewhat Gaussian in the frequency domain.
I would like to write an algorithm that finds the best filter kernel given a range of passband frequencies for the FIR filter. My initial attempt was to create an ideal filter kernel, calculate the integral and then step through a range of orders and settle on the order which had an integral closest to the ideal kernel. The issue with this approach was the chosen filter orders were too high and the shape of the kernel was not a smooth gaussian as shown on the left.
Left graph shows desired kernel, right is undesired higher order kernel. Red point plot is the ideal filter. Y-axis is frequency.
Here is the Matlab code I have been using to generate the filter and what I'm hoping to expand off of to create a gaussian-like, smooth FIR filter for any inputted frequency range.
clc,clear
close all
srate = 250e3;
nyquist = srate/2;
npnts = srate*5; %generate number of points for 5 seconds of sampling
time = (0:npnts-1)/srate;
% simulate brownian noise
bnoise = 30*cumsum(randn(1,npnts));
% simulate white noise
wnoise = 50*randn(1,npnts);
% vector of frequencies in kHz
hz = linspace(0,srate/2,floor(npnts/2)+1)/1e3; %go from 0 to nyquist, frequency resolution is defined by last term
% signal settings
freq1 = 26e3;
ampl = 1;
Y = ampl*sin(2*pi*freq1*time) + bnoise + wnoise;
% amplitude spectrum via Fourier transform
signalX = fft(Y);
signalAmp = 2*abs(signalX)/npnts; %need to multiply by 2 to recover amplitude from negative freqs
% divide by npnts to normalize fourier coefficients
% FIR filter specs
passband = [freq1-2e3 freq1+2e3];
transw = 0.01; %how the filter's edges taper
test_order = 7;
order = round(test_order*srate/passband(1)); % define the number of time points for the filter kernel
shape = [ 0 0 1 1 0 0 ]; %FIR shape
%define frequency shape of the FIR shape, firls function requires
%frequencies to go from 0 to 1
frex = [0 passband(1)-passband(1)*transw passband passband(2)+passband(2)*transw nyquist]/nyquist;
%define kernel
filter_kernel = firls(order, frex, shape);
% power spectrum of filter kernel
filtkernX = abs(fft(filter_kernel,npnts)).^2;
% initialize filtered signal matrix
yFilt = zeros(1,length(Y));
% set initial conditions zi for filter function
lead_dim = (max(length(1),length(filter_kernel))-1);
zi = zeros(1,lead_dim)';
% apply the filter to the data
[yFilt, zf] = filter(filter_kernel, 1, Y, zi, 2);
signalX3 = fft(yFilt);
signalAmp3 = 2*abs(signalX3)/npnts;
% plotting
xlimits = [20e3/1e3 120e3/1e3];
% freq response plot
figure;
subplot(4,1,1)
stem(hz,signalAmp(1:length(hz)))
title('Frequency Response')
ylabel('Amplitude')
xlim(xlimits)
% freq response after filter
subplot(4,1,2)
stem(hz,signalAmp3(1:length(hz)))
ylabel('Amplitude')
xlim(xlimits)
title('Filtered Subband')
% ideal vs actual filter kernel
subplot(4,1,3)
plot(hz,filtkernX(1:length(hz)),'-','linew',2,'markersize',1)
hold on
plot([0 passband(1) passband passband(2) nyquist]./1e3,[0 0 1 1 0 0],'ro-','linew',2,'markerfacecolor','w')
xlim(xlimits)
ylabel('Filter gain')
title('Frequency response of filter (fir1)')
legend({'Actual';'Ideal'})
%plot power
subplot(4,1,4)
plot(hz,10*log10(filtkernX(1:length(hz))))
xlim(xlimits)
xlabel('Frequency (Hz)')
ylabel('Filter gain (dB)')
title('Frequency response of filter (fir1)')
Code Output Plots: