diff --git a/src/Numerics/LtiSystems/TransferFunctionDiscrete.cs b/src/Numerics/LtiSystems/TransferFunctionDiscrete.cs deleted file mode 100644 index c050eca8..00000000 --- a/src/Numerics/LtiSystems/TransferFunctionDiscrete.cs +++ /dev/null @@ -1,968 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using MathNet.Numerics; - -namespace MathNet.Numerics.LtiSystems -{ - /// Class for LTI discrete transfer functions - public class TransferFunctionDiscrete - { - - private double[] _num; - - private double[] _den; - - /// - /// numberator (input dependent) Polynomial coefficients as array - /// in order - /// => index high ... index low - /// => [n], [n-1], +..., [0] - /// => 1 + q^-1 + ... + q^-n - /// - public double[] num - { - get - { - return _num; - } - set - { - _num = cutTrailingZeros(value); - shiftNumDenIfPossible(); - } - } - - /// den (state dependent) Polynomial coefficients as array - /// in order - /// => index high ... index low - /// => [n], [n-1], +..., [0] - /// => 1 + q^-1 + ... + q^-n - /// - public double[] den - { - get - { - return _den; - } - set - { - _den = cutTrailingZeros(value); - shiftNumDenIfPossible(); - } - } - - - - /// b (input dependent) Polynomial coefficients as array ( - public double[] b - { - get - { - return _num; - } - set - { - _num = cutTrailingZeros(value); - shiftNumDenIfPossible(); - } - } - - /// a (state dependent) Polynomial coefficients as array - public double[] a - { - get - { - return _den; - } - set - { - _den = cutTrailingZeros(value); - shiftNumDenIfPossible(); - } - } - - /// Internal FIR States -> updated in every response calculation - public double[] z_FIR { get; set; } - - /// Internal IIR States -> updated in every response calculation - public double[] z_IIR { get; set; } - - /// any name you want to give this transfer function - public string Name { get; set; } - - /// sampling time of discrete transfer function (default = 1) - public double Ts { get; set; } - - /// variable for transfer function so far all tf's are in the q^-1 (or equivalently z^-1) form. Changing this will have NO nfluence besides displaying te TF - public string variable = "q^-1"; - - - - /// - /// Check if this Transfer Function is stable - /// - /// the tolerance for euclidian distance at which a pole/zero pair is considered to be canceling each other - /// false if system is unsable true if system is stable - public bool IsStable(double numTolerance = 1e-8) - { - - var p = GetPoles(); - - var z = GetZeros(); - var z_isCompensated = new bool[z.Length]; - for (int j = 0; j < p.Length; j++) - { - // check if pole would lead to unstable behaviour - if (p[j].Magnitude > 1.0) - { - - // init some values - double minDistance = Double.PositiveInfinity; - int idxMinDistanceZero = -1; - - // analyze the distance between each zero and the pole now - for (int i = 0; i < z.Length; i++) - { - // check if pole has already been used for compensation - if (z_isCompensated[i]) - continue; - - // calculate geometrical distance between each zero and the pole now and store the closest neighbour - var dist = (p[j] - z[i]).Magnitude; - if (dist < minDistance) - { - minDistance = dist; - idxMinDistanceZero = i; - } - } - - // if closest neighbour is too far away to compensate the unstable pole - if (minDistance >= numTolerance) - return false; // the system is unsable - else - z_isCompensated[idxMinDistanceZero] = true; // if not: mark the zero as already used for compensation - - } - - } - - return true; - - } - - - - /// constructor setting no properties at all - public TransferFunctionDiscrete() - { - this.Ts = 1.0; - } - - /// - /// constructor setting a and b vectors as well as initializing the z_FIR and z_IIR states - /// - public TransferFunctionDiscrete(double b_in, double[] a_in, double Ts_in = 1.0d) - { - if (a_in == null) - throw new ArgumentNullException("a_in"); - - this.a = (double[])a_in.Clone(); - this.b = new double[1]; - this.b[0] = b_in; - this.z_IIR = new double[a_in.Length]; - this.z_FIR = new double[1]; - this.Ts = Ts_in; - } - - /// - /// constructor setting a and b vectors as well as initializing the z_FIR and z_IIR states - /// - public TransferFunctionDiscrete(double[] b_in, double a_in, double Ts_in = 1.0d) - { - if (b_in == null) - throw new ArgumentNullException("b_in"); - - this.a = new double[1]; - this.a[0] = a_in; - this.b = (double[])b_in.Clone(); - this.z_IIR = new double[1]; - this.z_FIR = new double[b_in.Length]; - this.Ts = Ts_in; - } - - /// - /// constructor setting a and b vectors as well as initializing the z_FIR and z_IIR states - /// - public TransferFunctionDiscrete(double b_in, double a_in, double Ts_in = 1.0d) - { - this.a = new double[1]; - this.a[0] = a_in; - this.b = new double[1]; - this.b[0] = b_in; - this.z_IIR = new double[1]; - this.z_FIR = new double[1]; - this.Ts = Ts_in; - } - - /// - /// constructor setting a and b vectors as well as initializing the z_FIR and z_IIR states - /// - public TransferFunctionDiscrete(double[] b_in, double[] a_in, double Ts_in = 1.0d) - { - if (b_in == null) - throw new ArgumentNullException("b_in"); - - if (a_in == null) - throw new ArgumentNullException("a_in"); - - this.a = (double[])a_in.Clone(); - this.b = (double[])b_in.Clone(); - this.z_IIR = new double[a_in.Length]; - this.z_FIR = new double[b_in.Length]; - this.Ts = Ts_in; - } - - - /// - /// Adds delay to the numerator array (shifting the values by d steps) - /// - /// integer value of daly to add to this TF - public void AddDelay(int d) - { - double[] b_new = new double[b.Length + d]; - b.CopyTo(b_new, d); - b = b_new; - } - - #region Helpers - /// - /// if num and den both start at a later step (e.G highest power num = q^-3 and highest power den = q^-4), the whole tf can be shifted by n (3) steps - /// - private void shiftNumDenIfPossible() - { - var offset = 0; - if (_num == null || _den == null) - return; - - var n = Math.Min(_num.Length, _den.Length); - for (int i = 0; i < n; i++) - { - if (num[i] == 0.0d && _den[i] == 0.0d) - offset = i + 1; - else - break; - } - - if (offset > 0) - { - double[] tmp1 = new double[_num.Length - offset]; - Array.Copy(_num, offset, tmp1, 0, tmp1.Length); - double[] tmp2 = new double[_den.Length - offset]; - Array.Copy(_den, offset, tmp2, 0, tmp2.Length); - _num = tmp1; - _den = tmp2; - } - } - - private double[] cutTrailingZeros(double[] vIn) - { - int lengthNew = vIn.Length; - - for (int i = vIn.Length - 1; i >= 0; i--) - { - if (vIn[i] != 0) - { - lengthNew = i + 1; - break; - } - } - - var v = new double[lengthNew]; - Array.Copy(vIn, v, lengthNew); - - return v; - } - - /// - /// checks and adjusts internal states to a and b arrays - /// - private void checkStateSizes() - { - if (this.z_IIR == null) - this.z_IIR = new double[this.a.Length]; - - if (this.z_FIR == null) - this.z_FIR = new double[this.b.Length]; - - if (this.a.Length != this.z_IIR.Length) - this.z_IIR = new double[this.a.Length]; - - if (this.b.Length != this.z_FIR.Length) - this.z_FIR = new double[this.b.Length]; - - if (this.a.Length != this.z_IIR.Length) - this.z_IIR = new double[this.a.Length]; - } - #endregion Helpers - - #region Operators - - /// - /// LTI System theory division of a transfer function object by a scalar - /// - /// transfer function - /// scalar for divison - /// new transfer function object divided by k - public static TransferFunctionDiscrete operator /(TransferFunctionDiscrete G1, double k) - { - Polynomial A1 = new Polynomial(G1.a); - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete(G1.b, (A1 * k).ToArray(), G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - /// - /// LTI System theory division of a scalar by a transfer function object - /// - /// scalar value - /// transfer function for division - /// new transfer function object - public static TransferFunctionDiscrete operator /(double k, TransferFunctionDiscrete G1) - { - Polynomial A1 = new Polynomial(G1.a); - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete((A1 * k).ToArray(), G1.b, G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - /// - /// LTI System theory multiplication of a transfer function object by a scalar - /// - /// transfer function - /// scalar for multiplication - /// new transfer function object - public static TransferFunctionDiscrete operator *(TransferFunctionDiscrete G1, double k) - { - Polynomial B1 = new Polynomial(G1.b); - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete((B1 * k).ToArray(), G1.a, G1.Ts) - { - Name = G1.Name - }; - return Gres; - - } - - /// - /// LTI System theory multiplication of a transfer function object by a scalar - /// - /// scalar for multiplication - /// transfer function - /// new transfer function object - public static TransferFunctionDiscrete operator *(double k, TransferFunctionDiscrete G1) - { - Polynomial B1 = new Polynomial(G1.b); - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete((B1 * k).ToArray(), G1.a, G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - - /// - /// LTI System theory substraction of a transfer function with a scalar - /// - /// transfer function - /// scalar - /// new transfer function object - public static TransferFunctionDiscrete operator -(TransferFunctionDiscrete G1, double k) - { - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A_res = (A1); - Polynomial B_res = B1 - (A1 * k); - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - /// - /// LTI System theory substraction of a scalar by a transfer function - /// - /// scalar - /// transfer function - /// new transfer function object - public static TransferFunctionDiscrete operator -(double k, TransferFunctionDiscrete G1) - { - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A_res = (A1); - Polynomial B_res = (A1 * k) - B1; - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - /// - /// LTI System theory addition of a transfer function with a scalar - /// - /// transfer function - /// scalar - /// new transfer function object - public static TransferFunctionDiscrete operator +(TransferFunctionDiscrete G1, double k) - { - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A_res = (A1); - Polynomial B_res = (A1 * k) + B1; - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - /// - /// LTI System theory addition of a transfer function with a scalar - /// - /// transfer function - /// scalar - /// new transfer function object - public static TransferFunctionDiscrete operator +(double k, TransferFunctionDiscrete G1) - { - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A_res = (A1); - Polynomial B_res = B1 + (A1 * k); - - TransferFunctionDiscrete Gres = new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts) - { - Name = G1.Name - }; - return Gres; - } - - /// - /// LTI System theory addition of two transfer functions - /// - /// transfer function left - /// transfer function right - /// new transfer function object - public static TransferFunctionDiscrete operator +(TransferFunctionDiscrete G1, TransferFunctionDiscrete G2) - { - if (Math.Abs(G1.Ts - G2.Ts) > 1e-12) - throw new ArgumentException(String.Format("The two supplied transfer functions do not have equal sampling times. G1.Ts = {0} G2.Ts = {1}", G1.Ts, G2.Ts)); - - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A2 = new Polynomial(G2.a); - Polynomial B2 = new Polynomial(G2.b); - - Polynomial A_res = (A1 * A2); - Polynomial B_res = (B1 * A2) + (B2 * A1); - - return new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts); - } - - /// - /// LTI System theory substraction of two transfer functions - /// - /// transfer function left - /// transfer function right - /// new transfer function object - public static TransferFunctionDiscrete operator -(TransferFunctionDiscrete G1, TransferFunctionDiscrete G2) - { - if (Math.Abs(G1.Ts - G2.Ts) > 1e-12) - throw new ArgumentException(String.Format("The two supplied transfer functions do not have equal sampling times. G1.Ts = {0} G2.Ts = {1}", G1.Ts, G2.Ts)); - - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A2 = new Polynomial(G2.a); - Polynomial B2 = new Polynomial(G2.b); - - Polynomial A_res = (A1 * A2); - Polynomial B_res = (B1 * A2) - (B2 * A1); - - return new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts); - } - - /// - /// LTI System theory addition of two transfer functions - /// - /// transfer function left - /// transfer function right - /// new transfer function object - public static TransferFunctionDiscrete operator *(TransferFunctionDiscrete G1, TransferFunctionDiscrete G2) - { - if (Math.Abs(G1.Ts - G2.Ts) > 1e-12) - throw new ArgumentException(String.Format("The two supplied transfer functions do not have equal sampling times. G1.Ts = {0} G2.Ts = {1}", G1.Ts, G2.Ts)); - - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A2 = new Polynomial(G2.a); - Polynomial B2 = new Polynomial(G2.b); - - Polynomial A_res = A1 * A2; - Polynomial B_res = B1 * B2; - - return new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts); - } - - /// - /// LTI System theory division of two transfer functions - /// - /// transfer function left - /// transfer function right - /// new transfer function object - public static TransferFunctionDiscrete operator /(TransferFunctionDiscrete G1, TransferFunctionDiscrete G2) - { - if (Math.Abs(G1.Ts - G2.Ts) > 1e-12) - throw new ArgumentException(String.Format("The two supplied transfer functions do not have equal sampling times. G1.Ts = {0} G2.Ts = {1}", G1.Ts, G2.Ts)); - - Polynomial A1 = new Polynomial(G1.a); - Polynomial B1 = new Polynomial(G1.b); - - Polynomial A2 = new Polynomial(G2.a); - Polynomial B2 = new Polynomial(G2.b); - - Polynomial A_res = A1 * B2; - Polynomial B_res = B1 * A2; - - return new TransferFunctionDiscrete(B_res.ToArray(), A_res.ToArray(), G1.Ts); - } - #endregion - - /// calculates y_k = G(q^-1) * x_k for a given x_k array - public IEnumerable CalcResponse(IEnumerable x) - { - return this.CalcResponse(x.ToArray()); - } - - /// calculates y_k = G(q^-1) * x_k for a given x_k array - public double[] CalcResponse(double[] x) - { - // this is basically a two step convolution and could be replaced by a - // conv implementation. - // however... this code works fine and replacing it would be more work - - double y_now = 0.0d; - int idx_a = 0; - int idx_b = 0; - double[] y = new double[x.Length]; - - this.checkStateSizes(); - - // Loop all inputs - for (int ii_x = 0; ii_x < x.Length; ii_x++) - { - y_now = 0.0d; - idx_b = 0; - - // loop through b-matrix until end of momentary tempx-array - for (int ii_b = 0; ii_b <= ii_x && idx_b < b.Length; ii_b++) - { - - z_FIR[idx_b] = x[ii_x - ii_b]; - y_now += b[idx_b] * z_FIR[idx_b]; - idx_b++; - } - - - // start at second position, since it's the a-matrix - idx_a = 1; - // loop for a-matrix - for (int ii_a = 0; ii_a <= (ii_x - 1) && idx_a < a.Length; ii_a++) - { - z_IIR[idx_a] = y[(ii_x - 1) - ii_a]; - y_now -= a[idx_a] * z_IIR[idx_a]; - idx_a++; - } - // write result - y[ii_x] = (y_now / a[0]); - z_IIR[0] = y[ii_x]; - } - return (y); - } - - // Todo: Implement FiltFilt - /* - /// - /// A wrapper for the StaticFilters.FiltFilt method using the internal a and b arrays - /// - /// The data to filter - /// initial state coefficients null for aotomatic generation via steady state solution - /// the number of datapoints to pad at each side use less than 0 for Math.Max(a.Length, b.Length) * 3 - /// The filterd data - /// - /// In order to prevent transients at the end or start of the sequence we have to pad it - /// The padding is done by rotating the sequence by 180° at the ends and append it to the data - /// - public double[] FiltFilt(double[] data, double[] zi = null, int padlen = 0) - { - if (this.a == null || this.a.Length == 0) - throw new Exception("This transfer function has no a array with data"); - if (this.b == null || this.b.Length == 0) - throw new Exception("This transfer function has no a array with data"); - - return StaticFilters.FiltFilt(data, this.a, this.b, zi, padlen); - } - */ - - #region Dynamics - - /// - /// returns the impulse response with nSteps for the tf model - /// - /// number of steps for impulse response - /// - public double[] Impulse(int nSteps) - { - - var Inp = new double[nSteps]; - Inp[0] = 1.0; - - var ImpulseResponse = this.CalcResponse(Inp); - return (ImpulseResponse); - } - - /// - /// returns the impulse response with nSettling * 1.3 steps for the tf model - /// - public double[] Impulse() - { - - var nSteps = Convert.ToInt32((double)CalcSettlingSteps() * 1.3); - if (nSteps <= 0) - return null; - var Inp = new double[nSteps]; - Inp[0] = 1.0; - - var ImpulseResponse = this.CalcResponse(Inp); - return (ImpulseResponse); - } - - - public Complex[] Bode(int nPoints = 100) - { - // substituting z = exp(j * omega * Ts) - var omega_vec = Generate.LinearSpaced(nPoints, 0, 2 * Math.PI * 1 / Ts); - - return Bode(omega_vec); - } - - public Complex[] Bode(int nPoints, out double[] omega_vec) - { - // substituting z = exp(j * omega * Ts) - omega_vec = Generate.LinearSpaced(nPoints, 0, 2 * Math.PI * 1 / Ts); - - - return Bode(omega_vec); - } - - public Complex[] Bode(double[] omega_vec) - { - - var nPoints = omega_vec.Length; - - double omega; - double expVal; - Complex zVal; - Complex denVal; - Complex numVal; - - var bodeVal = new Complex[nPoints]; - - for (int idx = 0; idx < nPoints; idx++) - { - - - omega = omega_vec[idx]; - - zVal = new Complex(0.0, 0.0); - - denVal = new Complex(0.0, 0.0); - for (int ii = 0; ii < a.Length; ii++) - { - expVal = ii * omega * Ts; - zVal = new Complex(0.0, expVal); - - denVal += a[ii] * zVal.Exp(); - } - - numVal = new Complex(0.0, 0.0); - for (int ii = 0; ii < b.Length; ii++) - { - expVal = ii * omega * Ts; - zVal = new Complex(0.0, expVal); - - numVal += b[ii] * zVal.Exp(); - } - bodeVal[idx] = numVal / denVal; - } - - return bodeVal; - } - - /// The poles resulting from the denominator Polynomial root - public Complex[] GetPoles() - { - Polynomial a_poly = new Polynomial(a, isFlip:true); - Complex[] r = a_poly.GetRoots(); - return r; - } - - /// The zeros resulting from the nominator Polynomial root - public Complex[] GetZeros() - { - Polynomial b_poly = new Polynomial(b, isFlip:true); - Complex[] r = b_poly.GetRoots(); - return r; - } - - - /// - /// calculate the number of steps the system will need until it can be assumed to be settled - /// - /// tolerance in decimal percent at which to assume that the system is settled (default = 0.3) - /// maximum number of steps to simulate (default = 500000) - /// number of steps at which the system is assumed to be settled, or 0 if unstable - public int CalcSettlingSteps(double tol = 0.03, int n_max = 500000) - { - - // init settling time as zero for never settled - int n_sttl = 0; - - // if the system is unstable return zero since the system will never be settled - if (this.IsStable() == false) - return 0; - - int n_sim = 0; - - double[] dampVals = GetDampings(out double[] EigenFrequencys); - - double dampWorst = dampVals.Min(); - - //for (int ii = 1; ii < dampVals.Length; ii++) - // dampWorst = dampWorst * dampVals[ii]; - - double t_simFull; - - // appromate a settling time based on damping - var t_stlDamp = -Math.Log(tol) / dampWorst; - - // approximate a settling time from time constants - var tau = new double[dampVals.Length]; - for (int ii = 0; ii < tau.Length; ii++) - tau[ii] = 1.0 / (dampVals[ii] * EigenFrequencys[ii]); - - // approx after 5 * biggest time constant - var t_stlTimeConst = tau.Max() * 5; - - // choose bigger approximation - t_simFull = Math.Max(t_stlTimeConst, t_stlDamp); - - // recalculate to number of steps - int nStepsBase = (int)Math.Ceiling(t_simFull / Ts); - - - // simulate impulse responses with n*10*nStepsBase time steps - // incrementing n if necessary until steady state is reached - n_sim = nStepsBase <= 0 ? 5 : nStepsBase; - int count = 0; - while (count < 10 && n_sttl == 0) - { - if (n_sim > n_max) - return n_sttl; - - n_sim = 10 * n_sim; - - double[] dirac_sim = new double[n_sim]; - dirac_sim[0] = 1.0; - var tmp_outp = this.CalcResponse(dirac_sim); - - int idxPos = n_sim - 1; - - // find first step beeing bigger than tolerance - while (idxPos > 0 && n_sttl == 0) - { - if (tmp_outp[idxPos] > tol) - n_sttl = idxPos; - - idxPos--; - } - count++; - } - return n_sttl; - - } - - #endregion Dynamics - - - #region Dampings - /// - /// gets the damping coefficients from this transfer function, - /// since all transfer functions so far are discrete time, - /// these values do not directly translate to lambda. - /// the theoretical recalculation is: - /// Z = -cos(angle(log(lambda))) - /// - /// Array of damping values for this transfer function - public double[] GetDampings() - { - return GetDampings(out double[] f); - } - - /// - /// gets the damping coefficients from this transfer function, - /// since all transfer functions so far are discrete time, - /// these values do not directly translate to lambda. - /// the theoretical recalculation is: - /// Z = -cos(angle(log(lambda))) - /// - /// Array of damping values for this transfer function - public double[] GetDampings(out double[] wn) - { - - var r = GetPoles().Clone() as Complex[]; - var s = new Complex[r.Length]; - var f = new double[r.Length]; - var z = new double[r.Length]; - - for (int idx = 0; idx < r.Length; idx++) - { - s[idx] = Complex.Log(r[idx]) / Ts; - f[idx] = s[idx].Magnitude; - z[idx] = -s[idx].Real / f[idx]; - } - - wn = (double[])f.Clone(); - - return z; - - } - - - #endregion Dampings - - - #region displaying - /// - /// - /// - /// - public string DispTF() - { - return (DispTF(this.b, this.a, this.Name, this.variable.Substring(0, variable.Length - 1))); - } - - public string NumString() - { - var varStr = this.variable.Substring(0, variable.Length - 1); - var num = b.Clone() as double[]; - return getFractString(num, varStr); - } - - public string DenString() - { - var varStr = this.variable.Substring(0, variable.Length - 1); - var den = a.Clone() as double[]; - return getFractString(den, varStr); - } - - - private static string getFractString(double[] num, string varStr) - { - string str1; - string str2; - string strNum = ""; - for (int item = 0; item < num.Length; item++) - { - if (num[item] == 0) - continue; - - //str2 = Math.Abs(num[item]).ToString(); - str2 = Math.Abs(num[item]).ToString("0.######"); - if (item == 0) - { - if (num[item] < 0) - str1 = "-"; - else - str1 = ""; - - strNum = String.Concat(strNum, str1, str2); - } - else - { - if (num[item] > 0) - str1 = " + "; - else - str1 = " - "; - strNum = String.Concat(strNum, str1, str2, varStr, item.ToString()); - } - } - - if (strNum.StartsWith("+") || strNum.StartsWith(" ")) - strNum = strNum.Substring(1); - - return strNum; - } - - - - /// - /// - /// - /// - /// - /// - /// - public static string DispTF(double[] num, double[] den, string name, string varStr = " q^-") - { - - string strNum = getFractString(num, varStr); - string strDen = getFractString(den, varStr); - string strHead = ""; - - if (String.IsNullOrEmpty(name)) - strHead = "TF = "; - else - strHead = name; - - int nbar = Math.Max(strDen.Length, strNum.Length); - - string strBar = new String('-', nbar); - string strOut = String.Concat(strHead, "\n\n", strNum, '\n', strBar, '\n', strDen); - - return (strOut); - } - - #endregion displaying - - } -}