using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Media; using System.Xml; using DTS.Common.Enums; using DTS.Common.Enums.Hardware; using DTS.Common.Interface; using DTS.Common.Interface.TestDefinition; namespace DTS.Common.Utils { public static class Utils { private const string LINEAR_PATTERN = ".lin."; public delegate string GetIsoCodeDelegate(string binaryFileName); //https://stackoverflow.com/questions/11412956/what-is-the-best-way-of-validating-an-ip-address public static bool ValidateIPv4(string ipString) { if (string.IsNullOrWhiteSpace(ipString)) { return false; } string[] splitValues = ipString.Split('.'); if (splitValues.Length != 4) { return false; } byte tempForParsing; return splitValues.All(r => byte.TryParse(r, out tempForParsing)); } private static List GetFileList(string path) { if (path.Contains("ALL") || path.Contains("ROI")) { return FileUtils.FindFilesInDirectory(path, "chn"); } else { FileUtils.FileList = new List(); FileUtils.FindFiles(path, "chn"); return FileUtils.FileList; } } private static string GetFullFilePathNewStyle(List fileList, ITestMetadata tmd, string testId, ITestChannel ch) { if (ch.Bridge == "SQUIB") { return ch.Number % 2 == 0 ? fileList.Where(l => l.Contains(tmd.TestRun.DataType)).FirstOrDefault(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000"))) : fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Where(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000"))).ToList()[1]; } else { var matches = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Where(f => f.Contains(testId + "Ch" + (ch.AbsoluteDisplayOrder + 1).ToString("000"))); //14581 Data for linear, cubic poly sensor differs in run test and view data tile //if we end up with 2 matches, one is the linear added version and one is the non linear version //determine which file we actually want, and use it. if (2 <= matches.Count()) { if (string.IsNullOrWhiteSpace(ch.LinearizationFormula)) { var fullFileName = matches.FirstOrDefault(f => f.Contains(LINEAR_PATTERN)); //if there are two files and neither contains a .lin, then we've like hit a situation described in //MS 26913 where there was multiple downloads which resulted in the same channel information in two files //we can likely select either of the files in this case as they are probably identical return null == fullFileName ? matches.FirstOrDefault() : fullFileName; } else { return matches.FirstOrDefault(f => !f.Contains(LINEAR_PATTERN)); } } else { return matches.FirstOrDefault(); } } } private static string GetFullFilePath(List fileList, ITestMetadata tmd, string testId, int channelCount) { // DataPRO if (!string.IsNullOrEmpty(tmd.TestRun.Software)) { var testFileList = fileList .Where(l => l.Contains(tmd.TestRun.DataType) && l.Contains(testId)).ToList(); if (0 == testFileList.Count) { testFileList = fileList.Where(l => l.Contains(testId)).ToList(); } if (channelCount < testFileList.Count) { return testFileList[channelCount]; } } // SLICEWare else { var testFileList = fileList .Where(l => l.Contains(testId)).ToList(); if (channelCount < testFileList.Count) { return testFileList[channelCount]; } } return string.Empty; } /// /// populates Test.Channels properties with details /// /// Test Metadata list /// file location /// function that returns an isocode given a filepath public static void SetChannelInfo(ITestMetadata tmd, string path, GetIsoCodeDelegate GetIsoCode) { var fileList = GetFileList(path); var testId = tmd.TestRun.Id; tmd.TestRun.Channels = new List(); tmd.TestRun.CalculatedChannels = new List(); var channelCount = 0; foreach (var m in tmd.TestRun.Modules) { SetChannelInfoModule(m, fileList, tmd, testId, ref channelCount, GetIsoCode); } AddGraphs(tmd); } private static void SetChannelInfoModule(ITestModule m, List fileList, ITestMetadata tmd, string testId, ref int channelCount, GetIsoCodeDelegate getIsoCode) { var sn = m.SerialNumber; foreach (var ch in m.Channels) { ch.ModuleSerialNumber = sn; var fullFileName = string.Empty; var newStyle = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).Any(f => f.Contains(testId + "Ch")); if (!newStyle) { fullFileName = GetFullFilePath(fileList, tmd, testId, channelCount); } else { fullFileName = GetFullFilePathNewStyle(fileList, tmd, testId, ch); } if (string.IsNullOrEmpty(fullFileName)) { ch.ErrorMessage = "File does not exist"; } else { ch.BinaryFilePath = Path.GetDirectoryName(fullFileName); ch.BinaryFileName = Path.GetFileName(fullFileName); if (string.IsNullOrEmpty(ch.IsoCode)) { ch.IsoCode = getIsoCode(ch.BinaryFilePath + $"\\" + ch.BinaryFileName); } } ch.ParentModule = m; ch.ParentTestSetup = tmd.TestSetup; tmd.TestRun.Channels.Add(ch); channelCount++; } foreach (var ch in m.CalculatedChannels) { var fullFileName = fileList.Where(l => l.Contains(tmd.TestRun.DataType)).FirstOrDefault(f => f.Contains(testId + "." + ch.Number.ToString())); if (string.IsNullOrEmpty(fullFileName)) { ch.ErrorMessage = "File does not exist"; } else { ch.ModuleSerialNumber = sn; ch.ParentModule = m; ch.ParentTestSetup = tmd.TestSetup; ch.BinaryFilePath = Path.GetDirectoryName(fullFileName); ch.BinaryFileName = Path.GetFileName(fullFileName); } tmd.TestRun.CalculatedChannels.Add(ch); } } private static void AddGraphs(ITestMetadata tmd) { foreach (var g in tmd.TestSetup.TestGraphs) { g.Channels = new List(); foreach (var chId in g.ChannelIds) { var ch = (from c in tmd.TestRun.Channels where c.ChannelId == chId select c).FirstOrDefault(); if (ch != null) g.Channels.Add(ch.Copy()); } } } /// /// Copied from DataPro /// per http://fogbugz/fogbugz/default.asp?8281 use 120% /// The idea here is to have a moving window no smaller than +/-1% of capacity /// Also make sure that the top of the signal is seen so make the visible window /// +/-120% of the signal /// this was abstrated here from two different locations for: /// 29416 Limit view of TSR AIR data to 1% of full scale in auto range mode /// /// plot expected maximum EU /// actual plot min value /// actual plot max value /// the scaler to apply to signal as the window max edge /// the scaler to apply to expected maximum to form window min edges /// min Y value to apply as plot constraint /// max Y value to apply as plot constraint /// /// public static void CalculateMinMaxAutoScaling(double capacity, double dataMin, double dataMax, double minAutoZoom, double maxAutoZoom, out double currentYMax, out double currentYMin ) { capacity = Math.Abs(capacity); var middleOfSignalExtrema = ((dataMax - dataMin) / 2.0D) + dataMin; var minZoom = capacity * minAutoZoom; var topOfMinZoomWindow = middleOfSignalExtrema + minZoom; var bottomOfMinZoomWindow = middleOfSignalExtrema - minZoom; currentYMax = (dataMax + Math.Abs(dataMax * (1 - maxAutoZoom)) >= topOfMinZoomWindow) ? dataMax + Math.Abs(dataMax * (1 - maxAutoZoom)) : topOfMinZoomWindow; currentYMin = (dataMin - Math.Abs(dataMin * (1 - maxAutoZoom)) <= bottomOfMinZoomWindow) ? dataMin - Math.Abs(dataMin * (1 - maxAutoZoom)) : bottomOfMinZoomWindow; } /// /// test code courtesy of AutoLab XML import/conversion code via Chad Ivan /// the CRC should be prepended to the ID (CRC + ID) /// /// short ID /// returns CRC bit for given string public static string GenerateEIDCRC(string ShortID) { var int64 = Int64.Parse(ShortID, System.Globalization.NumberStyles.HexNumber); var bytes = BitConverter.GetBytes(int64); var bitArray = new System.Collections.BitArray(bytes); var CRCbit = new System.Collections.BitArray(8); var CRCTemp = new System.Collections.BitArray(1); byte[] CRCbyte = new byte[1]; for (int i = 0; i < 56; i++) { CRCTemp[0] = CRCbit[0] ^ bitArray[i]; CRCbit[0] = CRCbit[1]; CRCbit[1] = CRCbit[2]; CRCbit[2] = CRCbit[3] ^ CRCTemp[0]; CRCbit[3] = CRCbit[4] ^ CRCTemp[0]; CRCbit[4] = CRCbit[5]; CRCbit[5] = CRCbit[6]; CRCbit[6] = CRCbit[7]; CRCbit[7] = CRCTemp[0]; } CRCbit.CopyTo(CRCbyte, 0); return BitConverter.ToString(CRCbyte); ; } /// /// handles indenting and formatting for xml text /// /// /// public static string PrettyPrint(string XML) { var Result = ""; using (var MS = new MemoryStream()) { using (var W = new XmlTextWriter(MS, Encoding.Unicode)) { var D = new XmlDocument(); try { // Load the XmlDocument with the XML. D.LoadXml(XML); W.Formatting = Formatting.Indented; // Write the XML into a formatting XmlTextWriter D.WriteContentTo(W); W.Flush(); MS.Flush(); // Have to rewind the MemoryStream in order to read // its contents. MS.Position = 0; // Read MemoryStream contents into a StreamReader. using (var SR = new StreamReader(MS)) {// Extract the text from the StreamReader. var FormattedXML = SR.ReadToEnd(); Result = FormattedXML; } } catch (XmlException) { } MS.Close(); W.Close(); } } return Result; } //https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-display-milliseconds-in-date-and-time-values //FB 29410 Format the date time to string with milliseonds /// /// Format a DateTime with nanoseconds part to a date time string with milliseconds in a current culture /// /// DateTime object with nano seconds part /// Formatted date time in string public static string FormatTimeStamp(DateTime dateTimeWithNanoSeconds) { string fullPattern = DateTimeFormatInfo.CurrentInfo.LongTimePattern; // Create a format similar to .fff but based on the current culture. string millisecondFormat = $"{NumberFormatInfo.CurrentInfo.NumberDecimalSeparator}fff"; // Append millisecond pattern to current culture's full date time pattern. fullPattern = Regex.Replace(fullPattern, "(:ss|:s)", $"$1{millisecondFormat}"); string time = dateTimeWithNanoSeconds.ToString(fullPattern); string date = dateTimeWithNanoSeconds.ToShortDateString(); string dateTime = $"{date} {time}"; return dateTime; } /// /// returns the next highest power of 2 /// /// /// public static int GetEnclosingPower2(int numSamples) { //return Convert.ToInt32(Math.Pow(2D, Math.Ceiling(Math.Log(Convert.ToDouble(numSamples)) / Math.Log(2D)))); // 24418 use bitwise calc for better performance var next = (uint)numSamples; next--; next |= next >> 1; next |= next >> 2; next |= next >> 4; next |= next >> 8; next |= next >> 16; next++; return (int)next; } public static long GetEnclosingPower2(long numSamples) { //return Convert.ToInt32(Math.Pow(2D, Math.Ceiling(Math.Log(Convert.ToDouble(numSamples)) / Math.Log(2D)))); // 24418 use bitwise calc for better performance var next = (ulong)numSamples; next--; next |= next >> 1; next |= next >> 2; next |= next >> 4; next |= next >> 8; next |= next >> 16; next |= next >> 32; next++; return (long)next; } public static string EscapeString(string str) { if (null == str) { return "#NOVALUE"; } var mustQuote = (str.Contains(",") || str.Contains("\"") || str.Contains("\r") || str.Contains("\n")); if (!mustQuote) return str; var sb = new StringBuilder(); sb.Append("\""); foreach (var nextChar in str) { sb.Append(nextChar); if (nextChar == '"') sb.Append("\""); } sb.Append("\""); return sb.ToString(); } public static string ReplaceLast(this string str, string find, string replace) { var place = str.LastIndexOf(find, StringComparison.Ordinal); return -1 == place ? str : str.Remove(place, find.Length).Insert(place, replace); } public static string ReplaceFirst(this string str, string find, string replace) { var place = str.IndexOf(find, StringComparison.Ordinal); return -1 == place ? str : str.Remove(place, find.Length).Insert(place, replace); } public static void ForEach(this IEnumerable list, Action action) { foreach (var item in list) { action(item); } } public static string ReplaceStrings(this string str, Dictionary toReplace, Common.Enums.StringReplacementMode mode) { try { switch (mode) { case StringReplacementMode.All: toReplace.ForEach(pair => str = str.Replace(pair.Key, pair.Value)); break; case StringReplacementMode.First: toReplace.ForEach(pair => str = str.ReplaceFirst(pair.Key, pair.Value)); break; case StringReplacementMode.Last: toReplace.ForEach(pair => str = str.ReplaceLast(pair.Key, pair.Value)); break; } } catch { // ignored, bad data in } return str; } public static void GetChildren(UIElement parent, Type targetType, ref List children) { var count = VisualTreeHelper.GetChildrenCount(parent); if (count <= 0) return; for (var i = 0; i < count; i++) { var child = (UIElement)VisualTreeHelper.GetChild(parent, i); if (child.GetType() == targetType) { children.Add(child); } GetChildren(child, targetType, ref children); } } public static void GetChildrenByName(UIElement parent, string targetName, ref List children) { var count = VisualTreeHelper.GetChildrenCount(parent); if (count <= 0) return; for (var i = 0; i < count; i++) { var child = (UIElement)VisualTreeHelper.GetChild(parent, i); if (((string)child.GetType().GetProperty("Name").GetValue(child, null)).Contains(targetName)) { children.Add((FrameworkElement)child); } GetChildrenByName(child, targetName, ref children); } } /// /// Finds a Child of a given item in the visual tree. /// /// A direct parent of the queried item. /// The type of the queried item. /// x:Name or Name of child. /// The first parent item that matches the submitted type parameter. /// If not matching item can be found, /// a null parent is being returned. public static T FindChild(DependencyObject parent, string childName) where T : DependencyObject { // Confirm parent and childName are valid. if (parent == null) return null; T foundChild = null; var childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (var i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); // If the child is not of the request child type child if (!(child is T)) { // recursively drill down the tree foundChild = FindChild(child, childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } else if (!string.IsNullOrEmpty(childName)) { // If the child's name is set for search if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName) { // if the child's name is of the request name foundChild = (T)child; break; } } else { // child element found. foundChild = (T)child; break; } } return foundChild; } public static T FindParent(DependencyObject child) where T : DependencyObject { if (null == child) return null; T visParent = VisualTreeHelper.GetParent(child) as T; T logParent = LogicalTreeHelper.GetParent(child) as T; return visParent ?? logParent ?? FindParent(VisualTreeHelper.GetParent(child)); } public static T FindVisualChild(DependencyObject obj) where T : DependencyObject { for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child is T variable) return variable; var childOfChild = FindVisualChild(child); if (childOfChild != null) return childOfChild; } return null; } public static List GetVisualChildren(DependencyObject obj) where T : DependencyObject { var childList = new List(); for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child is T) childList.Add(child as T); } return childList.Count > 0 ? childList : null; } public static string GetEnumDescription(this T enumerationValue) where T : struct { var type = enumerationValue.GetType(); if (!type.IsEnum) { throw new ArgumentException($"{nameof(enumerationValue)} must be of Enum type", nameof(enumerationValue)); } var memberInfo = type.GetMember(enumerationValue.ToString()); if (memberInfo.Length > 0) { var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); if (attrs.Length > 0) { return ((DescriptionAttribute)attrs[0]).Description; } } return enumerationValue.ToString(); } public static int FindValueInArray(double value, double[] valueArray) { for (var index = 0; index < valueArray.Length; index++) { if (value == valueArray[index]) { return index; } } return -1; } public static double StandardDeviation(this IEnumerable values) { var avg = values.Average(); return Math.Sqrt(values.Average(val => (val - avg) * (val - avg))); } public static void CalculateClip(double[] data, out double clipPeak, out double clipStartSeconds, out double clipEndSeconds, double sampleRate, int preZeroDataPoints) { clipPeak = 0; clipStartSeconds = 0; clipEndSeconds = 0; /* From TDC */ for (var index = 0; index < data.Length - Math.Floor(0.003 / (1 / sampleRate) + 0.5); index++) { var localclip = 10000D; for (var localIndex = index; localIndex < index + Math.Floor(0.003 / (1 / sampleRate) + 0.5); localIndex++) { if (data[localIndex] < localclip) { localclip = data[localIndex]; } } if (!(localclip > clipPeak)) continue; clipPeak = localclip; clipStartSeconds = (index - preZeroDataPoints) * (1 / sampleRate); clipEndSeconds = clipStartSeconds + 0.003; } } private static readonly short[] ODDPARITY = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; public static byte Math_CRC7(byte[] buffer, int length) { int i; int j; byte crc = 0xFF; for (i = 0; i < length; i++) { var currentByte = buffer[i]; for (j = 0; j < 8; j++) { crc = (byte)(crc << 1); if (0x00 != ((currentByte ^ crc) & 0x80)) { crc = (byte)(crc ^ 0x09); } currentByte <<= 1; } crc = (byte)(crc & 0x7F); } crc <<= 1; crc |= 0x01; return crc; } /** * Computes the 16-bit CRC of the n passed in data bytes. N should be * even (if it is not, the last byte will not get included in the * computation). The initial_crc is the value to seed the computation * from - normally it will be set to 0. * * @param data the unsigned character buffer of which to compute the CRC16 * @param n the number of bytes in data (must be even!) * @param initial_crc the CRC16 value to seed the computation with * * @return the updated CRC16 starting with initial_crc and computed over * n data bytes */ /* Slightly changed to handle array of bytes */ public static ushort CalculateCRC(byte[] data, ushort initialCRC) { ushort crc = 0;//initial_crc98765430. int i; for (i = 0; i < data.Length; i += 2) { crc = Math_DoCRC16Step((ushort)((data[i + 1] << 8) | data[i]), crc); } return crc; } /** * Computes the 16-bit CRC of the n passed in data bytes. CCITT implementation * N should be * even (if it is not, the last byte will not get included in the * computation). The initial_crc is the value to seed the computation * from - normally it will be set to 0. * * @param data the unsigned character buffer of which to compute the CRC16 * @param n the number of bytes in data (must be even!) * @param initial_crc the CRC16 value to seed the computation with * * @return the updated CRC16 starting with initial_crc and computed over * n data bytes */ /* Slightly changed to handle array of bytes */ public static ushort CalculateCRCCCITT(byte[] data, ushort initialCRC) { var crc = initialCRC; int i; for (i = 0; i < data.Length; i += 2) { crc = Math_DoCRCCCITTStep((ushort)((data[i + 1] << 8) | data[i]), crc); } return crc; } public static ushort Math_DoCRC16Step(ushort cdata, ushort currentCRC) { cdata = (ushort)((cdata ^ (currentCRC & 0xff)) & 0xff); currentCRC >>= 8; if (0 != (ODDPARITY[cdata & 0xf] ^ ODDPARITY[cdata >> 4])) currentCRC ^= 0xc001; cdata <<= 6; currentCRC ^= cdata; cdata <<= 1; currentCRC ^= cdata; return currentCRC; } private static readonly ushort[] CRC_TABLE = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; public static ushort Math_DoCRCCCITTStep(ushort cdata, ushort current_crc) { return (ushort)((ushort)(current_crc << 8) ^ CRC_TABLE[(ushort)(((ushort)(current_crc >> 8) ^ cdata) & 0xFF)]); } public static void FilterDataArray(ref double[] data, double sampleRate, double filterFrequency) { /********************************/ /*** MCW NHTSA FILTER ROUTINE ***/ /********************************/ // // CFILTER.C cfc filter program // // Filters data using SAE J211 algorithym (4 pole phaseless lo pass). // "Pads" data with 5% of the original number of points at the beginning with the first point // and 5% of the original number of points at the end with the last point // to "squelch" out filter start up spikes. // Previously read data file from disc, filtered and wrote new data file to disc. // NOW read/writes to array(s) in memory. // Optionally zeros baseline by subtracting the average of the first 5% // of the original number of points; SKIPS data which is "padded" by this routine // Optionally scales the data after filtering. SCALE = 1.0 skips to avoid unnecessary calc's. // // "syntax:" // // cfilter(INdata, SCALE, OUTdata, SampleRate, CFCnumb, BaseLine, NumPoints); // // INdata (pointer?): array (double) of data to be filtered // SCALE (double): a number multiplied by the above array after it is filtered. // Useful for "last minute" scaling and/or inversion // Outdata (pointer?): array (double) into which the results are stored. // INdata can = OUTdata, INdata will then be overwrittten. // SampleRate (double): the frequency(in samples per second) at which the INdata were sampled. // CFCnumb (double): SAE Channel Filter Class; // 60, 180, 600, or 1000; ONLY these values have been validated. // BaseLine (char): a single character; ONLY "Y" or "y" will zero baseline; // (This started as a command line style FORTRAN program and used to "ask" // the user. Some day this should be changed to an integer (1 or 0) switch.) // NumPoints (int): the number of points in the input array (INdata); // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // int cfilter(double *indata, double calib, double *out, double freq, // double cfc, char baseline[2], int n) // { double[] Z; double[] A; double t, pi, wd, wa, d0, d1, d2, e1, e2; double f1, f2, f3, f4, f5; int data_end, i, j, k, pad; double[] NewDataArray; var calib = 1.0; var CFC = filterFrequency * 0.6; // The set-up of the formulas. t = 1.0 / sampleRate; pi = 4.0 * Math.Atan(1.0); wd = 2.0 * pi * CFC * 2.0775; wa = Math.Sin(wd * (t / 2.0)) / Math.Cos(wd * (t / 2.0)); // The original formula d0 = wa * wa / (1 + Math.Sqrt(2.0) * wa + wa * wa); d1 = 2.0 * d0; d2 = d0; e1 = -2.0 * (wa * wa - 1) / (1 + Math.Sqrt(2.0) * wa + wa * wa); e2 = (-1 + Math.Sqrt(2.0) * wa - wa * wa) / (1 + Math.Sqrt(2.0) * wa + wa * wa); data_end = data.Length - 1; // data_end is used for loop counting corresponding // to the number of lines that the data file has pad = (int)(0.05 * data.Length); Z = new double[pad + pad + data.Length]; A = new double[pad + pad + data.Length]; NewDataArray = new double[pad + pad + data.Length]; for (i = 0; i <= pad - 1; i++) // pad newdata at beginning { // w/ first pt of indata NewDataArray[i] = data[i]; } for (i = pad; i <= data_end + pad - 1; i++) // Copy indata into newdata { // after initial pad NewDataArray[i] = data[i - (pad - 1)]; } for (i = data_end + pad; i <= data_end + 2 * pad - 1; i++) // pad newdata at end { // w/ last pt of indata NewDataArray[i] = data[data_end - 1]; } data_end = data_end + 2 * pad; // Data Passed Through Filter Backward for (j = data_end - 2; j >= 0; j--) { f1 = d0 * NewDataArray[j]; // Breaking f2 = d1 * NewDataArray[j + 1]; // down f3 = d2 * NewDataArray[j + 2]; // a big f4 = e1 * Z[j + 1]; // formula f5 = e2 * Z[j + 2]; // into smaller "groups". Z[j] = f1 + f2 + f3 + f4 + f5; // Putting the "groups" back together } Z[data_end - 1] = Z[data_end - 3]; Z[data_end - 2] = Z[data_end - 3]; // Data Passed Through Filter Forward for (k = 3; k <= data_end; k++) { f1 = d0 * Z[k]; // Breaking f2 = d1 * Z[k - 1]; // down f3 = d2 * Z[k - 2]; // a big f4 = e1 * A[k - 1]; // formula f5 = e2 * A[k - 2]; // into smaller "groups". A[k] = f1 + f2 + f3 + f4 + f5; // Putting the "groups" back together } A[1] = A[3]; // This sets the first data points to the same A[2] = A[3]; // value as the 3rd value. Without this, the i = 0; for (k = pad; k <= data_end - pad; k++) // DO loop to write output file { if (calib != 1.0) // { // Multiplying by the calibration factor A[k] = A[k] * calib; // Unless factor = 1.0 } // AFTER the filtering has been finished NewDataArray[i] = A[k]; i++; } Array.Copy(NewDataArray, 0, data, 0, data.Length); } public static bool IsLike(string text, string pattern, bool caseSensitive = false) { //Check for empties if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(pattern)) return true; //Prepare for regex pattern = pattern.Replace(".", @"\."); pattern = pattern.Replace("(", @"\("); pattern = pattern.Replace(")", @"\)"); pattern = pattern.Replace("?", "."); //pattern = pattern.Replace("*", ".*?"); pattern = pattern.Replace(@"\", @"\\"); pattern = pattern.Replace(" ", @"\s"); try { return new Regex(pattern, caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase).IsMatch(text); } catch { return false; } } public static bool? IsClockSynced(IDictionary clocksyncs, ClockSyncProfile profile, HardwareTypes hwType) { if (HardwareTypes.SLICE_Pro_Distributor == hwType) { return null; } if (HardwareTypes.SLICE6DB3 == hwType) { return null; } if ((HardwareTypes.SLICE6_Base == hwType || HardwareTypes.SLICE6DB == hwType || HardwareTypes.SLICE6DB_InDummy == hwType) && profile == ClockSyncProfile.Manual) { //18030 S6, S6DB can only do PTP so "manual" is always PTP profile = ClockSyncProfile.Auto_E2E; } bool? synced = null; switch (profile) { case ClockSyncProfile.Master_E2E: break; case ClockSyncProfile.Auto_E2E: case ClockSyncProfile.Slave_E2E: synced = clocksyncs[InputClockSource.PTP]; break; case ClockSyncProfile.EXT_PPS: case ClockSyncProfile.Master_E2E_EXT_PPS: synced = clocksyncs[InputClockSource.OnePPS]; break; case ClockSyncProfile.GPS: case ClockSyncProfile.Master_E2E_GPS: synced = clocksyncs[InputClockSource.GPS]; break; case ClockSyncProfile.IRIG: case ClockSyncProfile.Master_E2E_IRIG: synced = clocksyncs[InputClockSource.IRIG]; break; case ClockSyncProfile.GPS_EXT_PPS: case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: synced = clocksyncs[InputClockSource.GPS] && clocksyncs[InputClockSource.OnePPS]; break; case ClockSyncProfile.IRIG_EXT_PPS: case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: synced = clocksyncs[InputClockSource.IRIG] && clocksyncs[InputClockSource.OnePPS]; break; } return synced; } public static bool IsTmNSString(string tmns) { return tmns.StartsWith("(") && tmns.EndsWith(")") && Array.TrueForAll(tmns.TrimStart(new[] { '(' }).TrimEnd(new[] { ')' }).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), sval => uint.TryParse(sval, out var uval) && uval < 65536); } const double _1 = 0.1; const double _2 = 0.01; const double _3 = 0.001; const double _4 = 0.0001; const double _5 = 0.00001; const double _6 = 0.000001; const double _7 = 0.0000001; public static bool EqualsDigitPrecision(this double left, double right, uint precision) { var diff = Math.Abs(left - right); switch (precision) { case 0: return (int)left == (int)right; case 1: return diff < _1; case 2: return diff < _2; case 3: return diff < _3; case 4: return diff < _4; case 5: return diff < _5; case 6: return diff < _6; default: case 7: return diff < _7; } } } }