soft/test/std_utils/fr_test_measure.m

174 lines
6.3 KiB
Mathematica
Raw Normal View History

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 fiels. The output
% is read from file t.fn_out.
%
% Input parameters
% t.f_lo - Measure ripple start frequency
% t.f_hi - Measure ripple end frequency
% t.rp_max - Max. ripple +/- dB
% t.mask_f - Frequencies for mask
% t.mask_lo - Upper limits of mask
% t.mask_hi - Lower limits of mask
%
% Output parameters
% t.m - Measured frequency response
% 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
%
% E.g.
% t.fs=48e3; t.f_max=20e3; t.bits_out=16; t.ch=1; t.nch=2; t=fr_test_input(t);
% t.fn_out=t.fn_in; t.f_lo=20; t.f_hi=20e3; t.rp_max=[]; t.mask_f=[];
% t=fr_test_measure(t);
%
%%
% Copyright (c) 2017, Intel Corporation
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in the
% documentation and/or other materials provided with the distribution.
% * Neither the name of the Intel Corporation nor the
% names of its contributors may be used to endorse or promote products
% derived from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
%
% Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
%
%% Reference: AES17 6.2.3 Frequency response
% http://www.aes.org/publications/standards/
[x, nx] = load_test_output(test);
j = 1;
for channel = test.ch
if nx == 0
test.rp(j) = NaN;
test.fr3db_hz(j) = NaN;
test.fail = 1;
return
end
%% Find sync
[d, nt, nt_use, nt_skip] = find_test_signal(x(:,j), test);
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
idx0 = find(test.f < test.f_hi);
idx1 = find(test.f(idx0) > test.f_lo);
range_db = max(test.m(idx1,j))-min(test.m(idx1,j));
test.rp(j) = range_db/2;
test.fail = 0;
if ~isempty(test.rp_max)
if test.rp > test.rp_max
fprintf('Failed response +/- %f dBpp (max +/- %f dB)\n', ...
test.rp, test.rp_max);
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.mask_f)
f_log = log(test.f);
mask_hi = interp1(log(test.mask_f), test.mask_hi(:,j), ...
f_log, 'linear');
mask_lo = interp1(log(test.mask_f), test.mask_lo(:,j), ...
f_log, 'linear');
over_mask = test.m(:,j)-mask_hi';
under_mask = mask_lo'-test.m(:,j);
idx = find(isnan(over_mask) == 0);
[m_over_mask, io] = max(over_mask(idx));
idx = find(isnan(under_mask) == 0);
[m_under_mask, iu] = max(under_mask(idx));
if m_over_mask > 0
fprintf('Failed upper response mask around %.0f Hz\n', ...
test.f(io));
test.fail = 1;
end
if m_under_mask > 0
fprintf('Failed lower response mask around %.0f Hz\n', ...
test.f(iu));
test.fail = 1;
end
end
test.fh(j) = figure('visible', test.visible);
subplot(1,1,1);
test.ph(j) = subplot(1,1,1);
%semilogx(test.f, test.m(:,j));
plot(test.f, test.m(:,j));
grid on;
xlabel('Frequency (Hz)');
ylabel('Magnitude (dB)');
if length(test.mask_f) > 0
hold on;
plot(test.f, mask_hi, 'r--');
plot(test.f, mask_lo, 'r--');
hold off;
end
ax = axis();
axis([ax(1:2) -4 1]);
if max(max(test.m(idx1,j))-min(test.m(idx1,j))) < 1
axes('Position', [ 0.2 0.2 0.4 0.2]);
box on;
plot(test.f(idx1), test.m(idx1,j));
if ~isempty(test.rp_max)
hold on;
plot([test.f_lo test.f_hi], ...
[-test.rp_max/2 -test.rp_max/2], 'r--');
plot([test.f_lo test.f_hi], ...
[ test.rp_max/2 test.rp_max/2], 'r--');
hold off;
end
grid on;
axis([0 test.f_hi -0.1 0.1]);
end
%% Next channel to measure
j=j+1;
end
end