/* * DataScaler.cs * * Copyright © 2009 * Diversified Technical Systems, Inc. * All Rights Reserved */ using System; using DTS.Common.Classes.Sensors; using DTS.Common.Constant; using DTS.Common.Enums; using DTS.Common.Enums.Sensors; using DTS.Common.Utilities; using DTS.Common.Utilities.DotNetProgrammingConstructs; namespace DTS.Common.DAS.Concepts { /// /// A class to contain properties and methods associated with data scaling. /// public partial class DataScaler : Exceptional { public double UserOffsetEU { get; set; } public double Multiplier { get; set; } = 1D; private DTS.Common.Classes.Sensors.LinearizationFormula _linearizationEquation; public void SetLinearizationFormula(DTS.Common.Classes.Sensors.LinearizationFormula equation) { _linearizationEquation = equation; } public DTS.Common.Classes.Sensors.LinearizationFormula GetLinearizationFormula() { return _linearizationEquation ?? (_linearizationEquation = new DTS.Common.Classes.Sensors.LinearizationFormula()); } /// /// Get the ADC->EU scaling factor (EU/mV) /// private double AdcToEuScalingFactor { get { try { if (null != _adcToEuScalingFactor) return (double)_adcToEuScalingFactor; if (UseEUScaleFactors) { _adcToEuScalingFactor = (ScaleFactorEU * UnitConversion) * (BasedOnOutputAtCapacity ? CapacityOutputIsBasedOn : 1.0) * (IsInverted ? -1 : 1); } else { _adcToEuScalingFactor = (ScaleFactorMv * UnitConversion / (ProportionalToExcitation && SensitivityUnits != SensorConstants.SensUnits.mVperEU ? MvPerEu * GetExcitationVoltage() : MvPerEu)) * (BasedOnOutputAtCapacity ? CapacityOutputIsBasedOn : 1.0) * (IsInverted ? -1.0 : 1.0); } return (double)_adcToEuScalingFactor; } catch (System.Exception ex) { throw new Exception("encountered problem computing ADC->EU scaling factor", ex); } } } private double? _adcToEuScalingFactor = null; /// /// the ADCtoEUScalingFactor is a cached value /// and isn't recalculated once it's calculated /// this method clears the cached value so that the next time /// it is used the value is recalculated /// this should be done if the sensitivity or the scalefactors change /// public void ClearADCToEuScalingFactor() { _adcToEuScalingFactor = null; } /// /// Get the mV/ADC scale factor /// private double AdcToMvScalingFactor { get { try { if (null == _adcToMvScalingFactor) _adcToMvScalingFactor = ScaleFactorMv; return (double)_adcToMvScalingFactor; } catch (System.Exception ex) { throw new Exception("encountered problem computing ADC->MV scaling factor", ex); } } } private double? _adcToMvScalingFactor; /// /// set the mv/ADC scale factor for this channel. /// private double ScaleFactorMv { //we disable this, we don't want them doing calculations, we want them //to use us for calculations ... get => _scaleFactorMv.Value; set { _scaleFactorMv.Value = value; _adcToEuScalingFactor = null; CalculateInitialOffset(); } } private double ScaleFactorEU { //we disable this, we don't want them doing calculations, we want them //to use us for calculations ... get => _scaleFactorEU.Value; set { _scaleFactorEU.Value = value; _adcToEuScalingFactor = null; } } private bool UseEUScaleFactors { get => _useEUScaleFactors.Value; set => _useEUScaleFactors.Value = value; } /// /// Sets the scale factor. (mV/ADC) /// /// public void SetScaleFactorMv(double value) { ScaleFactorMv = value; } public void SetScaleFactorEU(double value) { ScaleFactorEU = value; } public void SetUseEUScaleFactors(bool value) { UseEUScaleFactors = value; } private DigitalInputScaleMultiplier _digitalMultiplier = new DigitalInputScaleMultiplier(); public void SetDigitalMultiplier(DigitalInputScaleMultiplier multiplier) { _digitalMultiplier = multiplier; } public DigitalInputScaleMultiplier GetDigitalMultiplier() { return _digitalMultiplier ?? (_digitalMultiplier = new DigitalInputScaleMultiplier()); } public bool Digital { get; set; } public bool DigitalOutput { get; set; } /// /// returns the conversion factor from ADC to mV /// /// mV/ADC public double GetScaleFactorMv() { return ScaleFactorMv; } private readonly Property _scaleFactorMv = new Property( typeof(DataScaler).Namespace + ".DataScaler.ScaleFactorMv", 0.0, false ); public double GetScaleFactorEU() { return ScaleFactorEU; } private readonly Property _scaleFactorEU = new Property( typeof(DataScaler).Namespace + ".DataScaler.ScaleFactorEU", 0.0, false ); private readonly Property _useEUScaleFactors = new Property( typeof(DataScaler).Namespace + ".DataScaler.UseEUScaleFactors", false, true ); public double UnitConversion { get; set; } = 1D; /// /// set the sensitivity mV/EU factor for this channel. /// private double MvPerEu { //we disable the get, we don't want outside code doing calculations, //we want them to use this class to do calculations get => _mvPerEu.Value; set => _mvPerEu.Value = value; } private readonly Property _mvPerEu = new Property( typeof(DataScaler).Namespace + ".DataScaler.MvPerEu", 1.0, // Yes, it's initialized on purpose. In case this isn't an analog channel. Normally true // we'd leave it uninitialized and trap the error if someone tries to use it, but our ); // binary serialization format always expects an EU value from all channels, so... /// /// sets mV per EU scale factor /// /// public void SetMvPerEu(double value) { MvPerEu = value; } public double GetMvPerEu() { return MvPerEu; } /// /// Get/set the descriptor indiciating whether or not this channel is /// based on output at capacity. /// public bool BasedOnOutputAtCapacity { get => _basedOnOutputAtCapacity.Value; set => _basedOnOutputAtCapacity.Value = value; } private readonly Property _basedOnOutputAtCapacity = new Property( typeof(DataScaler).Namespace + ".DataScaler.BasedOnOutputAtCapacity", false, true ); /// /// Get/set the capacity output is based on value. /// public double CapacityOutputIsBasedOn { get => _capacityOutputIsBasedOn.Value; set { if (0 < value) { _capacityOutputIsBasedOn.Value = value; } else { _capacityOutputIsBasedOn.UnInitialize(); } } } private readonly Property _capacityOutputIsBasedOn = new Property( typeof(DataScaler).Namespace + ".DataScaler.CapacityOutputIsBasedOn", 1.000, true ); public SensorConstants.SensUnits SensitivityUnits { get; set; } = SensorConstants.SensUnits.NONE; /// /// Get/set the descriptor indiciating whether or not this channel is /// proportional to excitation. /// public bool ProportionalToExcitation { get => _proportionalToExcitation.Value; set => _proportionalToExcitation.Value = value; } private readonly Property _proportionalToExcitation = new Property( typeof(DataScaler).Namespace + ".DataScaler.ProportionalToExcitation", false, true ); /// /// Get the excitation voltage value. /// public double GetExcitationVoltage() { try { //FB 18727 use measured excitation if the setting is true & ExcitationVoltage is 2 volt if (NominalExcitationVoltage == ExcitationVoltageOptions.ExcitationVoltageOption.Volt2 && Settings.SettingsDB.GetGlobalValueBool(Constants.UseMeasuredExcitation, false)) { return _measuredExcitationVoltage.IsInitialized ? MeasuredExcitationVoltage : Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(NominalExcitationVoltage); } else { if (_factoryExcitationVoltage.IsInitialized) return FactoryExcitationVoltage; return _measuredExcitationVoltage.IsInitialized ? MeasuredExcitationVoltage : Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(NominalExcitationVoltage); } } catch (System.Exception ex) { throw new Exception("encountered problem determining excitation voltage", ex); } } /// /// Get/set the excitation voltage for this channel. /// public ExcitationVoltageOptions.ExcitationVoltageOption NominalExcitationVoltage { get => _excitationVoltage.Value == ExcitationVoltageOptions.ExcitationVoltageOption.Undefined ? throw new InvalidExcitationVoltageException( "cannot use undefined excitation voltage to calculate scaling factor") : _excitationVoltage.Value; set => _excitationVoltage.Value = value; } private readonly Property _excitationVoltage = new Property( typeof(DataScaler).Namespace + ".DataScaler.ExcitationVoltage", ExcitationVoltageOptions.ExcitationVoltageOption.Volt5, false ); public bool FactoryExcitationVoltageValid => _factoryExcitationVoltage.IsInitialized; /// /// Get/set the factory excitation voltage value. /// public double FactoryExcitationVoltage { get => _factoryExcitationVoltage.Value; set { if (0 < value) { _factoryExcitationVoltage.Value = value; } else { _factoryExcitationVoltage.UnInitialize(); } } } private readonly Property _factoryExcitationVoltage = new Property( typeof(DataScaler).Namespace + ".DataScaler.FactoryExcitationVoltage", 0.0, false ); public bool MeasuredExcitationVoltageValid => _measuredExcitationVoltage.IsInitialized; /// /// Get/set the measured excitation voltage value. /// public double MeasuredExcitationVoltage { get => _measuredExcitationVoltage.Value; set => _measuredExcitationVoltage.Value = value; } private readonly Property _measuredExcitationVoltage = new Property( typeof(DataScaler).Namespace + ".DataScaler.MeasuredExcitationVoltage", 0.0, false ); /// /// Get/set channel inversion status. /// public bool IsInverted { get => _isInverted.Value; set => _isInverted.Value = value; } private readonly Property _isInverted = new Property( typeof(DataScaler).Namespace + ".DataScaler.IsInverted", false, false ); private double _initialOffsetmVinADC = 0D; private double _initialOffsetEU = 0D; private InitialOffset _initialOffset = new InitialOffset(); public void SetInitialOffset(InitialOffset offset) { _initialOffset = offset; CalculateInitialOffset(); } public void SetInitialOffset(string offset) { _initialOffset.FromDbSerializeString(offset); CalculateInitialOffset(); } private void CalculateInitialOffset() { switch (_initialOffset.Form) { case InitialOffsetTypes.FRONTAL: case InitialOffsetTypes.LHS: case InitialOffsetTypes.RHS: case InitialOffsetTypes.EU: InitialEU = _initialOffset.EU; break; case InitialOffsetTypes.EUAtMV: //calculated INSIDE the EU equation { InitialEU = 0; //only true if we are using zeromethod = none if (ZeroMethodType == ZeroMethodType.None && _scaleFactorMv.IsValueInitialized) { _initialOffsetEU = _initialOffset.EU; _initialOffsetmVinADC = ScaleFactorMv != 0 ? _initialOffset.MV / ScaleFactorMv : 0D; } else { _initialOffsetmVinADC = 0D; _initialOffsetEU = 0D; } } break; default: _initialOffsetmVinADC = 0D; _initialOffsetEU = 0D; InitialEU = 0D; break; } } public double GetCorrectedADC(short adc, bool convertUnsigned = false) { if (!Digital // Never correct for non-Digital || (Digital && DigitalInputs.DisplaySPDADC // Don't correct SPD if we're showing RAW ADC && (adc != short.MaxValue && adc != short.MinValue)) // ...unless this is an older data set || (Digital && ScaleFactorMv >= 0)) // Don't correct for G5 DI or TDAS DIM { if (convertUnsigned) { var unsigned = (ushort)(adc - 0x8000); return Convert.ToDouble(unsigned); } return Convert.ToDouble(adc); } var breakPoint = DigitalInputs.ConstantCurrentBreakPoint; if (DigitalMode == DigitalInputModes.THL || DigitalMode == DigitalInputModes.TLH) { breakPoint = DigitalInputs.VoltageInputBreakPoint; } //handle digital sensors var bAboveBreak = adc > breakPoint; if ((short.MaxValue == adc || short.MinValue == adc) && (DigitalMode == DigitalInputModes.CCNC || DigitalMode == DigitalInputModes.CCNO)) // Handle pre-14435 change digital data) { //14435 Store SPD data as analog, but show in viewers as digital, expose Digital break point //SLICE2 has inverted ADC, so we need to swap the direction of above/below bAboveBreak = !bAboveBreak; } return bAboveBreak ? Convert.ToDouble(short.MaxValue) : Convert.ToDouble(short.MinValue); } public double GetEU(short adc) { try { return GetEU(Convert.ToDouble(adc)); } catch (System.Exception ex) { Utilities.Logging.APILogger.Log(ex); return double.NaN; } } /// /// represents the old concept of initial eu, just a value added to the end of the eu equation. /// private double InitialEU = 0D; /// /// returns the Initial EU property - this is the old concept of EU, it's just a value added /// to the end of the eu equation ... /// public double GetInitialEU() => InitialEU; private ZeroMethodType _zeroMethodType = ZeroMethodType.None; public ZeroMethodType ZeroMethodType { get => _zeroMethodType; set { _zeroMethodType = value; CalculateInitialOffset(); } } /// /// returns eu given a mV value /// originally written for /// 14700 Diagnostic offsets should show in EU also /// ordinarily we go from ADC to mV or ADC to EU, but never mV to EU /// however SLICE reads initial offsets in terms of mV /// /// /// public double GetEUFromMv(double mV) { //first intercept the process if we have a non linear channel if (null != _linearizationEquation && _linearizationEquation.IsValid()) { double eu = 0; if (UseEUScaleFactors) { for (var i = 0; i < _linearizationEquation.PolynomialCoefficients.Length && i < _linearizationEquation.PolynomialExponents.Length; i++) { eu += _linearizationEquation.GetCoefficient(i) * Math.Pow(mV, _linearizationEquation.PolynomialExponents[i]); } } else { //FB 29728 pass parameter to determine is proportional eu = _linearizationEquation.GetLinearizedValue(mV, GetExcitationVoltage(), ProportionalToExcitation) * (IsInverted ? -1D : 1D); var zero = double.NaN; //if datazeroleveladc is zero, then we shouldn't applying zeroing as it will give bad data //however if it is non zero, go ahead and apply it switch (_linearizationEquation.NonLinearStyle) { //the 0MMmV knows where zero mm is by default and doesn't need to calculate a zero value //5512 mV for 0MM [IRTRACC] sometimes makes funny offset. case NonLinearStyles.IRTraccZeroMMmV: // IRTRACC with Cal Factor is always zero method of NONE // http://manuscript.dts.local/f/cases/resolve/17783/IRTRACC-w-CalFactor-should-always-process-as-Zero-Method-NONE case NonLinearStyles.IRTraccCalFactor: zero = double.NaN; break; default: //http://fogbugz/fogbugz/default.asp?11399 dont set zero if method is NONE if (0 != DataZeroLevelADC && ZeroMethodType != ZeroMethodType.None) { zero = _linearizationEquation.GetLinearizedValue(GetMv(DataZeroLevelADC), GetExcitationVoltage()) * (IsInverted ? -1D : 1D); } break; } if (double.IsNaN(zero) || double.IsNegativeInfinity(zero) || double.IsPositiveInfinity(zero)) { zero = 0D; } eu = eu - zero; switch (_linearizationEquation.NonLinearStyle) { case NonLinearStyles.IRTraccAverageOverTime: case NonLinearStyles.IRTraccDiagnosticsZero: case NonLinearStyles.IRTraccManual: case NonLinearStyles.IRTraccZeroMMmV: eu *= -1D; break; } } return eu + InitialEU + UserOffsetEU; } //handle analog sensors //note that a negative scale factor results in a negative MvPerEU //however, the EU output needs to consider polarity as well, IsInverted shows only the value of polarity, so //we can use it here if (!Digital) { //14918 Init offset displayed in EU during diag looks incorrect for EU@mV sensor //Calculate Offset is what is setting the initial offset, but for EU@mV //we need to make sure we compensate, ordinarily this is dont throug the initialoffsetmvinadc //but this route wasn't taking it into consideration, it does now. return (mV * (IsInverted ? -1 : 1)) / (MvPerEu * (ProportionalToExcitation ? GetExcitationVoltage() : 1)) * Multiplier + InitialEU + UserOffsetEU + _initialOffsetEU - _initialOffsetmVinADC * ScaleFactorMv; } return double.NaN; } //returns EU given an ADC value (taking into consideration software zeroing and initial EU) public double GetEU(double adc) { // Correct ADC for DIM Data var correctedadc = adc; //GetCorrectedADC, will coalesce adc to a short, so avoid calling it if we don't need to if (Digital) { correctedadc = GetCorrectedADC(Convert.ToInt16(adc)); } //first intercept the process if we have a non linear channel if (null != _linearizationEquation && _linearizationEquation.IsValid()) { double eu = 0; if (UseEUScaleFactors) { var mV = (correctedadc - DataZeroLevelADC) * ScaleFactorMv / MeasuredExcitationVoltage; for (var i = 0; i < _linearizationEquation.PolynomialCoefficients.Length && i < _linearizationEquation.PolynomialExponents.Length; i++) { eu += _linearizationEquation.GetCoefficient(i) * Math.Pow(mV, _linearizationEquation.PolynomialExponents[i]); } } else { eu = _linearizationEquation.GetLinearizedValue(GetMv(correctedadc), GetExcitationVoltage()) * (IsInverted ? -1D : 1D); var zero = double.NaN; //if datazeroleveladc is zero, then we shouldn't applying zeroing as it will give bad data //however if it is non zero, go ahead and apply it switch (_linearizationEquation.NonLinearStyle) { //the 0MMmV knows where zero mm is by default and doesn't need to calculate a zero value //5512 mV for 0MM [IRTRACC] sometimes makes funny offset. case NonLinearStyles.IRTraccZeroMMmV: // IRTRACC with Cal Factor is always zero method of NONE //http://manuscript.dts.local/f/cases/resolve/17783/IRTRACC-w-CalFactor-should-always-process-as-Zero-Method-NONE case NonLinearStyles.IRTraccCalFactor: zero = double.NaN; break; default: //http://fogbugz/fogbugz/default.asp?11399 dont set zero if method is NONE if (0 != DataZeroLevelADC && ZeroMethodType != ZeroMethodType.None) { zero = _linearizationEquation.GetLinearizedValue(GetMv(DataZeroLevelADC), GetExcitationVoltage()) * (IsInverted ? -1D : 1D); } break; } if (double.IsNaN(zero) || double.IsNegativeInfinity(zero) || double.IsPositiveInfinity(zero)) { zero = 0D; } eu = eu - zero; switch (_linearizationEquation.NonLinearStyle) { case NonLinearStyles.IRTraccAverageOverTime: case NonLinearStyles.IRTraccDiagnosticsZero: case NonLinearStyles.IRTraccManual: case NonLinearStyles.IRTraccZeroMMmV: eu *= -1D; break; } } return eu * Multiplier + InitialEU + UserOffsetEU; } //handle analog sensors if (!Digital) { var eu = (correctedadc - DataZeroLevelADC - _initialOffsetmVinADC) * Multiplier * AdcToEuScalingFactor + InitialEU + UserOffsetEU + _initialOffsetEU; return eu; } var breakPoint = DigitalInputs.ConstantCurrentBreakPoint; if (DigitalMode == DigitalInputModes.THL || DigitalMode == DigitalInputModes.TLH) { breakPoint = DigitalInputs.VoltageInputBreakPoint; } //handle digital sensors var bAboveBreak = correctedadc > breakPoint; if (ScaleFactorMv < 0) { switch (DigitalMode) { case DigitalInputModes.CCNC: case DigitalInputModes.CCNO: //14435 Store SPD data as analog, but show in viewers as digital, expose Digital break point //SLICE2 has inverted ADC, so we need to swap the direction of above/below bAboveBreak = !bAboveBreak; break; default: //Do Nothing break; } } if (bAboveBreak) { switch (DigitalMode) { case DigitalInputModes.CCNC: return _digitalMultiplier.DefaultValue; case DigitalInputModes.CCNO: return _digitalMultiplier.ActiveValue; case DigitalInputModes.THL: return _digitalMultiplier.DefaultValue; case DigitalInputModes.TLH: return _digitalMultiplier.ActiveValue; default: return _digitalMultiplier.ActiveValue; } } switch (DigitalMode) { case DigitalInputModes.CCNC: return _digitalMultiplier.ActiveValue; case DigitalInputModes.CCNO: return _digitalMultiplier.DefaultValue; case DigitalInputModes.THL: return _digitalMultiplier.ActiveValue; case DigitalInputModes.TLH: return _digitalMultiplier.DefaultValue; default: return _digitalMultiplier.DefaultValue; } } public DigitalInputModes DigitalMode { get; set; } = DigitalInputModes.CCNC; /// /// zeroMvInADC is the concept of ADC when 0mV is injected into the DAC /// this is used with TDAS equipment to determine and remove offset on some /// sensors /// this value will be removed in SLICEWare to remove any measured offset present not from the sensor /// DataZeroLevel also is an ADC value removed when converting to ADC (typically from software zeroing like window averaging) /// however I keep it separate to allow the factor to be applied or not to ADC/EU/MV, while DataZeroLevel is only applied to EU /// [however DataZeroLevel is ZeroMvInADC when zeroing method is "use 0mV", so we only need to apply ZeroMvInADC to mV scaling] /// private readonly Property _zeroMvInADC = new Property( typeof(DataScaler).Namespace + ".DataScaler.ZeroMVInADC", 0, true); public int ZeroMvInADC { get => _zeroMvInADC.Value; private set => _zeroMvInADC.Value = value; } public void SetZeroMvInADC(int adc) { ZeroMvInADC = adc; } public void SetZeroMvInADC(double adc) { if (double.IsNaN(adc)) { ZeroMvInADC = 0; } else { try { ZeroMvInADC = Convert.ToInt32(adc); } catch (System.Exception) { ZeroMvInADC = 0; } } } /// /// WindowAverageADC is not needed for the DataScaler (as it automatically uses DataZeroLevelADC) /// but I preserve it here so that it's set just like the other zero values (ZeroMvInADC) and maybe /// will be available for use in the future /// private readonly Property _windowAverageADC = new Property( typeof(DataScaler).Namespace + ".DataScaler.WindowAverageADC", short.MinValue, true); /// /// WindowAverageADC is the ADC average over the Zero Window for the channel /// it is private here to force external access through methods /// private int WindowAverageADC { get => _windowAverageADC.Value; set => _windowAverageADC.Value = value; } /// /// Set the window average ADC for the scaler /// This does not set the ZeroLevelADC which is used for software zeroing /// /// public void SetWindowAverageADC(int adc) { WindowAverageADC = adc; } public void SetWindowAverageADC(double adc) { if (double.IsNaN(adc)) { WindowAverageADC = 0; } else { try { WindowAverageADC = Convert.ToInt32(adc); } catch (System.Exception) { WindowAverageADC = 0; } } } /// /// SetRemovedADC stores the adc removed by H/W zeroing functions /// the purpose of storing this is for use in mV functions that need to go from /// ADC to mV but also need to adjust back in the removed ADC /// /// public void SetRemovedADC(int adc) { RemovedADC = adc; } private int RemovedADC { get => _removedADC.Value; set => _removedADC.Value = value; } private readonly Property _removedADC = new Property( typeof(DataScaler).Namespace + ".DataScaler.RemovedADC", 0, true ); /// /// SetRemovedInternalADC stores the internal adc removed by H/W zeroing functions /// the purpose of storing this is for use in mV functions that need to go from /// ADC to mV but also need to adjust back in the removed ADC /// /// public void SetRemovedInternalADC(int adc) { RemovedInternalADC = adc; } private int RemovedInternalADC { get => _removedInternalADC.Value; set => _removedInternalADC.Value = value; } private readonly Property _removedInternalADC = new Property( typeof(DataScaler).Namespace + ".DataScaler.RemovedInternalADC", 0, true ); public double GetAdcToEuScalingFactor() { return AdcToEuScalingFactor; } public double GetAdcToMvScalingFactor() { return AdcToMvScalingFactor; } private readonly Property _dataZeroLevelADC = new Property( typeof(DataScaler).Namespace + ".DataScaler.DataZeroLevelADC", 0, true ); /// /// DataZeroLevelADC is the amount of ADC to be removed by software to zero out the data. This value is typically determined /// using Software Zeroing like averaging window. /// private short DataZeroLevelADC { get => _dataZeroLevelADC.IsValueInitialized ? _dataZeroLevelADC.Value : Convert.ToInt16(0); set => _dataZeroLevelADC.Value = value; } public void SetDataZeroLevelADC(short value) { DataZeroLevelADC = value; } public short GetDataZeroLevelADC() { return DataZeroLevelADC; } /// /// returns the mV corresponding to the given ADC value /// /// /// public double GetMv(short adc) { return GetMv(Convert.ToDouble(adc)); } /// /// returns mV unless IEPE is true, then returns V /// /// /// public double GetMvOrV(short adc, bool volts) { return GetMv(adc) / (volts ? 1000D : 1D); } /// /// returns mV unless IEPE is true, then returns V /// /// /// public double GetMvOrV(double adc, bool volts) { return GetMv(adc) / (volts ? 1000D : 1D); } public bool IEPE { get; set; } private readonly int IEPESCALE = 1; public DataScaler() { IEPE = false; DigitalOutput = false; Digital = false; UserOffsetEU = 0D; } public DataScaler(DataScaler copy) { IsInverted = copy.IsInverted; SetLinearizationFormula(copy._linearizationEquation); Digital = copy.Digital; SetDigitalMultiplier(copy._digitalMultiplier); DigitalMode = copy.DigitalMode; SetScaleFactorMv(copy.GetScaleFactorMv()); SetScaleFactorEU(copy.GetScaleFactorEU()); SetUseEUScaleFactors(copy.UseEUScaleFactors); UnitConversion = copy.UnitConversion; BasedOnOutputAtCapacity = copy.BasedOnOutputAtCapacity; CapacityOutputIsBasedOn = copy.CapacityOutputIsBasedOn; SensitivityUnits = copy.SensitivityUnits; Multiplier = copy.Multiplier; UserOffsetEU = copy.UserOffsetEU; IEPE = copy.IEPE; SetMvPerEu(copy.GetMvPerEu()); SetDataZeroLevelADC(copy.GetDataZeroLevelADC()); SetRemovedADC(copy.RemovedADC); SetRemovedInternalADC(copy.RemovedInternalADC); SetZeroMvInADC(copy.ZeroMvInADC); SetWindowAverageADC(copy.WindowAverageADC); SetInitialOffset(copy._initialOffset); ZeroMethodType = copy.ZeroMethodType; NominalExcitationVoltage = copy.NominalExcitationVoltage; MeasuredExcitationVoltage = copy.MeasuredExcitationVoltage; FactoryExcitationVoltage = copy.FactoryExcitationVoltage; ProportionalToExcitation = copy.ProportionalToExcitation; } /// /// 10485 Disable mV graphs for digital inputs /// 10436 When moving from ADC to mV unit scale is incorrect on both TDAS PRO DIM and G5 for digital input channels. /// this constant is just a value that we display when displaying mV for a digital input /// public const double DIGITAL_ON_VALUE = 2500D; public double GetMv(double adc) { //we don't consider software zero'ing for mV, so leave out DataZeroLevel, but take into consideration the //removed offset. //note we do consider ZeroMvInADC in mV values as TDC does //digitals per 10436 we display -2500/2500 if (Digital) { switch (DigitalMode) { case DigitalInputModes.TLH: case DigitalInputModes.THL: return adc > DigitalInputs.VoltageInputBreakPoint ? DIGITAL_ON_VALUE : -1D * DIGITAL_ON_VALUE; default: return adc > DigitalInputs.ConstantCurrentBreakPoint ? DIGITAL_ON_VALUE : -1D * DIGITAL_ON_VALUE; } } if (null != _linearizationEquation && _linearizationEquation.IsValid()) { var mV = (adc - ZeroMvInADC) * ScaleFactorMv; if (IEPE) { return Converters.InitialOffsetToIEPESensorOffsetConverter.ConvertDouble(mV) * 1000.0; } return mV; } else { var mV = (adc + RemovedADC - ZeroMvInADC) * ScaleFactorMv; if (IEPE) { return Converters.InitialOffsetToIEPESensorOffsetConverter.ConvertDouble(mV) * 1000.0; } return mV; } } public void SetIRTraccZeros(double averageOverTime, double diagnosticADC) { } } }