using System; using System.Globalization; namespace TBF.BenchControl.WaterMeters.iPerl { 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 Int32 VolumeRaw; public Int16 Impedance; public Int64 Timestamp; 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)VolumeRaw; } public Int32 FlipTime() { return Impedance; } public decimal TimestampDec() { return (decimal)Timestamp / (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 /// ConfigStruct or null when byte array was not complete public bool UpdateFromString(string telegram, int counter) { DateTime = DateTime.Now; Counter = counter; RefFlow = 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')) { 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 = Int32.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 = Int64.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; } public static OptoTelegramRaw FromString(string telegram, int counter) { OptoTelegramRaw opto = new OptoTelegramRaw(); if (opto.UpdateFromString(telegram, counter)) { return opto; } else { return null; } } 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]; } } } }