Files
DP44/Common/DTS.Common/Utils/Utils.cs
2026-04-17 14:55:32 -04:00

1089 lines
46 KiB
C#

using DTS.Common.Enums;
using DTS.Common.Enums.DASFactory;
using DTS.Common.Enums.Hardware;
using DTS.Common.Interface;
using DTS.Common.Interface.TestDefinition;
using DTS.Common.SharedResource.Strings;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;
using System.Xml;
namespace DTS.Common.Utils
{
public static class Utils
{
private const float DefaultEpsilon = 1e-4f;
/// <summary>
/// Checks if a value is zero within 4 significant digits.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsZero(float value, float epsilon = DefaultEpsilon)
{
return Math.Abs(value) < epsilon;
}
/// <summary>
/// Compares two floats for equality within significant digits,
/// scaling the epsilon based on the magnitude of the numbers.
/// </summary>
public static bool AlmostEqual(float a, float b, float epsilon = DefaultEpsilon)
{
float diff = Math.Abs(a - b);
// Shortcut for zeros/near-zeros
if (a == b) return true;
// Use relative error for larger numbers
// This ensures "4 significant digits" works whether the number is 0.001 or 1,000,000
return diff < epsilon * Math.Max(Math.Abs(a), Math.Abs(b));
}
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));
}
/// <summary>
/// If any of the UDP addresses are invalid, return an error
/// </summary>
/// <param name="udpAddressStreamIn"></param>
/// <param name="errors"></param>
public static void ValidateUDPAddress(string udpAddressStreamIn, ref List<string> errors)
{
if (string.IsNullOrWhiteSpace(udpAddressStreamIn)) { return; }
string[] udpAddresses = udpAddressStreamIn.Split(',');
foreach (var udpAddress in udpAddresses)
{
var trimmedUdpAddress = udpAddress.ToLower().Replace("udp://", "");
var firstIndexOfColon = trimmedUdpAddress.IndexOf(":");
var ipAddress = firstIndexOfColon > 1 ? trimmedUdpAddress.Substring(0, firstIndexOfColon) : string.Empty;
if ((!udpAddress.ToLower().StartsWith("udp") || !Uri.IsWellFormedUriString(udpAddress, UriKind.Absolute) || !ValidateIPv4(ipAddress)) &&
!errors.Contains(StringResources.EditStreamInputOrOutputControl_InvalidUDPAddress))
{
errors.Add(StringResources.EditStreamInputOrOutputControl_InvalidUDPAddress);
}
}
}
private static List<string> GetFileList(string path)
{
if (path.Contains("ALL") || path.Contains("ROI"))
{
return FileUtils.FindFilesInDirectory(path, "chn");
}
else
{
FileUtils.FileList = new List<string>();
FileUtils.FindFiles(path, "chn");
return FileUtils.FileList;
}
}
private static string GetFullFilePathNewStyle(List<string> 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<string> 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;
}
/// <summary>
/// populates Test.Channels properties with details
/// </summary>
/// <param name="tmd">Test Metadata list</param>
/// <param name="path">file location</param>
/// <param name="GetIsoCode">function that returns an isocode given a filepath</param>
public static void SetChannelInfo(ITestMetadata tmd, string path, GetIsoCodeDelegate GetIsoCode)
{
var fileList = GetFileList(path);
var testId = tmd.TestRun.Id;
tmd.TestRun.Channels = new List<ITestChannel>();
tmd.TestRun.CalculatedChannels = new List<ITestChannel>();
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<string> 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<ITestChannel>();
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());
}
}
}
/// <summary>
/// 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
/// </summary>
/// <param name="capacity">plot expected maximum EU</param>
/// <param name="dataMin">actual plot min value</param>
/// <param name="dataMax">actual plot max value</param>
/// <param name="maxAutoZoom">the scaler to apply to signal as the window max edge</param>
/// <param name="minAutoZoom">the scaler to apply to expected maximum to form window min edges</param>
/// <param name="currentYMin">min Y value to apply as plot constraint</param>
/// <param name="currentYMax">max Y value to apply as plot constraint</param>
/// <remarks>
/// </remarks>
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;
}
/// <summary>
/// test code courtesy of AutoLab XML import/conversion code via Chad Ivan
/// the CRC should be prepended to the ID (CRC + ID)
/// </summary>
/// <param name="ShortID">short ID</param>
/// <returns>returns CRC bit for given string</returns>
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); ;
}
/// <summary>
/// handles indenting and formatting for xml text
/// </summary>
/// <param name="XML"></param>
/// <returns></returns>
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
/// <summary>
/// Format a DateTime with nanoseconds part to a date time string with milliseconds in a current culture
/// </summary>
/// <param name="dateTimeWithNanoSeconds">DateTime object with nano seconds part</param>
/// <returns>Formatted date time in string</returns>
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;
}
/// <summary>
/// returns the next highest power of 2
/// </summary>
/// <param name="numSamples"></param>
/// <returns></returns>
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<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (var item in list)
{
action(item);
}
}
public static string ReplaceStrings(this string str, Dictionary<string, string> 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<UIElement> 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<FrameworkElement> 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);
}
}
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(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<T>(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<T>(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<T>(VisualTreeHelper.GetParent(child));
}
public static T FindVisualChild<T>(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<T>(child);
if (childOfChild != null)
return childOfChild;
}
return null;
}
public static List<T> GetVisualChildren<T>(DependencyObject obj) where T : DependencyObject
{
var childList = new List<T>();
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<T>(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<double> 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<InputClockSource, bool> 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;
}
}
public static string GetThermocouplerTypeString(int thermocouplerType)
{
switch (thermocouplerType)
{
case (int)ChannelTypes.SliceTcType_C: return Strings.Strings.ThermocouplerC;
case (int)ChannelTypes.SliceTcType_E: return Strings.Strings.ThermocouplerE;
case (int)ChannelTypes.SliceTcType_G: return Strings.Strings.ThermocouplerG;
case (int)ChannelTypes.SliceTcType_J: return Strings.Strings.ThermocouplerJ;
case (int)ChannelTypes.SliceTcType_K: return Strings.Strings.ThermocouplerK;
case (int)ChannelTypes.SliceTcType_N: return Strings.Strings.ThermocouplerN;
case (int)ChannelTypes.SliceTcType_R: return Strings.Strings.ThermocouplerR;
case (int)ChannelTypes.SliceTcType_S: return Strings.Strings.ThermocouplerS;
case (int)ChannelTypes.SliceTcType_T: return Strings.Strings.ThermocouplerT;
default: return Strings.Strings.UnknownThermocouplerType;
}
}
}
}