EDIT: I can get the corrections accuratly when i take the difference after abs(fft(x1)) and I am able to ahieve correct values for either power correciton or dB SPL correciton as vectors but by that point I am no longer able to ifft back to the time domain.
Hi, How can I create an FIR from difference of two impulse responses?
47 views (last 30 days)
Show older comments
Hello, I am trying to create an FIR and assocaited waveform for a trasnfer fucntion that is the magnitude difference between the two impulses responses. I have recorded both IR's but am struggling with getting the correction factor to be correct. Here is my code and results of the IRs and difference which is clearly wrong because it is reducing magntiude incorrectly. I can see kind of where it is going wrong which seems to be in taking the difference between them which results in the following magnitude graph and DBFS graph. I have inlcuded wav files as csv if trying to run.
when i mak apply 20*log(x) to each vector to find DBFS
% Define the sampling frequency
close all
%[x1,fs] = audioread('711_Tap1024.wav');
%[x2,fs] = audioread('5128_Tap1024.wav');
x1 = readmatrix('711_Tap1024.csv');
x2 = readmatrix('5128_Tap1024.csv');
fs = 48000;
Nfft = length(x1);
% Frequency vector (assume frequencies are normalized, or you need to normalize by fs/2)
f = linspace(0, fs, Nfft);
f = f';
% Define or import the frequency responses H1 and H2
H1 = fft(x1,Nfft);
H2 = fft(x2,Nfft);
% Compute the difference in frequency responses
H_diff = H1-H2;
% Inverse FFT to obtain the impulse response
y = real(ifft(H_diff,Nfft));
% Normalize the impulse response
y_new = 0.99*y/max(abs(y));
% Save the impulse response as a WAV file
audiowrite('impulse_response_diff.wav', y, fs);
% Plot the impulse response (optional)
figure;
plot(y)
y1=abs(fft(x1,Nfft));
y2=abs(fft(x2,Nfft));
y3=abs(fft(y,Nfft));
y1= 20*log10(y1);
y2= 20*log10(y2);
y3= 20*log10(y3);
figure
plot(f(1:Nfft/2), y1(1:Nfft/2));
hold on
plot(f(1:Nfft/2), y2(1:Nfft/2));
hold on
plot(f(1:Nfft/2), y3(1:Nfft/2));
xlim([20 20000])
xticks([100 1000 10000])
grid on
set(gca, 'XScale', 'log')
xlabel ('Frequency (Hz)');
ylabel ('Amplitude ');
3 Comments
Star Strider
on 22 Nov 2024 at 18:39
You need to subttract the complex results, not the abs results. Then, you should be able to invert the difference. (This is essentiially frequency-domain filtering, and while not recommended, is possible.)
That aside, it is difficult to understand what you want to do.
Answers (1)
Star Strider
on 22 Nov 2024 at 18:25
I slightly changed your original code here, in order to define my questions about it.
It is possible to use the firls function to approximatee the ‘y3’ plot. If you want to use it on your existing data, be miindful of the filter length (in this instance the filter order), since it should be less than one-third of the signal length.
This result is far from perfect, however it should provide a startiing point —
x1 = readmatrix('711_Tap1024.csv');
x2 = readmatrix('5128_Tap1024.csv');
fs = 48000;
Nfft = length(x1);
% Frequency vector (assume frequencies are normalized, or you need to normalize by fs/2)
f = linspace(0, fs, Nfft);
f = f';
% Define or import the frequency responses H1 and H2
H1 = fft(x1,Nfft);
H2 = fft(x2,Nfft);
% Compute the difference in frequency responses
H_diff = H1-H2;
% Inverse FFT to obtain the impulse response
y = H_diff;
% % y = real(ifft(H_diff,Nfft));
% Normalize the impulse response
y_new = 0.99*y/max(abs(y));
% Save the impulse response as a WAV file
% % audiowrite('impulse_response_diff.wav', y, fs);
% Plot the impulse response (optional)
% % figure;
% % plot(y)
y1=abs(fft(x1,Nfft));
y2=abs(fft(x2,Nfft));
y3=abs(fft(y,Nfft));
y1= 20*log10(y1);
y2= 20*log10(y2);
y3= 20*log10(y3);
figure
plot(f(1:Nfft/2), y1(1:Nfft/2), DisplayName='y_1');
hold on
plot(f(1:Nfft/2), y2(1:Nfft/2), DisplayName='y_2');
hold on
plot(f(1:Nfft/2), y3(1:Nfft/2), DisplayName='y_3');
xlim([20 20000])
xticks([100 1000 10000])
grid on
set(gca, 'XScale', 'log')
xlabel ('Frequency (Hz)');
ylabel ('Amplitude ');
legend('Location','best')
% ylim([-50 50])
format shortG
fn = fs/2
Lv = f < fn;
fstop = (y3(Lv) < -275);
stopbands = f(fstop)
firn = 150;
fv = f < fn;
firf = f(fv);
a = y3(fv);
ftype = 'hilbert';
b = firls(firn, firf, a, ftype);
figure
freqz(b, 1, 2^16, fs)
.
5 Comments
Star Strider
on 27 Nov 2024 at 3:06
I cannot get a good approximation to your desired transfer function using the Signal Proceessing Toolbox functions.
Note that there was some high-freequency noise at the high end of the ‘H_diff’ transfer functiion. I simply eliminated that by truncating the vectors.
Using the System Identification Toolbox functions, I was able to get a good approximation in the continuous s domain. I could not determine a sampling frequency from your available data, so you will have to transform the estimated transfer function to a discrete (digital) filter using the c2d function. It should work, however I can offer no guarantees. When you get the discrete numerator and denominator vectors, it would be best to use the tf2sos function, and then use the second-order-section representatiion to filter your signal, using the filtfilt function.
% Define the sampling frequency
% close all
%[x1,fs] = audioread('711_Tap1024.wav');
%x2,fs] = audioread('5128_Tap1024.wav');
x1 = readmatrix('711_Tap1024.csv');
x2 = readmatrix('5128_Tap1024.csv');
fs = 48000;
Nfft = length(x1);
% Frequency vector (assume frequencies are normalized, or you need to normalize by fs/2)
f = linspace(0, fs, Nfft);
f = f';
%define Target Curve
x1FR = 20*log10(abs(fft(x1,Nfft)));
x2FR = 20*log10(abs(fft(x2,Nfft)));
targetCurve = x1FR-x2FR;
targetCurve = targetCurve*-1;
% Define or import the frequency responses H1 and H2
% H1 = abs(fft(x1,Nfft));
% H2 = abs(fft(x2,Nfft));
H1 = fft(x1,Nfft);
H2 = fft(x2,Nfft);
% Compute the difference in frequency responses divide
%H_diff = H1-H2;
H_diff = H2./H1;
% Inverse FFT to obtain the impulse response
y = real(ifft(H_diff,Nfft));
% Normalize the impulse response
y = 0.99*y/max(abs(y));
% Save the impulse response as a WAV file
audiowrite('impulse_response_diff.wav', y, fs);
% Plot the impulse response (optional)
figure;
plot(y)
% plot result of y
y_new=abs(fft(y,Nfft));
y_new= 20*log10(y_new);
figure
plot(f(1:Nfft/2), x1FR(1:Nfft/2));
hold on
plot(f(1:Nfft/2), x2FR(1:Nfft/2));
hold on
plot(f(1:Nfft/2), targetCurve(1:Nfft/2), LineWidth=3);
hold on
plot(f(1:Nfft/2), y_new(1:Nfft/2));
xlim([20 20000])
xticks([100 1000 10000])
grid on
set(gca, 'XScale', 'log')
xlabel ('Frequency (Hz)');
ylabel ('Magnitude');
legend('H1', 'H2', 'Target Curve','Correction Curve', Location='best')
fn = fs/2;
fp = f(1:Nfft/2);
H_diff = H_diff(1:Nfft/2);
Lv = fp<=2.13E+4;
fp = fp(Lv);
H_diff = H_diff(Lv);
nnz(Lv)
wp = fp/fn * pi;
% H_diff = H_diff(Lv);
% H_diff(1:10)
H_diff(1) = H_diff(2);
H_diffn = abs(H_diff) / max(abs(H_diff));
[b,a] = invfreqz(wp, abs(H_diffn(1:numel(fp))), 64, 64);
[hfz1,wfz1] = freqz(b, a, 2^16);
b = firls(64, wp, abs(H_diffn(1:numel(wp))), 'hilbert')
[hfz2,wfz2] = freqz(b, 1, 2^16);
frd = idfrd(H_diffn(1:numel(fp)), fp, 0)
sys = tfest(frd, 6)
format longE
b = sys.Numerator
a = sys.Denominator
format short
figure
compare(frd, sys)
figure
tiledlayout(3,1)
nexttile
plot(wp, abs(H_diffn(1:numel(fp))))
xlim([0 pi])
title('Original Transfer Function')
% hold on
% plot(fp, ftf)
% xline(2.14E+4,'--k')
% hold off
grid
nexttile
plot(wfz1, abs(hfz1))
title('‘invfreqz’ Estimate')
grid
nexttile
plot(wfz2, abs(hfz2))
title('‘firls’ Estimate')
grid
This is the best that I can do with your data. This is definitely not straightforward.
.
See Also
Categories
Find more on Single-Rate Filters in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!