/// /// Copyright (c) 2015-2017 Sensus Metering Systems /// using System; using System.Globalization; namespace TBF.BenchControl.TestMethods.iPerlCommunication.iPerlHead { public class OptoTelegramRaw { public static readonly int Length = 42; private static CultureInfo culture; /// /// Strobed value /// public static decimal TestStartTimestampDec; /// /// Stored values /// public OptoTelegramFlags Flags; public DateTime DateTime; /// From PC public float RefFlow; /// [m3/h] public int Counter; public UInt32 EmfRaw; /// From iPerl opto data public Int16 MagneticFieldRaw; public Int16 FlowRaw; public UInt32 VolumeRaw; public Int64 VolumeRawExt; public Int16 Impedance; public UInt32 Timestamp; public Int64 TimestampExt; public byte CheckSum; /// /// Calculated values /// public double EMF() { Int32 signedEmf = (EmfRaw > 0x7FFFFF) ? ((int)EmfRaw - 0x1000000) : (int)EmfRaw; return 0.000000333 * (double)signedEmf; } public double MagneticField() { return (double)MagneticFieldRaw; } public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; } public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; } public Int32 FlipTime() { return Impedance; } public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; } public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); } public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; } public string Label() { if (Flags == OptoTelegramFlags.OK_TestStart) return "#### start test ####"; else if (Flags == OptoTelegramFlags.OK_TestEnd) return "#### end of test ####"; else return string.Empty; } static OptoTelegramRaw() { culture = CultureInfo.CreateSpecificCulture("DE"); /// This is to use comma as decimal number separator } public OptoTelegramRaw() { } /// /// Parses optical telegram and returns OptoTelegramRaw object /// /// /// Create a configuration structure from a complete byte array /// /// Telegram description: /// /// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes) /// /// Data Comment Type Calculate to decimal /// ---------------------------------------------------------------- /// AAAAAA EMF Int24 Value * 0.000000333 /// BBBB Magnetic field Int16 Value /// CCCC Flow Int16 Value * 0.225 * Scalig factor /// DDDDDD Volume Int24 Value / 16000 * Scaling factor /// EEEE Impedance Int16 Value /// FFFFFFFF Timestamp Uint32 Value / 8192 /// GG Checksum Byte /// ---------------------------------------------------------------- /// /// Example: /// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86 /// FFDD3A 51F9 0000 65324E 0088 F631A60B 45 /// ... /// /// A complete byte array data /// true = telegram OK, false = telegram NOK public bool UpdateFromString(string telegram, int counter, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast) { DateTime = DateTime.Now; Counter = counter; RefFlow = (float)Sequences.ProcessData.RefFlow.Val; if ((telegram == null) || (telegram.Length < Length) || (telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') || (telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') || (telegram[40] != '\r') || (telegram[41] != '\n')) { Flags = OptoTelegramFlags.InvalidTelegram; return false; } bool f1 = UInt32.TryParse(telegram.Substring(0, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out EmfRaw); bool f2 = Int16.TryParse(telegram.Substring(7, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out MagneticFieldRaw); bool f3 = Int16.TryParse(telegram.Substring(12, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out FlowRaw); bool f4 = UInt32.TryParse(telegram.Substring(17, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw); bool f5 = Int16.TryParse(telegram.Substring(24, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Impedance); bool f6 = UInt32.TryParse(telegram.Substring(29, 8), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp); bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum); bool allOk = f1 && f2 && f3 && f4 && f5 && f6 && f7; Flags = allOk ? OptoTelegramFlags.OK : OptoTelegramFlags.InvalidTelegram; /// /// Cope with 'VolumeRaw' overflow /// Int64 uncorrected = (Int64)(((UInt64)volumeRawExtLast & 0xFFFFFFFFFF000000UL) | VolumeRaw); if (Math.Abs(uncorrected - volumeRawExtLast) <= 0x800000L) { VolumeRawExt = volumeRawExtLast = uncorrected; } else if (Math.Abs(uncorrected + 0x1000000L - volumeRawExtLast) <= 0x800000L) { VolumeRawExt = volumeRawExtLast = uncorrected + 0x1000000L; } else if (Math.Abs(uncorrected - 0x1000000L - volumeRawExtLast) <= 0x800000L) { VolumeRawExt = volumeRawExtLast = uncorrected - 0x1000000L; } else { VolumeRawExt = volumeRawExtLast = uncorrected; } /// /// Cope with 'Timestamp' overflow /// uncorrected = (Int64)(((UInt64)timestampExtLast & 0xFFFFFFFF00000000UL) | Timestamp); if (Math.Abs(uncorrected - timestampExtLast) <= 0x80000000L) { TimestampExt = timestampExtLast = uncorrected; } else if (Math.Abs(uncorrected + 0x100000000L - timestampExtLast) <= 0x80000000L) { TimestampExt = timestampExtLast = uncorrected + 0x100000000L; } else if (Math.Abs(uncorrected - 0x100000000L - timestampExtLast) <= 0x80000000L) { TimestampExt = timestampExtLast = uncorrected - 0x100000000L; } else { TimestampExt = timestampExtLast = uncorrected; } return allOk; } /// /// Alternative to UpdateFromString(...) when data are flushed /// public bool UpdateFromStringDummy(string telegram) { DateTime = DateTime.Now; RefFlow = (float)Sequences.ProcessData.RefFlow.Val; if ((telegram == null) || (telegram.Length < Length) || (telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') || (telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') || (telegram[40] != '\r') || (telegram[41] != '\n')) { Flags = OptoTelegramFlags.InvalidTelegram; return false; } //bool f1 = UInt32.TryParse(telegram.Substring(0, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out EmfRaw); //bool f2 = Int16.TryParse(telegram.Substring(7, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out MagneticFieldRaw); //bool f3 = Int16.TryParse(telegram.Substring(12, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out FlowRaw); //bool f4 = UInt32.TryParse(telegram.Substring(17, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw); //bool f5 = Int16.TryParse(telegram.Substring(24, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Impedance); //bool f6 = UInt32.TryParse(telegram.Substring(29, 8), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp); //bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum); //return f1 && f2 && f3 && f4 && f5 && f6 && f7; return true; } public void SetFlags(OptoTelegramFlags flags) { this.Flags = flags; } public string ToString(double scalingFactor, OptoTelegramRaw previous) { if (Flags == OptoTelegramFlags.SyncError) { return "Sychronization error"; } else if (Flags == OptoTelegramFlags.InvalidTelegram) { return "Invalid telegram"; } else /// if (flags == OptoTelegramFlags.OK / OptoTelegramFlags.OK_TestStart / OptoTelegramFlags.OK_TestEnd) { return string.Format("{0}:{1}:{2}.{3}\t{4} :\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\t{13}\t{14}\t{15}\t{16}\t{17}\t{18}\t{19}\t{20}\t{21}\t{22}", DateTime.Hour.ToString("D2"), DateTime.Minute.ToString("D2"), DateTime.Second.ToString("D2"), DateTime.Millisecond.ToString("D4"), Counter, EmfRaw.ToString("X6"), MagneticFieldRaw.ToString("X4"), FlowRaw.ToString("X4"), VolumeRaw.ToString("X6"), Impedance.ToString("X4"), Timestamp.ToString("X8"), CheckSum.ToString("X2"), EMF().ToString("F4", culture), MagneticField().ToString("F0", culture), Flow(scalingFactor).ToString("F2", culture), Volume(scalingFactor).ToString("F4", culture), FlipTime().ToString("F0", culture), TimestampDec().ToString("F4", culture), (RefFlow * 1000).ToString("F2", culture), VolumeDelta(scalingFactor, previous).ToString("F4", culture), TimeDelta().ToString("F3", culture), scalingFactor.ToString("F1", culture), Label()); } } /// /// Filter RefFlow data in an array of OptoTelegramRaw objects by a FIR filter: /// /// kSize = 5, kSize2 = 2 /// /// i k /// --------------------------------------------------------------------------- /// 0 -5 filtered[0] = data[0] /// 1 -4 filtered[1] = data[1] /// 2 -3 filtered[2] = data[0]*k[0] + ... + data[4]*k[4] /// 3 -2 filtered[3] = data[1]*k[0] + ... + data[5]*k[4] /// 4 -1 filtered[4] = data[2]*k[0] + ... + data[6]*k[4] /// 5 0 data[0] = filtered[0], filtered[0] = data[3]*k[0] + ... + data[7]*k[4] /// 6 1 data[1] = filtered[1], filtered[1] = data[4]*k[0] + ... + data[8]*k[4] /// 7 ... /// /// array of OptoTelegramRaw objects /// number of objects to process public static void FIRFilterFlow(OptoTelegramRaw[] optoData, int optoDataCount) { float[] kernel = new float[] { 0.1f, 0.2f, 0.4f, 0.2f, 0.1f }; int kSize = kernel.Length; int kSize2 = kernel.Length / 2; float[] filtered = new float[kSize]; for (int i = 0; i < optoDataCount; i++) { int k = i - kSize; if (k >= 0) optoData[k].RefFlow = filtered[i % kSize]; if (i < kSize2 || i >= optoDataCount - kSize2) { filtered[i % kSize] = optoData[i].RefFlow; } else { float weoightedSum = 0; for (int j = -kSize2; j <= kSize2; j++) weoightedSum += optoData[i + j].RefFlow * kernel[j + kSize2]; filtered[i % kSize] = weoightedSum; } } for (int k = optoDataCount - kSize; k < optoDataCount; k++) { if (k >= 0) optoData[k].RefFlow = filtered[k % kSize]; } } } }