/// <summary> /// Calculate the orderPlus1 of Transfer function and βmax from the filter specification. /// H. G. Dimopoulos, Analog Electronic Filters: theory, design and synthesis, Springer, 2012. pp.41 /// </summary> /// <param name="h0">gain of 0 Hz</param> /// <param name="hc">gain of cutoff frequency</param> /// <param name="hs">stopband gain</param> /// <param name="ωc">cut off frequency. rad/s</param> /// <param name="ωs">stopband frequency. rad/s</param> public ButterworthDesign(double h0, double hc, double hs, double ωc, double ωs, ApproximationBase.BetaType bt) { if (h0 <= 0) { throw new System.ArgumentOutOfRangeException("h0"); } if (hc <= 0 || h0 <= hc) { throw new System.ArgumentOutOfRangeException("hc"); } if (hs <= 0 || hc <= hs) { throw new System.ArgumentOutOfRangeException("hs"); } if (ωs <= ωc) { throw new System.ArgumentOutOfRangeException("ωs"); } mH0 = h0; mHc = hc; mHs = hs; mωc = ωc; mΩs = ωs / ωc; mN = CalcNfMin(); switch (bt) { case ApproximationBase.BetaType.BetaMax: mβ = Calcβmax(); break; case ApproximationBase.BetaType.BetaMin: mβ = Calcβmin(); break; } }
/// <summary> /// ローパスフィルターの設計。 /// </summary> /// <param name="mG0">0Hzのゲイン (dB)</param> /// <param name="mGc">カットオフ周波数のゲイン (dB)</param> /// <param name="mGs">ストップバンドのゲイン (dB)</param> /// <param name="mFc">カットオフ周波数(Hz)</param> /// <param name="mFs">ストップバンドの下限周波数(Hz)</param> /// <returns></returns> public bool DesignLowpass(double g0, double gc, double gs, double fc, double fs, FilterType ft, ApproximationBase.BetaType betaType) { // Hz → rad/s double ωc = fc * 2.0 * Math.PI; double ωs = fs * 2.0 * Math.PI; // dB → linear double h0 = Math.Pow(10, g0 / 20); double hc = Math.Pow(10, gc / 20); double hs = Math.Pow(10, gs / 20); ApproximationBase filter; switch (ft) { case FilterType.Butterworth: filter = new ButterworthDesign(h0, hc, hs, ωc, ωs, betaType); break; case FilterType.Chebyshev: filter = new ChebyshevDesign(h0, hc, hs, ωc, ωs, betaType); break; case FilterType.Pascal: filter = new PascalDesign(h0, hc, hs, ωc, ωs, betaType); break; case FilterType.InverseChebyshev: filter = new InverseChebyshevDesign(h0, hc, hs, ωc, ωs, betaType); break; case FilterType.Cauer: filter = new CauerDesign(h0, hc, hs, ωc, ωs, betaType); break; default: throw new NotImplementedException(); } mOrder = filter.Order; mNumeratorConstant = filter.TransferFunctionConstant(); // 伝達関数のポールの位置。 mPoleList.Clear(); for (int i = 0; i < filter.NumOfPoles(); ++i) { mPoleList.Add(filter.PoleNth(i)); } // 伝達関数の零の位置。 mZeroList.Clear(); for (int i = 0; i < filter.NumOfZeroes(); ++i) { mZeroList.Add(filter.ZeroNth(i)); } TransferFunction = (WWComplex s) => { return(TransferFunctionValue(WWComplex.Div(s, ωc))); }; PoleZeroPlotTransferFunction = (WWComplex s) => { return(TransferFunctionValue(s)); }; { // Unit Step Function WWPolynomial.PolynomialAndRationalPolynomial H_s; { var unitStepRoots = new WWComplex[filter.NumOfPoles() + 1]; for (int i = 0; i < filter.NumOfPoles(); ++i) { var p = filter.PoleNth(i); unitStepRoots[i] = p; } unitStepRoots[unitStepRoots.Length - 1] = WWComplex.Zero(); var numerCoeffs = new List <WWComplex>(); if (filter.NumOfZeroes() == 0) { numerCoeffs.Add(new WWComplex(mNumeratorConstant, 0)); } else { numerCoeffs.AddRange(WWPolynomial.RootListToCoeffList(mZeroList.ToArray(), new WWComplex(mNumeratorConstant, 0))); } H_s = WWPolynomial.Reduction(numerCoeffs.ToArray(), unitStepRoots); } var H_fraction = WWPolynomial.PartialFractionDecomposition(H_s.numerCoeffList, H_s.denomRootList); var H_integer = FirstOrderComplexRationalPolynomial.CreateFromCoeffList(H_s.coeffList); UnitStepResponseFunction = (double t) => { return(InverseLaplaceTransformValue(H_fraction, H_integer, t)); }; } { // 伝達関数 Transfer function WWPolynomial.PolynomialAndRationalPolynomial H_s; { var H_Roots = new WWComplex[filter.NumOfPoles()]; for (int i = 0; i < filter.NumOfPoles(); ++i) { var p = filter.PoleNth(i); H_Roots[i] = p; } var numerCoeffs = new List <WWComplex>(); if (filter.NumOfZeroes() == 0) { numerCoeffs.Add(new WWComplex(mNumeratorConstant, 0)); } else { numerCoeffs.AddRange(WWPolynomial.RootListToCoeffList(mZeroList.ToArray(), new WWComplex(mNumeratorConstant, 0))); } H_s = WWPolynomial.Reduction(numerCoeffs.ToArray(), H_Roots); } var H_fraction = WWPolynomial.PartialFractionDecomposition(H_s.numerCoeffList, H_s.denomRootList); var H_integer = FirstOrderComplexRationalPolynomial.CreateFromCoeffList(H_s.coeffList); ImpulseResponseFunction = (double t) => { return(InverseLaplaceTransformValue(H_fraction, H_integer, t)); }; #if true mH_PFD = new List <FirstOrderComplexRationalPolynomial>(); for (int i = 0; i < H_fraction.Count; ++i) { mH_PFD.Add(H_fraction[i]); if (1 == H_integer.Count && i == H_fraction.Count / 2 - 1) { mH_PFD.Add(H_integer[0]); } } #else mH_PFD = FirstOrderComplexRationalPolynomial.Add(H_fraction, H_integer); #endif TimeDomainFunctionTimeScale = 1.0 / filter.CutoffFrequencyHz(); if (NumOfZeroes() == 0) { // 共役複素数のペアを組み合わせて伝達関数の係数を全て実数にする。 // s平面のjω軸から遠い項から並べる。 mRealPolynomialList.Clear(); if ((H_fraction.Count() & 1) == 1) { // 奇数。 int center = H_fraction.Count() / 2; mRealPolynomialList.Add(H_fraction[center]); for (int i = 0; i < H_fraction.Count() / 2; ++i) { mRealPolynomialList.Add(WWPolynomial.Mul( H_fraction[center - i - 1], H_fraction[center + i + 1])); } } else { // 偶数。 int center = H_fraction.Count() / 2; for (int i = 0; i < H_fraction.Count() / 2; ++i) { mRealPolynomialList.Add(WWPolynomial.Mul( H_fraction[center - i - 1], H_fraction[center + i])); } } #if true System.Diagnostics.Debug.Assert(H_integer.Count == 0); #else { // integral polynomialは全て実数係数の多項式。単に足す。 foreach (var p in H_integer) { mRealPolynomialList.Add(p); } } #endif } else { // 共役複素数のペアを組み合わせて伝達関数の係数を全て実数にする。 // s平面のjω軸から遠い項から並べる。 // zeroListとPoleListを使って、実係数の2次多項式を作り、 // 伝達関数をこういう感じに実係数2次有理多項式の積の形にする。 // (s^2+c1)(s^2+c2) (s^2+c1) (s^2+c2) // H(s) = ──────────────────────── ⇒ ──────────── * ──────────── // (s^2+a1s+b1)(s^2+a2s+b2) (s^2+a1s+b1) (s^2+a2s+b2) mRealPolynomialList.Clear(); if ((mPoleList.Count & 1) == 1) { // 奇数。 int center = mPoleList.Count / 2; mRealPolynomialList.Add(new FirstOrderComplexRationalPolynomial( WWComplex.Zero(), WWComplex.Unity(), WWComplex.Unity(), WWComplex.Minus(mPoleList[center]))); for (int i = 0; i < mPoleList.Count / 2; ++i) { var p0 = new FirstOrderComplexRationalPolynomial( WWComplex.Unity(), WWComplex.Minus(mZeroList[center - i - 1]), WWComplex.Unity(), WWComplex.Minus(mPoleList[center - i - 1])); var p1 = new FirstOrderComplexRationalPolynomial( WWComplex.Unity(), WWComplex.Minus(mZeroList[center + i]), WWComplex.Unity(), WWComplex.Minus(mPoleList[center + i + 1])); var p = WWPolynomial.Mul(p0, p1); mRealPolynomialList.Add(p); } } else { // 偶数。 int center = mPoleList.Count / 2; for (int i = 0; i < mPoleList.Count / 2; ++i) { var p0 = new FirstOrderComplexRationalPolynomial( WWComplex.Unity(), WWComplex.Minus(mZeroList[center - i - 1]), WWComplex.Unity(), WWComplex.Minus(mPoleList[center - i - 1])); var p1 = new FirstOrderComplexRationalPolynomial( WWComplex.Unity(), WWComplex.Minus(mZeroList[center + i]), WWComplex.Unity(), WWComplex.Minus(mPoleList[center + i])); var p = WWPolynomial.Mul(p0, p1); mRealPolynomialList.Add(p); } } } return(true); } }
private bool GetParametersFromUI() { if (!double.TryParse(textBoxG0.Text, out mG0)) { MessageBox.Show("G0 parse error."); return(false); } if (!double.TryParse(textBoxGc.Text, out mGc) || mG0 <= mGc) { MessageBox.Show("Gc parse error. mGc must be smaller than mG0"); return(false); } if (!double.TryParse(textBoxGs.Text, out mGs) || mGc <= mGs) { MessageBox.Show("Gs parse error. mGs must be smaller than mGc"); return(false); } if (!TryParseNumberWithUnit(textBoxFc.Text, out mFc) || mFc <= 0) { MessageBox.Show("Fc parse error. Fc must be greater than 0"); return(false); } if (!TryParseNumberWithUnit(textBoxFs.Text, out mFs)) { MessageBox.Show("Fs parse error. Fs must be number."); return(false); } if (mFs <= 0 || mFs <= mFc) { MessageBox.Show("Fs parse error. Fs must be greater than Fc and greater than 0"); return(false); } mBetaType = ApproximationBase.BetaType.BetaMax; if (comboBoxOptimization.SelectedItem == comboBoxItemβmin) { mBetaType = ButterworthDesign.BetaType.BetaMin; } mFilterType = AnalogFilterDesign.FilterType.Butterworth; if (radioButtonFilterTypeChebyshev.IsChecked == true) { mFilterType = AnalogFilterDesign.FilterType.Chebyshev; } if (radioButtonFilterTypePascal.IsChecked == true) { mFilterType = AnalogFilterDesign.FilterType.Pascal; } if (radioButtonFilterTypeInverseChebyshev.IsChecked == true) { mFilterType = AnalogFilterDesign.FilterType.InverseChebyshev; } if (radioButtonFilterTypeCauer.IsChecked == true) { mFilterType = AnalogFilterDesign.FilterType.Cauer; } if (!TryParseNumberWithUnit(textBoxSamplingFrequency.Text, out mSamplingFrequency) || mSamplingFrequency <= 0) { MessageBox.Show("SamplingFrequency parse error. it must be greater than 0"); return(false); } return(true); }
public InverseChebyshevDesign(double h0, double hc, double hs, double ωc, double ωs, ApproximationBase.BetaType bt) { if (h0 <= 0) { throw new System.ArgumentOutOfRangeException("h0"); } if (hc <= 0 || h0 <= hc) { throw new System.ArgumentOutOfRangeException("hc"); } if (hs <= 0 || hc <= hs) { throw new System.ArgumentOutOfRangeException("hs"); } if (ωs <= ωc) { throw new System.ArgumentOutOfRangeException("ωs"); } mH0 = h0; mHc = hc; mHs = hs; mωc = ωc; mΩs = ωs / ωc; mN = CalcOrder(); // H. G. Dimopoulos, Analog Electronic Filters: theory, design and synthesis, Springer, 2012. pp.118 // pp.126のFig 3.17参照。 // calc ε switch (bt) { case ApproximationBase.BetaType.BetaMin: mε = 1.0 / Math.Sqrt(h0 * h0 / hs / hs - 1); break; case ApproximationBase.BetaType.BetaMax: { // Calc cn(Ωs) var cn = ChebyshevDesign.ChebyshevPolynomialCoefficients(mN); double cnΩs = 0; double Ω = 1; for (int i = 0; i < cn.Length; ++i) { cnΩs += Ω * cn[i]; Ω *= mΩs; } mε = 1.0 / cnΩs / Math.Sqrt(h0 * h0 / hc / hc - 1); } break; } }
/// <summary> /// Cauer Elliptic lowpass filter /// H. G. Dimopoulos, Analog Electronic Filters: theory, design and synthesis, Springer, 2012. pp.165 /// </summary> /// <param name="h0">gain of 0 Hz</param> /// <param name="hc">gain of cutoff frequency</param> /// <param name="hs">stopband gain</param> /// <param name="ωc">cut off frequency. rad/s</param> /// <param name="ωs">stopband frequency. rad/s</param> public CauerDesign(double h0, double hc, double hs, double ωc, double ωs, ApproximationBase.BetaType bt) { if (h0 <= 0) { throw new System.ArgumentOutOfRangeException("h0"); } if (hc <= 0 || h0 <= hc) { throw new System.ArgumentOutOfRangeException("hc"); } if (hs <= 0 || hc <= hs) { throw new System.ArgumentOutOfRangeException("hs"); } if (ωs <= ωc) { throw new System.ArgumentOutOfRangeException("ωs"); } mH0 = h0; mHc = hc; mHs = hs; mωc = ωc; mΩs = ωs / ωc; mN = CalcNfMin(); switch (bt) { case ApproximationBase.BetaType.BetaMax: mε = Calcβmax(); break; case ApproximationBase.BetaType.BetaMin: mε = Calcβmin(); mHc = hc = CalcHcMax(); break; } // H. G. Dimopoulos, Analog Electronic Filters: theory, design and synthesis, Springer, 2012. // pp.192 Equation 4.82 double Λ = 1.0 / 2.0 / Math.PI / mN * Math.Log((Math.Sqrt(mε * mε + 1) + 1) / (Math.Sqrt(mε * mε + 1) - 1)); // pp.192 // Equation 4.84 double σ = Functions.JacobiTheta1h(Λ, Functions.JacobiNomeQ(1.0 / mΩs)) / Functions.JacobiTheta0h(Λ, Functions.JacobiNomeQ(1.0 / mΩs)); int η = mN & 1; double k = 1.0 / mΩs; // 定数Cの計算。 // Equation 4.93 if (η == 0) { mC = mH0 / Math.Sqrt(1.0 + mε * mε) / Math.Pow(mΩs, mN); } else { mC = mH0 * σ / Math.Pow(mΩs, (2.0 * mN - 3.0) / 2); } for (int m = 1; m <= (mN - η) / 2; ++m) { double Ωzrm = Ω_ZR(m, k); double Ω0m = Ω_0(m, k, σ); mC *= Ωzrm * Ωzrm * Ω0m * Ω0m; } // 分子 // Equation 4.81 for (int i = 0; i < mN / 2; ++i) { int m = i + 1; double b = mΩs / Ω_ZR(m, k); mZeroList.Add(new WWComplex(0, -b)); mZeroList.Insert(0, new WWComplex(0, b)); } // 分母 if (η == 1) { // Equation 4.91 double Ω_R = σ * Math.Sqrt(mΩs); mPoleList.Add(new WWComplex(-Ω_R, 0)); } for (int i = 0; i < (mN / 2); ++i) { int m = i + 1; // Equation 4.90 double Ω0m = Ω_0(m, k, σ); double Q0m = Q_0(m, k, σ); double σHm = -Ω0m / 2.0 / Q0m; double ΩHm = Ω0m * Math.Sqrt(1.0 - 1.0 / 4.0 / Q0m / Q0m); mPoleList.Add(new WWComplex(σHm, ΩHm)); mPoleList.Insert(0, new WWComplex(σHm, -ΩHm)); } }
public PascalDesign(double h0, double hc, double hs, double ωc, double ωs, ApproximationBase.BetaType bt) { if (h0 <= 0) { throw new System.ArgumentOutOfRangeException("h0"); } if (hc <= 0 || h0 <= hc) { throw new System.ArgumentOutOfRangeException("hc"); } if (hs <= 0 || hc <= hs) { throw new System.ArgumentOutOfRangeException("hs"); } if (ωs <= ωc) { throw new System.ArgumentOutOfRangeException("ωs"); } if (bt != BetaType.BetaMax) { throw new System.ArgumentOutOfRangeException("This version of Pascal approximation only supports stopband optimized design"); } mH0 = h0; mHc = hc; mHs = hs; mωc = ωc; mΩs = ωs / ωc; double αmax = 20.0 * Math.Log10(mH0 / mHc); if (αmax < 0.01) { throw new System.ArgumentOutOfRangeException("H0:Hc gain difference is too small"); } else if (αmax < 0.1) { mPoleTable = mPoleTable0_01; } else if (αmax < 0.5) { mPoleTable = mPoleTable0_1; } else if (αmax < 1.0) { mPoleTable = mPoleTable0_5; } else if (αmax < 1.25) { mPoleTable = mPoleTable1; } else if (αmax < 1.5) { mPoleTable = mPoleTable1_25; } else { mPoleTable = mPoleTable1_5; } for (mN = 2; mN < 9; ++mN) { double stopbandGain = H(new WWComplex(0, mΩs)).Magnitude(); Console.WriteLine("n={0} G={1}", mN, stopbandGain); if (stopbandGain < mHs) { // フィルター完成。 return; } } throw new System.ArgumentOutOfRangeException("Filter is too steep to design"); }
public ChebyshevDesign(double h0, double hc, double hs, double ωc, double ωs, ApproximationBase.BetaType bt) { if (h0 <= 0) { throw new System.ArgumentOutOfRangeException("h0"); } if (hc <= 0 || h0 <= hc) { throw new System.ArgumentOutOfRangeException("hc"); } if (hs <= 0 || hc <= hs) { throw new System.ArgumentOutOfRangeException("hs"); } if (ωs <= ωc) { throw new System.ArgumentOutOfRangeException("ωs"); } mH0 = h0; mHc = hc; mHs = hs; mωc = ωc; mΩs = ωs / ωc; mN = CalcOrder(); // calc ε switch (bt) { case ApproximationBase.BetaType.BetaMax: mε = Math.Sqrt(h0 * h0 / hc / hc - 1); break; case ApproximationBase.BetaType.BetaMin: { // Calc cn(Ωs) var cn = ChebyshevPolynomialCoefficients(mN); double cnΩs = 0; double Ω = 1; for (int i = 0; i < cn.Length; ++i) { cnΩs += Ω * cn[i]; Ω *= mΩs; } mε = Math.Sqrt(h0 * h0 / hs / hs - 1) / cnΩs; } break; } }