523 lines
20 KiB
Plaintext
523 lines
20 KiB
Plaintext
|
|
/*
|
||
|
|
* 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
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 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.
|
||
|
|
/// </summary>
|
||
|
|
public class SaeJ211Filter
|
||
|
|
: Filter
|
||
|
|
{
|
||
|
|
public SaeJ211Filter(SaeJ211Filter originalFilter)
|
||
|
|
{
|
||
|
|
OriginalType = originalFilter.OriginalType;
|
||
|
|
_cutoffFrequencyHz.Value = originalFilter.CutoffFrequencyHz;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Initialize an instance of the DTS.Utility.SaeJ211Filter class.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="originalType">
|
||
|
|
/// The <see cref="ChannelFilter"/> to be applied by this filter (ad hoc
|
||
|
|
/// filters that correspond to CFC values will be converted to the CFC, hence the
|
||
|
|
/// "original" qualification).
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Initialize an instance of the DTS.Utility.SaeJ211Filter class.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="cutoffFrequencyHz"></param>
|
||
|
|
public SaeJ211Filter(double cutoffFrequencyHz)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
OriginalType = ChannelFilter.AdHoc;
|
||
|
|
_cutoffFrequencyHz.Value = cutoffFrequencyHz;
|
||
|
|
}
|
||
|
|
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem constructing " + GetType().FullName, ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get <see cref="Boolean"/> value indicating whether or not this filter matches one of the
|
||
|
|
/// specified CFC values.
|
||
|
|
/// </summary>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Convert the specified frequency into a ChannelFilter type.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="frequency">
|
||
|
|
/// The <see cref="double"/> frequency to be converted.
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// The best matching <see cref="ChannelFilter"/> type. A CFC match is preferred;
|
||
|
|
/// if one does not exist, "ad hoc" will be selected (if frequency > 0).
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get the "best fitting" <see cref="ChannelFilter"/> value for this filter.
|
||
|
|
/// </summary>
|
||
|
|
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];
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Get the <see cref="double"/> cutoff frequency value.
|
||
|
|
/// </summary>
|
||
|
|
public override double CutoffFrequencyHz => _cutoffFrequencyHz.Value;
|
||
|
|
|
||
|
|
private readonly Property<double> _cutoffFrequencyHz
|
||
|
|
= new Property<double>(
|
||
|
|
typeof(SaeJ211Filter).Namespace + ".SaeJ211Filter.CutoffFrequencyHz",
|
||
|
|
-1,
|
||
|
|
true
|
||
|
|
);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// The <see cref="ChannelFilter"/>ing done by this object.
|
||
|
|
/// </summary>
|
||
|
|
public ChannelFilter OriginalType
|
||
|
|
{
|
||
|
|
get => _originalType.Value;
|
||
|
|
private set => _originalType.Value = value;
|
||
|
|
}
|
||
|
|
|
||
|
|
private readonly Property<ChannelFilter> _originalType
|
||
|
|
= new Property<ChannelFilter>(
|
||
|
|
typeof(SaeJ211Filter).Namespace + ".SaeJ211Filter.OriginalType",
|
||
|
|
ChannelFilter.Unfiltered,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
private const string CUTOFF_FREQUENCY_UNIT_STRING = "Hz";
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// The <see cref="string"/> name of this filter.
|
||
|
|
/// </summary>
|
||
|
|
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<ChannelFilter>().DecodeAttributeValue(Type);
|
||
|
|
return _name;
|
||
|
|
}
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem generating name string for " + GetType().FullName, ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private string _name;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Apply this filter to the specified channel.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="channel">
|
||
|
|
/// The <see cref="DTS.Slice.Control.Event.Module.Channel"/> to be filtered.
|
||
|
|
/// </param>
|
||
|
|
/// <param name="displayUnits"></param>
|
||
|
|
/// <param name="bUseLegacyTDCSoftwareFilterAdjustment"></param>
|
||
|
|
/// <returns>
|
||
|
|
/// The array of filtered <see cref="double"/> EU data.
|
||
|
|
/// </returns>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Apply this filter to the specified channel.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="channel">
|
||
|
|
/// The <see cref="DTS.Slice.Control.Event.Module.Channel"/> to be filtered.
|
||
|
|
/// </param>
|
||
|
|
/// <param name="data"></param>
|
||
|
|
/// <param name="sampleRate"></param>
|
||
|
|
/// <param name="bUseLegacyTDCSoftwareFilterAdjustment">
|
||
|
|
/// controls whether filtered data is adjusted by one sample to match TDC behavior
|
||
|
|
/// 8747
|
||
|
|
/// </param>
|
||
|
|
/// <returns>
|
||
|
|
/// The array of filtered <see cref="double"/> EU data.
|
||
|
|
/// </returns>
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Generate a string representation of this object.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// A <see cref="string"/> representation of this object.
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
public override string ToString()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return Name;
|
||
|
|
}
|
||
|
|
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem generating the string value for " + GetType().FullName, ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Generate a string representation of this object.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// A <see cref="string"/> representation of this object.
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
public override string ToBaseString()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return Name;
|
||
|
|
}
|
||
|
|
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
throw new Exception("encountered problem generating the string value for " + GetType().FullName, ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Determines whether this filter and the specified filter are the same.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="obj">
|
||
|
|
/// The filter <see cref="object"/> to be compared with this one.
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// <see cref="bool"/> true if the filters are the same, false otherwise.
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
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 + "\"" : "<null>"),
|
||
|
|
ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// provides an index for a given <see cref="SaeJ211Filter"/>
|
||
|
|
/// 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
|
||
|
|
/// </summary>
|
||
|
|
/// <returns></returns>
|
||
|
|
public override int GetHashCode()
|
||
|
|
{
|
||
|
|
return Name.ToLower().GetHashCode();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Create a filter from the specified string.
|
||
|
|
/// </summary>
|
||
|
|
///
|
||
|
|
/// <param name="serialization">
|
||
|
|
/// The <see cref="string"/> representation of the filter to be instantiated.
|
||
|
|
/// </param>
|
||
|
|
///
|
||
|
|
/// <returns>
|
||
|
|
/// A <see cref="Filter"/> equivalent of the
|
||
|
|
/// specified string. Throws an exception if object could not be created.
|
||
|
|
/// </returns>
|
||
|
|
///
|
||
|
|
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<ChannelFilter>();
|
||
|
|
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 + "\"" : "<NULL>") + " into filter", ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|