Mercurial > forge
changeset 8454:bc4257a29d2a octave-forge
impinvar/invimpinvar: new functions for signal package submitted by Rudy Eschauzier <reschauzier@yahoo.com>
author | carandraug |
---|---|
date | Fri, 30 Sep 2011 13:42:44 +0000 |
parents | c8750827ad19 |
children | 062e063e7161 |
files | main/signal/inst/impinvar.m main/signal/inst/invimpinvar.m main/signal/inst/private/h1_z_deriv.m main/signal/inst/private/inv_residue.m main/signal/inst/private/pad_poly.m main/signal/inst/private/polyrev.m main/signal/inst/private/to_real.m |
diffstat | 7 files changed, 390 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/impinvar.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,103 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## -*- texinfo -*- +## @deftypefn{Function File} {[@var{b_out}, @var{a_out}] =} impinvar (@var{b}, @var{a}, @var{ts}, @var{tol}) +## @deftypefnx{Function File} {[@var{b_out}, @var{a_out}] =} impinvar (@var{b}, @var{a}, @var{ts}) +## @deftypefnx{Function File} {[@var{b_out}, @var{a_out}] =} impinvar (@var{b}, @var{a}) +## Converts analog filter with coefficients @var{b} and @var{a} to digital, +## conserving impulse response. +## +## If @var{ts} is not specificied, or is an empty vector, it defaults to 1Hz. +## +## If @var{tol} is not specified, it defaults to 0.0001 (0.1%) +## This function does the inverse of impinvar so that the following example should +## restore the original values of @var{a} and @var{b}. +## +## @command{invimpinvar} implements the reverse of this function. +## @example +## [b, a] = impinvar (b, a); +## [b, a] = invimpinvar (b, a); +## @end example +## +## @seealso{bilinear, invimpinvar} +## @end deftypefn + +function [b_out, a_out] = impinvar (b_in, a_in, ts = 1, tol = 0.0001) + + if (nargin <2) + print_usage; + endif + + ## to be compatible with the matlab implementation where an empty vector can + ## be used to get the default + if (isempty(ts)) + ts = 1; + endif + + [r_in, p_in, k_in] = residue(b_in, a_in); % partial fraction expansion + + n = length(r_in); % Number of poles/residues + + if (length(k_in)>0) % Greater than zero means we cannot do impulse invariance + error("Order numerator >= order denominator"); + endif + + r_out = zeros(1,n); % Residues of H(z) + p_out = zeros(1,n); % Poles of H(z) + k_out = 0; % Contstant term of H(z) + + i=1; + while (i<=n) + m = 1; + first_pole = p_in(i); % Pole in the s-domain + while (i<n && abs(first_pole-p_in(i+1))<tol) % Multiple poles at p(i) + i++; % Next residue + m++; % Next multiplicity + endwhile + [r, p, k] = z_res(r_in(i-m+1:i), first_pole, ts); % Find z-domain residues + k_out += k; % Add direct term to output + p_out(i-m+1:i) = p; % Copy z-domain pole(s) to output + r_out(i-m+1:i) = r; % Copy z-domain residue(s) to output + + i++; % Next s-domain residue/pole + endwhile + + [b_out, a_out] = inv_residue(r_out, p_out, k_out); + a_out = to_real(a_out); % Get rid of spurious imaginary part + b_out = to_real(b_out); + b_out = polyreduce(b_out); + +endfunction + +## Convert residue vector for single and multiple poles in s-domain (located at sm) to +## residue vector in z-domain. The variable k is the direct term of the result. +function [r_out, p_out, k_out] = z_res (r_in, sm, ts); + + p_out = exp(ts * sm); % z-domain pole + n = length(r_in); % Multiplicity of the pole + r_out = zeros(1,n); % Residue vector + + %% First pole (no multiplicity) + k_out = r_in(1) * ts; % PFE of z/(z-p) = p/(z-p)+1; direct part + r_out(1) = r_in(1) * ts * p_out; % pole part of PFE + + for i=(2:n) % Go through s-domain residues for multiple pole + r_out(1:i) += r_in(i) * polyrev(h1_z_deriv(i-1, p_out, ts)); % Add z-domain residues + endfor + +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/invimpinvar.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,98 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## -*- texinfo -*- +## @deftypefn{Function File} {[@var{b_out}, @var{a_out}] =} invimpinvar (@var{b}, @var{a}, @var{ts}, @var{tol}) +## @deftypefnx{Function File} {[@var{b_out}, @var{a_out}] =} invimpinvar (@var{b}, @var{a}, @var{ts}) +## @deftypefnx{Function File} {[@var{b_out}, @var{a_out}] =} invimpinvar (@var{b}, @var{a}) +## Converts digital filter with coefficients @var{b} and @var{a} to analog, +## conserving impulse response. +## +## This function does the inverse of impinvar so that the following example should +## restore the original values of @var{a} and @var{b}. +## @example +## [b, a] = impinvar (b, a); +## [b, a] = invimpinvar (b, a); +## @end example +## +## If @var{ts} is not specificied, or is an empty vector, it defaults to 1Hz. +## +## If @var{tol} is not specified, it defaults to 0.0001 (0.1%) +## +## @seealso{bilinear, impinvar} +## @end deftypefn + +## Impulse invariant conversion from s to z domain +function [b_out, a_out] = invimpinvar (b_in, a_in, ts = 1, tol = 0.0001) + + if (nargin <2) + print_usage; + endif + + [r_in, p_in, k_in] = residue(b_in, a_in); % partial fraction expansion + + n = length(r_in); % Number of poles/residues + + if (length(k_in) > 1) % Greater than one means we cannot do impulse invariance + error("Order numerator > order denominator"); + endif + + r_out = zeros(1,n); % Residues of H(s) + sm_out = zeros(1,n); % Poles of H(s) + + i=1; + while (i<=n) + m=1; + first_pole = p_in(i); % Pole in the z-domain + while (i<n && abs(first_pole-p_in(i+1))<tol) % Multiple poles at p(i) + i++; % Next residue + m++; % Next multiplicity + endwhile + [r, sm, k] = inv_z_res(r_in(i-m+1:i), first_pole, ts); % Find s-domain residues + k_in -= k; % Just to check, should end up zero for physical system + sm_out(i-m+1:i) = sm; % Copy s-domain pole(s) to output + r_out(i-m+1:i) = r; % Copy s-domain residue(s) to output + + i++; % Next z-domain residue/pole + endwhile + [b_out, a_out] = inv_residue(r_out, sm_out ,0); + a_out = to_real(a_out); % Get rid of spurious imaginary part + b_out = to_real(b_out); + b_out = polyreduce(b_out); + +endfunction + +## Inverse function of z_res (see impinvar source) +function [r_out sm_out k_out] = inv_z_res (r_in,p_in,ts) + + n = length(r_in); % multiplicity of the pole + r_in = r_in'; % From column vector to row vector + + i=n; + while (i>1) % Go through residues starting from highest order down + r_out(i) = r_in(i) / ((ts * p_in)^i); % Back to binomial coefficient for highest order (always 1) + r_in(1:i) -= r_out(i) * polyrev(h1_z_deriv(i-1,p_in,ts)); % Subtract highest order result, leaving r_in(i) zero + i--; + endwhile + + %% Single pole (no multiplicity) + r_out(1) = r_in(1) / ((ts * p_in)); + k_out = r_in(1) / p_in; + + sm_out = log(p_in) / ts; + +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/private/h1_z_deriv.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,53 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## This function is necessary for impinvar and invimpinvar of the signal package + +## Find {-zd/dz}^n*H1(z). I.e., first multiply by -z, then diffentiate, then multiply by -z, etc. +## The result is (ts^(n+1))*(b(1)*p/(z-p)^1 + b(2)*p^2/(z-p)^2 + b(n+1)*p^(n+1)/(z-p)^(n+1)). +## Works for n>0. +function b = h1_z_deriv(n, p, ts) + + %% Build the vector d that holds coefficients for all the derivatives of H1(z) + %% The results reads d(n)*z^(1)*(d/dz)^(1)*H1(z) + d(n-1)*z^(2)*(d/dz)^(2)*H1(z) +...+ d(1)*z^(n)*(d/dz)^(n)*H1(z) + d = (-1)^n; % Vector with the derivatives of H1(z) + for i=(1:n-1) + d = conv(d,[1 0]); % Shift result right (multiply by -z) + d += pad_poly(polyderiv(d),i+1); % Add the derivative + endfor + + %% Build output vector + b = zeros(1,n+1); + for i=(1:n) + b += d(i) * pad_poly(h1_deriv(n-i+1, ts), n+1); + endfor + + b *= ts^(n+1)/factorial(n); + for i=(1:n+1) + b(n-i+2) *= p^i; % Multiply coefficients with p^i, where i is the index of the coeff. + endfor + +endfunction + +## Find (z^n)*(d/dz)^n*H1(z), where H1(z)=ts*z/(z-p), ts=sampling period, +## p=exp(sm*ts) and sm is the s-domain pole with multiplicity n+1. +## The result is (ts^(n+1))*(b(1)*p/(z-p)^1 + b(2)*p^2/(z-p)^2 + b(n+1)*p^(n+1)/(z-p)^(n+1)), +## where b(i) is the binomial coefficient bincoeff(n,i) times n!. Works for n>0. +function b = h1_deriv(n,ts) + b = factorial(n)*bincoeff(n,0:n); % Binomial coefficients: [1], [1 1], [1 2 1], [1 3 3 1], etc. + b *= (-1)^n; +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/private/inv_residue.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,62 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## This function is necessary for impinvar and invimpinvar of the signal package + +## Inverse of Octave residue function +function [b_out, a_out] = inv_residue(r_in, p_in, k_in) + + tol=0.0001; + + n = length(r_in); % Number of poles/residues + + k = 0; % Capture contstant term + if (length(k_in)==1) % A single direct term (order N = order D) + k = k_in(1); % Capture constant term + elseif (length(k_in)>1) % Greater than one means non-physical system + error("Order numerator > order denominator"); + endif + + a_out = 1; % Calculate denominator + for i=(1:n) + a_out = conv(a_out,[1 -p_in(i)]); + endfor + + b_out = zeros(1,n+1); + b_out += k*a_out; % Constant term: add k times denominator to numerator + i=1; + while (i<=n) + term = [1 -p_in(i)]; % Term to be factored out + p = r_in(i)*deconv(a_out,term); % Residue times resulting polynomial + p = pad_poly(p,n+1); % Pad for proper length + b_out += p; + + m = 1; + mterm = term; + first_pole = p_in(i); + while (i<n && abs(first_pole-p_in(i+1))<tol) % Multiple poles at p(i) + i++; %Next residue + m++; + mterm = conv(mterm,term); % Next multiplicity to be factored out + p = r_in(i)*deconv(a_out,mterm); % Resulting polynomial + p = pad_poly(p,n+1); % Pad for proper length + b_out += p; + endwhile + i++; + endwhile + b_out = polyreduce(b_out); +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/private/pad_poly.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,25 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## This function is necessary for impinvar and invimpinvar of the signal package + +## Pad polynomial to length n +function p_out = pad_poly(p_in,n) + l = length(p_in); % Length of input polynomial + p_out(n-l+1:n) = p_in(1:l); % Right shift for proper length + p_out(1:n-l) = 0; % Set first elements to zero +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/private/polyrev.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,26 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## This function is necessary for impinvar and invimpinvar of the signal package + +## Reverse the coefficients of a polynomial +function p_out = polyrev (p_in) + n = length(p_in); + for i=(1:n) + p_out(i) = p_in(n-i+1); + endfor +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/signal/inst/private/to_real.m Fri Sep 30 13:42:44 2011 +0000 @@ -0,0 +1,23 @@ +## Copyright 2007 R.G.H. Eschauzier <reschauzier@yahoo.com> +## Adapted by Carnë Draug on 2011 <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +## This function is necessary for impinvar and invimpinvar of the signal package + +## Round complex number to nearest real number +function p_out = to_real(p_in) + p_out = abs(p_in) .* sign(real(p_in)); +endfunction