-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Second-order section structure filters for NFC-HOA driving function #57
Comments
As I have no experience with the SOS filtering stuff, could you maybe create a branch and implement an example of your solution? |
I just created a branch and updated the SOS implementation of 2.5D NFC-HOA driving functions. Previously, the matlab function Now the driving functions are computed as follows:
SFS_start;
conf = SFS_config;
conf.dimension = '2.5D';
conf.secondary_sources.geometry = 'circle';
conf.secondary_sources.size = 3;
conf.nfchoa.order = 16;
conf.plot.usedb = true;
x0 = secondary_source_positions(conf);
X = [-2 2];
Y = [-2 2];
Z = 0;
t = 200;
% virtual plane waves
xs = [0,-1,0];
src = 'pw';
conf.driving_functions = 'default';
sound_field_imp_nfchoa(X,Y,Z,xs,src,t,conf);
title('bilinear transform')
% print('-dpng','d_pw_bilinear') conf.driving_functions = 'matchedz';
sound_field_imp_nfchoa(X,Y,Z,xs,src,t,conf);
title('matched-z transform')
% print('-dpng','d_pw_matchedz') The difference between two driving functions is almost negligible. |
I got the following error, when I try to execute your above code in Octave: >> sound_field_imp_nfchoa(X,Y,Z,xs,src,t,conf);
error: bilinear: must have at least as many poles as zeros in s-plane
error: called from
bilinear at line 91 column 5
driving_function_imp_nfchoa_pw at line 102 column 14
driving_function_imp_nfchoa at line 93 column 13
sound_field_imp_nfchoa at line 94 column 3 If I remember correctly there is a slight difference in the behavior of the |
I realized that Could you check if it works now? |
I have added the Jupyter notebook to sfstoolbox/data repository https://github.com/sfstoolbox/data/blob/master/sphbesselh_zeros/sphHankel_zeros.ipynb |
I created a pull request (#65) to discuss the changes proposed in the In this issue we continue the discussion on the problems with finding the zeros of the Bessel function and related numerical problems. |
I could reproduce the results from @narahahn above, there are really some problems with the zeros calculated by the Multiprecission-Toolbox. As there are also numerical problems in python as well, we should maybe have a look at alternative ways to calculate the zeros. I had a first start on this, see https://github.com/sfstoolbox/sfs-deprecated-code/tree/master/hankel_zeros, but never really tried it. |
I think the problem is at least twofold:
Regarding the first problem, there is a recursion formula for the numerator coefficients in http://iem.kug.ac.at/fileadmin/media/iem/altdaten/projekte/acoustics/awt/winkel/pomberger.pdf, page 43. A while ago, I implemented a function utilizing this recursion formula. Dunno, if it leads to more stable results. function [hankel, derivative] = sphhankel_laplace(order)
hankel = struct(...
'zeros',[],...
'poles',[],...
'scale',[],...
'delay',[],...
'nominator',[],...
'denominator',[]);
hankel = repmat(hankel,order+1,1);
for n=0:order
% nominator
hankel(n+1).nominator = zeros(1, n+1);
for k=0:n-1
hankel(n+1).nominator(k+1) = ...
((2*n-k-1)*(2*n - k))/(2*(n-k))*hankel(n).nominator(k+1);
end
hankel(n+1).nominator(n+1) = 1;
end
if nargout >= 2
derivative = hankel;
for n=0:order
% nominator
derivative(n+1).nominator = zeros(1, n+2);
if n == 0
derivative(1).nominator = -hankel(2).nominator;
else
derivative(n+1).nominator(1:n+1) = (n+1)*hankel(n+1).nominator;
for k=2:n+1
derivative(n+1).nominator(k+1) = derivative(n+1).nominator(k+1) + ...
hankel(n).nominator(k-1);
end
end
end
for n=0:order
% flip nominator polynoms
derivative(n+1).nominator = derivative(n+1).nominator(end:-1:1);
% zeros
derivative(n+1).zeros = roots(derivative(n+1).nominator);
% denominator
derivative(n+1).denominator = zeros(1, n+3);
derivative(n+1).denominator(1) = 1;
% poles
derivative(n+1).poles = roots(derivative(n+1).denominator);
% additional scaling factor
derivative(n+1).scale = 1j.^(n+1);
% additional delay
derivative(n+1).delay = 1;
end
end
for n=0:order
% flip nominator polynoms
hankel(n+1).nominator = hankel(n+1).nominator(end:-1:1);
% zeros
hankel(n+1).zeros = roots(hankel(n+1).nominator);
% denominator
hankel(n+1).denominator = zeros(1, n+2);
hankel(n+1).denominator(1) = 1;
% poles
hankel(n+1).poles = roots(hankel(n+1).denominator);
% additional scaling factor
hankel(n+1).scale = -1j^n;
% additional delay
hankel(n+1).delay = 1;
end
end |
See #57 (comment) The result seems to be more or less identical to the one obtained with sphbesselh_zeros() in terms of stability -- the obtained values for z, p differ quite a lot. One advantage of sphhankel_laplace() is that it will not lead to Inf or Nan, but only to large numerical errors in the reproduced sound field for orders >85. Maybe it would be a good idea to try to calculate z directly via an formula without using the zeros() function.
What precision did you use in computations with Multiprecision Toolbox? Default precision in toolbox is quadruple (~34 decimal digits ) and obviously it was not enough for the computations. The good thing is that toolbox is able to work with arbitrary level of precision. User just need to call mp.Digits(XXX) before doing any computations with toolbox (XXX is number of decimal digits to use). I am author of Multiprecision Toolbox. |
The SOS coefficients in the Laplace domain is mapped to the SOS coefficients in the z-domain by using the bilinear transform. The latter are stored in two cell arrays, b and a. The filtering is then performed in a for-loop. I think we can do this in a simpler way:
The text was updated successfully, but these errors were encountered: