using System; using System.Collections.Generic; using System.Text; namespace DatabaseExport { public class LinearizationFormula { private bool _bIsValid; public bool IsValid() { return _bIsValid; } public void MarkValid(bool bValid) { _bIsValid = bValid; } public enum Styles { IRTraccManual, IRTraccDiagnosticsZero, IRTraccZeroMMmV, IRTraccAverageOverTime, Polynomial, IRTraccCalFactor } // public enum SLICEWareStyles // { // Manual, // DiagnosticZeroMMmV, // ZeroMMmV, // AverageOverTime, // Polynomial // } // // Translation // public SLICEWareStyles SLICEWareStyle // { // get { return (SLICEWareStyles)_style; } // set { _style = (Styles)value; } // } public Styles Style { get; set; } = Styles.IRTraccDiagnosticsZero; public double PolynomialSensitivity { get; set; } = 1D; public double LinearizationExponent { get; set; } = 1D; // THIS IS MM/V, (UI has already been updated, we need to update the variable name) public double MMPerV { get; set; } public double MVAt0MM { get; set; } public double Slope { get; set; } public double Intercept { get; set; } public double CalibrationFactor { get; set; } public double ZeroPositionIntercept { get; set; } public LinearizationFormula() { ZeroPositionIntercept = 0D; CalibrationFactor = 0D; Intercept = 0D; _coefficients = new List(new double[] { 0, 0, 0, 0 }); _exponents = new List(new double[] { 0, 1, 2, 3 }); } public LinearizationFormula(LinearizationFormula copy) { UsemVOverVForPolys = copy.UsemVOverVForPolys; _bIsValid = copy._bIsValid; _coefficients = new List(copy._coefficients.ToArray()); _exponents = new List(copy._exponents.ToArray()); Intercept = copy.Intercept; LinearizationExponent = copy.LinearizationExponent; MMPerV = copy.MMPerV; MVAt0MM = copy.MVAt0MM; Slope = copy.Slope; Style = copy.Style; _coefficients = new List(copy._coefficients); _exponents = new List(copy._exponents); PolynomialSensitivity = copy.PolynomialSensitivity; ZeroPositionIntercept = copy.ZeroPositionIntercept; CalibrationFactor = copy.CalibrationFactor; } // public double GetCoefficient(double exponent) // { // for (int i = 0; i < _exponents.Count && i < _coefficients.Count; i++) // { // if (_exponents[i] == exponent) { return _coefficients[i]; } // } // return 0; // } // public void SetCoefficient(double exponent, double coefficient) // { // for (int i = 0; i < _exponents.Count && i < _coefficients.Count; i++) // { // if (_exponents[i] == exponent) { _coefficients[i] = coefficient; return; } // } // } // public double GetLinearizedValue(double input, double excitation) // { // if (Style != Styles.Polynomial && input <= 0) // { // //ir-tracc should never be < 0, however we may get readings less than zero due to // //noise and other factors, treat these as positive near to zero // input = .001; // } // //first linearize // input /= 1000D;//assume input is in mV and we want it in Volts // input = Math.Pow(input, LinearizationExponent); // switch (Style) // { // case Styles.IRTraccDiagnosticsZero: // return GetEUDiagnosticsZero(input); // case Styles.IRTraccManual: // return GetEUIRTraccManual(input); // case Styles.IRTraccZeroMMmV: // return GetEUZeroMMmV(input); // case Styles.IRTraccAverageOverTime: // return GetEUAverageOverTime(input); // case Styles.Polynomial: // return GetEUPolynomial(input, excitation); // case Styles.IRTraccCalFactor: // return GetEUIRTraccCalFactor(input); // default: // throw new NotSupportedException("unknown format: " + Style.ToString()); // } // } // private double GetEUIRTraccCalFactor(double volts) // { // return volts * CalibrationFactor + ZeroPositionIntercept; // } // private double GetEUIRTraccManual(double volts) // { // return (volts - Intercept) / Slope; // } public bool UsemVOverVForPolys { get; set; } = true; // private double GetEUZeroMMmV(double volts) // { // double input = MVAt0MM / 1000D; // input = Math.Pow(input, LinearizationExponent); // if (double.IsNaN(input) || double.IsNegativeInfinity(input) || double.IsPositiveInfinity(input)) // { // return volts * MMPerV; // } // else { return (volts * MMPerV - MMPerV * input); } // } private List _coefficients = new List(); private List _exponents = new List(); // private double GetEUPolynomial(double volts, double excitation) // { // //per J2517 // //3.4 Use of the Calibration Coefficients // //The potentiometer assembly should be re-installed in the dummy without any mechanical adjustment of the // //potentiometer. Prior to a crash test, the original zero offset level must be preserved by either not zeroing the // //potentiometer (by signal conditioning or post-processing) or the amount that was zeroed must be added during postprocessing. // //During the test the absolute voltage output time history should be recorded. This voltage signal is then // //converted to engineering units by: // //1. Convert voltage signal to mV/V at the sensor. This is the sensor reading S. // //2. Convert the sensor reading S to displacement D by using the equation: // //D = A*S^3 + B*S^2 + C*S + M (Eq. 2) // //where: // //D is the displacement relative to the thorax design position in mm // //S is the sensor output reading in mV/V // //A, B, C, and M are the calibration coefficients // //NOTE: Make sure to use sufficient significant digits on all coefficients to assure accuracy of the conversion to // //engineering units. It is recommended to use 5 significant digits (example 0.000012345). // //double mV = volts * 1000D; // //double gain = 1D; // //mV = mV / (gain * excitation); // //if (0 != PolynomialSensitivity && 1!= PolynomialSensitivity) { mV /= PolynomialSensitivity; } // //double eu = 0D; // //for (int i = 0; i < _coefficients.Count && i < _exponents.Count; i++) // //{ // // eu += _coefficients[i] * Math.Pow(mV, _exponents[i]); // //} // //return eu; // //CHANGED FOR GM TESTING // if (0 != PolynomialSensitivity && 1 != PolynomialSensitivity) // { // volts /= PolynomialSensitivity; // } // double voltsOverV = 0D; // if (UsemVOverVForPolys) // { // //convert to mV first // voltsOverV = (volts * 1000D) / excitation; // } // else // { // //used by GM // voltsOverV = volts / excitation; // } // double eu = 0; // for (int i = 0; i < _coefficients.Count && i < _exponents.Count; i++) // { // if (_exponents[i] != 0) // { // eu += _coefficients[i] * Math.Pow(voltsOverV, _exponents[i]); // } // else // { // eu += _coefficients[i]; // } // } // return eu; // } // /// // /// MvAt0MM set at diagnostics // /// // /// // /// // private double GetEUDiagnosticsZero(double volts) // { // //double input = MVAt0MM/1000D; // //input = System.Math.Pow(input,LinearizationExponent); // double input = double.NaN; // if (double.IsNaN(input) || double.IsPositiveInfinity(input) || double.IsNegativeInfinity(input)) // { // return volts * MMPerV; // } // else // { // return volts * MMPerV - MMPerV * input; // } // } // /// // /// MVAt0MM set by diagnostics and then later on at // /// Average Over Time // /// // /// // /// // private double GetEUAverageOverTime(double volts) // { // //double input = MVAt0MM / 1000D; // //input = System.Math.Pow(input, LinearizationExponent); // double input = double.NaN; // if (double.IsNaN(input) || double.IsNegativeInfinity(input) || double.IsPositiveInfinity(input)) // { // return volts * MMPerV; // } // else // { // return volts * MMPerV - MMPerV * input; // } // } // public string ToSLICEWareSerializeString() // { // if (!_bIsValid) { return ""; } // StringBuilder sb = new StringBuilder(); // sb.AppendFormat("{0}_", Style.ToString()); // switch (Style) // { // case Styles.IRTraccDiagnosticsZero: // sb.Append(ToIRTraccDiagnosticZeroString()); // break; // case Styles.IRTraccManual: // sb.Append(ToIRTraccManualString()); // break; // case Styles.IRTraccZeroMMmV: // sb.Append(ToIRTraccZeroMMmVString()); // break; // case Styles.IRTraccAverageOverTime: // sb.Append(ToIRTraccAverageOverTimeString()); // break; // case Styles.Polynomial: // sb.Append(ToSLICEWarePolynomialString()); // break; // case Styles.IRTraccCalFactor: // throw new NotSupportedException("CalFactor not supported in SLICEWare"); // default: // throw new NotSupportedException("unknown type: " + Style.ToString()); // } // return sb.ToString(); // } /// /// serializes to a string of the format "c0xe0 c1xe1...cnxen" /// this will allow us arbitrary length polynomials and fractional exponents. /// /// public string ToSerializeString() { if (!_bIsValid) { return ""; } var sb = new StringBuilder(); sb.AppendFormat("{0}_", Style.ToString()); switch (Style) { case Styles.IRTraccDiagnosticsZero: sb.Append(ToIRTraccDiagnosticZeroString()); break; case Styles.IRTraccManual: sb.Append(ToIRTraccManualString()); break; case Styles.IRTraccZeroMMmV: sb.Append(ToIRTraccZeroMMmVString()); break; case Styles.IRTraccAverageOverTime: sb.Append(ToIRTraccAverageOverTimeString()); break; case Styles.Polynomial: sb.Append(ToPolynomialString()); break; case Styles.IRTraccCalFactor: sb.Append(ToIRTraccCalFactorString()); break; default: throw new NotSupportedException("unknown type: " + Style.ToString()); } return sb.ToString(); } public string ToIRTraccDiagnosticZeroString() { return string.Format("{0}x{1}", MMPerV.ToString(System.Globalization.CultureInfo.InvariantCulture), LinearizationExponent .ToString(System.Globalization.CultureInfo.InvariantCulture)); } public string ToIRTraccCalFactorString() { return string.Format("{0}x{1}x{2}", CalibrationFactor.ToString(System.Globalization.CultureInfo.InvariantCulture), LinearizationExponent.ToString(System.Globalization.CultureInfo.InvariantCulture), ZeroPositionIntercept.ToString(System.Globalization.CultureInfo.InvariantCulture)); } public void FromIRTraccCalFactorString(string s, System.Globalization.CultureInfo culture) { var tokens = s.Split('x'); if (tokens.Length < 3) { throw new NotSupportedException("Invalid CalFactor format: " + s); } CalibrationFactor = double.Parse(tokens[0], culture); LinearizationExponent = double.Parse(tokens[1], culture); ZeroPositionIntercept = double.Parse(tokens[2], culture); } public void FromIRTraccDiagnosticZeroString(string s, System.Globalization.CultureInfo culture) { var tokens = s.Split('x'); if (tokens.Length < 2) { throw new NotSupportedException("Invalid DiagnosticsZero format: " + s); } MMPerV = double.Parse(tokens[0], culture); LinearizationExponent = double.Parse(tokens[1], culture); } public string ToIRTraccManualString() { return string.Format("{0}x{1}x{2}", Slope.ToString(System.Globalization.CultureInfo.InvariantCulture), Intercept.ToString (System.Globalization.CultureInfo.InvariantCulture), LinearizationExponent.ToString( System.Globalization.CultureInfo.InvariantCulture)); } public void FromIRTraccManualString(string s, System.Globalization.CultureInfo culture) { var tokens = s.Split('x'); if (tokens.Length < 3) { throw new NotSupportedException("Invalid IRTraccManual format: " + s); } Slope = double.Parse(tokens[0], culture); Intercept = double.Parse(tokens[1], culture); LinearizationExponent = double.Parse(tokens[2], culture); } public string ToIRTraccZeroMMmVString() { return string.Format("{0}x{1}x{2}", MMPerV.ToString(System.Globalization.CultureInfo.InvariantCulture), MVAt0MM.ToString(System.Globalization.CultureInfo.InvariantCulture), LinearizationExponent.ToString(System.Globalization.CultureInfo.InvariantCulture)); } // public string ToSLICEWarePolynomialString() // { // //SLICEWare is the reverse order of our DataPRO Database // StringBuilder sb = new StringBuilder(); // for (int i = _exponents.Count - 1; i >= 0; i--) // { // if (i != _exponents.Count - 1) { sb.Append(","); } // sb.AppendFormat("{0}x{1}", _coefficients[i].ToString(System.Globalization.CultureInfo.InvariantCulture), // _exponents[i].ToString(System.Globalization.CultureInfo.InvariantCulture)); // } // sb.AppendFormat(",S={0}", PolynomialSensitivity.ToString(System.Globalization.CultureInfo.InvariantCulture)); // return sb.ToString(); // } public string ToPolynomialString() { var sb = new StringBuilder(); for (var i = 0; i < _coefficients.Count && i < _exponents.Count; i++) { if (i > 0) { sb.Append(","); } sb.AppendFormat("{0}x{1}", _coefficients[i].ToString(System.Globalization.CultureInfo.InvariantCulture), _exponents[i].ToString(System.Globalization.CultureInfo.InvariantCulture)); } sb.AppendFormat(",S={0},mV={1}", PolynomialSensitivity.ToString(System.Globalization.CultureInfo.InvariantCulture), UsemVOverVForPolys.ToString(System.Globalization.CultureInfo.InvariantCulture)); return sb.ToString(); } // public double[] PolynomialCoefficients // { // get { return _coefficients.ToArray(); } // set { _coefficients = new List(value); } // } // public double[] PolynomialExponents // { // get { return _exponents.ToArray(); } // set { _exponents = new List(value); } // } public string ToIRTraccAverageOverTimeString() { return string.Format("{0}x{1}", MMPerV.ToString(System.Globalization.CultureInfo.InvariantCulture), LinearizationExponent.ToString(System.Globalization.CultureInfo.InvariantCulture)); } public void FromIRTraccAverageOverTimeString(string s, System.Globalization.CultureInfo culture) { var tokens = s.Split('x'); if (tokens.Length < 2) { throw new NotSupportedException("Invalid IRTRaccAverageOverTime format: " + s); } MMPerV = double.Parse(tokens[0], culture); LinearizationExponent = double.Parse(tokens[1], culture); } public void FromIRTraccZeroMMmVString(string s, System.Globalization.CultureInfo culture) { var tokens = s.Split('x'); if (tokens.Length < 3) { throw new NotSupportedException("Invalid IRTraccZeroMMmV format: " + s); } MMPerV = double.Parse(tokens[0], culture); MVAt0MM = double.Parse(tokens[1], culture); LinearizationExponent = double.Parse(tokens[2], culture); } public void FromPolynomialString(string s, System.Globalization.CultureInfo culture) { _coefficients.Clear(); _exponents.Clear(); var tokens = s.Split(','); foreach (var t in tokens) { var subtokens = t.Split('x'); if (2 == subtokens.Length) { double d; if (double.TryParse(subtokens[0], System.Globalization.NumberStyles.Float, culture, out d)) { _coefficients.Add(d); _exponents.Add(double.Parse(subtokens[1], culture)); } else { PolynomialSensitivity = double.Parse(subtokens[1], culture); } } else { subtokens = t.Split('='); if (subtokens.Length == 2) { switch (subtokens[0]) { case "S": PolynomialSensitivity = double.Parse(subtokens[1], culture); break; case "mV": UsemVOverVForPolys = Convert.ToBoolean(subtokens[1], culture); break; } } } } } public void FromSerializeString(string s, System.Globalization.CultureInfo culture) { if (string.IsNullOrEmpty(s)) { _bIsValid = false; return; } if (s.Equals("1") || s.Equals("0") || s.Equals("1 ")) { _bIsValid = false; return; } var tokens = s.Split('_'); if (tokens.Length < 2) { throw new NotSupportedException("unsupported Linearization Formula Format"); } var style = (Styles)Enum.Parse(typeof(Styles), tokens[0], true); Style = style; switch (Style) { case Styles.IRTraccDiagnosticsZero: FromIRTraccDiagnosticZeroString(tokens[1], culture); _bIsValid = true; break; case Styles.IRTraccManual: FromIRTraccManualString(tokens[1], culture); _bIsValid = true; break; case Styles.IRTraccZeroMMmV: FromIRTraccZeroMMmVString(tokens[1], culture); _bIsValid = true; break; case Styles.Polynomial: FromPolynomialString(tokens[1], culture); _bIsValid = true; break; case Styles.IRTraccAverageOverTime: FromIRTraccAverageOverTimeString(tokens[1], culture); _bIsValid = true; break; case Styles.IRTraccCalFactor: FromIRTraccCalFactorString(tokens[1], culture); _bIsValid = true; break; default: throw new NotSupportedException("Unknown format: " + Style.ToString()); } } public void FromSerializeString(string s) { FromSerializeString(s, System.Globalization.CultureInfo.InvariantCulture); } // public void FromTDCSerializeString() // { // _bIsValid = true; // } // /// // /// Will return a display string for a nonlinear calibration based on N4 formating // /// // public string ToDisplayString() // { // return ToDisplayString("N4"); // } // /// // /// Will return a display string for a nonlinear calibration based on 1st paramater formating string // /// // /// // /// // public string ToDisplayString(string nonlinearFormat) // { // if (string.IsNullOrEmpty(nonlinearFormat)) { nonlinearFormat = "N4"; } // switch (Style) // { // case Styles.Polynomial: // { // return ToPolynomial(nonlinearFormat); // } // case Styles.IRTraccZeroMMmV: // { // return string.Format("mV = {0:n4}, {1:n4}*(V^{2})", MVAt0MM, MMPerV, ToSuperScript(LinearizationExponent.ToString(nonlinearFormat))); // } // case Styles.IRTraccManual: // { // return string.Format("((V^{0})-{1:n4})/{2:n4}", ToSuperScript(LinearizationExponent.ToString(nonlinearFormat)), Intercept, Slope); // } // case Styles.IRTraccDiagnosticsZero: // { // return string.Format("{0:n4}*(V^{1})", MMPerV, ToSuperScript(LinearizationExponent.ToString(nonlinearFormat))); // } // case Styles.IRTraccAverageOverTime: // { // return string.Format("{0:n4}*(V^{1})", MMPerV, ToSuperScript(LinearizationExponent.ToString(nonlinearFormat))); // } // case Styles.IRTraccCalFactor: // { // return string.Format("{2:n4}+{1:n4}*(V^{0})", ToSuperScript(LinearizationExponent.ToString(nonlinearFormat)), CalibrationFactor, ZeroPositionIntercept); // } // default: // return string.Empty; // } // } // private string ToPolynomial(string nonlinearFormat) // { // if (string.IsNullOrEmpty(nonlinearFormat)) { nonlinearFormat = "N4"; } // StringBuilder sb = new StringBuilder(); // int termNumber = PolynomialCoefficients.Length - 1; // foreach (var x in PolynomialCoefficients) // { // if (PolynomialCoefficients[termNumber] != 0) // { // double coeff = PolynomialCoefficients[termNumber]; // // Let the appended math symbol handle sign unless we're the first term. // if (termNumber != PolynomialCoefficients.Length - 1) // { // coeff = Math.Abs(coeff); // } // sb.Append(coeff.ToString(nonlinearFormat)); // if (PolynomialExponents[termNumber] != 0) // { // sb.Append("x"); // if (PolynomialExponents[termNumber] != 1) // { // sb.Append(ToSuperScript(PolynomialExponents[termNumber].ToString("N0"))); // } // } // if (termNumber > 0) // { // // Coerricients are Displayed in absolute value. We need to combine the sign with the addition symbol // sb.Append(PolynomialCoefficients[termNumber - 1] > 0 ? " + " : " - "); // } // } // termNumber--; // } // return sb.ToString(); // } // private string ToSuperScript(string source) // { // StringBuilder superScript = new StringBuilder(); // foreach (char c in source) // { // switch (c) // { // case '-': // superScript.Append('\u207B'); // break; // case '.': // superScript.Append('\u00B7'); // break; // case '1': // superScript.Append('\u00B9'); // break; // case '2': // superScript.Append('\u00B2'); // break; // case '3': // superScript.Append('\u00B3'); // break; // case '4': // superScript.Append('\u2074'); // break; // case '5': // superScript.Append('\u2075'); // break; // case '6': // superScript.Append('\u2076'); // break; // case '7': // superScript.Append('\u2077'); // break; // case '8': // superScript.Append('\u2078'); // break; // case '9': // superScript.Append('\u2079'); // break; // case '0': // superScript.Append('\u2070'); // break; // case '\'': // superScript.Append('\u02C8'); // break; // case ',': // superScript.Append('\u22C5'); // there is no unicode superscript comma. this comes close // break; // case '\u00A0': // superScript.Append('\u2009'); // unicode 'thin' space // break; // default: // superScript.Append('\u207F'); // break; // } // } // return superScript.ToString(); // } // /* // * we are given an equation in the form of y = ax^1 + b, except x and y are backwards for us (y=V where we'd prefer X was voltage, so we switch it) // * y/a - b/a = x, and then switch y and x, (1/a)x^1 -(b/a)x^0 = y // * now we want to get the coefficient of the first equation, which is "a", we get this by taking the inverse // * we get b on the other hand by taking -1 * (b/a)*a. if we have the "coefficient", we have a // */ // /* // public double GetIRTraccCoefficient() // { // foreach (Factor f in Factors) // { // if (f.Exponent == 1D) { return System.Math.Pow(f.Coefficient, -1); } // } // return 1D; //0 doesn't make sense for ir // } // public double GetIRTraccConstant() // { // foreach (Factor f in Factors) // { // if (f.Exponent == 0D) { return -1D * GetIRTraccCoefficient() * f.Coefficient; } // } // return 0D; // } // public void SetIRTraccFactor(double coefficient, double constant) // { // if (0 == coefficient) // { // //well this doesn't make any sense ... // coefficient = 1; // } // Factors = new Factor[] // { // new Factor(1/coefficient,1), // new Factor(-constant/coefficient,0), // }; // }*/ } }