mirror of https://github.com/thesofproject/sof.git
217 lines
5.7 KiB
Matlab
217 lines
5.7 KiB
Matlab
function test = fr_test_measure(test)
|
|
|
|
%% t = fr_test_measure(t)
|
|
%
|
|
% Measure frequency response from captured frequency sweep created by
|
|
% fr_test_input() that sets most of the input struct fields. The output
|
|
% is read from file t.fn_out.
|
|
%
|
|
% The test criteria is defined with first three or four latter input
|
|
% parameters. The latter has precedence if defined.
|
|
%
|
|
% Input parameters
|
|
% t.fr_lo - Measure ripple start frequency
|
|
% t.fr_hi - Measure ripple end frequency
|
|
% t.fr_rp_max_db - Max. ripple +/- dB
|
|
% t.fr_mask_flo - Frequencies for lower mask
|
|
% t.fr_mask_fhi - Frequencies for higher mask
|
|
% t.fr_mask_mlo - Upper limits of mask
|
|
% t.fr_mask_mhi - Lower limits of mask
|
|
%
|
|
% Output parameters
|
|
% t.f - Frequency grid (Hz)
|
|
% t.m - Measured frequency response (dB)
|
|
% t.rp - Measured ripple +/- dB
|
|
% t.fr3db_hz - Bandwidth in Hz for -3 dB attenuated response
|
|
% t.fail - Returns zero for passed
|
|
% t.fh - Figure handle
|
|
% t.ph - Plot handle
|
|
%
|
|
|
|
% SPDX-License-Identifier: BSD-3-Clause
|
|
% Copyright(c) 2017 Intel Corporation. All rights reserved.
|
|
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
|
|
|
|
%% Check if upper and lower mask is defined
|
|
if length(test.fr_mask_flo) || length(test.fr_mask_fhi)
|
|
test.fr_lo = 0;
|
|
test.fr_hi = 0;
|
|
end
|
|
if test.fr_lo > 0 || test.fr_hi > 0
|
|
test.fr_mask_flo = [];
|
|
test.fr_mask_fhi = [];
|
|
test.fr_mask_mlo = [];
|
|
test.fr_mask_mhi = [];
|
|
end
|
|
|
|
test.ph = [];
|
|
test.fh = [];
|
|
|
|
%% Reference: AES17 6.2.3 Frequency response
|
|
% http://www.aes.org/publications/standards/
|
|
|
|
[x, nx] = load_test_output(test);
|
|
|
|
default_result = NaN * ones(1,length(test.ch));
|
|
test.rp = default_result;
|
|
test.fr3db_hz = default_result;
|
|
test.fail = 0;
|
|
j = 1;
|
|
for channel = test.ch
|
|
|
|
if nx == 0
|
|
test.fail = 1;
|
|
return
|
|
end
|
|
|
|
%% Find sync
|
|
[d, nt, nt_use, nt_skip] = find_test_signal(x(:,j), test);
|
|
if isempty(d)
|
|
test.fail = 1;
|
|
return
|
|
end
|
|
|
|
win = hamming(nt_use);
|
|
m0 = zeros(test.nf,1);
|
|
for n=1:test.nf
|
|
fprintf('Measuring %.0f Hz ...\n', test.f(n));
|
|
i1 = d+(n-1)*nt+nt_skip;
|
|
i2 = i1+nt_use-1;
|
|
m0(n) = level_dbfs(x(i1:i2,j).*win) -test.a_db;
|
|
end
|
|
|
|
%% Scale 997 Hz point as 0 dB
|
|
df = test.f - test.f_ref;
|
|
[~,i997] = min(abs(df));
|
|
m_offs = m0(i997);
|
|
test.m(:,j) = m0 - m_offs;
|
|
|
|
%% Check pass/fail
|
|
if test.fr_lo > 0 && test.fr_hi > 0 && ~isempty(test.fr_rp_max_db)
|
|
idx0 = find(test.f < test.fr_hi);
|
|
idx1 = find(test.f(idx0) > test.fr_lo);
|
|
range_db = max(test.m(idx1,j))-min(test.m(idx1,j));
|
|
test.rp(j) = range_db/2;
|
|
if test.rp(j) > test.fr_rp_max_db
|
|
fprintf('Failed response ch%d +/- %f dBpp (max +/- %f dB)\n', ...
|
|
test.ch(j), test.rp(j), test.fr_rp_max_db);
|
|
test.fail = 1;
|
|
end
|
|
end
|
|
|
|
%% Find frequency response 3 dB 0-X Hz
|
|
idx3 = find(test.m(:,j) > -3);
|
|
test.fr3db_hz(j) = test.f(idx3(end));
|
|
|
|
%% Interpolate mask in logaritmic frequencies, check
|
|
if ~isempty(test.fr_mask_fhi)
|
|
f_log = log(test.f);
|
|
mask_hi = interp1(log(test.fr_mask_fhi), ...
|
|
test.fr_mask_mhi(:,j), f_log, 'linear');
|
|
over_mask = test.m(:,j)-mask_hi';
|
|
idx = find(isnan(over_mask) == 0);
|
|
[m_over_mask, io] = max(over_mask(idx));
|
|
if m_over_mask > 0
|
|
fprintf('Failed upper response mask around %.0f Hz\n', ...
|
|
test.f(io(1)));
|
|
test.fail = 1;
|
|
end
|
|
end
|
|
if ~isempty(test.fr_mask_flo)
|
|
f_log = log(test.f);
|
|
mask_lo = interp1(log(test.fr_mask_flo), ...
|
|
test.fr_mask_mlo(:,j), f_log, 'linear');
|
|
under_mask = mask_lo'-test.m(:,j);
|
|
idx = find(isnan(under_mask) == 0);
|
|
[m_under_mask, iu] = max(under_mask(idx));
|
|
if m_under_mask > 0
|
|
fprintf('Failed lower response mask around %.0f Hz\n', ...
|
|
test.f(iu(1)));
|
|
test.fail = 1;
|
|
end
|
|
end
|
|
|
|
%% Next channel to measure
|
|
j=j+1;
|
|
end
|
|
|
|
%% Do plots
|
|
|
|
nr = j-1;
|
|
for i = 1:nr
|
|
test.fh(i) = figure('visible', test.plot_visible);
|
|
test.ph(i) = subplot(1,1,1);
|
|
|
|
if test.plot_channels_combine
|
|
semilogx(test.f, test.m);
|
|
else
|
|
semilogx(test.f, test.m(:,i));
|
|
end
|
|
|
|
grid on;
|
|
xlabel('Frequency (Hz)');
|
|
ylabel('Magnitude (dB)');
|
|
if ~isempty(test.fr_mask_fhi)
|
|
hold on;
|
|
plot(test.f, mask_hi, 'k--');
|
|
hold off;
|
|
end
|
|
if ~isempty(test.fr_mask_flo)
|
|
hold on;
|
|
plot(test.f, mask_lo, 'k--');
|
|
hold off;
|
|
end
|
|
|
|
if ~isempty(test.plot_fr_axis)
|
|
axis(test.plot_fr_axis);
|
|
end
|
|
|
|
if test.plot_channels_combine && (test.nch > 1)
|
|
switch test.nch
|
|
case 2
|
|
lstr1 = sprintf("ch%d", test.ch(1));
|
|
lstr2 = sprintf("ch%d", test.ch(2));
|
|
legend(lstr1, lstr2, ...
|
|
'Location', 'NorthEast');
|
|
end
|
|
else
|
|
lstr = sprintf("ch%d", test.ch(i));
|
|
legend(lstr, 'Location', 'NorthEast');
|
|
end
|
|
|
|
if test.plot_passband_zoom
|
|
axes('Position', [ 0.2 0.2 0.4 0.2]);
|
|
box on;
|
|
i1 = find(test.f < test.fr_lo, 1, 'last');
|
|
i2 = find(test.f > test.fr_hi, 1, 'first');
|
|
if isempty(i1)
|
|
i1 = 1;
|
|
end
|
|
if isempty(i2)
|
|
i2 = length(test.f);
|
|
end
|
|
if test.plot_channels_combine
|
|
plot(test.f(i1:i2), test.m(i1:i2,:));
|
|
else
|
|
plot(test.f(i1:i2), test.m(i1:i2,i));
|
|
end
|
|
rp = 1.0;
|
|
if ~isempty(test.fr_rp_max_db)
|
|
rp = test.fr_rp_max_db;
|
|
hold on;
|
|
plot([test.fr_lo test.fr_hi], [-rp/2 -rp/2], 'r--');
|
|
plot([test.fr_lo test.fr_hi], [ rp/2 rp/2], 'r--');
|
|
hold off
|
|
end
|
|
axis([0 test.f_max -rp rp]);
|
|
grid on;
|
|
end
|
|
|
|
if test.plot_channels_combine
|
|
break
|
|
end
|
|
|
|
end
|
|
|
|
end
|