2015-04-15 18:32:56 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Copyright (c) 2013-2015 Sensus Metering Systems
|
|
|
|
|
|
///
|
|
|
|
|
|
using System;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
|
|
namespace TBF.BenchControl
|
|
|
|
|
|
{
|
|
|
|
|
|
public static class Formulas
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculate water density from temperature
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="t">Temperature in [degC]</param>
|
|
|
|
|
|
/// <returns>Density in [kg/m3]</returns>
|
|
|
|
|
|
public static float WaterDensityFromTemp(float t)
|
|
|
|
|
|
{
|
|
|
|
|
|
double tt = (double)t;
|
|
|
|
|
|
const double a0 = 999.842594;
|
|
|
|
|
|
const double a1 = 0.06793952;
|
|
|
|
|
|
const double a2 = -0.00909529;
|
|
|
|
|
|
const double a3 = 0.0001001685;
|
|
|
|
|
|
const double a4 = -0.000001120083;
|
|
|
|
|
|
const double a5 = 6.536332e-09;
|
|
|
|
|
|
|
|
|
|
|
|
return (float)(((((a5 * tt + a4) * tt + a3) * tt + a2) * tt + a1) * tt + a0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static float AirDensityFromAmbientVales(float tempC, float pressureBar, float humiPct)
|
|
|
|
|
|
{
|
|
|
|
|
|
double pressurePa = 100000.0 * (double)pressureBar; /// [Pa]
|
|
|
|
|
|
double tempKelvin = 273.15 + (double)tempC;
|
|
|
|
|
|
double coef1 = 1.2811805 / 10000.0 * tempKelvin * tempKelvin
|
|
|
|
|
|
- 1.950987 / 100.0 * tempKelvin
|
|
|
|
|
|
+ 34.04926034
|
|
|
|
|
|
- 6.353631 * 1000.0 / tempKelvin;
|
|
|
|
|
|
double coef3 = humiPct / 100.0 * System.Math.Exp(coef1) / pressurePa;
|
|
|
|
|
|
double airDensityKgm3 = 0.00348353 * pressurePa * (1.0 - 0.378 * coef3) / tempKelvin; /// kg/m3
|
|
|
|
|
|
return (float)airDensityKgm3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Convert 'pulses' to 'volume', prevent division by zero
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static float VolumeFromPulses(int pulses, float pulsesPerLiter)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pulsesPerLiter <= float.Epsilon) return 0;
|
|
|
|
|
|
return Convert.ToSingle(pulses) / pulsesPerLiter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculate the error in % from 'measured' and 'true' volume, prevent division by zero
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static float ErrorFromVolumes(float measuredVolume, float trueVolume)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (trueVolume <= float.Epsilon)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (measuredVolume <= float.Epsilon) return 0;
|
|
|
|
|
|
return 100.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
return 100.0f * (measuredVolume - trueVolume) / trueVolume;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calcuate progress of the test while setting the flow.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="currentTime">Current time from the beginning in [s]</param>
|
|
|
|
|
|
/// <param name="flowSetTime">Estimated time to set the flow in [s]</param>
|
|
|
|
|
|
/// <param name="testTime">Estimated test time in [s]</param>
|
|
|
|
|
|
/// <returns>Test progress 0 .. 1.0f</returns>
|
|
|
|
|
|
public static float TestProgress(float currentTime, float flowSetTime, float testTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Never return more then 'fixedPart'
|
|
|
|
|
|
if (flowSetTime <= float.Epsilon) return 0;
|
|
|
|
|
|
float fixedPart = flowSetTime / (flowSetTime + testTime);
|
|
|
|
|
|
return (currentTime < flowSetTime) ? (currentTime / flowSetTime) * fixedPart : fixedPart;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculate progress of the test during measurement.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="refPulses">Current number of reference pulses</param>
|
|
|
|
|
|
/// <param name="totalPulses">Total number of reference pulses to complete the test</param>
|
|
|
|
|
|
/// <param name="flowSetTime">Estimated time to set the flow in [s]</param>
|
|
|
|
|
|
/// <param name="testTime">Estimated test time in [s]</param>
|
|
|
|
|
|
/// <returns>Test progress 0 .. 1.0f</returns>
|
|
|
|
|
|
public static float TestProgress(int refPulses, int totalPulses, float flowSetTime, float testTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Never return less then 'fixedPart' and more then 1.0f
|
|
|
|
|
|
if (refPulses > totalPulses) refPulses = totalPulses;
|
|
|
|
|
|
float fixedPart = flowSetTime / (flowSetTime + testTime);
|
|
|
|
|
|
return fixedPart + (1.0f - fixedPart) * Convert.ToSingle(refPulses) / Convert.ToSingle(totalPulses);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Calculates corrected value form a list of corrections by interpolation.
|
|
|
|
|
|
/// It is assumed that values in the list 'corrections' are sorted.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="rawMeasurement">Raw uncorrected value</param>
|
|
|
|
|
|
/// <param name="corrections">Sorted (value, correction) pairs</param>
|
|
|
|
|
|
/// <returns>Corrected value</returns>
|
|
|
|
|
|
public static float CorrectedValue(float rawValue, IList<Entities.MeasurementCorrection> corrections)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (corrections == null || corrections.Count == 0) return rawValue;
|
|
|
|
|
|
|
|
|
|
|
|
if (rawValue < corrections[0].Measurement)
|
|
|
|
|
|
{
|
|
|
|
|
|
return rawValue + corrections[0].Correction;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int count = corrections.Count;
|
|
|
|
|
|
for (int i = 1; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rawValue < corrections[i].Measurement)
|
|
|
|
|
|
{
|
|
|
|
|
|
float d1 = rawValue - corrections[i-1].Measurement;
|
|
|
|
|
|
float d2 = corrections[i].Measurement - rawValue;
|
|
|
|
|
|
|
|
|
|
|
|
float corr;
|
|
|
|
|
|
if (d1 + d2 <= float.Epsilon)
|
|
|
|
|
|
{
|
|
|
|
|
|
corr = (corrections[i - 1].Correction + corrections[i].Correction) / 2.0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
corr = (corrections[i - 1].Correction * d2 + corrections[i].Correction * d1) / (d1 + d2);
|
|
|
|
|
|
}
|
|
|
|
|
|
return rawValue + corr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rawValue + corrections[count - 1].Correction;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|