920 lines
39 KiB
C#
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)
|
|
{
|
|
}
|
|
}
|
|
}
|