/* * DTS.Slice.Control.Event.Module.Channel.SaeJ211Filter.cs * * Copyright © 2009 * Diversified Technical Systems, Inc. * All Rights Reserved */ using System; using DTS.Common.DAS.Concepts.DAS.Channel; using DTS.Common.Utilities; using DTS.Common.Utilities.DotNetProgrammingConstructs; using DTS.Common.Utilities.Logging; using DTS.Common.Utilities.SaeJ211; namespace DTS.Common.SerializationPlus { /// /// Base class for all SaeJ211-based event module channel filters. It is intended /// that fixed-filter-setting filters be derived from this class that will set their /// filter setting using the protected constructor. /// public class SaeJ211Filter : Filter { public SaeJ211Filter(SaeJ211Filter originalFilter) { OriginalType = originalFilter.OriginalType; _cutoffFrequencyHz.Value = originalFilter.CutoffFrequencyHz; } /// /// Initialize an instance of the DTS.Utility.SaeJ211Filter class. /// /// /// /// The to be applied by this filter (ad hoc /// filters that correspond to CFC values will be converted to the CFC, hence the /// "original" qualification). /// /// public SaeJ211Filter(ChannelFilter originalType) { try { switch (OriginalType = originalType) { // // Try to set the frequency value according to type. Note that we can't set to // "ad hoc" using this particular constructor as we don't know what frequency // should be associated with it. // case ChannelFilter.AdHoc: throw new Exception("cannot initialize SaeJ211 filter using only ChannelFilter of type " + OriginalType.ToString()); default: _cutoffFrequencyHz.Value = (double)originalType; break; } } catch (System.Exception ex) { throw new Exception("encountered problem constructing " + GetType().FullName, ex); } } /// /// Initialize an instance of the DTS.Utility.SaeJ211Filter class. /// /// /// public SaeJ211Filter(double cutoffFrequencyHz) { try { OriginalType = ChannelFilter.AdHoc; _cutoffFrequencyHz.Value = cutoffFrequencyHz; } catch (System.Exception ex) { throw new Exception("encountered problem constructing " + GetType().FullName, ex); } } /// /// Get value indicating whether or not this filter matches one of the /// specified CFC values. /// public override bool IsCfc { get { try { // // If we're not unfiltered and we're not ad hoc, then we // must be CFC-compliant. // return Type != ChannelFilter.Unfiltered && Type != ChannelFilter.UnfilteredZero && Type != ChannelFilter.AdHoc; } catch (System.Exception ex) { throw new Exception( "encountered problem determining whether or not filter corresponds to a CFC value", ex); } } } /// /// Convert the specified frequency into a ChannelFilter type. /// /// /// /// The frequency to be converted. /// /// /// /// The best matching type. A CFC match is preferred; /// if one does not exist, "ad hoc" will be selected (if frequency > 0). /// /// private static ChannelFilter ConvertFrequencyToChannelFilter(double frequency) { try { var matchingFilterType = ChannelFilter.Unfiltered; if (!(frequency > 0)) return matchingFilterType; matchingFilterType = ChannelFilter.AdHoc; foreach (int filterValue in Enum.GetValues(typeof(ChannelFilter))) { if (frequency == filterValue) { matchingFilterType = (ChannelFilter)filterValue; } } return matchingFilterType; } catch (System.Exception ex) { throw new Exception("encountered problem trying to match frequency to CRC value", ex); } } /// /// Get the "best fitting" value for this filter. /// public override ChannelFilter Type { get { try { ChannelFilter actualType; switch (OriginalType) { case ChannelFilter.AdHoc: actualType = ConvertFrequencyToChannelFilter(CutoffFrequencyHz); break; default: // // Don't waste time on stuff we don't need to convert. // actualType = OriginalType; break; } return actualType; } catch (System.Exception ex) { throw new Exception("encountered problem determining filter type", ex); } } } public override char IsoDescription => new IsoDescriptionAttributeCoder().DecodeAttributeValue(Type)[0]; /// /// Get the cutoff frequency value. /// public override double CutoffFrequencyHz => _cutoffFrequencyHz.Value; private readonly Property _cutoffFrequencyHz = new Property( typeof(SaeJ211Filter).Namespace + ".SaeJ211Filter.CutoffFrequencyHz", -1, true ); /// /// The ing done by this object. /// public ChannelFilter OriginalType { get => _originalType.Value; private set => _originalType.Value = value; } private readonly Property _originalType = new Property( typeof(SaeJ211Filter).Namespace + ".SaeJ211Filter.OriginalType", ChannelFilter.Unfiltered, false ); private const string CUTOFF_FREQUENCY_UNIT_STRING = "Hz"; /// /// The name of this filter. /// public override string Name { get { try { if (null != _name) return _name; var cult = System.Globalization.CultureInfo.InvariantCulture; _name = ChannelFilter.AdHoc == Type ? CutoffFrequencyHz.ToString(cult) + CUTOFF_FREQUENCY_UNIT_STRING : new DescriptionAttributeCoder().DecodeAttributeValue(Type); return _name; } catch (System.Exception ex) { throw new Exception("encountered problem generating name string for " + GetType().FullName, ex); } } } private string _name; /// /// Apply this filter to the specified channel. /// /// /// /// The to be filtered. /// /// /// /// /// The array of filtered EU data. /// public override double[] Apply ( DTS.Slice.Control.Event.Module.Channel channel, Slice.Control.Event.Module.Channel.DataDisplayUnits displayUnits, bool bUseLegacyTDCSoftwareFilterAdjustment ) { try { var filterUtility = new FilterUtility(); filterUtility.Cfc = Type; filterUtility.AdHocFrequency = CutoffFrequencyHz; filterUtility.SampleRate = channel.ParentModule.SampleRateHz; double[] data; short[] adc = null; if (channel.UnfilteredData is Serialization.SliceRaw.File.PersistentChannel unfilteredData) { adc = unfilteredData.GetAllData(); } switch (displayUnits) { case Slice.Control.Event.Module.Channel.DataDisplayUnits.Adc: if (null != adc) { using (var persistentUnfilteredData = channel.UnfilteredData as Serialization.SliceRaw.File.PersistentChannel) // ; { data = new double[adc.Length]; for (var i = 0; i < data.Length; i++) data[i] = adc[i]; } } else if (channel.UnfilteredData is Serialization.TDAS.File.PersistentChannel persistentChannel) { if (persistentChannel is ILargeDataAware largeDataAware) if (!largeDataAware.IsDataArraySized) throw new Serialization.TDAS.File.PersistentChannel.DataTooBigForArrayException( "Data is too big to be viewed or filtered."); using (var persistentUnfilteredData = channel.UnfilteredData as Serialization.TDAS.File.PersistentChannel) // ; { var dataCount = persistentUnfilteredData.Count; data = new double[dataCount]; for (var i = 0; i < dataCount; i++) data[i] = persistentUnfilteredData[(ulong)i]; } } else data = channel.UnfilteredData.ConvertAll(datum => (double)datum).ToArray(); break; case Slice.Control.Event.Module.Channel.DataDisplayUnits.Eu: data = channel.UnfilteredDataEu.ToArray(); break; case Slice.Control.Event.Module.Channel.DataDisplayUnits.Mv: data = channel.UnfilteredDataMv.ToArray(); break; default: throw new NotImplementedException("handling for display unit type \"" + displayUnits + "\" has not been implemented"); } return filterUtility.ApplyFilter(data, delegate { var msg = $"Invalid data in channel: {channel.ChannelDescriptionString}."; APILogger.Log(msg); throw new Exception(msg); }, bUseLegacyTDCSoftwareFilterAdjustment); } catch (System.Exception ex) { throw new Exception("encountered problem applying filter \"" + Name + "\" to channel", ex); } } /// /// Apply this filter to the specified channel. /// /// /// /// The to be filtered. /// /// /// /// /// controls whether filtered data is adjusted by one sample to match TDC behavior /// 8747 /// /// /// The array of filtered EU data. /// public override double[] Apply ( double[] data, double sampleRate, bool bUseLegacyTDCSoftwareFilterAdjustment ) { try { var filterUtility = new FilterUtility(); filterUtility.Cfc = Type; filterUtility.AdHocFrequency = CutoffFrequencyHz; filterUtility.SampleRate = sampleRate; return filterUtility.ApplyFilter(data, delegate { const string msg = "Invalid data in channel."; APILogger.Log(msg); throw new Exception(msg); }, bUseLegacyTDCSoftwareFilterAdjustment); } catch (System.Exception ex) { throw new Exception("encountered problem applying filter \"" + Name + "\" to channel", ex); } } /// /// Generate a string representation of this object. /// /// /// /// A representation of this object. /// /// public override string ToString() { try { return Name; } catch (System.Exception ex) { throw new Exception("encountered problem generating the string value for " + GetType().FullName, ex); } } /// /// Generate a string representation of this object. /// /// /// /// A representation of this object. /// /// public override string ToBaseString() { try { return Name; } catch (System.Exception ex) { throw new Exception("encountered problem generating the string value for " + GetType().FullName, ex); } } /// /// Determines whether this filter and the specified filter are the same. /// /// /// /// The filter to be compared with this one. /// /// /// /// true if the filters are the same, false otherwise. /// /// public override bool Equals(object obj) { try { if (!(obj is SaeJ211Filter)) { return false; } return Name.Equals(((SaeJ211Filter)obj).Name, StringComparison.OrdinalIgnoreCase); } catch (System.Exception ex) { throw new Exception( "encountered problem equality checking filter \"" + Name + "\" with filter " + ((obj as SaeJ211Filter)?.Name != null ? "\"" + (obj as SaeJ211Filter).Name + "\"" : ""), ex); } } /// /// provides an index for a given /// since we override Equals we should override get hashcode to ensure that any to objects considered /// "Equal" are also hashed to the same location, however the result index does not need to be unique /// between non equal objects. /// 6/10/2010 - dtm /// /// public override int GetHashCode() { return Name.ToLower().GetHashCode(); } /// /// Create a filter from the specified string. /// /// /// /// The representation of the filter to be instantiated. /// /// /// /// A equivalent of the /// specified string. Throws an exception if object could not be created. /// /// public static Filter Parse(string serialization) { try { Filter filter = null; //FB 13120 remove Hz if it presents inorder to convert it to frequency if (!serialization.Contains(CUTOFF_FREQUENCY_UNIT_STRING)) { double freq = 0; if (double.TryParse(serialization, out freq)) { serialization = serialization + CUTOFF_FREQUENCY_UNIT_STRING; } } if (string.IsNullOrEmpty(serialization)) return new SaeJ211Filter(ChannelFilter.Unfiltered); if (serialization.Contains(CUTOFF_FREQUENCY_UNIT_STRING)) { var cult = new System.Globalization.CultureInfo(""); filter = new DefaultSaeJ211Filter( double.Parse(serialization.Replace(CUTOFF_FREQUENCY_UNIT_STRING, ""), cult)); } else { foreach (ChannelFilter filterType in Enum.GetValues(typeof(ChannelFilter))) { var coder = new DescriptionAttributeCoder(); if (coder.DecodeAttributeValue(filterType) .Equals(serialization, StringComparison.OrdinalIgnoreCase)) filter = new DefaultSaeJ211Filter(filterType); } } return filter ?? new SaeJ211Filter(ChannelFilter.Unfiltered); } catch (System.Exception ex) { throw new Exception( "encountered problem parsing string " + (null != serialization ? "\"" + serialization + "\"" : "") + " into filter", ex); } } } }