/// <devdoc> /// Given the z-plane poles and zeros, compute the rational fraction (the top and bottom polynomial coefficients) /// of the filter transfer function in Z. /// </devdoc> private static Polynomials.RationalFraction ComputeTransferFunction(PolesAndZeros zPlane) { var topCoeffsComplex = Polynomials.Expand(zPlane.Zeros); var bottomCoeffsComplex = Polynomials.Expand(zPlane.Poles); // If the GetRealPart() conversion fails because the coffficients are not real numbers, the poles // and zeros are not complex conjugates. return new Polynomials.RationalFraction { Top = topCoeffsComplex.Select(x => GetRealPart(x)).ToArray(), Bottom = bottomCoeffsComplex.Select(x => GetRealPart(x)).ToArray(), }; }
/// <devdoc> /// Maps the poles and zeros from the s-plane (<c>sPlane)</c>to the z-plane. /// </devdoc> private static PolesAndZeros MapSPlaneToZPlane(PolesAndZeros sPlane, SToZMappingMethod sToZMappingMethod) { switch (sToZMappingMethod) { case SToZMappingMethod.BilinearTransform: { return new PolesAndZeros { Poles = DoBilinearTransform(sPlane.Poles), Zeros = Extend(DoBilinearTransform(sPlane.Zeros), sPlane.Poles.Length, new Complex(-1, 0)) }; } case SToZMappingMethod.MatchedZTransform: { return new PolesAndZeros { Poles = DoMatchedZTransform(sPlane.Poles), Zeros = DoMatchedZTransform(sPlane.Zeros) }; } default: throw new System.ComponentModel.InvalidEnumArgumentException("sToZMappingMethod", (int)sToZMappingMethod, typeof(SToZMappingMethod)); } }
/// <summary> /// Transforms the s-plane poles of the prototype filter into the s-plane poles and zeros for a filter /// with the specified pass type and cutoff frequencies. /// </summary> /// <param name="poles">The s-plane poles of the prototype LP filter.</param> /// <param name="type">The filter type (supports only low-pass, high-pass, band-pass and band-stop.</param> /// <param name="fcf1"> /// The relative filter cutoff frequency for low-pass/high-pass, lower cutoff frequency for band-pass/band-stop. /// The cutoff frequency is specified relative to the sampling rate and must be between 0 and 0.5. /// </param> /// <param name="fcf2"> /// Ignored for low-pass/high-pass, the relative upper cutoff frequency for band-pass/band-stop. /// The cutoff frequency is specified relative to the sampling rate and must be between 0 and 0.5. /// </param> /// <param name="preWarp"> /// <see langword="true"/>true to enable prewarping of the cutoff frequencies (for a later bilinear transform from s-plane to z-plane), /// <see langword="false"/> to skip prewarping (for a later matched Z-transform). /// </param> /// <returns>The s-plane poles and zeros for the specific filter.</returns> private static PolesAndZeros Normalize(Complex[] poles, FilterKind type, double fcf1, double fcf2, bool preWarp) { bool fcf2IsRelevant = type == FilterKind.BandPass || type == FilterKind.BandStop; Debug.Assert(fcf1 > 0 && fcf1 < 0.5); Debug.Assert(!fcf2IsRelevant || (fcf2 > 0 && fcf2 < 0.5)); int n = poles.Length; double fcf1Warped = Math.Tan(Math.PI * fcf1) / Math.PI; double fcf2Warped = fcf2IsRelevant ? Math.Tan(Math.PI * fcf2) / Math.PI : 0; double w1 = 2 * Math.PI * (preWarp ? fcf1Warped : fcf1); double w2 = 2 * Math.PI * (preWarp ? fcf2Warped : fcf2); if (type == FilterKind.LowPass) { return new PolesAndZeros { Poles = poles.Select(x => x * w1).ToArray(), Zeros = new Complex[0] }; } if (type == FilterKind.HighPass) { var sPlane = new PolesAndZeros(n, n); for (int i = 0; i < n; ++i) sPlane.Poles[i] = w1 / poles[i]; return sPlane; } if (type == FilterKind.BandPass) { double w0 = Math.Sqrt(w1 * w2); double bw = w2 - w1; var sPlane = new PolesAndZeros(n * 2, n); for (int i = 0; i < n; ++i) { var hba = poles[i] * (bw / 2); var temp = Complex.Sqrt(1 - ((w0 / hba) * (w0 / hba))); sPlane.Poles[i] = hba * (temp + 1); sPlane.Poles[n + i] = hba * (1 - temp); } return sPlane; } if (type == FilterKind.BandStop) { double w0 = Math.Sqrt(w1 * w2); double bw = w2 - w1; var sPlane = new PolesAndZeros(n * 2, n * 2); for (int i = 0; i < n; ++i) { var hba = (bw / 2) / poles[i]; var temp = Complex.Sqrt(1 - ((w0 / hba) * (w0 / hba))); sPlane.Poles[i] = hba * (temp + 1); sPlane.Poles[n + i] = hba * (1 - temp); } for (int i = 0; i < n; i++) { sPlane.Zeros[i] = new Complex(0, w0); sPlane.Zeros[n + i] = new Complex(0, -w0); } return sPlane; } throw new System.ComponentModel.InvalidEnumArgumentException("type", (int)type, typeof(FilterKind)); }