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 % %% 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