blob: 4520af99a850cd33b7aa6a61fcc4f78ce903cb8b [file] [log] [blame]
function src = src_get(cnv)
% src_get - calculate coefficients for a src
%
% src = src_get(cnv);
%
% cnv - src parameters
% src - src result
%
% Copyright (c) 2016, 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>
%
use_firpm = 0;
use_remez = 0;
use_kaiser = 0;
switch lower(cnv.design)
case 'firpm'
if exist('OCTAVE_VERSION', 'builtin')
use_remez = 1;
else
use_firpm = 1;
end
case 'kaiser'
use_kaiser = 1;
otherwise
error('Unknown FIR design request!');
end
if abs(cnv.fs1-cnv.fs2) < 1
%% Return minimum needed for scripts to work
src.L=1;
src.M=1;
src.odm=1;
src.idm=1;
src.MOPS=0;
src.c_pb = 0;
src.c_sb = 0;
src.fir_delay_size = 0;
src.out_delay_size = 0;
src.blk_in = 1;
src.blk_out = 1;
src.gain = 1;
src.filter_length = 0;
return
end
%% fractional SRC parameters
[L, M] = src_factor1_lm(cnv.fs1, cnv.fs2);
src.L = L;
src.M = M;
src.num_of_subfilters = L;
[src.idm src.odm] = src_find_l0m0(src.L, src.M);
min_fs = min(cnv.fs1,cnv.fs2);
src.f_pb = min_fs*cnv.c_pb;
src.f_sb = min_fs*cnv.c_sb;
src.c_pb = cnv.c_pb;
src.c_sb = cnv.c_sb;
src.fs1 = cnv.fs1;
src.fs2 = cnv.fs2;
src.rp = cnv.rp;
src.rs = cnv.rs;
%% FIR PM design
fs3 = src.L*cnv.fs1;
f = [src.f_pb src.f_sb];
a = [1 0];
% Kaiser fir is not equiripple, can allow more ripple in the passband
% lower freq.
dev = [(10^(cnv.rp/20)-1)/(10^(cnv.rp/20)+1) 10^(-cnv.rs/20)];
[kn0, kw, kbeta, kftype] = kaiserord(f, a, dev, fs3);
if use_firpm
%dev = [(10^(cnv.rp/20)-1)/(10^(cnv.rp/20)+1) 10^(-cnv.rs/20)];
[n0,fo,ao,w] = firpmord(f,a,dev,fs3);
n0 = round(n0*0.95); % Decrease order to 95%
end
if use_remez
n0 = round(kn0*0.60); % Decrease order to 70%
fo = [0 2*f/fs3 1];
ao = [1 1 0 0];
w = [1 dev(1)/dev(2)];
end
if use_kaiser
n0 = round(kn0*0.70); % Decrease order to 70%
end
% Constrain filter length to be a suitable multiple. Multiple of
% interpolation factor ensures that all subfilters are equal length.
% Make each sub-filter order multiple of N helps in making efficient
% implementation.
nfir_increment = src.num_of_subfilters * cnv.filter_length_mult;
nsf0 = (n0+1)/nfir_increment;
if nsf0 > floor(nsf0)
n = (floor(nsf0)+1)*nfir_increment-1;
else
n = n0;
end
src.subfilter_length = (n+1)/src.num_of_subfilters;
src.filter_length = n+1;
nfir = n;
f_sb = linspace(src.f_sb, fs3/2, 500);
stopband_ok = 0;
need_to_stop = 0;
delta = 100;
n_worse = 0;
n_worse_max = 20;
n = 1;
n_max = 100;
dn = ones(1, n_max)*1000;
fn = zeros(1, n_max);
while (stopband_ok) == 0 && (n < n_max)
if use_firpm || use_remez
if nfir > 1800
b = fir1(nfir, kw, kftype, kaiser(nfir+1, kbeta));
else
if use_firpm
b = firpm(nfir,fo,ao,w);
else
b = remez(nfir,fo,ao,w);
end
end
else
b = fir1(nfir, kw, kftype, kaiser(nfir+1, kbeta));
end
m_b = max(abs(b));
%sref = 2^(cnv.coef_bits-1);
%bq = round(b*sref/m_b)*m_b/sref;
bq = b;
h_sb = freqz(bq, 1, f_sb, fs3);
m_sb = 20*log10(abs(h_sb));
delta_prev = delta;
delta = cnv.rs+max(m_sb);
fprintf('Step=%3d, Delta=%f dB, N=%d\n', n, delta, nfir);
dn(n) = delta;
fn(n) = nfir;
if delta < 0
stopband_ok = 1;
else
if delta_prev < delta
n_worse = n_worse+1;
if n_worse > n_worse_max
need_to_stop = 1; % No improvement, reverse
idx = find(dn == min(dn), 1, 'first');
nfir = fn(idx);
else
nfir = nfir + nfir_increment;
end
else
if need_to_stop == 0
nfir = nfir + nfir_increment;
else
stopband_ok = 1; % Exit after reverse
fprintf('Warning: Filter stop band could not be ');
fprintf('reached.\n', cnv.coef_bits);
end
end
end
n = n + 1;
end
f_p = linspace(0, fs3/2, 2000);
m_db = 20*log10(abs(freqz(bq, 1, f_p, fs3)));
i_pb = find(f_p < src.f_pb);
g_pb = max(m_db(i_pb));
g_att_lin = 10^(-g_pb/20);
if 1
p_ymin = floor((-cnv.rs-50)/10)*10;
p_ymax = 10;
figure;
clf;
plot(f_p, m_db);
grid on;
xlabel('Frequency (Hz)');
ylabel('Magnitude (dB)');
axis([0 fs3/2 p_ymin p_ymax]);
hold on
plot([src.f_sb src.f_sb],[p_ymin p_ymax], 'r--');
plot([0 fs3],[-cnv.rs -cnv.rs], 'r--');
hold off
axes('Position', [ 0.58 0.7 0.3 0.2]);
box on;
plot(f_p(i_pb), m_db(i_pb));
axis([0 src.f_pb -cnv.rp-0.01 cnv.rp+0.01]);
hold on
plot([0 src.f_pb], +0.5*cnv.rp*ones(1,2), 'r--');
plot([0 src.f_pb], -0.5*cnv.rp*ones(1,2), 'r--');
hold off
grid on;
box off;
end
src.subfilter_length = ceil((nfir+1)/src.num_of_subfilters);
src.filter_length = src.subfilter_length*src.num_of_subfilters;
src.b = zeros(src.filter_length,1);
src.gain = 1;
src.b(1:nfir+1) = b*src.L*g_att_lin;
m = max(abs(src.b));
gmax = (32767/32768)/m;
maxshift = floor(log(gmax)/log(2));
src.b = src.b * 2^maxshift;
src.gain = 1/2^maxshift;
src.shift = maxshift;
%% Reorder coefficients
if 1
src.coefs = src.b;
else
src.coefs = zeros(src.filter_length,1);
for n = 1:src.num_of_subfilters
i11 = (n-1)*src.subfilter_length+1;
i12 = i11+src.subfilter_length-1;
src.coefs(i11:i12) = src.b(n:src.num_of_subfilters:end);
end
end
src.halfband = 0;
src.blk_in = M;
src.blk_out = L;
src.MOPS = cnv.fs1/M*src.filter_length/1e6;
src.fir_delay_size = src.subfilter_length + ...
(src.num_of_subfilters-1)*src.idm + src.blk_in;
src.out_delay_size = (src.num_of_subfilters-1)*src.odm + 1;
end