Files
2026-04-17 14:55:32 -04:00

920 lines
39 KiB
C#

/*
* 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
{
/// <summary>
/// A class to contain properties and methods associated with data scaling.
/// </summary>
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());
}
/// <summary>
/// Get the ADC->EU scaling factor (EU/mV)
/// </summary>
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;
/// <summary>
/// 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
/// </summary>
public void ClearADCToEuScalingFactor()
{
_adcToEuScalingFactor = null;
}
/// <summary>
/// Get the mV/ADC scale factor
/// </summary>
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;
/// <summary>
/// set the mv/ADC scale factor for this channel.
/// </summary>
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;
}
/// <summary>
/// Sets the scale factor. (mV/ADC)
/// </summary>
/// <param name="value"></param>
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; }
/// <summary>
/// returns the conversion factor from ADC to mV
/// </summary>
/// <returns>mV/ADC</returns>
public double GetScaleFactorMv() { return ScaleFactorMv; }
private readonly Property<double> _scaleFactorMv
= new Property<double>(
typeof(DataScaler).Namespace + ".DataScaler.ScaleFactorMv",
0.0,
false
);
public double GetScaleFactorEU() { return ScaleFactorEU; }
private readonly Property<double> _scaleFactorEU
= new Property<double>(
typeof(DataScaler).Namespace + ".DataScaler.ScaleFactorEU",
0.0,
false
);
private readonly Property<bool> _useEUScaleFactors
= new Property<bool>(
typeof(DataScaler).Namespace + ".DataScaler.UseEUScaleFactors",
false,
true
);
public double UnitConversion { get; set; } = 1D;
/// <summary>
/// set the sensitivity mV/EU factor for this channel.
/// </summary>
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<double> _mvPerEu
= new Property<double>(
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...
/// <summary>
/// sets mV per EU scale factor
/// </summary>
/// <param name="value"></param>
public void SetMvPerEu(double value) { MvPerEu = value; }
public double GetMvPerEu() { return MvPerEu; }
/// <summary>
/// Get/set the descriptor indiciating whether or not this channel is
/// based on output at capacity.
/// </summary>
public bool BasedOnOutputAtCapacity
{
get => _basedOnOutputAtCapacity.Value;
set => _basedOnOutputAtCapacity.Value = value;
}
private readonly Property<bool> _basedOnOutputAtCapacity
= new Property<bool>(
typeof(DataScaler).Namespace + ".DataScaler.BasedOnOutputAtCapacity",
false,
true
);
/// <summary>
/// Get/set the capacity output is based on value.
/// </summary>
public double CapacityOutputIsBasedOn
{
get => _capacityOutputIsBasedOn.Value;
set
{
if (0 < value)
{
_capacityOutputIsBasedOn.Value = value;
}
else
{
_capacityOutputIsBasedOn.UnInitialize();
}
}
}
private readonly Property<double> _capacityOutputIsBasedOn
= new Property<double>(
typeof(DataScaler).Namespace + ".DataScaler.CapacityOutputIsBasedOn",
1.000,
true
);
public SensorConstants.SensUnits SensitivityUnits { get; set; } = SensorConstants.SensUnits.NONE;
/// <summary>
/// Get/set the descriptor indiciating whether or not this channel is
/// proportional to excitation.
/// </summary>
public bool ProportionalToExcitation
{
get => _proportionalToExcitation.Value;
set => _proportionalToExcitation.Value = value;
}
private readonly Property<bool> _proportionalToExcitation
= new Property<bool>(
typeof(DataScaler).Namespace + ".DataScaler.ProportionalToExcitation",
false,
true
);
/// <summary>
/// Get the <see cref="double"/> excitation voltage value.
/// </summary>
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);
}
}
/// <summary>
/// Get/set the excitation voltage for this channel.
/// </summary>
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<ExcitationVoltageOptions.ExcitationVoltageOption> _excitationVoltage
= new Property<ExcitationVoltageOptions.ExcitationVoltageOption>(
typeof(DataScaler).Namespace + ".DataScaler.ExcitationVoltage",
ExcitationVoltageOptions.ExcitationVoltageOption.Volt5,
false
);
public bool FactoryExcitationVoltageValid => _factoryExcitationVoltage.IsInitialized;
/// <summary>
/// Get/set the factory excitation voltage value.
/// </summary>
public double FactoryExcitationVoltage
{
get => _factoryExcitationVoltage.Value;
set
{
if (0 < value)
{
_factoryExcitationVoltage.Value = value;
}
else
{
_factoryExcitationVoltage.UnInitialize();
}
}
}
private readonly Property<double> _factoryExcitationVoltage
= new Property<double>(
typeof(DataScaler).Namespace + ".DataScaler.FactoryExcitationVoltage",
0.0,
false
);
public bool MeasuredExcitationVoltageValid => _measuredExcitationVoltage.IsInitialized;
/// <summary>
/// Get/set the measured excitation voltage value.
/// </summary>
public double MeasuredExcitationVoltage
{
get => _measuredExcitationVoltage.Value;
set => _measuredExcitationVoltage.Value = value;
}
private readonly Property<double> _measuredExcitationVoltage
= new Property<double>(
typeof(DataScaler).Namespace + ".DataScaler.MeasuredExcitationVoltage",
0.0,
false
);
/// <summary>
/// Get/set channel inversion status.
/// </summary>
public bool IsInverted
{
get => _isInverted.Value;
set => _isInverted.Value = value;
}
private readonly Property<bool> _isInverted
= new Property<bool>(
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;
}
}
/// <summary>
/// represents the old concept of initial eu, just a value added to the end of the eu equation.
/// </summary>
private double InitialEU = 0D;
/// <summary>
/// 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 ...
/// </summary>
public double GetInitialEU() => InitialEU;
private ZeroMethodType _zeroMethodType = ZeroMethodType.None;
public ZeroMethodType ZeroMethodType
{
get => _zeroMethodType;
set
{
_zeroMethodType = value;
CalculateInitialOffset();
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="mV"></param>
/// <returns></returns>
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;
/// <summary>
/// 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]
/// </summary>
private readonly Property<int> _zeroMvInADC = new Property<int>(
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; }
}
}
/// <summary>
/// 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
/// </summary>
private readonly Property<int> _windowAverageADC = new Property<int>(
typeof(DataScaler).Namespace + ".DataScaler.WindowAverageADC",
short.MinValue,
true);
/// <summary>
/// WindowAverageADC is the ADC average over the Zero Window for the channel
/// it is private here to force external access through methods
/// </summary>
private int WindowAverageADC
{
get => _windowAverageADC.Value;
set => _windowAverageADC.Value = value;
}
/// <summary>
/// Set the window average ADC for the scaler
/// This does not set the ZeroLevelADC which is used for software zeroing
/// </summary>
/// <param name="adc"></param>
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; }
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="adc"></param>
public void SetRemovedADC(int adc) { RemovedADC = adc; }
private int RemovedADC
{
get => _removedADC.Value;
set => _removedADC.Value = value;
}
private readonly Property<int> _removedADC
= new Property<int>(
typeof(DataScaler).Namespace + ".DataScaler.RemovedADC",
0,
true
);
/// <summary>
/// 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
/// </summary>
/// <param name="adc"></param>
public void SetRemovedInternalADC(int adc) { RemovedInternalADC = adc; }
private int RemovedInternalADC
{
get => _removedInternalADC.Value;
set => _removedInternalADC.Value = value;
}
private readonly Property<int> _removedInternalADC
= new Property<int>(
typeof(DataScaler).Namespace + ".DataScaler.RemovedInternalADC",
0,
true
);
public double GetAdcToEuScalingFactor() { return AdcToEuScalingFactor; }
public double GetAdcToMvScalingFactor() { return AdcToMvScalingFactor; }
private readonly Property<short> _dataZeroLevelADC
= new Property<short>(
typeof(DataScaler).Namespace + ".DataScaler.DataZeroLevelADC",
0,
true
);
/// <summary>
/// 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.
/// </summary>
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; }
/// <summary>
/// returns the mV corresponding to the given ADC value
/// </summary>
/// <param name="adc"></param>
/// <returns></returns>
public double GetMv(short adc)
{
return GetMv(Convert.ToDouble(adc));
}
/// <summary>
/// returns mV unless IEPE is true, then returns V
/// </summary>
/// <param name="adc"></param>
/// <returns></returns>
public double GetMvOrV(short adc, bool volts)
{
return GetMv(adc) / (volts ? 1000D : 1D);
}
/// <summary>
/// returns mV unless IEPE is true, then returns V
/// </summary>
/// <param name="adc"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 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
/// </summary>
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)
{
}
}
}