init
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace DTS.Serialization.IRIGCh10.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// this class is used to annotate enums with a version attribute
|
||||
/// for example each data packet type has a corresponding version number
|
||||
/// example TABLE 10-7. DATA TYPE NAMES AND DESCRIPTIONS in chapter 10.pdf
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class DataTypeVersionValueAttribute : Attribute
|
||||
{
|
||||
/// <summary>Specifies the default value for the <see cref="DataTypeVersionValueAttribute" />, which is 0x00. This <see langword="static" /> field is read-only.</summary>
|
||||
public static readonly DataTypeVersionValueAttribute Default = new DataTypeVersionValueAttribute();
|
||||
private byte _dataTypeVersion;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DataTypeVersionValueAttribute" /> class with no parameters.</summary>
|
||||
public DataTypeVersionValueAttribute()
|
||||
: this(0x00)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DataTypeVersionValueAttribute" /> class with a DataTypeVersionValue.</summary>
|
||||
/// <param name="dataTypeVersion">The data type version. </param>
|
||||
public DataTypeVersionValueAttribute(byte dataTypeVersion)
|
||||
{
|
||||
_dataTypeVersion = dataTypeVersion;
|
||||
}
|
||||
|
||||
/// <summary>Gets the data type version stored in this attribute.</summary>
|
||||
/// <returns>The data type version stored in this attribute.</returns>
|
||||
public virtual byte DataTypeVersion => DataTypeVersionValue;
|
||||
|
||||
/// <summary>Gets or sets the byte stored as the datatype version.</summary>
|
||||
/// <returns>The byte stored as the data type version. The default value is 0x00.</returns>
|
||||
protected byte DataTypeVersionValue
|
||||
{
|
||||
get => _dataTypeVersion;
|
||||
set => _dataTypeVersion = value;
|
||||
}
|
||||
|
||||
/// <summary>Returns whether the value of the given object is equal to the current <see cref="DataTypeVersionValueAttribute" />.</summary>
|
||||
/// <param name="obj">The object to test the value equality of. </param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the value of the given object is equal to that of the current; otherwise, <see langword="false" />.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (obj is DataTypeVersionValueAttribute attribute)
|
||||
return attribute.DataTypeVersion == DataTypeVersion;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Returns the hash code for this instance.</summary>
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return DataTypeVersion.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>Returns a value indicating whether this is the default <see cref="DataTypeVersionValueAttribute" /> instance.</summary>
|
||||
/// <returns>
|
||||
/// <see langword="true" />, if this is the default <see cref="DataTypeVersionValueAttribute" /> instance; otherwise, <see langword="false" />.</returns>
|
||||
public override bool IsDefaultAttribute()
|
||||
{
|
||||
return Equals(Default);
|
||||
}
|
||||
|
||||
public static byte GetDataTypeVersionValue(Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
var attributes = (DataTypeVersionValueAttribute[])fi.GetCustomAttributes(
|
||||
typeof(DataTypeVersionValueAttribute), false);
|
||||
if (attributes.Any())
|
||||
{
|
||||
return attributes.First().DataTypeVersionValue;
|
||||
}
|
||||
return Default.DataTypeVersionValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
/// <summary>
|
||||
/// allows annotating enums with a description attribute, in chapter 10 this allows us
|
||||
/// to associate an enum with a string to put in the TMATS file for that enum
|
||||
/// </summary>
|
||||
public static class DescriptionDecoder
|
||||
{
|
||||
public static string GetDescription(Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(
|
||||
typeof(DescriptionAttribute), false);
|
||||
if (attributes.Any())
|
||||
{
|
||||
return attributes.First().Description;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// a lot of attributes in chapter 10 have a max length associated with them
|
||||
/// note however though that max length in chapter 10 is just a suggested
|
||||
/// max length, not a requirement ...
|
||||
/// </summary>
|
||||
public static class MaxLengthDecoder
|
||||
{
|
||||
public static int GetMaxLength(Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
var attributes = (MaxLengthAttribute[])fi.GetCustomAttributes(
|
||||
typeof(MaxLengthAttribute), false);
|
||||
if (attributes.Any())
|
||||
{
|
||||
return attributes.First().Length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace DTS.Serialization.IRIGCh10.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// allows annotating an enum with a value,
|
||||
/// see TABLE 10-7. DATA TYPE NAMES AND DESCRIPTIONS in Chapter 10 pdf
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class PacketHeaderValueAttribute : Attribute
|
||||
{
|
||||
/// <summary>Specifies the default value for the <see cref="PacketHeaderValueAttribute" />, which is 0x00. This <see langword="static" /> field is read-only.</summary>
|
||||
public static readonly PacketHeaderValueAttribute Default = new PacketHeaderValueAttribute();
|
||||
private byte _packetHeader;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="PacketHeaderValueAttribute" /> class with no parameters.</summary>
|
||||
public PacketHeaderValueAttribute()
|
||||
: this(0x00)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="PacketHeaderValueAttribute" /> class with a PacketHeader.</summary>
|
||||
/// <param name="packetHeader">packet header value. </param>
|
||||
public PacketHeaderValueAttribute(byte packetHeader)
|
||||
{
|
||||
_packetHeader = packetHeader;
|
||||
}
|
||||
|
||||
/// <summary>Gets the packet header value stored in this attribute.</summary>
|
||||
/// <returns>The packet header value stored in this attribute.</returns>
|
||||
public virtual byte PacketHeader => PacketHeaderValue;
|
||||
|
||||
/// <summary>Gets or sets the byte stored as the packet header value.</summary>
|
||||
/// <returns>The byte stored as the packet header version value. The default value is 0x00.</returns>
|
||||
protected byte PacketHeaderValue
|
||||
{
|
||||
get => _packetHeader;
|
||||
set => _packetHeader = value;
|
||||
}
|
||||
|
||||
/// <summary>Returns whether the value of the given object is equal to the current <see cref="PacketHeaderValueAttribute" />.</summary>
|
||||
/// <param name="obj">The object to test the value equality of. </param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if the value of the given object is equal to that of the current; otherwise, <see langword="false" />.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (obj is PacketHeaderValueAttribute packetHeaderAttribute)
|
||||
return packetHeaderAttribute.PacketHeader == PacketHeader;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Returns the hash code for this instance.</summary>
|
||||
/// <returns>A 32-bit signed integer hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return PacketHeader.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>Returns a value indicating whether this is the default <see cref="PacketHeaderValueAttribute" /> instance.</summary>
|
||||
/// <returns>
|
||||
/// <see langword="true" />, if this is the default <see cref="PacketHeaderValueAttribute" /> instance; otherwise, <see langword="false" />.</returns>
|
||||
public override bool IsDefaultAttribute()
|
||||
{
|
||||
return Equals(Default);
|
||||
}
|
||||
|
||||
public static byte GetPacketHeaderValue(Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
var attributes = (PacketHeaderValueAttribute[])fi.GetCustomAttributes(
|
||||
typeof(PacketHeaderValueAttribute), false);
|
||||
if (attributes.Any())
|
||||
{
|
||||
return attributes.First().PacketHeaderValue;
|
||||
}
|
||||
return Default.PacketHeaderValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using DTS.Common.Utils;
|
||||
using DTS.DASLib.Command.SLICE.MulticastCommands;
|
||||
using DTS.Serialization.IRIGCH10.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
public class CH10AnalogStreamDecode
|
||||
{
|
||||
private static object MyLock = new object();
|
||||
|
||||
private readonly ManualResetEvent _stopListening = new ManualResetEvent(false);
|
||||
|
||||
public string MulticastReceiveAddress { get; set; } = MulticastCommandBase.DEFAULT_RECEIVE_ADDRESS;
|
||||
public int ResponsePort { get; set; } = (int)MulticastCommandBase.Ports.Response;
|
||||
private Task _listeningTask = null;
|
||||
public IPAddress BindToAdapterIPAddress { get; set; } = IPAddress.Any;
|
||||
/// <summary>
|
||||
/// starts listening for UDP stream packets
|
||||
/// </summary>
|
||||
public void StartListening()
|
||||
{
|
||||
lock (MyLock)
|
||||
{
|
||||
_stopListening.Set();
|
||||
if (null != _listeningTask)
|
||||
{
|
||||
_listeningTask.Wait();
|
||||
}
|
||||
_stopListening.Reset();
|
||||
_listeningTask = Task.Run(ListenThread);
|
||||
}
|
||||
}
|
||||
public delegate void TimePacketDelegate(TimePacketFormat2 packet);
|
||||
public delegate void AnalogDataPacketDelegate(AnalogDataFormat1Packet packet);
|
||||
public delegate void TMATSPacketDelegate(TMATSPacket packet);
|
||||
public delegate void BadCRCDelegate(IPacketHeader packet);
|
||||
/// <summary>
|
||||
/// action to perform when a packet is received but has a bad CRC
|
||||
/// </summary>
|
||||
public BadCRCDelegate OnBadCRC;
|
||||
/// <summary>
|
||||
/// action to perform when a time packet is received
|
||||
/// </summary>
|
||||
public TimePacketDelegate OnTimePacket;
|
||||
/// <summary>
|
||||
/// action to perform when an analog data packet is received
|
||||
/// </summary>
|
||||
public AnalogDataPacketDelegate OnAnalogPacket;
|
||||
/// <summary>
|
||||
/// action to perform when a tmats packet is received
|
||||
/// </summary>
|
||||
public TMATSPacketDelegate OnTMATSPacket;
|
||||
|
||||
private void ListenThread()
|
||||
{
|
||||
var rxGroupAddress = IPAddress.Parse(MulticastReceiveAddress);
|
||||
var endPoint = new IPEndPoint(BindToAdapterIPAddress, ResponsePort);
|
||||
var receiver = new UdpClient();
|
||||
|
||||
receiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
|
||||
if (BindToAdapterIPAddress == IPAddress.Any) { receiver.ExclusiveAddressUse = false; }
|
||||
|
||||
try
|
||||
{
|
||||
receiver.Client.Bind(endPoint);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (BindToAdapterIPAddress == IPAddress.Any)
|
||||
{
|
||||
receiver.JoinMulticastGroup(rxGroupAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
receiver.JoinMulticastGroup(rxGroupAddress, BindToAdapterIPAddress);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(ex);
|
||||
}
|
||||
|
||||
var data = new byte[0];
|
||||
IAsyncResult asyncResult = null;
|
||||
do
|
||||
{
|
||||
if (BindToAdapterIPAddress != IPAddress.Any && !NetworkUtils.IsNetworkInterfaceUp(BindToAdapterIPAddress))
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
continue;
|
||||
}
|
||||
asyncResult = receiver.BeginReceive(null, null);
|
||||
asyncResult.AsyncWaitHandle.WaitOne(100);
|
||||
|
||||
if (asyncResult.IsCompleted)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPEndPoint remoteEP = null;
|
||||
data = receiver.EndReceive(asyncResult, ref remoteEP);
|
||||
data = CombineBytesIfNeeded(data);
|
||||
ParseBytes(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(ex);
|
||||
}
|
||||
}
|
||||
} while (!_stopListening.WaitOne(0, false));
|
||||
receiver.Close();
|
||||
}
|
||||
private byte[] _leftOvertBytes = new byte[0];
|
||||
private static object LEFT_OVER_BYTES_LOCK = new object();
|
||||
private void QueueBytes(byte[] bytes)
|
||||
{
|
||||
lock (LEFT_OVER_BYTES_LOCK)
|
||||
{
|
||||
var newBytes = new byte[bytes.Length + _leftOvertBytes.Length];
|
||||
Buffer.BlockCopy(_leftOvertBytes, 0, newBytes, 0, _leftOvertBytes.Length);
|
||||
Buffer.BlockCopy(bytes, 0, newBytes, _leftOvertBytes.Length, bytes.Length);
|
||||
_leftOvertBytes = bytes;
|
||||
}
|
||||
}
|
||||
private byte[] CombineBytesIfNeeded(byte[] newData)
|
||||
{
|
||||
lock (LEFT_OVER_BYTES_LOCK)
|
||||
{
|
||||
var newBytes = new byte[newData.Length + _leftOvertBytes.Length];
|
||||
Buffer.BlockCopy(_leftOvertBytes, 0, newBytes, 0, _leftOvertBytes.Length);
|
||||
Buffer.BlockCopy(newData, 0, newBytes, _leftOvertBytes.Length, newData.Length);
|
||||
return newBytes;
|
||||
}
|
||||
}
|
||||
private void ParseBytes(byte[] bytes)
|
||||
{
|
||||
var currentIndex = 0L;
|
||||
while (currentIndex < bytes.Length)
|
||||
{
|
||||
var nextIndex = Chapter10File.ReadChapter10PacketHeader(bytes, currentIndex, out var header);
|
||||
if (header.PacketSyncPattern != PacketHeader.EXPECTED_SYNC_PATTERN)
|
||||
{
|
||||
currentIndex = Chapter10File.GetIndexOfNextPacket(bytes, currentIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (header.CheckSum != header.ComputeCheckSum())
|
||||
{
|
||||
OnBadCRC?.Invoke(header);
|
||||
continue;
|
||||
}
|
||||
if (header.PacketLength > bytes.Length)
|
||||
{
|
||||
QueueBytes(bytes);
|
||||
return;
|
||||
}
|
||||
var buffer = new byte[header.PacketLength];
|
||||
Buffer.BlockCopy(bytes, Convert.ToInt32(currentIndex), buffer, 0, Convert.ToInt32(header.PacketLength));
|
||||
switch (header.DataFileType)
|
||||
{
|
||||
case Enums.DataFileDataTypes.ComputerGeneratedDataFormat0:
|
||||
case Enums.DataFileDataTypes.ComputerGeneratedDataFormat1:
|
||||
APILogger.Log("TMATS packet received");
|
||||
var tmatsPacket = new TMATSPacket(buffer);
|
||||
OnTMATSPacket?.Invoke(tmatsPacket);
|
||||
break;
|
||||
case Enums.DataFileDataTypes.AnalogDataFormat1:
|
||||
var analog = new AnalogDataFormat1Packet(buffer);
|
||||
OnAnalogPacket?.Invoke(analog);
|
||||
break;
|
||||
case Enums.DataFileDataTypes.TimeDataFormat2:
|
||||
var timePacket = new TimePacketFormat2(buffer);
|
||||
OnTimePacket?.Invoke(timePacket);
|
||||
break;
|
||||
default:
|
||||
APILogger.Log($"unknown header file type: {header.DataFileType}, ");
|
||||
break;
|
||||
}
|
||||
currentIndex = nextIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// stops listening for stream packets
|
||||
/// </summary>
|
||||
public void StopListening()
|
||||
{
|
||||
lock (MyLock)
|
||||
{
|
||||
_stopListening.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
* Chapter10.File.Writer.cs
|
||||
*
|
||||
* Copyright © 2020
|
||||
* Diversified Technical Systems, Inc.
|
||||
* All Rights Reserved
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Text;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DTS.Common;
|
||||
using DTS.Common.DAS.Concepts;
|
||||
using DTS.Common.Enums;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using DTS.Common.Utils;
|
||||
using static DTS.Serialization.Test.Module;
|
||||
// ReSharper disable PossiblyMistakenUseOfParamsMethod
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
public partial class File
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of the Serialization.File.Writer class for Chapter10
|
||||
/// </summary>
|
||||
public class Writer : Writer<File>, IWriter<Test>
|
||||
{
|
||||
#region properties
|
||||
/// <summary>
|
||||
/// the owning file that controls this writer
|
||||
/// </summary>
|
||||
internal File WriterParent { get; }
|
||||
|
||||
public double Start { get; set; }
|
||||
public double Stop { get; set; }
|
||||
public bool Filtered { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region methods
|
||||
/// <summary>
|
||||
/// writes out test to given path
|
||||
/// </summary>
|
||||
/// <param name="pathname"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="test"></param>
|
||||
/// <param name="bFiltering"></param>
|
||||
/// <param name="includeGroupNameInISOExport"></param>
|
||||
/// <param name="dataCollectionLength"></param>
|
||||
/// <param name="minStartTime"></param>
|
||||
public void Write(string pathname, string id, Test test, bool bFiltering, bool includeGroupNameInISOExport, double minStartTime, int dataCollectionLength)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// updates the progress if possible
|
||||
/// </summary>
|
||||
/// <param name="dValue"></param>
|
||||
/// <param name="tickEventHandler"></param>
|
||||
private void UpdateProgress(double dValue, TickEventHandler tickEventHandler)
|
||||
{
|
||||
tickEventHandler?.Invoke(this, dValue);
|
||||
}
|
||||
/// <summary>
|
||||
/// returns a name for the given channel
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
/// <returns></returns>
|
||||
private static string GetChannelName(Channel channel)
|
||||
{
|
||||
|
||||
if (!(channel is AnalogInputChannel aic))
|
||||
{
|
||||
return channel.ChannelName2;
|
||||
}
|
||||
|
||||
switch (IsoViewModeStatic.ViewMode)
|
||||
{
|
||||
case IsoViewMode.ISOOnly:
|
||||
return $"{aic.IsoChannelName}";
|
||||
case IsoViewMode.ISOAndUserCode:
|
||||
return $"{aic.IsoChannelName}\\{aic.UserChannelName}";
|
||||
case IsoViewMode.UserCodeOnly:
|
||||
return $"{aic.UserCode}";
|
||||
case IsoViewMode.ChannelNameOnly:
|
||||
return $"{aic.UserChannelName}";
|
||||
}
|
||||
return aic.HardwareChannelName;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// we don't really have an RTC, so we make up one
|
||||
/// with an arbitrary value, this is actually a 6byte value,
|
||||
/// we're just using a long for convenience
|
||||
/// </summary>
|
||||
public const long BASE_RTC = 141989612500056L;
|
||||
private void GetTimeStamp(Test test, out int nanoseconds, out int seconds)
|
||||
{
|
||||
nanoseconds = 0;
|
||||
seconds = 0;
|
||||
var basemodules = test.Modules.GroupBy(module => module.BaseSerialNumber).Select(group => group.First());
|
||||
|
||||
var testModuleTimeStamps = GetModuleTimeStamps(basemodules, out var testModuleStartTimestamps);
|
||||
|
||||
Tuple<double, double> minUnixTime = null;
|
||||
|
||||
if (testModuleTimeStamps.Any())
|
||||
{
|
||||
minUnixTime = TestUtils.MinUnixTime(testModuleTimeStamps);
|
||||
}
|
||||
else if (testModuleStartTimestamps.Any())
|
||||
{
|
||||
minUnixTime = TestUtils.MinUnixTime(testModuleStartTimestamps);
|
||||
}
|
||||
|
||||
if (null == minUnixTime)
|
||||
{
|
||||
var ticks = test.InceptionDate.ToUniversalTime().Ticks;
|
||||
var s = Math.Truncate((decimal)ticks / TimeSpan.TicksPerSecond);
|
||||
ticks -= Convert.ToInt64(s * TimeSpan.TicksPerSecond);
|
||||
var n = Math.Truncate(ticks * Common.Constants.NANOS_PER_SECOND / TimeSpan.TicksPerSecond);
|
||||
minUnixTime = new Tuple<double, double>(Convert.ToDouble(n), Convert.ToDouble(s));
|
||||
}
|
||||
|
||||
var minStartTime = double.MinValue;
|
||||
foreach (var module in test.Modules)
|
||||
{
|
||||
var dStartTime = (double)module.StartRecordSampleNumber / module.SampleRateHz;
|
||||
if (module.TriggerSampleNumbers.Count > 0)
|
||||
{
|
||||
dStartTime -= (double)module.TriggerSampleNumbers[0] / module.SampleRateHz;
|
||||
}
|
||||
minStartTime = Math.Max(minStartTime, dStartTime);
|
||||
}
|
||||
var nanos = ((decimal)(minStartTime) * Common.Constants.NANOS_PER_SECOND) + (decimal)minUnixTime.Item2 + (decimal)minUnixTime.Item1 * Common.Constants.NANOS_PER_SECOND;
|
||||
seconds = Convert.ToInt32(Math.Truncate(nanos / Common.Constants.NANOS_PER_SECOND));
|
||||
nanos -= seconds * Common.Constants.NANOS_PER_SECOND;
|
||||
nanoseconds = Convert.ToInt32(nanos);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// whether to include secondary time header or not
|
||||
/// 33199 Add Time format 1 as an export option for CH 10 export
|
||||
/// </summary>
|
||||
public bool IncludeSecondaryHeader { get; set; } = true;
|
||||
/// <summary>
|
||||
/// Whether to use analog format or not during export
|
||||
/// </summary>
|
||||
public bool UseAnalogFormat { get; set; } = true;
|
||||
/// <summary>
|
||||
/// Whether to use PCM format or not during export
|
||||
/// </summary>
|
||||
public bool UsePCMFormat { get; set; } = false;
|
||||
/// <summary>
|
||||
/// returns a list of binary readers given a test that all point at the start of data
|
||||
/// </summary>
|
||||
/// <param name="test"></param>
|
||||
/// <returns></returns>
|
||||
private List<BinaryReader> GetBinaryReaders(Test test, IReadOnlyDictionary<Channel, ChannelInformation> lookup)
|
||||
{
|
||||
var binaryReaders = new List<BinaryReader>();
|
||||
for (var i = 0; i < test.Channels.Count; i++)
|
||||
{
|
||||
var channel = test.Channels[i];
|
||||
//get the filename and the start of data before we go and nuke the persistantchannelinfo object ...
|
||||
var fileName = lookup[channel].ChannelFileName;
|
||||
var offset = lookup[channel].OffsetDataStart;
|
||||
//get the reader and advance it to the start of data
|
||||
var br = new BinaryReader(new FileStream(fileName, FileMode.Open));
|
||||
br.ReadBytes(Convert.ToInt32(offset));
|
||||
lookup[channel].Reader = br;
|
||||
binaryReaders.Add(br);
|
||||
}
|
||||
return binaryReaders;
|
||||
}
|
||||
/// <summary>
|
||||
/// internal class for encapsulating some of the information from analoginputdaschannels without keeping any files open
|
||||
/// </summary>
|
||||
internal class ChannelInformation
|
||||
{
|
||||
public string ChannelFileName;
|
||||
public ulong OffsetDataStart;
|
||||
public ulong NumberOfSamples;
|
||||
public short MinADC { get; set; } = short.MaxValue;
|
||||
public short MaxADC { get; set; } = short.MinValue;
|
||||
public BinaryReader Reader;
|
||||
public ChannelInformation(AnalogInputChannel channel)
|
||||
{
|
||||
ChannelFileName = channel.PersistentChannelInfo.Filename;
|
||||
OffsetDataStart = channel.PersistentChannelInfo.OffsetOfSampleDataStart;
|
||||
NumberOfSamples = channel.PersistentChannelInfo.NumberOfSamples;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// retrieves channel summary information for channels in a test
|
||||
/// </summary>
|
||||
/// <param name="test"></param>
|
||||
/// <returns></returns>
|
||||
private IReadOnlyDictionary<Channel, ChannelInformation> GetChannelSummaries(Test test)
|
||||
{
|
||||
var lookup = new Dictionary<Channel, ChannelInformation>();
|
||||
|
||||
foreach (var channel in test.Channels)
|
||||
{
|
||||
if (channel is AnalogInputChannel aic)
|
||||
{
|
||||
lookup[channel] = new ChannelInformation(aic);
|
||||
}
|
||||
}
|
||||
|
||||
return lookup;
|
||||
}
|
||||
/// <summary>
|
||||
/// closes and disposes of all readers in list, then clears the list
|
||||
/// </summary>
|
||||
/// <param name="readers"></param>
|
||||
private void CloseAndDisposeReaders(List<BinaryReader> readers)
|
||||
{
|
||||
for (var i = 0; i < readers.Count; i++)
|
||||
{
|
||||
readers[i].Close();
|
||||
readers[i].Dispose();
|
||||
}
|
||||
readers.Clear();
|
||||
}
|
||||
/// <summary>
|
||||
/// go throughs all binary readers and determines the min and max ADC from files
|
||||
/// </summary>
|
||||
/// <param name="test"></param>
|
||||
/// <param name="lookup"></param>
|
||||
/// <param name="tickEventHandler"></param>
|
||||
private void GetMinAndMax(Test test, IReadOnlyDictionary<Channel, ChannelInformation> lookup,
|
||||
TickEventHandler tickEventHandler)
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var channel in test.Channels)
|
||||
{
|
||||
if (!lookup.ContainsKey(channel)) { return; }
|
||||
var info = lookup[channel];
|
||||
try
|
||||
{
|
||||
for (var i = 0UL; i < info.NumberOfSamples; i++)
|
||||
{
|
||||
var adc = ReadShort(info.Reader);
|
||||
if (adc < info.MinADC) { info.MinADC = adc; }
|
||||
else if (adc > info.MaxADC) { info.MaxADC = adc; }
|
||||
}
|
||||
index++;
|
||||
tickEventHandler?.Invoke(this, (50D * index) / (double)test.Channels.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log($"Failed to get Min/Max for file {info.ChannelFileName} - ", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Readers a single signed short from file
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
/// <returns></returns>
|
||||
private short ReadShort(BinaryReader reader)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = reader.ReadBytes(2);
|
||||
//to get the next sample from the file we just read two bytes and make a short out of it
|
||||
//I'm not sure why to use this version rather than alternatives, but the existing code
|
||||
//used this method, so I just preserved it here ...
|
||||
return (short)((bytes[0] & 0xFFFF) + ((bytes[1] & 0xFFFF) << 8));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log($"Failed to read from file", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// writes out test to given path
|
||||
/// </summary>
|
||||
/// <param name="pathname"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="dataFolder"></param>
|
||||
/// <param name="test"></param>
|
||||
/// <param name="bFiltering"></param>
|
||||
/// <param name="includeGroupNameInISOExport"></param>
|
||||
/// <param name="fd"></param>
|
||||
/// <param name="tmChannel"></param>
|
||||
/// <param name="channelNumber"></param>
|
||||
/// <param name="beginEventHandler"></param>
|
||||
/// <param name="cancelEventHandler"></param>
|
||||
/// <param name="endEventHandler"></param>
|
||||
/// <param name="tickEventHandler"></param>
|
||||
/// <param name="errorEventHandler"></param>
|
||||
/// <param name="cancelRequested"></param>
|
||||
/// <param name="minStartTime"></param>
|
||||
/// <param name="dataCollectionLength"></param>
|
||||
public void Write(string pathname,
|
||||
string id,
|
||||
string dataFolder,
|
||||
Test test,
|
||||
bool bFiltering,
|
||||
bool includeGroupNameInISOExport,
|
||||
FilteredData fd,
|
||||
Channel tmChannel,
|
||||
int channelNumber,
|
||||
BeginEventHandler beginEventHandler,
|
||||
CancelEventHandler cancelEventHandler,
|
||||
EndEventHandler endEventHandler,
|
||||
TickEventHandler tickEventHandler,
|
||||
ErrorEventHandler errorEventHandler,
|
||||
CancelRequested cancelRequested,
|
||||
double minStartTime,
|
||||
int dataCollectionLength)
|
||||
{
|
||||
System.Exception caughtException = null;
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(Path.GetDirectoryName(pathname)))
|
||||
{
|
||||
_ = Directory.CreateDirectory(Path.GetDirectoryName(pathname));
|
||||
}
|
||||
var summaryLookup = GetChannelSummaries(test);
|
||||
var binaryReaders = GetBinaryReaders(test, summaryLookup);
|
||||
GetMinAndMax(test, summaryLookup, tickEventHandler);
|
||||
tickEventHandler?.Invoke(this, 50D);
|
||||
CloseAndDisposeReaders(binaryReaders);
|
||||
string tmatsDoc = UseAnalogFormat ? GetTMATSAnalog(test, summaryLookup) : GetTMATSPCM(test, summaryLookup);
|
||||
GetTimeStamp(test, out var nanoseconds, out var seconds);
|
||||
var channelsCount = test.Channels.Count;
|
||||
|
||||
binaryReaders = GetBinaryReaders(test, summaryLookup);
|
||||
|
||||
if (UseAnalogFormat)
|
||||
{
|
||||
Chapter10File.WriteFileAnalog(tmatsDoc,
|
||||
(int chIdx) =>
|
||||
{
|
||||
return ReadShort(binaryReaders[chIdx]);
|
||||
},
|
||||
(int chIdx) => (long)summaryLookup[test.Channels[chIdx]].NumberOfSamples,
|
||||
channelsCount, nanoseconds, seconds, test.Modules[0].SampleRateHz,
|
||||
IncludeSecondaryHeader, pathname, tickEventHandler, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Chapter10File.WriteFilePCM(tmatsDoc,
|
||||
(int chIdx) => { return ReadShort(binaryReaders[chIdx]); },
|
||||
(int chIdx) => (long)summaryLookup[test.Channels[chIdx]].NumberOfSamples,
|
||||
channelsCount, nanoseconds, seconds, test.Modules[0].SampleRateHz,
|
||||
IncludeSecondaryHeader, pathname, tickEventHandler, this);
|
||||
}
|
||||
|
||||
CloseAndDisposeReaders(binaryReaders);
|
||||
tickEventHandler?.Invoke(this, 100D);
|
||||
}
|
||||
catch (IOException ioException)
|
||||
{
|
||||
if (ioException.HResult == -2147024816)
|
||||
{
|
||||
var msg = $"failed to create file, check that file isn't currently open: {pathname}";
|
||||
APILogger.Log(msg, ioException);
|
||||
caughtException = new IOException(msg, ioException);
|
||||
}
|
||||
else
|
||||
{
|
||||
APILogger.Log("encountered problem writing Ch10 file", pathname, ioException);
|
||||
caughtException = ioException;
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
APILogger.Log("encountered problem writing Ch10 file", pathname, ex);
|
||||
caughtException = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tickEventHandler?.Invoke(this, 100D);
|
||||
endEventHandler?.Invoke(this);
|
||||
if (null != caughtException)
|
||||
{
|
||||
errorEventHandler?.Invoke(this, caughtException);
|
||||
}
|
||||
}
|
||||
}
|
||||
private const int DATA_CHANNEL_ID = 3;
|
||||
/// <summary>
|
||||
/// returns a string with multiple lines for a single channel for a PCM TMATS file
|
||||
/// </summary>
|
||||
private static string GetTMATSChannelPCM(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_PCM_ExportChannel.tmt");
|
||||
for ( var i = 0; i< test.Channels.Count; i++)
|
||||
{
|
||||
sb.AppendLine(GetTMATSChannelPCM(test, summaryLookup, i, baseText));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a string with multiple lines for a single channel for a PCM TMATS file
|
||||
/// <summary>
|
||||
private static string GetTMATSChannelPCM(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup, int channelIdx,
|
||||
string unmodifiedText)
|
||||
{
|
||||
//we'll return the basetext, but just make it clear the original string isn't modified, strings are immutable and we
|
||||
//are creating new strings and we're holding onto the old string
|
||||
var baseText = unmodifiedText;
|
||||
var channel = test.Channels[channelIdx];
|
||||
try
|
||||
{
|
||||
var summary = summaryLookup[channel];
|
||||
if (channel is AnalogInputChannel aic)
|
||||
{
|
||||
var ds = SliceRaw.File.Reader.GetDataScaler(aic);
|
||||
var eu = aic.EngineeringUnits?.Trim() ?? "";
|
||||
var scaler = ds.GetAdcToEuScalingFactor();
|
||||
//we want 0 * ds = ds.GetEU(0), but the way to do that is to add in ds.GetEU(0)
|
||||
//so that's the offset to add back in ... I'm not sure why analog is using a different formula
|
||||
var offset = ds.GetEU(0);
|
||||
var minEU = ds.GetEU(summary.MinADC);
|
||||
var maxEU = ds.GetEU(summary.MaxADC);
|
||||
var channelName = GetChannelName(channel);
|
||||
baseText = baseText.Replace("{CHANNEL NUMBER}", $"{1 + channelIdx}");
|
||||
baseText = baseText.Replace("{CHANNEL NAME}", channelName);
|
||||
baseText = baseText.Replace("{CHANNEL OFFSET EU}", $"{offset:0.0000}");
|
||||
baseText = baseText.Replace("{CHANNEL SCALEFACTOR EU}", $"{scaler:0.0000}");
|
||||
baseText = baseText.Replace("{CHANNEL EU}", PrepareOutput(eu));
|
||||
baseText = baseText.Replace("{CHANNEL MAX RANGE EU}", $"{maxEU:0.0000}");
|
||||
baseText = baseText.Replace("{CHANNEL MIN RANGE EU}", $"{minEU:0.0000}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log($"Failed to get TMATS for channel: {channelIdx} - {channel.ChannelName2}", ex);
|
||||
}
|
||||
return baseText;
|
||||
}
|
||||
private static string GetTMATSChannelsAnalog(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(@"R-1\COM:--------------------- Start of channels ---------------------;");
|
||||
|
||||
for (var i = 0; i < test.Channels.Count; i++)
|
||||
{
|
||||
var channel = test.Channels[i];
|
||||
try
|
||||
{
|
||||
var summary = summaryLookup[channel];
|
||||
var ds = new DataScaler();
|
||||
var eu = "EU";
|
||||
if (channel is AnalogInputChannel aic)
|
||||
{
|
||||
ds = SliceRaw.File.Reader.GetDataScaler(aic);
|
||||
eu = aic.EngineeringUnits?.Trim() ?? "";
|
||||
}
|
||||
var channelName = PrepareOutput(GetChannelName(channel));
|
||||
sb.Append(GetTMATSChannel(channelName, summary.MinADC, summary.MaxADC, 1 + i, ds, eu));
|
||||
sb.AppendLine();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log($"Failed to get TMATS for channel: {i} - {channel.ChannelName2}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine(@"R-1\COM:--------------------- End of channels ---------------------;");
|
||||
sb.AppendLine(@"R-1\COM:--------------------- End of TMATS ---------------------;");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
private static string GetTMATSChannel(string channelName, short ADCMin, short ADCMax, int channelNumber, DataScaler ds,
|
||||
string eu)
|
||||
{
|
||||
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_ANALOG_ExportChannel.tmt");
|
||||
|
||||
baseText = baseText.Replace("{CHANNEL_NUMBER}", $"{channelNumber}");
|
||||
baseText = baseText.Replace("{CHANNEL_NAME}", channelName);
|
||||
|
||||
var scaler = ds.GetAdcToEuScalingFactor();
|
||||
var adcToEU = ds.GetAdcToEuScalingFactor();
|
||||
var midPointRemoval = adcToEU * Constants.ADC_MIDPOINT;
|
||||
|
||||
//the scaler is already aware of datazerolevelADC, so just get 0 and datazerolevel is applied, as is
|
||||
//initial eu or user offset
|
||||
var offset = ds.GetEU(0) - midPointRemoval;
|
||||
var minEU = ds.GetEU(ADCMin);
|
||||
var maxEU = ds.GetEU(ADCMax);
|
||||
|
||||
baseText = baseText.Replace("{CHANNEL_OFFSETEU}", $"{offset}");
|
||||
baseText = baseText.Replace("{CHANNEL_SCALEFACTOREU}", $"{scaler}");
|
||||
baseText = baseText.Replace("{CHANNEL_EU}", PrepareOutput(eu));
|
||||
baseText = baseText.Replace("{CHANNEL_MAXRANGEEU}", $"{maxEU}");
|
||||
baseText = baseText.Replace("{CHANNEL_MINRANGEEU}", $"{minEU}");
|
||||
|
||||
return baseText;
|
||||
}
|
||||
private static string PrepareOutput(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s)) { return string.Empty; }
|
||||
return s.Replace(' ', '_');
|
||||
}
|
||||
/// <summary>
|
||||
/// returns the entire TMATS document as a string for a PCM export
|
||||
/// <summary>
|
||||
private static string GetTMATSPCM(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_PCM_ExportBase.tmt");
|
||||
|
||||
baseText = baseText.Replace("{NAME OF PROGRAM}", "DataPRO");
|
||||
baseText = baseText.Replace("{TEST ID}", test.Modules[0].SerialNumber);
|
||||
baseText = baseText.Replace("{STREAM TIME FORMAT}", "2");
|
||||
baseText = baseText.Replace("{DAS SERIAL NUMBER}", test.Modules[0].SerialNumber);
|
||||
int channelCount = test.Modules.Sum(m => m.Channels.Count);
|
||||
var bitsPerFrame = 32 + 16 * channelCount;
|
||||
baseText = baseText.Replace("{DAS BIT RATE}", $"{bitsPerFrame * test.Modules[0].SampleRateHz:0}");
|
||||
var now = DateTime.UtcNow;
|
||||
baseText = baseText.Replace("{CREATE DATE}", $"{now.Year:0000}-{now.Month:00}-{now.Day:00} {now.Hour:00}:{now.Minute:00}:{now.Second:00}");
|
||||
baseText = baseText.Replace("{DAS SAMPLE RATE}", $"{test.Modules[0].SampleRateHz}");
|
||||
baseText = baseText.Replace("{NUMBER OF CHANNELS}", $"{channelCount}");
|
||||
baseText = baseText.Replace("{NUMBER OF WORDS}", $"{1 + channelCount}");
|
||||
baseText = baseText.Replace("{NUMBER OF BITS}", $"{32 + 16*channelCount}");
|
||||
sb.Append(baseText);
|
||||
sb.Append(GetTMATSChannelPCM(test, summaryLookup));
|
||||
return sb.ToString();
|
||||
}
|
||||
private string GetTMATSAnalog(Test test, IReadOnlyDictionary<Channel, ChannelInformation> summaryLookup)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var baseText = System.IO.File.ReadAllText(@"TMTTemplates\S6ATMTTemplate_ANALOG_ExportBase.tmt");
|
||||
|
||||
baseText = baseText.Replace("{NAME OF PROGRAM}", PrepareOutput(GetApplicationVersion()));
|
||||
baseText = baseText.Replace("{TEST ID}", PrepareOutput(test.Id));
|
||||
var now = DateTime.UtcNow;
|
||||
baseText = baseText.Replace("{CREATE DATE}", $"{now.Year:0000}-{now.Month:00}-{now.Day:00} {now.Hour:00}:{now.Minute:00}:{now.Second:00}");
|
||||
baseText = baseText.Replace("{UDP STREAM DATA CHANNEL ID}", $"{DATA_CHANNEL_ID}");
|
||||
baseText = baseText.Replace("{DAS SAMPLE RATE}", $"{test.Modules[0].SampleRateHz}");
|
||||
baseText = baseText.Replace("{NUMBER_OF_CHANNELS}", $"{test.Modules.Sum(m => m.Channels.Count)}");
|
||||
sb.Append(baseText);
|
||||
sb.AppendLine();
|
||||
sb.Append(GetTMATSChannelsAnalog(test, summaryLookup));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GetApplicationVersion()
|
||||
{
|
||||
var v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
return string.Format("DataPRO {0}.{1:00}.{2:00000}", v.Major, v.Minor, v.Build);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// constructs the writer with a given file and encoding
|
||||
/// </summary>
|
||||
/// <param name="fileType"></param>
|
||||
/// <param name="encoding"></param>
|
||||
internal Writer(File fileType, int encoding)
|
||||
: base(fileType, encoding)
|
||||
{
|
||||
WriterParent = fileType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// initializes the writer
|
||||
/// </summary>
|
||||
/// <param name="pathname"></param>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="dataFolder"></param>
|
||||
/// <param name="test"></param>
|
||||
/// <param name="bFiltering"></param>
|
||||
/// <param name="includeGroupNameInISOExport"></param>
|
||||
/// <param name="fd"></param>
|
||||
/// <param name="tmChannel"></param>
|
||||
/// <param name="channelNumber"></param>
|
||||
/// <param name="beginEventHandler"></param>
|
||||
/// <param name="cancelEventHandler"></param>
|
||||
/// <param name="endEventHandler"></param>
|
||||
/// <param name="tickEventHandler"></param>
|
||||
/// <param name="errorEventHandler"></param>
|
||||
/// <param name="cancelRequested"></param>
|
||||
public void Initialize(string pathname,
|
||||
string id,
|
||||
string dataFolder,
|
||||
Test test,
|
||||
bool bFiltering,
|
||||
bool includeGroupNameInISOExport,
|
||||
FilteredData fd,
|
||||
Channel tmChannel,
|
||||
int channelNumber,
|
||||
BeginEventHandler beginEventHandler,
|
||||
CancelEventHandler cancelEventHandler,
|
||||
EndEventHandler endEventHandler,
|
||||
TickEventHandler tickEventHandler,
|
||||
ErrorEventHandler errorEventHandler,
|
||||
CancelRequested cancelRequested)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Common/DTS.Common.Serialization/IRIGCH10/Chapter10.File.cs
Normal file
52
Common/DTS.Common.Serialization/IRIGCH10/Chapter10.File.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Chapter10.File.cs
|
||||
*
|
||||
* Copyright © 2020
|
||||
* Diversified Technical Systems, Inc.
|
||||
* All Rights Reserved
|
||||
*/
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation of the Serialization.File class for Chapter10
|
||||
/// </summary>
|
||||
public partial class File
|
||||
: Serialization.File, IWritable<Test>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// constructor
|
||||
/// </summary>
|
||||
public File()
|
||||
: base("Chapter10")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the file writer for this file type.
|
||||
/// </summary>
|
||||
public IWriter<Test> Exporter
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_exporter == null)
|
||||
{
|
||||
var writer = new Writer(this, DefaultEncoding);
|
||||
|
||||
_exporter = writer;
|
||||
}
|
||||
return _exporter;
|
||||
}
|
||||
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new Exception("encountered problem getting exporter", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
private IWriter<Test> _exporter;
|
||||
}
|
||||
}
|
||||
434
Common/DTS.Common.Serialization/IRIGCH10/Chapter10File.cs
Normal file
434
Common/DTS.Common.Serialization/IRIGCH10/Chapter10File.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
using DTS.Common;
|
||||
using DTS.Common.Utilities;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using DTS.Common.Utils;
|
||||
using DTS.Serialization.IRIGCH10.Packets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// this class is used to simplify parsing out CH10 data from a binary stream
|
||||
/// </summary>
|
||||
public class Chapter10File
|
||||
{
|
||||
/// <summary>
|
||||
/// used to hold transport stream headers as they are read
|
||||
/// </summary>
|
||||
private readonly List<ITransportStreamHeader> _transportHeaders = new List<ITransportStreamHeader>();
|
||||
/// <summary>
|
||||
/// returns all transport headers that were read, in order they were read
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ITransportStreamHeader[] GetTransportHeaders() { return _transportHeaders.ToArray(); }
|
||||
/// <summary>
|
||||
/// holds a list of packet headers as they are read
|
||||
/// </summary>
|
||||
private readonly List<IPacketHeader> _packets = new List<IPacketHeader>();
|
||||
/// <summary>
|
||||
/// returns all packet headers that were read, in the order they were read in
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IPacketHeader[] GetPacketHeaders() { return _packets.ToArray(); }
|
||||
/// <summary>
|
||||
/// holds any secondary time packets that were read
|
||||
/// </summary>
|
||||
private readonly List<ISecondaryTimeFormatHeader> _secondaryTimePackets = new List<ISecondaryTimeFormatHeader>();
|
||||
/// <summary>
|
||||
/// returns all secondary time headers in the order they were read in
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ISecondaryTimeFormatHeader[] GetSecondaryTimeHeaders() { return _secondaryTimePackets.ToArray(); }
|
||||
|
||||
/// <summary>
|
||||
/// lookup of a packet header that was read in as well as the start of that packet and end of that packet amongst
|
||||
/// all bytes in the file
|
||||
/// </summary>
|
||||
private readonly Dictionary<IPacketHeader, Tuple<long, long>> _packetToByteStartStop = new Dictionary<IPacketHeader, Tuple<long, long>>();
|
||||
|
||||
private readonly byte[] _bytes = null;
|
||||
/// <summary>
|
||||
/// returns all bytes for a packet (as apposed to just the bytes for the packet header)
|
||||
/// all bytes returned are in their own buffer, independent of bytes read in
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public byte[] GetBytesForPacket(IPacketHeader packet)
|
||||
{
|
||||
if (!_packetToByteStartStop.ContainsKey(packet)) { return null; }
|
||||
var tuple = _packetToByteStartStop[packet];
|
||||
var length = tuple.Item2 - tuple.Item1;
|
||||
var bytes = new byte[length];
|
||||
Buffer.BlockCopy(_bytes, (int)tuple.Item1, bytes, 0, (int)length);
|
||||
return bytes;
|
||||
}
|
||||
public int GoodPackets { get; private set; } = 0;
|
||||
public int RejectedPackets { get; private set; } = 0;
|
||||
/// <summary>
|
||||
/// adds a time packet 1 packet to the byte stream
|
||||
/// </summary>
|
||||
private static void InsertTimePacket1(BinaryWriter bw, int seconds, int nanoseconds, ref byte timeSequenceNumber,
|
||||
int startSeconds, int startNanoSeconds)
|
||||
{
|
||||
//time packet 1 must always start on a second boundary, so we need to calulate the nearest start second to our
|
||||
//actual start time, and all further packets also need to be on that boundary
|
||||
var time = PTP1588Timestamps.UnitTimeStampToDateTimeLocal((decimal)startSeconds, (decimal)startNanoSeconds).ToUniversalTime();
|
||||
var currentTime = PTP1588Timestamps.UnitTimeStampToDateTimeLocal((decimal)seconds, (decimal)nanoseconds).ToUniversalTime();
|
||||
|
||||
var newTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, currentTime.Hour, currentTime.Minute, currentTime.Second);
|
||||
var tickDelta = time.Ticks - newTime.Ticks;
|
||||
var newRTC = GetRTC(tickDelta);
|
||||
|
||||
var timePacket = new TimePacketFormat1(timeSequenceNumber, newTime, newRTC, nanoseconds, seconds);
|
||||
timeSequenceNumber++;
|
||||
bw.Write(timePacket.GetBytes());
|
||||
}
|
||||
/// <summary>
|
||||
/// describes a function to get the next Int16 ADC sample from a binary file
|
||||
/// </summary>
|
||||
/// <param name="channelIdx">the index of the channel among channels in the test</param>
|
||||
/// <returns></returns>
|
||||
public delegate short GetNextSampleDelegate(int channelIdx);
|
||||
/// <summary>
|
||||
/// returns the number of samples for the channel
|
||||
/// </summary>
|
||||
/// <param name="channelIndex">the index of the channel among channels in the test</param>
|
||||
/// <returns></returns>
|
||||
public delegate long GetChannelLengthDelegate(int channelIndex);
|
||||
|
||||
/// <summary>
|
||||
/// writes an output PCM export file
|
||||
/// </summary>
|
||||
public static void WriteFilePCM(string tmats, GetNextSampleDelegate getNextSample,
|
||||
GetChannelLengthDelegate getChannelLength,
|
||||
int totalChannels,
|
||||
int nanoseconds, int seconds, double sampleRate,
|
||||
bool includeSecondaryHeader,
|
||||
string fileName,
|
||||
TickEventHandler tickEventHandler,
|
||||
object tickObject)
|
||||
{
|
||||
var startNanoSeconds = nanoseconds;
|
||||
var startSeconds = seconds;
|
||||
if (System.IO.File.Exists(fileName))
|
||||
{
|
||||
FileUtils.DeleteFileOrMove(fileName, APILogger.Log);
|
||||
}
|
||||
using (var ms = new FileStream(fileName, FileMode.OpenOrCreate))
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
//simulate writing the transport bytes ... not needed for ch10 file though ...
|
||||
//first packet should be tmats
|
||||
var tmatsPacket = new TMATSPacket(nanoseconds, seconds, tmats, includeSecondaryHeader);
|
||||
bw.Write(tmatsPacket.GetBytes());
|
||||
var currentSample = 0L;
|
||||
byte sequenceNumber = 0x01;
|
||||
byte timeSequenceNumber = 0x01;
|
||||
var lastSecondTransmitted = 0;
|
||||
if (!includeSecondaryHeader)
|
||||
{
|
||||
//insert first time packet at time second 1
|
||||
InsertTimePacket1(bw, seconds, nanoseconds, ref timeSequenceNumber, startSeconds, startNanoSeconds);
|
||||
lastSecondTransmitted = seconds;
|
||||
}
|
||||
|
||||
var channel0Length = getChannelLength(0);
|
||||
while (currentSample < channel0Length)
|
||||
{
|
||||
var newRTC = GetRTC(currentSample, sampleRate);
|
||||
if (includeSecondaryHeader)
|
||||
{
|
||||
var timePacket = new TimePacketFormat2(timeSequenceNumber, false, nanoseconds, seconds, newRTC,
|
||||
includeSecondaryHeader);
|
||||
|
||||
var time = PTP1588Timestamps.UnitTimeStampToDateTimeLocal((decimal)seconds, (decimal)nanoseconds).ToUniversalTime();
|
||||
|
||||
timeSequenceNumber++;
|
||||
bw.Write(timePacket.GetBytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (seconds > lastSecondTransmitted)
|
||||
{
|
||||
lastSecondTransmitted = seconds;
|
||||
InsertTimePacket1(bw, seconds, nanoseconds, ref timeSequenceNumber, startSeconds, startNanoSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
var pcmPacket = GetPcmPacket(currentSample, getNextSample, channel0Length, totalChannels, nanoseconds, seconds, sampleRate, out var samplesProcessed,
|
||||
sequenceNumber, includeSecondaryHeader);
|
||||
bw.Write(pcmPacket.GetBytes());
|
||||
currentSample += samplesProcessed;
|
||||
sequenceNumber++;
|
||||
var elapsedNanoSeconds = samplesProcessed / (decimal)sampleRate * Constants.NANOS_PER_SECOND;
|
||||
|
||||
var newNanos = nanoseconds + elapsedNanoSeconds;
|
||||
if (newNanos > Constants.NANOS_PER_SECOND)
|
||||
{
|
||||
while (newNanos >= Constants.NANOS_PER_SECOND)
|
||||
{
|
||||
seconds++;
|
||||
newNanos -= Constants.NANOS_PER_SECOND;
|
||||
}
|
||||
}
|
||||
nanoseconds = Convert.ToInt32(Math.Truncate(newNanos));
|
||||
tickEventHandler?.Invoke(tickObject, 50D + 50D * currentSample / channel0Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteFileAnalog(string tmats, GetNextSampleDelegate getNextSample,
|
||||
GetChannelLengthDelegate getChannelLength,
|
||||
int totalChannels,
|
||||
int nanoseconds, int seconds, double sampleRate,
|
||||
bool includeSecondaryHeader,
|
||||
string fileName,
|
||||
TickEventHandler tickEventHandler,
|
||||
object tickObject)
|
||||
{
|
||||
var startNanoSeconds = nanoseconds;
|
||||
var startSeconds = seconds;
|
||||
if (System.IO.File.Exists(fileName))
|
||||
{
|
||||
FileUtils.DeleteFileOrMove(fileName, APILogger.Log);
|
||||
}
|
||||
using (var ms = new FileStream(fileName, FileMode.OpenOrCreate))
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
//simulate writing the transport bytes ... not needed for ch10 file though ...
|
||||
//first packet should be tmats
|
||||
var tmatsPacket = new TMATSPacket(nanoseconds, seconds, tmats, includeSecondaryHeader);
|
||||
bw.Write(tmatsPacket.GetBytes());
|
||||
var currentSample = 0L;
|
||||
byte sequenceNumber = 0x01;
|
||||
byte timeSequenceNumber = 0x01;
|
||||
var lastSecondTransmitted = 0;
|
||||
if (!includeSecondaryHeader)
|
||||
{
|
||||
//insert first time packet at time second 1
|
||||
InsertTimePacket1(bw, seconds, nanoseconds, ref timeSequenceNumber, startSeconds, startNanoSeconds);
|
||||
lastSecondTransmitted = seconds;
|
||||
}
|
||||
|
||||
var channel0Length = getChannelLength(0);
|
||||
while (currentSample < channel0Length)
|
||||
{
|
||||
var newRTC = GetRTC(currentSample, sampleRate);
|
||||
if (includeSecondaryHeader)
|
||||
{
|
||||
var timePacket = new TimePacketFormat2(timeSequenceNumber, false, nanoseconds, seconds, newRTC,
|
||||
includeSecondaryHeader);
|
||||
|
||||
var time = PTP1588Timestamps.UnitTimeStampToDateTimeLocal((decimal)seconds, (decimal)nanoseconds).ToUniversalTime();
|
||||
var newTime = new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second);
|
||||
|
||||
timeSequenceNumber++;
|
||||
bw.Write(timePacket.GetBytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (seconds > lastSecondTransmitted)
|
||||
{
|
||||
lastSecondTransmitted = seconds;
|
||||
InsertTimePacket1(bw, seconds, nanoseconds, ref timeSequenceNumber, startSeconds, startNanoSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
var analogPacket = GetAnalogPacket(currentSample, getNextSample, channel0Length, totalChannels, nanoseconds, seconds, sampleRate, out var samplesProcessed,
|
||||
sequenceNumber, includeSecondaryHeader);
|
||||
bw.Write(analogPacket.GetBytes());
|
||||
currentSample += samplesProcessed;
|
||||
sequenceNumber++;
|
||||
var elapsedNanoSeconds = samplesProcessed / (decimal)sampleRate * Constants.NANOS_PER_SECOND;
|
||||
|
||||
var newNanos = nanoseconds + elapsedNanoSeconds;
|
||||
if (newNanos > Constants.NANOS_PER_SECOND)
|
||||
{
|
||||
while (newNanos >= Constants.NANOS_PER_SECOND)
|
||||
{
|
||||
seconds++;
|
||||
newNanos -= Constants.NANOS_PER_SECOND;
|
||||
}
|
||||
}
|
||||
nanoseconds = Convert.ToInt32(Math.Truncate(newNanos));
|
||||
tickEventHandler?.Invoke(tickObject, 50D + 50D * currentSample / channel0Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// returns a PCM data packet with the next samples (targetting 100ms of data)
|
||||
/// </summary>
|
||||
private static PCMDataPacket GetPcmPacket(long currentSample, GetNextSampleDelegate getNextSample,
|
||||
long channelLength, int totalChannels, int nanoseconds,
|
||||
int seconds, double sampleRate, out long samplesProcessed, byte sequenceNumber, bool includeSecondaryHeader)
|
||||
{
|
||||
var newRTC = GetRTC(currentSample, sampleRate);
|
||||
|
||||
var numSamples = channelLength;
|
||||
numSamples -= currentSample;
|
||||
|
||||
//we should target 100ms of data per packet, sample rate is in seconds
|
||||
//so divide by 10 to get number of samples per 100ms
|
||||
var numSamplesPerPacket = sampleRate / 10;
|
||||
//if we want more samples than are available, just truncate down to max available
|
||||
if (numSamples > numSamplesPerPacket)
|
||||
{
|
||||
numSamples = Convert.ToInt64(numSamplesPerPacket);
|
||||
}
|
||||
var pcmPacket = new PCMDataPacket(nanoseconds, seconds,
|
||||
getNextSample, totalChannels, channelLength, newRTC, numSamples, currentSample, sequenceNumber, 3,
|
||||
includeSecondaryHeader);
|
||||
|
||||
samplesProcessed = numSamples;
|
||||
return pcmPacket;
|
||||
}
|
||||
private static AnalogDataFormat1Packet GetAnalogPacket(long currentSample, GetNextSampleDelegate getNextSample,
|
||||
long channelLength, int totalChannels, int nanoseconds,
|
||||
int seconds, double sampleRate, out long samplesProcessed, byte sequenceNumber, bool includeSecondaryHeader)
|
||||
{
|
||||
var newRTC = GetRTC(currentSample, sampleRate);
|
||||
|
||||
var numSamples = channelLength;
|
||||
numSamples -= currentSample;
|
||||
|
||||
//we should target 100ms of data per packet
|
||||
var numSamplesPerPacket = Math.Floor(sampleRate / 10);
|
||||
|
||||
if (numSamples > numSamplesPerPacket)
|
||||
{
|
||||
numSamples = Convert.ToInt64(numSamplesPerPacket);
|
||||
}
|
||||
|
||||
var analogPacket = new AnalogDataFormat1Packet(nanoseconds, seconds,
|
||||
getNextSample, totalChannels, channelLength, newRTC, numSamples, currentSample, sequenceNumber, 3,
|
||||
includeSecondaryHeader);
|
||||
|
||||
samplesProcessed = numSamples;
|
||||
return analogPacket;
|
||||
}
|
||||
|
||||
//for convenience just store relative time counter 10MHZ conversion factor
|
||||
private const int RTC_PER_SECOND = 10000000;
|
||||
private static long GetRTC(long currentSample, double sampleRate)
|
||||
{
|
||||
var secondsPassed = currentSample / sampleRate;
|
||||
return AbstractDataPacket.BASE_RTC + Convert.ToInt64(secondsPassed * RTC_PER_SECOND);
|
||||
}
|
||||
|
||||
private static long GetRTC(long tickDelta)
|
||||
{
|
||||
var seconds = (double)tickDelta / TimeSpan.TicksPerSecond;
|
||||
return AbstractDataPacket.BASE_RTC - Convert.ToInt64(seconds * RTC_PER_SECOND);
|
||||
}
|
||||
/// <summary>
|
||||
/// parses out all packets from array of bytes
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
public Chapter10File(byte[] bytes)
|
||||
{
|
||||
_bytes = new byte[bytes.Length];
|
||||
Buffer.BlockCopy(bytes, 0, _bytes, 0, bytes.Length);
|
||||
var currentByteIndex = 0L;
|
||||
var badPackets = 0;
|
||||
var goodPackets = 0;
|
||||
while (currentByteIndex < (bytes.Length - PacketHeader.PACKET_HEADER_LENGTH))
|
||||
{
|
||||
try
|
||||
{
|
||||
currentByteIndex = ReadTransportHeader(bytes, currentByteIndex, out var transportHeader);
|
||||
var startOfChapter10Packet = currentByteIndex;
|
||||
currentByteIndex = ReadChapter10PacketHeader(bytes, currentByteIndex, out var packetHeader);
|
||||
if (packetHeader.PacketSyncPattern != PacketHeader.EXPECTED_SYNC_PATTERN || packetHeader.CheckSum != packetHeader.ComputeCheckSum())
|
||||
{
|
||||
badPackets++;
|
||||
//this packet is invalid, so we need to skip ahead
|
||||
//skip until we see the next sync pattern, then backstep to the start of the transport bytes, then continue on
|
||||
currentByteIndex = GetIndexOfNextPacket(bytes, startOfChapter10Packet);
|
||||
continue;
|
||||
}
|
||||
_transportHeaders.Add(transportHeader);
|
||||
if (packetHeader.SecondaryHeaderPresent)
|
||||
{
|
||||
ReadChapter10SecondHeaderTimeFormat(bytes, startOfChapter10Packet + PacketHeader.PACKET_HEADER_LENGTH, out var secondaryTimeHeader);
|
||||
_secondaryTimePackets.Add(secondaryTimeHeader);
|
||||
}
|
||||
_packets.Add(packetHeader);
|
||||
|
||||
_packetToByteStartStop[packetHeader] = new Tuple<long, long>(startOfChapter10Packet, currentByteIndex);
|
||||
goodPackets++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Hit exception processing file at byte: {currentByteIndex}, {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
GoodPackets = goodPackets;
|
||||
RejectedPackets = badPackets;
|
||||
}
|
||||
/// <summary>
|
||||
/// determines the next valid packet starting location after the provided starting spot
|
||||
/// this is used to skip over bad data to the next good packet
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static long GetIndexOfNextPacket(byte[] bytes, long startIndex)
|
||||
{
|
||||
for (var i = startIndex; i < bytes.Length - 1; i++)
|
||||
{
|
||||
if (BitConverter.ToUInt16(bytes, (int)i) == PacketHeader.EXPECTED_SYNC_PATTERN)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
//if we got here there are no more valid packets, you are done.
|
||||
return bytes.Length;
|
||||
}
|
||||
/// <summary>
|
||||
/// reads a secondary time header from provided bytes and starting spot
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="secondaryTimeFormat"></param>
|
||||
private void ReadChapter10SecondHeaderTimeFormat(byte[] bytes, long start, out ISecondaryTimeFormatHeader secondaryTimeFormat)
|
||||
{
|
||||
var headerBytes = new byte[SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH];
|
||||
Array.Copy(bytes, start, headerBytes, 0, SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH);
|
||||
secondaryTimeFormat = new SecondaryTimeFormatHeader(headerBytes);
|
||||
}
|
||||
/// <summary>
|
||||
/// reads a transport header from provided bytes and starting spot
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <param name="transportHeader"></param>
|
||||
/// <returns></returns>
|
||||
private long ReadTransportHeader(byte[] bytes, long startIndex, out TransportStreamHeader transportHeader)
|
||||
{
|
||||
var transportHeaderBytes = new byte[4];
|
||||
Array.Copy(bytes, startIndex, transportHeaderBytes, 0, 4);
|
||||
transportHeader = new TransportStreamHeader(transportHeaderBytes);
|
||||
return startIndex + 4L;
|
||||
}
|
||||
/// <summary>
|
||||
/// reads a Packet from stream of bytes at given starting spot
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <param name="packetHeader"></param>
|
||||
/// <returns></returns>
|
||||
public static long ReadChapter10PacketHeader(byte[] bytes, long startIndex, out IPacketHeader packetHeader)
|
||||
{
|
||||
var headerBytes = new byte[PacketHeader.PACKET_HEADER_LENGTH];
|
||||
Array.Copy(bytes, startIndex, headerBytes, 0, PacketHeader.PACKET_HEADER_LENGTH);
|
||||
packetHeader = new PacketHeader(headerBytes);
|
||||
return startIndex + packetHeader.PacketLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
695
Common/DTS.Common.Serialization/IRIGCH10/Enums/Enums.cs
Normal file
695
Common/DTS.Common.Serialization/IRIGCH10/Enums/Enums.cs
Normal file
@@ -0,0 +1,695 @@
|
||||
using DTS.Serialization.IRIGCh10.Attributes;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Enums
|
||||
{
|
||||
public enum DataFileDataTypes
|
||||
{
|
||||
[Description("User-Defined")]
|
||||
[PacketHeaderValue(0x00)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat0,
|
||||
|
||||
[Description("Setup Record")]
|
||||
[PacketHeaderValue(0x01)]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
ComputerGeneratedDataFormat1,
|
||||
|
||||
[Description("Recording Events")]
|
||||
[PacketHeaderValue(0x02)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat2,
|
||||
|
||||
[Description("Recording Index")]
|
||||
[PacketHeaderValue(0x03)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat3,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x04)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat4,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x05)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat5,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x06)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat6,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x07)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ComputerGeneratedDataFormat7,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x08)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat0,
|
||||
|
||||
[Description("Chapter 4 or 8")]
|
||||
[PacketHeaderValue(0x09)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat1,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x0A)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat2,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x0B)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat3,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x0C)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat4,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x0D)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat5,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x0E)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat6,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x0F)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
PCMDataFormat7,
|
||||
|
||||
[Description("ReservedForFutureUse")]
|
||||
[PacketHeaderValue(0x10)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat0,
|
||||
|
||||
[Description("RCC/Global Positioning System [GPS]/RTC")]
|
||||
[PacketHeaderValue(0x11)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat1,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x12)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat2,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x13)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat3,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x14)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat4,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x15)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat5,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x16)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat6,
|
||||
|
||||
[Description("Reserved for future use")]
|
||||
[PacketHeaderValue(0x17)]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TimeDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x18)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x19)]
|
||||
[Description("MIL-STD-1553B Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x1A)]
|
||||
[Description("16PP194 Bus")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x1B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x1C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x1D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x1E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x1F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MILSTD1553DataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x20)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x21)]
|
||||
[Description("Analog Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x22)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x23)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x24)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x25)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x26)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x27)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
AnalogDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x28)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x29)]
|
||||
[Description("Discrete Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x2A)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x2B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x2C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x2D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x2E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x2F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
DiscreteDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x30)]
|
||||
[Description("Generic Message Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x31)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x32)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x33)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x34)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x35)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x36)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x37)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
MessageDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x38)]
|
||||
[Description("ARINC-429 Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x39)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x3A)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x3B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x3C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x3D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x3E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x3F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ARINC429DataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x40)]
|
||||
[Description("MPEG-2/H.264 Video")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
VideoDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x41)]
|
||||
[Description("ISO 13818-1 MPEG-2")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
VideoDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x42)]
|
||||
[Description("ISO 14496 MPEG-4 Part10 110 AVC/H.264")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
VideoDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x43)]
|
||||
[Description("MJPEG")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
VideoDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x44)]
|
||||
[Description("MJPEG 2000")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
VideoDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x45)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
VideoDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x46)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
VideoDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x47)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
VideoDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x48)]
|
||||
[Description("Image Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x49)]
|
||||
[Description("Still Imagery")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x4A)]
|
||||
[Description("Dynamic Imagery")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x4B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x4C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x4D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x4E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x4F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ImageDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x50)]
|
||||
[Description("UART Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x51)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x52)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x53)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x54)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x55)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x56)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x57)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
UARTDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x58)]
|
||||
[Description("IEEE 1394 Transaction")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x59)]
|
||||
[Description("IEEE 1394 Physical Layer")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x5A)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x5B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x5C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x5D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x5E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x5F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
IEEE1394DataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x60)]
|
||||
[Description("Parallel Data")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ParallelDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x61)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ParallelDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x68)]
|
||||
[Description("Ethernet Data")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
EthernetDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x69)]
|
||||
[Description("Ethernet UDP Payload")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x6A)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x6B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x6C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x6D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x6E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x6F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
EthernetDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x70)]
|
||||
[Description("GPS NMEA-RTCM")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x71)]
|
||||
[Description("EAG ACMI")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat1,
|
||||
|
||||
[PacketHeaderValue(0x72)]
|
||||
[Description("ACTTS")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat2,
|
||||
|
||||
[PacketHeaderValue(0x73)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat3,
|
||||
|
||||
[PacketHeaderValue(0x74)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat4,
|
||||
|
||||
[PacketHeaderValue(0x75)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat5,
|
||||
|
||||
[PacketHeaderValue(0x76)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat6,
|
||||
|
||||
[PacketHeaderValue(0x77)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
TSPI_CTSDataFormat7,
|
||||
|
||||
[PacketHeaderValue(0x78)]
|
||||
[Description("CAN Bus")]
|
||||
[DataTypeVersionValue(0x06)]
|
||||
ControllerAreaNetworkBus,
|
||||
|
||||
[PacketHeaderValue(0x79)]
|
||||
[Description("Fibre Channel Data")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormat0,
|
||||
|
||||
[PacketHeaderValue(0x7A)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormats1,
|
||||
|
||||
[PacketHeaderValue(0x7B)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormats2,
|
||||
|
||||
[PacketHeaderValue(0x7C)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormats3,
|
||||
|
||||
[PacketHeaderValue(0x7D)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormats4,
|
||||
|
||||
[PacketHeaderValue(0x7E)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormats5,
|
||||
|
||||
[PacketHeaderValue(0x7F)]
|
||||
[Description("Reserved for future use")]
|
||||
[DataTypeVersionValue(0x07)]
|
||||
FibreChannelDataFormats6
|
||||
}
|
||||
public enum TimeSource
|
||||
{
|
||||
[PacketHeaderValue(0x00)]
|
||||
[Description("Time derived from a clock in the recorder")]
|
||||
Internal,
|
||||
[PacketHeaderValue(0x01)]
|
||||
[Description("Time derived from a clock not in the recorder")]
|
||||
External,
|
||||
[PacketHeaderValue(0x02)]
|
||||
[Description("Internal from RMM (Time derived from the clock in the RMM")]
|
||||
InternalFromRMM,
|
||||
[PacketHeaderValue(0x0F)]
|
||||
[Description("None")]
|
||||
None
|
||||
}
|
||||
|
||||
public enum TimeFormats
|
||||
{
|
||||
[Description("IRIG-B")]
|
||||
[PacketHeaderValue(0x00)]
|
||||
IRIGB,
|
||||
[PacketHeaderValue(0x01)]
|
||||
[Description("IRIG-A")]
|
||||
IRIGA,
|
||||
[PacketHeaderValue(0x02)]
|
||||
[Description("IRIG-G")]
|
||||
IRIGG,
|
||||
[PacketHeaderValue(0x03)]
|
||||
[Description("Real-Time Clock")]
|
||||
RTC,
|
||||
[PacketHeaderValue(0x04)]
|
||||
[Description("UTC Time from GPS")]
|
||||
UTC,
|
||||
[PacketHeaderValue(0x05)]
|
||||
[Description("Native GPS Time")]
|
||||
GPS,
|
||||
[PacketHeaderValue(0x0F)]
|
||||
[Description("None")]
|
||||
None
|
||||
}
|
||||
|
||||
public enum DataTypeVersion
|
||||
{
|
||||
[Description("Initial Release (IRIG-106-04)")]
|
||||
[PacketHeaderValue(0x01)]
|
||||
InitialRelease,
|
||||
|
||||
[PacketHeaderValue(0x02)]
|
||||
[Description("TG-78 (IRIG-106-05)")]
|
||||
TG78,
|
||||
|
||||
[PacketHeaderValue(0x03)]
|
||||
[Description("IRIG-106-07")]
|
||||
IRIG106_07
|
||||
}
|
||||
|
||||
public enum SecondaryHeaderTimeFormat
|
||||
{
|
||||
[PacketHeaderValue(0x00)]
|
||||
[Description("IRIG 106 Chapter 4 binary weighted 48-bit time format")]
|
||||
IRIG106Chapter4,
|
||||
[PacketHeaderValue(0x01)]
|
||||
[Description("IEEE-1588 Time format")]
|
||||
IEEE1588
|
||||
}
|
||||
|
||||
public enum DataCheckSumType
|
||||
{
|
||||
[PacketHeaderValue(0x00)]
|
||||
None,
|
||||
[PacketHeaderValue(0x01)]
|
||||
EightBit,
|
||||
[PacketHeaderValue(0x02)]
|
||||
SixteenBit,
|
||||
[PacketHeaderValue(0x03)]
|
||||
ThirtyTwoBit
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
public class AnalogDataFormat1Packet : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
|
||||
public DateTime LocalTimeOfFirstSample
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
//bit 28
|
||||
public bool Same
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
return bits[28];
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
b[28] = value;
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
//bits 27-24
|
||||
public int Factor
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
return Utils.Utils.BitArrayToInt32(bits, 24, 27);
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 24, 27);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
//bits 23-16
|
||||
public int TotChan
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
return Utils.Utils.BitArrayToInt32(bits, 16, 23);
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 16, 23);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
//bits 15-8
|
||||
public long Subchan
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
return Utils.Utils.BitArrayToInt32(bits, 8, 15);
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 8, 15);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
//bits 7-2
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
return Utils.Utils.BitArrayToInt32(bits, 2, 7);
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 2, 7);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
public enum Modes
|
||||
{
|
||||
DataIsPacked,
|
||||
DataIsUnpackedLSBPadded,
|
||||
Reserved,
|
||||
DataIsUnpackedMSBPadded
|
||||
}
|
||||
//bits 1-0
|
||||
public Modes Mode
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
var mode = Utils.Utils.BitArrayToInt32(bits, 0, 1);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: return Modes.DataIsPacked;
|
||||
case 1: return Modes.DataIsUnpackedLSBPadded;
|
||||
case 3: return Modes.DataIsUnpackedMSBPadded;
|
||||
case 2:
|
||||
default: return Modes.Reserved;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 0, 1);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
public SampleData[] Samples = new SampleData[0];
|
||||
public class SampleData
|
||||
{
|
||||
public short[] ChannelData { private set; get; }
|
||||
protected SampleData() { }
|
||||
public SampleData(short[] channels)
|
||||
{
|
||||
ChannelData = channels;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// it seems the EMC ch10 validation tool only supports CH10 106v5, so use this header value for the analog packet when in this mode
|
||||
/// whether to include secondary time header or not
|
||||
/// 33199 Add Time format 1 as an export option for CH 10 export
|
||||
/// </summary>
|
||||
private const byte DATA_VERSION_V105 = 0x01;
|
||||
|
||||
|
||||
private const byte DATA_VERSION_DASSAULT = 0x06;
|
||||
public AnalogDataFormat1Packet(int nanoseconds, int seconds,
|
||||
Chapter10File.GetNextSampleDelegate getNextSample, int totalChannels, long channelLength, long rtc, long numSamples, long currentSample, byte sequenceNumber,
|
||||
ushort channelId, bool includeSecondaryHeader) : base(Enums.DataFileDataTypes.AnalogDataFormat1, includeSecondaryHeader)
|
||||
{
|
||||
TotChan = totalChannels;
|
||||
Subchan = 0;
|
||||
Same = true;
|
||||
Factor = 0;
|
||||
Length = 16;
|
||||
Mode = Modes.DataIsUnpackedMSBPadded;
|
||||
|
||||
var dataLength = Convert.ToUInt32(4 + sizeof(ushort) * totalChannels * numSamples); ;
|
||||
var offset = CommonHeaderWork(channelId, includeSecondaryHeader ? DATA_VERSION_DASSAULT : DATA_VERSION_V105, sequenceNumber, rtc, dataLength, nanoseconds, seconds);
|
||||
|
||||
for (var sampleIdx = 0; sampleIdx < numSamples; sampleIdx++)
|
||||
{
|
||||
for (var chIdx = 0; chIdx < totalChannels; chIdx++)
|
||||
{
|
||||
var signed = getNextSample(chIdx);
|
||||
var unsigned = (ushort)(((signed - 0x8000 & 0x00FF) << 8) | ((signed - 0x8000 >> 8) & 0x00FF));
|
||||
var bytes = BitConverter.GetBytes(unsigned).Reverse().ToArray();
|
||||
Buffer.BlockCopy(bytes, 0, _dataBytes, offset, 2);
|
||||
offset += 2;
|
||||
}
|
||||
currentSample++;
|
||||
}
|
||||
}
|
||||
public AnalogDataFormat1Packet() : base(Enums.DataFileDataTypes.AnalogDataFormat1, true)
|
||||
{
|
||||
}
|
||||
public AnalogDataFormat1Packet(byte[] bytes) : base(bytes)
|
||||
{
|
||||
var offset = IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH;
|
||||
|
||||
if (PacketHeader.SecondaryHeaderPresent)
|
||||
{
|
||||
var secondaryHeaderBytes = new byte[SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH];
|
||||
Buffer.BlockCopy(bytes, offset,
|
||||
secondaryHeaderBytes, 0, SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH);
|
||||
var secondaryHeader = new SecondaryTimeFormatHeader(secondaryHeaderBytes);
|
||||
LocalTimeOfFirstSample = secondaryHeader.LocalTime;
|
||||
offset += SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(bytes, offset);
|
||||
offset += sizeof(uint);
|
||||
var numChannels = TotChan;
|
||||
var sizeOf1SampleForAllChannels = sizeof(ushort) * numChannels;
|
||||
//There's a CSDW in the data section, then all ADC data. CSDW is uint, 4 bytes
|
||||
var numSamples = (PacketHeader.DataLength - 4) / sizeOf1SampleForAllChannels;
|
||||
|
||||
Samples = new SampleData[numSamples];
|
||||
for (var i = 0; i < Samples.Length; i++)
|
||||
{
|
||||
var channels = GetChannels(bytes, offset, numChannels);
|
||||
Samples[i] = new SampleData(channels);
|
||||
offset += sizeOf1SampleForAllChannels;
|
||||
}
|
||||
}
|
||||
|
||||
private short[] GetChannels(byte[] bytes, int sampleStart, int numChannels)
|
||||
{
|
||||
var ret = new short[numChannels];
|
||||
for (var i = 0; i < numChannels; i++)
|
||||
{
|
||||
var unsignedBytes = new byte[2];
|
||||
Buffer.BlockCopy(bytes, sampleStart + i * 2, unsignedBytes, 0, 2);
|
||||
//we have to change the byte order here
|
||||
var unsigned = BitConverter.ToUInt16(new[] { unsignedBytes[1], unsignedBytes[0] }, 0);
|
||||
//go from unsigned to signed
|
||||
var adc = (short)((((unsigned & 0x00FF) << 8) | ((unsigned >> 8) & 0x00FF)) + 0x8000);
|
||||
ret[i] = adc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
211
Common/DTS.Common.Serialization/IRIGCH10/Packets/IDataPacket.cs
Normal file
211
Common/DTS.Common.Serialization/IRIGCH10/Packets/IDataPacket.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using DTS.Serialization.IRIGCH10.Packets;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// this is the interface that all datapackets implement, it defines how packets can be interacted with in general
|
||||
/// </summary>
|
||||
public interface IDataPacket
|
||||
{
|
||||
/// <summary>
|
||||
/// all chapter 10 packets have a packet header
|
||||
/// </summary>
|
||||
IPacketHeader PacketHeader { get; }
|
||||
/// <summary>
|
||||
/// computes the checksum for the datapacket data
|
||||
/// </summary>
|
||||
uint ComputeCheckSum();
|
||||
/// <summary>
|
||||
/// returns all bytes for the packet
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
byte[] GetBytes();
|
||||
/// <summary>
|
||||
/// sets the Realtime Counter (10 MHZ) for the packet
|
||||
/// </summary>
|
||||
/// <param name="rtc"></param>
|
||||
void SetRTC(long rtc);
|
||||
/// <summary>
|
||||
/// sets the DataVersion of the packet
|
||||
/// (part of the packet header)
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
void SetDataVersion(DataTypeVersion version);
|
||||
|
||||
/// <summary>
|
||||
/// sets the channel id for the packet (part of packet header)
|
||||
/// </summary>
|
||||
/// <param name="channelID"></param>
|
||||
void SetChannelID(ushort channelID);
|
||||
/// <summary>
|
||||
/// sets the sequence number for the packet,
|
||||
/// in general should be increasing and flip back to 0
|
||||
/// after max value
|
||||
/// </summary>
|
||||
/// <param name="seq"></param>
|
||||
void SetSequenceNumber(ushort seq);
|
||||
}
|
||||
/// <summary>
|
||||
/// this is the general base class all packets extend, it
|
||||
/// has common features like computing the checksum, setting the
|
||||
/// packet specific Channel Data Word, etc
|
||||
/// </summary>
|
||||
public abstract class AbstractDataPacket
|
||||
{
|
||||
protected void SetCSDWBit(int bit, bool value)
|
||||
{
|
||||
if (bit <0 || bit > 31)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!value)
|
||||
{
|
||||
//and with all ones except for the bit in question
|
||||
ChannelSpecificDataWord &= ~(1u << bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
//or with all 0s except for bit in question
|
||||
ChannelSpecificDataWord |= (1u << bit);
|
||||
}
|
||||
}
|
||||
protected bool GetCSDWBit(int bit)
|
||||
{
|
||||
if (bit < 0 || bit > 31) { return false; }
|
||||
return (ChannelSpecificDataWord & (1u << bit)) != 0;
|
||||
}
|
||||
protected int CommonHeaderWork(ushort channelId, byte dataVersion,
|
||||
byte sequenceNumber, long rtc, uint dataLength, int nanoseconds, int seconds)
|
||||
{
|
||||
PacketHeader.ChannelId = channelId;
|
||||
PacketHeader.DataVersion = dataVersion;
|
||||
PacketHeader.SequenceNum = sequenceNumber;
|
||||
PacketHeader.IPTSTimeSource = PacketHeader.SecondaryHeaderPresent;
|
||||
PacketHeader.RTCSyncError = false;
|
||||
PacketHeader.SecondaryHeaderTimeFormat = IRIGCH10.PacketHeader.SecondaryHeaderTimeFormats.IEEE1588;
|
||||
PacketHeader.DataOverflowError = false;
|
||||
PacketHeader.CheckSumPresence = IRIGCH10.PacketHeader.DataCheckSumPresences.NotPresent;
|
||||
PacketHeader.SetRTC(rtc);
|
||||
PacketHeader.DataLength = dataLength;
|
||||
|
||||
PacketHeader.PacketLength = PacketHeader.DataLength + IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH;
|
||||
if (PacketHeader.SecondaryHeaderPresent)
|
||||
{
|
||||
PacketHeader.PacketLength += SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH;
|
||||
}
|
||||
if (0 != PacketHeader.PacketLength % 4)
|
||||
{
|
||||
var pad = 4 - PacketHeader.PacketLength % 4;
|
||||
//pad out packet length to a multiple of 4 ...
|
||||
PacketHeader.PacketLength += pad;
|
||||
}
|
||||
_dataBytes = new byte[PacketHeader.PacketLength - IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH];
|
||||
|
||||
var offset = 0;
|
||||
|
||||
if (PacketHeader.SecondaryHeaderPresent)
|
||||
{
|
||||
var secondaryHeader = SecondaryTimeFormatHeader.GetBytes(nanoseconds, seconds);
|
||||
|
||||
Buffer.BlockCopy(secondaryHeader, 0, _dataBytes, offset, secondaryHeader.Length);
|
||||
offset += secondaryHeader.Length;
|
||||
}
|
||||
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
Buffer.BlockCopy(channelSpecificDataWord, 0, _dataBytes, offset, channelSpecificDataWord.Length);
|
||||
offset += channelSpecificDataWord.Length;
|
||||
return offset;
|
||||
}
|
||||
private uint _ChannelSpecificDataWork = 0;
|
||||
[Browsable(false)]
|
||||
public uint ChannelSpecificDataWord
|
||||
{
|
||||
get => _ChannelSpecificDataWork;
|
||||
protected set => _ChannelSpecificDataWork = value;
|
||||
}
|
||||
[Browsable(false)]
|
||||
public IPacketHeader PacketHeader { get; protected set; }
|
||||
public uint ComputeCheckSum()
|
||||
{
|
||||
return Utils.Utils.GetCheckSum32(_dataBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the sequence number for the packet
|
||||
/// </summary>
|
||||
/// <param name="seq"></param>
|
||||
public void SetSequenceNumber(ushort seq)
|
||||
{
|
||||
PacketHeader.SequenceNum = BitConverter.GetBytes(seq)[0];
|
||||
}
|
||||
|
||||
private long _rtc;
|
||||
/// <summary>
|
||||
/// returns the Realtime Counter (10Mhz) value for the packet
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public long GetRTC() { return _rtc; }
|
||||
/// <summary>
|
||||
/// sets the Realtime Counter (10Mhz) value for the packet
|
||||
/// </summary>
|
||||
/// <param name="rtc"></param>
|
||||
public void SetRTC(long rtc)
|
||||
{
|
||||
_rtc = rtc;
|
||||
PacketHeader.SetRTC(rtc);
|
||||
|
||||
}
|
||||
|
||||
public const long BASE_RTC = 141989612500056L;
|
||||
/// <summary>
|
||||
/// returns all bytes for the packet
|
||||
/// is virtual so extending classes can define their own behavior as needed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual byte[] GetBytes()
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write(PacketHeader.GetBytes());
|
||||
bw.Write(_dataBytes);
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractDataPacket(DataFileDataTypes dataType, bool secondaryHeaderPresent)
|
||||
{
|
||||
PacketHeader = new PacketHeader(dataType);
|
||||
PacketHeader.SecondaryHeaderPresent = secondaryHeaderPresent;
|
||||
}
|
||||
protected AbstractDataPacket(byte[] bytes)
|
||||
{
|
||||
var header = new byte[24];
|
||||
Buffer.BlockCopy(bytes, 0, header, 0, 24);
|
||||
PacketHeader = new PacketHeader(header);
|
||||
_dataBytes = new byte[PacketHeader.DataLength];
|
||||
var length = _dataBytes.Length;
|
||||
var length2 = bytes.Length - 24;
|
||||
if (length2 < length) { length = length2; }
|
||||
Buffer.BlockCopy(bytes, 24, _dataBytes, 0, length);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(_dataBytes, 0);
|
||||
}
|
||||
|
||||
protected byte[] _dataBytes;
|
||||
public void SetDataVersion(DataTypeVersion version)
|
||||
{
|
||||
PacketHeader.SetDataVersion(version);
|
||||
}
|
||||
|
||||
public void SetChannelID(ushort channelID)
|
||||
{
|
||||
PacketHeader.ChannelId = channelID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
public interface ISecondaryTimeFormatHeader
|
||||
{
|
||||
int NanoSeconds { get; }
|
||||
int Seconds { get; }
|
||||
ushort Reserved { get; }
|
||||
ushort CheckSum { get; }
|
||||
DateTime LocalTime { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
public interface ITransportStreamHeader
|
||||
{
|
||||
int MessageFormat { get; }
|
||||
int MessageType { get; }
|
||||
int SequenceNumber { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
public class PCMDataPacket : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
//Bit 30 Intra packet headers
|
||||
public bool HasIntraPacketHeader
|
||||
{
|
||||
get => GetCSDWBit(30);
|
||||
set => SetCSDWBit(30, value);
|
||||
}
|
||||
//Bit29 Major Frame Indicator
|
||||
//not applicable in throughput mode
|
||||
public bool IsMajorFrame
|
||||
{
|
||||
get => GetCSDWBit(29);
|
||||
set => SetCSDWBit(29, value);
|
||||
}
|
||||
//Bit28 IsMinorFrame
|
||||
//not applicable in throughput mode
|
||||
public bool IsMinorFrame
|
||||
{
|
||||
get => GetCSDWBit(28);
|
||||
set => SetCSDWBit(28, value);
|
||||
}
|
||||
public enum MinorFrameStatus
|
||||
{
|
||||
Reserved0 = 0, //00
|
||||
Reserved1 = 1, //01
|
||||
MinorFrameCheck = 2, //10
|
||||
MinorFrameLock = 3 //11
|
||||
}
|
||||
public enum MajorFrameStatus
|
||||
{
|
||||
MajorFrameNotLocked = 0, //00
|
||||
Reserved1 = 1, //01
|
||||
MajorFrameCheck = 2, //10
|
||||
MajorFrameLock = 3 //11
|
||||
}
|
||||
//bits 27-24
|
||||
//not applicable for throughput mode
|
||||
public MinorFrameStatus LockStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
var csdw = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(csdw);
|
||||
return (MinorFrameStatus)Utils.Utils.BitArrayToInt32(bits, 24, 27);
|
||||
}
|
||||
set
|
||||
{
|
||||
var iValue = (int)value;
|
||||
var csdw = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(csdw);
|
||||
Utils.Utils.SetBits(bits, (uint)iValue, 24, 27);
|
||||
var ret = new Byte[sizeof(uint)];
|
||||
bits.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
//bit 21
|
||||
public bool Is32BitAligned
|
||||
{
|
||||
get => GetCSDWBit(21);
|
||||
set => SetCSDWBit(21, value);
|
||||
}
|
||||
//bit 20
|
||||
public bool IsThroughputMode
|
||||
{
|
||||
get => GetCSDWBit(20);
|
||||
set => SetCSDWBit(20, value);
|
||||
}
|
||||
|
||||
//bit 19 packed mode
|
||||
public bool IsPackedMode
|
||||
{
|
||||
get => GetCSDWBit(19);
|
||||
set => SetCSDWBit(19, value);
|
||||
}
|
||||
//bit 18 unpacked mode
|
||||
public bool IsUnpackedMode
|
||||
{
|
||||
get => GetCSDWBit(18);
|
||||
set => SetCSDWBit(18, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// it seems the EMC ch10 validation tool only supports CH10 106v5, so use this header value for the analog packet when in this mode
|
||||
/// whether to include secondary time header or not
|
||||
/// 33199 Add Time format 1 as an export option for CH 10 export
|
||||
/// </summary>
|
||||
private const byte DATA_VERSION = 0x06;
|
||||
public PCMDataPacket(int nanoseconds, int seconds,
|
||||
Chapter10File.GetNextSampleDelegate getNextSample, int totalChannels, long channelLength, long rtc, long numSamples, long currentSample, byte sequenceNumber,
|
||||
ushort channelId, bool includeSecondaryHeader) : base(Enums.DataFileDataTypes.PCMDataFormat1, includeSecondaryHeader)
|
||||
{
|
||||
HasIntraPacketHeader = includeSecondaryHeader;
|
||||
IsMajorFrame = false;
|
||||
IsMinorFrame = false;
|
||||
LockStatus = MinorFrameStatus.Reserved0;
|
||||
IsThroughputMode = true;
|
||||
Is32BitAligned = false;
|
||||
IsPackedMode = false;
|
||||
IsUnpackedMode = false;
|
||||
|
||||
//4 bytes for CSDW, then 4 bytes for a PCM FRAMESYNC??? this isn't in the spec but f/w is using it
|
||||
var dataLength = Convert.ToUInt32(4 + 2 * totalChannels * numSamples + 4*numSamples);
|
||||
|
||||
var offset = CommonHeaderWork(channelId, DATA_VERSION, sequenceNumber, rtc, dataLength, nanoseconds, seconds);
|
||||
for (var sampleIdx = 0; sampleIdx < numSamples; sampleIdx++)
|
||||
{
|
||||
Buffer.BlockCopy(new byte[] { 0x6B, 0xFE, 0x40, 0x28 }, 0, _dataBytes, offset, 4);
|
||||
offset += 4;
|
||||
for (var chIdx = 0; chIdx < totalChannels; chIdx++)
|
||||
{
|
||||
var signed = getNextSample(chIdx);
|
||||
var unsigned = (ushort)(((signed - 0x8000 & 0x00FF) << 8) | ((signed - 0x8000 >> 8) & 0x00FF));
|
||||
var bytes = BitConverter.GetBytes(unsigned).Reverse().ToArray();
|
||||
Buffer.BlockCopy(bytes, 0, _dataBytes, offset, 2);
|
||||
offset += 2;
|
||||
}
|
||||
currentSample++;
|
||||
}
|
||||
}
|
||||
public PCMDataPacket() : base(Enums.DataFileDataTypes.PCMDataFormat1, true)
|
||||
{
|
||||
}
|
||||
public PCMDataPacket(byte[] bytes) : base (bytes)
|
||||
{
|
||||
|
||||
}
|
||||
public PCMDataPacket(bool _) : base(UDP_DATA)
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly byte[] UDP_DATA = { 0x25, 0xeb, 0x03, 0x00, 0x98, 0x05, 0x00, 0x00, 0x74, 0x05, 0x00, 0x00,
|
||||
0x06, 0x65, 0xc4, 0x09, 0x21, 0xe5, 0x5d, 0xed, 0x07, 0x00, 0x83, 0x37, 0x42, 0x89, 0x77, 0x2a,
|
||||
0x4c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, 0x10, 0x00, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x04, 0x80, 0xe3, 0x7f, 0xcf, 0x7f, 0xe1, 0x7f, 0xee, 0x7f, 0xea, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x07, 0x80, 0xe7, 0x7f, 0xcf, 0x7f, 0xe7, 0x7f, 0xf3, 0x7f, 0xee, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x04, 0x80, 0xe5, 0x7f, 0xcd, 0x7f, 0xe2, 0x7f, 0xf1, 0x7f, 0xed, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x05, 0x80, 0xe9, 0x7f, 0xcd, 0x7f, 0xe2, 0x7f, 0xf1, 0x7f, 0xeb, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x07, 0x80, 0xe9, 0x7f, 0xcd, 0x7f, 0xec, 0x7f, 0xf3, 0x7f, 0xf1, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x06, 0x80, 0xe3, 0x7f, 0xce, 0x7f, 0xe6, 0x7f, 0xee, 0x7f, 0xee, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x02, 0x80, 0xe1, 0x7f, 0xc9, 0x7f, 0xdc, 0x7f, 0xec, 0x7f, 0xea, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x04, 0x80, 0xe7, 0x7f, 0xc5, 0x7f, 0xe7, 0x7f, 0xef, 0x7f, 0xed, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x05, 0x80, 0xed, 0x7f, 0xca, 0x7f, 0xe3, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x04, 0x80, 0xe5, 0x7f, 0xca, 0x7f, 0xe2, 0x7f, 0xec, 0x7f, 0xec, 0x7f, 0x6b, 0xfe, 0x40, 0x28,
|
||||
0x04, 0x80, 0xe6, 0x7f, 0xcc, 0x7f, 0xe5, 0x7f, 0xec, 0x7f, 0xed, 0x7f, 0x6b, 0xfe, 0x40, 0x28 };/*,
|
||||
0x06, 80, e7, 7f, cc, 7f, e6, 7f, f0, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e7, 7f, d2, 7f, e4, 7f, f0, 7f, f2, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, eb, 7f, cc, 7f, e7, 7f, e9, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e7, 7f, d1, 7f, ed, 7f, ef, 7f, f1, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, ec, 7f, d7, 7f, e9, 7f, f2, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, ed, 7f, cf, 7f, e7, 7f, f1, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e7, 7f, d0, 7f, e6, 7f, f0, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e7, 7f, cf, 7f, e2, 7f, f0, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e6, 7f, c8, 7f, dc, 7f, ef, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, ec, 7f, cc, 7f, e4, 7f, f2, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, eb, 7f, c9, 7f, e8, 7f, f1, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, e9, 7f, c6, 7f, e6, 7f, ed, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e1, 7f, c9, 7f, e2, 7f, e9, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0xff, 7f, e2, 7f, c8, 7f, de, 7f, ea, 7f, eb, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e6, 7f, ca, 7f, df, 7f, f0, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x00, 80, e7, 7f, cb, 7f, e2, 7f, f0, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e7, 7f, d1, 7f, df, 7f, ef, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, eb, 7f, d0, 7f, e5, 7f, ed, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, e9, 7f, d3, 7f, ee, 7f, ef, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, eb, 7f, ca, 7f, e1, 7f, ef, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, e9, 7f, d0, 7f, e6, 7f, f2, 7f, f3, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, e3, 7f, d2, 7f, e6, 7f, f2, 7f, f1, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, ed, 7f, cf, 7f, e9, 7f, ed, 7f, f5, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, ed, 7f, d0, 7f, e6, 7f, ed, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, e8, 7f, cf, 7f, ec, 7f, ed, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, ea, 7f, cc, 7f, e8, 7f, ec, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x00, 80, eb, 7f, c9, 7f, e5, 7f, ef, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, de, 7f, ce, 7f, e7, 7f, eb, 7f, f1, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, e1, 7f, c6, 7f, e0, 7f, f0, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x01, 80, df, 7f, c7, 7f, df, 7f, e9, 7f, e9, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, e4, 7f, cd, 7f, e0, 7f, ea, 7f, eb, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, ea, 7f, ca, 7f, de, 7f, ed, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e7, 7f, c9, 7f, de, 7f, ef, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, e5, 7f, c9, 7f, df, 7f, ed, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e3, 7f, d0, 7f, e2, 7f, ed, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, e3, 7f, ce, 7f, e0, 7f, ed, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, f0, 7f, cc, 7f, e3, 7f, ee, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, ed, 7f, d0, 7f, e4, 7f, f3, 7f, ea, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, eb, 7f, c8, 7f, e6, 7f, ec, 7f, ea, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e9, 7f, ce, 7f, e3, 7f, ee, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x09, 80, e9, 7f, d1, 7f, e5, 7f, ea, 7f, eb, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, eb, 7f, ce, 7f, e6, 7f, f0, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, ea, 7f, ce, 7f, e4, 7f, eb, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x08, 80, ed, 7f, ce, 7f, e8, 7f, ef, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x08, 80, ee, 7f, ce, 7f, e7, 7f, ef, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, e3, 7f, cb, 7f, e0, 7f, ed, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, e4, 7f, d0, 7f, e0, 7f, ed, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x00, 80, e7, 7f, cb, 7f, e5, 7f, e9, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, df, 7f, cb, 7f, e3, 7f, ed, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0xff, 7f, e3, 7f, cf, 7f, ea, 7f, f0, 7f, eb, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, e3, 7f, cc, 7f, e2, 7f, ef, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e5, 7f, d3, 7f, e4, 7f, f1, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, e9, 7f, ce, 7f, e6, 7f, f2, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, ed, 7f, cf, 7f, e6, 7f, ec, 7f, f1, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, ef, 7f, d5, 7f, e6, 7f, ec, 7f, f1, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, e7, 7f, d1, 7f, e8, 7f, f1, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, e7, 7f, cc, 7f, e1, 7f, ed, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, e6, 7f, cb, 7f, e7, 7f, f1, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e5, 7f, ce, 7f, e9, 7f, ed, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e3, 7f, cd, 7f, e6, 7f, ec, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x00, 80, ea, 7f, cd, 7f, e8, 7f, ed, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e7, 7f, c8, 7f, e9, 7f, f3, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e5, 7f, cb, 7f, e3, 7f, f1, 7f, f0, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, e3, 7f, ce, 7f, df, 7f, f0, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e7, 7f, ca, 7f, e0, 7f, ef, 7f, ee, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, e7, 7f, d3, 7f, e5, 7f, f0, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x01, 80, e9, 7f, c9, 7f, e2, 7f, ef, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, df, 7f, c8, 7f, e1, 7f, ed, 7f, eb, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e7, 7f, ca, 7f, e4, 7f, ee, 7f, ea, 7f, 6b, fe, 40, 28,
|
||||
0x04, 80, e7, 7f, d1, 7f, e6, 7f, f0, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x02, 80, df, 7f, cc, 7f, e6, 7f, eb, 7f, ef, 7f, 6b, fe, 40, 28,
|
||||
0x05, 80, e7, 7f, d1, 7f, e4, 7f, ee, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, e9, 7f, cf, 7f, e9, 7f, ec, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x03, 80, e7, 7f, cb, 7f, e7, 7f, ed, 7f, ed, 7f, 6b, fe, 40, 28,
|
||||
0x06, 80, e3, 7f, cf, 7f, e4, 7f, f1, 7f, ec, 7f, 6b, fe, 40, 28,
|
||||
0x07, 80, e8, 7f, cc, 7f, e9, 7f, f2, 7f, ed, 7f
|
||||
}*/
|
||||
//public AnalogDataFormat1Packet(byte[] bytes) : base(bytes)
|
||||
//{
|
||||
// var offset = IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH;
|
||||
|
||||
// if (PacketHeader.SecondaryHeaderPresent)
|
||||
// {
|
||||
// var secondaryHeaderBytes = new byte[SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH];
|
||||
// Buffer.BlockCopy(bytes, offset,
|
||||
// secondaryHeaderBytes, 0, SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH);
|
||||
// var secondaryHeader = new SecondaryTimeFormatHeader(secondaryHeaderBytes);
|
||||
// LocalTimeOfFirstSample = secondaryHeader.LocalTime;
|
||||
// offset += SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH;
|
||||
// }
|
||||
|
||||
// ChannelSpecificDataWord = BitConverter.ToUInt32(bytes, offset);
|
||||
// offset += sizeof(uint);
|
||||
// var numChannels = TotChan;
|
||||
// var sizeOf1SampleForAllChannels = sizeof(ushort) * numChannels;
|
||||
// //There's a CSDW in the data section, then all ADC data. CSDW is uint, 4 bytes
|
||||
// var numSamples = (PacketHeader.DataLength - 4) / sizeOf1SampleForAllChannels;
|
||||
|
||||
// Samples = new SampleData[numSamples];
|
||||
// for (var i = 0; i < Samples.Length; i++)
|
||||
// {
|
||||
// var channels = GetChannels(bytes, offset, numChannels);
|
||||
// Samples[i] = new SampleData(channels);
|
||||
// offset += sizeOf1SampleForAllChannels;
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
367
Common/DTS.Common.Serialization/IRIGCH10/Packets/PCMPacket.cs
Normal file
367
Common/DTS.Common.Serialization/IRIGCH10/Packets/PCMPacket.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// implements the PCM/Pulse Control Modulation packet for CH 10
|
||||
/// </summary>
|
||||
public class PCMPacket : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
private byte [] ChannelSpecificData;
|
||||
|
||||
public PCMPacket()
|
||||
: base(DataFileDataTypes.PCMDataFormat1)
|
||||
{
|
||||
ChannelSpecificData = new byte[4];
|
||||
_dataBytes = new byte[ChannelSpecificData.Length];
|
||||
}
|
||||
/// <summary>
|
||||
/// Intra-Packet Header (IPH). (Bit 30) indicates if Intra-Packet Headers (IntraPacket Time Stamp and Intra-Packet Data Header) are inserted before each
|
||||
/// minor frame. Intra-Packet Headers are only optional because of the mode
|
||||
/// selection. This determines whether Intra-Packet Headers are included or
|
||||
/// omitted.
|
||||
/// false = Intra-Packet Headers are omitted for Throughput Mode.
|
||||
/// true = Intra-Packet Headers are required for Packed Data and Unpacked Data
|
||||
/// modes.
|
||||
/// </summary>
|
||||
public bool IntraPacketHeader
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[30];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[30] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Major Frame Indicator (MA). (Bit 29) indicates if the first word in the packet is
|
||||
/// the beginning of a major frame. Not valid for Throughput Mode.
|
||||
/// false = First word is not the beginning of a major frame.
|
||||
/// true = First word is the beginning of a major frame.
|
||||
/// </summary>
|
||||
public bool MajorFrameIndicator
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[29];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[29] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Minor Frame Indicator (MI). (Bit 28) indicates if the first word in the packet is
|
||||
/// the beginning of a minor frame. Not valid for Throughput Mode.
|
||||
/// false = First word is not the beginning of a minor frame.
|
||||
/// true = First word is the beginning of a minor frame.
|
||||
/// </summary>
|
||||
public bool MinorFrameIndicator
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[28];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[28] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Indicate Minor Frame Status
|
||||
/// true = Minor Frame Lock
|
||||
/// false = Minor Frame Check (after losing lock)
|
||||
/// </summary>
|
||||
public bool MinorFrameLockStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[27] && bitArray[26];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[27] = true;
|
||||
bitArray[26] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// true - Major Frame Lock
|
||||
/// false - Major Frame Check (after losing Lock)
|
||||
/// </summary>
|
||||
public bool MajorFrameStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[25] && bitArray[24];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
|
||||
bitArray[25] = true;
|
||||
bitArray[24] = value;
|
||||
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// true = 32Bit Alignment Mode
|
||||
/// false = 16Bit Alignment Mode
|
||||
/// </summary>
|
||||
public bool AlignmentMode
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[21];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
|
||||
bitArray[21] = value;
|
||||
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Throughput DataMode enabled or not
|
||||
/// </summary>
|
||||
public bool ThroughputDataMode
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[20];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[20] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool PackedData
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[19];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[19] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool UnpackedData
|
||||
{
|
||||
get
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
return bitArray[18];
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
bitArray[18] = value;
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// contains an 18-bit binary value
|
||||
/// representing the Word offset into the major frame for the first data word in the
|
||||
/// packet. Not valid for Packed or Throughput Mode.
|
||||
/// </summary>
|
||||
public byte[] SyncOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
var output = new byte[2];
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
var bitArray2 = new BitArray(output);
|
||||
for (int i = 0; i <= 17; i++)
|
||||
{
|
||||
bitArray2[i] = bitArray[i];
|
||||
}
|
||||
bitArray2.CopyTo(output, 0);
|
||||
|
||||
return output;
|
||||
}
|
||||
set
|
||||
{
|
||||
var bitArray = new BitArray(ChannelSpecificData);
|
||||
var bitArray2 = new BitArray(value);
|
||||
|
||||
for (int i = 0; i <= 17; i++)
|
||||
{
|
||||
bitArray[i] = bitArray2[i];
|
||||
}
|
||||
bitArray.CopyTo(ChannelSpecificData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly byte[] FrameSync = new byte[] { 0x90, 0xEB };
|
||||
|
||||
//private readonly byte[] FrameSync = new byte[] { 0x90, 0xEB, 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x90, 0xEB };
|
||||
|
||||
/// <summary>
|
||||
/// adds data in unpacked 16 bit alignment into the data buffer, resizing as needed
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void AddUnpacked16BitData(ushort[] data, long timeStamp, bool bFirst)
|
||||
{
|
||||
var intraPacketBytes = GetIntraPacketTimeStampBytes(timeStamp);
|
||||
var intraHeaderBytes = GetIntraPacketHeader();
|
||||
var insertSpot = 0;
|
||||
|
||||
//for some reason the spec seems to call out a different first order than all subsequent data sequences.
|
||||
//the first sequence has a timestamp at the front, while all subsequent do not, but have a trailer timestamp.
|
||||
if (bFirst)
|
||||
{
|
||||
var newBuffer = new byte[_dataBytes.Length + data.Length * 2 + FrameSync.Length + intraPacketBytes.Length + intraHeaderBytes.Length];
|
||||
Buffer.BlockCopy(_dataBytes, 0, newBuffer, insertSpot, _dataBytes.Length);
|
||||
insertSpot += _dataBytes.Length;
|
||||
Buffer.BlockCopy(intraPacketBytes, 0, newBuffer, insertSpot, intraPacketBytes.Length);
|
||||
insertSpot += intraPacketBytes.Length;
|
||||
Buffer.BlockCopy(intraHeaderBytes, 0, newBuffer, insertSpot, intraHeaderBytes.Length);
|
||||
insertSpot += intraHeaderBytes.Length;
|
||||
Buffer.BlockCopy(FrameSync, 0, newBuffer, insertSpot, FrameSync.Length);
|
||||
insertSpot += FrameSync.Length;
|
||||
Buffer.BlockCopy(data, 0, newBuffer, insertSpot, data.Length * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newBuffer = new byte[_dataBytes.Length + data.Length * 2 + intraPacketBytes.Length + intraHeaderBytes.Length];
|
||||
Buffer.BlockCopy(_dataBytes, 0, newBuffer, insertSpot, _dataBytes.Length);
|
||||
insertSpot += _dataBytes.Length;
|
||||
Buffer.BlockCopy(data, 0, newBuffer, insertSpot, data.Length * 2);
|
||||
insertSpot += data.Length * 2;
|
||||
Buffer.BlockCopy(intraPacketBytes, 0, newBuffer, insertSpot, intraPacketBytes.Length);
|
||||
insertSpot += intraPacketBytes.Length;
|
||||
Buffer.BlockCopy(intraHeaderBytes, 0, newBuffer, insertSpot, intraHeaderBytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// adds data in unpacked 16 bit alignment into the data buffer, resizing as needed
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public void AddPacked16BitData(ushort[] data, long timeStamp, bool bFirst)
|
||||
{
|
||||
var intraPacketBytes = GetIntraPacketTimeStampBytes(timeStamp);
|
||||
var intraHeaderBytes = GetIntraPacketHeader();
|
||||
var insertSpot = 0;
|
||||
|
||||
//for some reason the spec seems to call out a different first order than all subsequent data sequences.
|
||||
//the first sequence has a timestamp at the front, while all subsequent do not, but have a trailer timestamp.
|
||||
if (bFirst)
|
||||
{
|
||||
var newBuffer = new byte[_dataBytes.Length + data.Length * 2 + FrameSync.Length + intraPacketBytes.Length + intraHeaderBytes.Length];
|
||||
Buffer.BlockCopy(_dataBytes, 0, newBuffer, insertSpot, _dataBytes.Length);
|
||||
insertSpot += _dataBytes.Length;
|
||||
Buffer.BlockCopy(intraPacketBytes, 0, newBuffer, insertSpot, intraPacketBytes.Length);
|
||||
insertSpot += intraPacketBytes.Length;
|
||||
Buffer.BlockCopy(intraHeaderBytes, 0, newBuffer, insertSpot, intraHeaderBytes.Length);
|
||||
insertSpot += intraHeaderBytes.Length;
|
||||
Buffer.BlockCopy(FrameSync, 0, newBuffer, insertSpot, FrameSync.Length);
|
||||
insertSpot += FrameSync.Length;
|
||||
Buffer.BlockCopy(data, 0, newBuffer, insertSpot, data.Length * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newBuffer = new byte[_dataBytes.Length + data.Length * 2 + intraPacketBytes.Length + intraHeaderBytes.Length];
|
||||
Buffer.BlockCopy(_dataBytes, 0, newBuffer, insertSpot, _dataBytes.Length);
|
||||
insertSpot += _dataBytes.Length;
|
||||
Buffer.BlockCopy(data, 0, newBuffer, insertSpot, data.Length * 2);
|
||||
insertSpot += data.Length * 2;
|
||||
Buffer.BlockCopy(intraPacketBytes, 0, newBuffer, insertSpot, intraPacketBytes.Length);
|
||||
insertSpot += intraPacketBytes.Length;
|
||||
Buffer.BlockCopy(intraHeaderBytes, 0, newBuffer, insertSpot, intraHeaderBytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private byte [] GetIntraPacketHeader()
|
||||
{
|
||||
var array = new BitArray(new byte[2]);
|
||||
//bits 15-14 minor frame status
|
||||
//00,01 reserved
|
||||
//10 minor frame check
|
||||
//11 minor frame lock
|
||||
|
||||
//bits 13-12 major frame status
|
||||
// 00 - major frame not locked
|
||||
// 01 - reserved
|
||||
// 10 major frame check
|
||||
// 11 major frame lock
|
||||
array.Set(15, true);
|
||||
array.Set(14, true);
|
||||
|
||||
array.Set(12, true);
|
||||
array.Set(13, true);
|
||||
|
||||
var output = new byte[2];
|
||||
|
||||
array.CopyTo(output, 0);
|
||||
return output;
|
||||
}
|
||||
private byte [] GetIntraPacketTimeStampBytes(long timeStamp)
|
||||
{
|
||||
var output = new byte[8];
|
||||
var stamp = IRIGCH10.PacketHeader.GetRTCBytes(timeStamp);
|
||||
Buffer.BlockCopy(output, 0, stamp, 0, stamp.Length);
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// adds data in throughput mode to the packet, resizing the data buffer in the process
|
||||
/// [throughput mode as opposed to a packed/or aligned mode]
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
//public void AddThroughputData(ushort[] data)
|
||||
//{
|
||||
// var newBuffer = new byte[_dataBytes.Length + data.Length * 2 + FrameSync.Length];
|
||||
// Buffer.BlockCopy(_dataBytes, 0, newBuffer, 0, _dataBytes.Length);
|
||||
// Buffer.BlockCopy(FrameSync, 0, newBuffer, _dataBytes.Length, FrameSync.Length);
|
||||
// Buffer.BlockCopy(data, 0, newBuffer, _dataBytes.Length + FrameSync.Length, data.Length * 2);
|
||||
// _dataBytes = newBuffer;
|
||||
//}
|
||||
public override void ComputeCheckSum()
|
||||
{
|
||||
RefreshChannelSpecificDataInDataBytes();
|
||||
base.ComputeCheckSum();
|
||||
}
|
||||
public override byte[] GetBytes()
|
||||
{
|
||||
RefreshChannelSpecificDataInDataBytes();
|
||||
return base.GetBytes();
|
||||
}
|
||||
|
||||
private void RefreshChannelSpecificDataInDataBytes()
|
||||
{
|
||||
//make sure ChannelSpecificData is set ...
|
||||
Buffer.BlockCopy(ChannelSpecificData, 0, _dataBytes, 0, ChannelSpecificData.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
355
Common/DTS.Common.Serialization/IRIGCH10/Packets/PacketHeader.cs
Normal file
355
Common/DTS.Common.Serialization/IRIGCH10/Packets/PacketHeader.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
using DTS.Serialization.IRIGCh10.Attributes;
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// implements the packet header portion of a packet
|
||||
/// </summary>
|
||||
public class PacketHeader : IPacketHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// used to indicate the start of a packet, is defined to 0x25, 0xEB by chapter10.pdf
|
||||
/// </summary>
|
||||
public ushort PacketSyncPattern { get; set; } = BitConverter.ToUInt16(new byte[] { 0x25, 0xEB }, 0);
|
||||
/// <summary>
|
||||
/// this is just a shortcut to avoid calculating the ushort value for 0x25,0xEB
|
||||
/// this is the ushort pattern expected for pattern sync
|
||||
/// </summary>
|
||||
public const ushort EXPECTED_SYNC_PATTERN = 60197;
|
||||
/// <summary>
|
||||
/// channel id, default to 0
|
||||
/// </summary>
|
||||
public ushort ChannelId { get; set; } = BitConverter.ToUInt16(new byte[] { 0x00, 0x00 }, 0);
|
||||
/// <summary>
|
||||
/// total length of the packet including header, data, and filler, and any checksums
|
||||
/// </summary>
|
||||
public uint PacketLength { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// length of the data portion of the packet, so sans filler and checksums
|
||||
/// </summary>
|
||||
public uint DataLength { get; set; } = 0;
|
||||
public byte DataVersion { get; set; } = 0;
|
||||
public byte SequenceNum { get; set; } = 0;
|
||||
[Browsable(false)]
|
||||
public byte PacketFlags { get; private set; } = 0;
|
||||
|
||||
public bool SecondaryHeaderPresent
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
return b.Get(7);
|
||||
}
|
||||
set
|
||||
{
|
||||
PacketFlags = SetBit(PacketFlags, 7, value);
|
||||
}
|
||||
}
|
||||
public bool IPTSTimeSource
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
return b.Get(6);
|
||||
}
|
||||
set
|
||||
{
|
||||
PacketFlags = SetBit(PacketFlags, 6, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool RTCSyncError
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
return b.Get(5);
|
||||
}
|
||||
set
|
||||
{
|
||||
PacketFlags = SetBit(PacketFlags, 5, value);
|
||||
}
|
||||
}
|
||||
public enum SecondaryHeaderTimeFormats
|
||||
{
|
||||
Chapter4 = 0,
|
||||
IEEE1588 = 1,
|
||||
ExtendedRelativeTimeCounter = 2,
|
||||
Reserved = 3
|
||||
}
|
||||
public SecondaryHeaderTimeFormats SecondaryHeaderTimeFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
return (SecondaryHeaderTimeFormats)Utils.Utils.BitArrayToInt32(b, 2, 3);
|
||||
}
|
||||
set
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
Utils.Utils.SetBits(b, (uint)value, 2, 3);
|
||||
var ret = new byte[1];
|
||||
b.CopyTo(ret, 0);
|
||||
PacketFlags = ret[0];
|
||||
}
|
||||
}
|
||||
public bool DataOverflowError
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
return b.Get(4);
|
||||
}
|
||||
set
|
||||
{
|
||||
PacketFlags = SetBit(PacketFlags, 4, value);
|
||||
}
|
||||
}
|
||||
|
||||
private byte SetBit(byte input, int bit, bool bVal)
|
||||
{
|
||||
var b = new BitArray(new[] { input });
|
||||
b.Set(bit, bVal);
|
||||
var ret = new byte[1];
|
||||
b.CopyTo(ret, 0);
|
||||
return ret[0];
|
||||
}
|
||||
public enum DataCheckSumPresences
|
||||
{
|
||||
NotPresent = 0,
|
||||
EightBit = 1,
|
||||
SixteenBit = 2,
|
||||
ThirtyTwoBit = 3
|
||||
}
|
||||
|
||||
public DataCheckSumPresences CheckSumPresence
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
return (DataCheckSumPresences)Utils.Utils.BitArrayToInt32(b, 0, 1);
|
||||
}
|
||||
set
|
||||
{
|
||||
var b = new BitArray(new[] { PacketFlags });
|
||||
Utils.Utils.SetBits(b, (uint)value, 0, 1);
|
||||
var ret = new byte[1];
|
||||
b.CopyTo(ret, 0);
|
||||
PacketFlags = ret[0];
|
||||
}
|
||||
}
|
||||
[Browsable(false)]
|
||||
public byte DataType { get; set; } = 0;
|
||||
|
||||
public DataFileDataTypes DataFileType
|
||||
{
|
||||
get
|
||||
{
|
||||
var all = Enum.GetValues(typeof(DataFileDataTypes)).Cast<DataFileDataTypes>().ToArray();
|
||||
foreach (var datatype in all)
|
||||
{
|
||||
var b = PacketHeaderValueAttribute.GetPacketHeaderValue(datatype);
|
||||
if (b == DataType) { return datatype; }
|
||||
}
|
||||
return DataFileDataTypes.ComputerGeneratedDataFormat0;
|
||||
}
|
||||
}
|
||||
|
||||
//10 MHz Relative Time Counter
|
||||
[Browsable(false)]
|
||||
public byte[] RTC { get; set; } = new byte[6];
|
||||
|
||||
public long RTCLong
|
||||
{
|
||||
get
|
||||
{
|
||||
var bytes = new byte[8];
|
||||
Buffer.BlockCopy(RTC, 0, bytes, 0, 6);
|
||||
return BitConverter.ToInt64(bytes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort CheckSum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// the RealtimeCounter in the packet header is a 6 byte field
|
||||
/// for convenience we'll use a long to interact with it
|
||||
/// even though we are only using 6 bytes
|
||||
/// </summary>
|
||||
/// <param name="rtc"></param>
|
||||
public void SetRTC(long rtc)
|
||||
{
|
||||
var bytes = GetRTCBytes(rtc);
|
||||
Buffer.BlockCopy(bytes, 0, RTC, 0, RTC.Length);
|
||||
}
|
||||
private const int RTC_SIZE_BYTES = 6;
|
||||
public static byte[] GetRTCBytes(long rtc)
|
||||
{
|
||||
var s = rtc.ToString("X");
|
||||
var buffer = new byte[RTC_SIZE_BYTES];
|
||||
for (int i = 0; i < RTC_SIZE_BYTES; i++)
|
||||
{
|
||||
string sVal = s.Substring(i * 2, 2);
|
||||
var val = Convert.ToInt16(sVal, 16);
|
||||
buffer[buffer.Length - 1 - i] = BitConverter.GetBytes(val)[0];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
public PacketHeader(DataFileDataTypes dataType)
|
||||
{
|
||||
DataType = PacketHeaderValueAttribute.GetPacketHeaderValue(dataType);
|
||||
}
|
||||
public PacketHeader(byte[] bytes)
|
||||
{
|
||||
if (null == bytes) { throw new NullReferenceException("no bytes received"); }
|
||||
if (PACKET_HEADER_LENGTH != bytes.Length) { throw new Exception($"Expected {PACKET_HEADER_LENGTH} bytes, received: {bytes.Length}"); }
|
||||
PacketSyncPattern = BitConverter.ToUInt16(bytes, 0);
|
||||
if (PacketSyncPattern != EXPECTED_SYNC_PATTERN)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("Expected sync pattern not found");
|
||||
}
|
||||
ChannelId = BitConverter.ToUInt16(bytes, 2);
|
||||
PacketLength = BitConverter.ToUInt32(bytes, 4);
|
||||
DataLength = BitConverter.ToUInt32(bytes, 8);
|
||||
DataVersion = bytes[12];
|
||||
SequenceNum = bytes[13];
|
||||
PacketFlags = bytes[14];
|
||||
DataType = bytes[15];
|
||||
RTC = new byte[6];
|
||||
Array.Copy(bytes, 16, RTC, 0, 6);
|
||||
CheckSum = BitConverter.ToUInt16(bytes, 22);
|
||||
var computedCRC = ComputeCheckSum();
|
||||
if (CheckSum != computedCRC)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("Checksum doesn't match");
|
||||
}
|
||||
}
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
using (var sw = new MemoryStream())
|
||||
{
|
||||
using (var bw = new BinaryWriter(sw))
|
||||
{
|
||||
bw.Write(PacketSyncPattern);
|
||||
bw.Write(ChannelId);
|
||||
bw.Write(PacketLength);
|
||||
bw.Write(DataLength);
|
||||
bw.Write(DataVersion);
|
||||
bw.Write(SequenceNum);
|
||||
bw.Write(PacketFlags);
|
||||
bw.Write(DataType);
|
||||
bw.Write(RTC);
|
||||
bw.Write(ComputeCheckSum());
|
||||
}
|
||||
return sw.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDataVersion(DataTypeVersion version)
|
||||
{
|
||||
DataVersion = PacketHeaderValueAttribute.GetPacketHeaderValue(version);
|
||||
}
|
||||
|
||||
public void SetPacketFlags(bool SecondaryHeaderPresent,
|
||||
bool SecondaryHeaderTime,
|
||||
bool RTCSyncerror,
|
||||
bool DataOverflow,
|
||||
SecondaryHeaderTimeFormat fmt,
|
||||
DataCheckSumType checkSum
|
||||
)
|
||||
{
|
||||
var bitArray = new BitArray(new[] { PacketFlags });
|
||||
|
||||
bitArray[7] = SecondaryHeaderPresent;
|
||||
bitArray[6] = SecondaryHeaderTime;
|
||||
bitArray[5] = RTCSyncerror;
|
||||
bitArray[4] = DataOverflow;
|
||||
|
||||
var byteValue = new BitArray(new[] { PacketHeaderValueAttribute.GetPacketHeaderValue(fmt) });
|
||||
bitArray[3] = byteValue[0];
|
||||
bitArray[2] = byteValue[1];
|
||||
|
||||
byteValue = new BitArray(new[] { PacketHeaderValueAttribute.GetPacketHeaderValue(checkSum) });
|
||||
bitArray[1] = byteValue[0];
|
||||
bitArray[0] = byteValue[1];
|
||||
var bytes = new byte[1];
|
||||
bitArray.CopyTo(bytes, 0);
|
||||
PacketFlags = bytes[0];
|
||||
}
|
||||
|
||||
public ushort ComputeCheckSum()
|
||||
{
|
||||
byte[] bytesToCompute;
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write(PacketSyncPattern);
|
||||
bw.Write(ChannelId);
|
||||
bw.Write(PacketLength);
|
||||
bw.Write(DataLength);
|
||||
bw.Write(DataVersion);
|
||||
bw.Write(SequenceNum);
|
||||
bw.Write(PacketFlags);
|
||||
bw.Write(DataType);
|
||||
bw.Write(RTC);
|
||||
}
|
||||
bytesToCompute = ms.ToArray();
|
||||
}
|
||||
return Utils.Utils.GetCheckSum16(bytesToCompute);
|
||||
}
|
||||
public const int PACKET_HEADER_LENGTH = 24;
|
||||
}
|
||||
|
||||
public interface IPacketHeader
|
||||
{
|
||||
ushort PacketSyncPattern { get; set; }
|
||||
ushort ChannelId { get; set; }
|
||||
uint PacketLength { get; set; }
|
||||
uint DataLength { get; set; }
|
||||
byte DataVersion { get; set; }
|
||||
byte SequenceNum { get; set; }
|
||||
[Browsable(false)]
|
||||
byte PacketFlags { get; }
|
||||
|
||||
bool SecondaryHeaderPresent { get; set; }
|
||||
bool IPTSTimeSource { get; set; }
|
||||
|
||||
bool RTCSyncError { get; set; }
|
||||
|
||||
PacketHeader.SecondaryHeaderTimeFormats SecondaryHeaderTimeFormat { get; set; }
|
||||
|
||||
bool DataOverflowError { get; set; }
|
||||
|
||||
PacketHeader.DataCheckSumPresences CheckSumPresence { get; set; }
|
||||
|
||||
|
||||
[Browsable(false)]
|
||||
byte DataType { get; set; }
|
||||
DataFileDataTypes DataFileType { get; }
|
||||
[Browsable(false)]
|
||||
byte[] RTC { get; set; }
|
||||
long RTCLong { get; }
|
||||
ushort CheckSum { get; set; }
|
||||
void SetRTC(long rtc);
|
||||
|
||||
void SetDataVersion(DataTypeVersion version);
|
||||
|
||||
void SetPacketFlags(bool SecondaryHeaderPresent,
|
||||
bool SecondaryHeaderTime,
|
||||
bool RTCSyncerror,
|
||||
bool DataOverflow,
|
||||
SecondaryHeaderTimeFormat fmt,
|
||||
DataCheckSumType checkSum
|
||||
);
|
||||
|
||||
byte[] GetBytes();
|
||||
ushort ComputeCheckSum();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// this packet just points to specific time data packets as part of an indexing system
|
||||
/// </summary>
|
||||
public class RecorderIndexPacket : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
//not confident on breaking out the individual properties of this dataword yet
|
||||
//(root index, file size, presence of data header, etc, so just use a
|
||||
//prefilled one for now ...
|
||||
private readonly byte [] ChannelSpecificDataWord = new byte[] {0x8D, 0x02, 0x00, 0xE0};
|
||||
private readonly byte [] RootPacketAddress = new byte [8];
|
||||
public void SetRootPacketAddress(long address)
|
||||
{
|
||||
var bits = BitConverter.GetBytes(address);
|
||||
Buffer.BlockCopy(bits, 0, RootPacketAddress, 0, bits.Length);
|
||||
}
|
||||
|
||||
private List<RecordingIndex> _indices = new List<RecordingIndex>();
|
||||
|
||||
public DateTime GetDateTime()
|
||||
{
|
||||
return _indices.First().GetDateTime();
|
||||
}
|
||||
public int NumberOfEntries => _indices.Count;
|
||||
public void AddRecordingIndex(RecordingIndex index)
|
||||
{
|
||||
_indices.Add(index);
|
||||
}
|
||||
|
||||
public RecorderIndexPacket()
|
||||
: base(DataFileDataTypes.ComputerGeneratedDataFormat3)
|
||||
{
|
||||
_CheckSum = new byte[4];
|
||||
}
|
||||
public override byte[] GetBytes()
|
||||
{
|
||||
//we just need to make sure data bytes is populated then we can let the base class do it's thing
|
||||
_dataBytes = new byte[_indices.Count * RecordingIndex.SIZE + RootPacketAddress.Length +
|
||||
ChannelSpecificDataWord.Length];
|
||||
Buffer.BlockCopy(ChannelSpecificDataWord, 0, _dataBytes, 0, ChannelSpecificDataWord.Length);
|
||||
Buffer.BlockCopy(RootPacketAddress, 0, _dataBytes, ChannelSpecificDataWord.Length, RootPacketAddress.Length);
|
||||
var offset = ChannelSpecificDataWord.Length + RootPacketAddress.Length;
|
||||
foreach (var index in _indices)
|
||||
{
|
||||
var bytes = index.GetBytes();
|
||||
Buffer.BlockCopy(bytes, 0, _dataBytes, offset, bytes.Length);
|
||||
offset += bytes.Length;
|
||||
}
|
||||
return base.GetBytes();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// here's a structure for the recording indicies in the recording index packet
|
||||
/// </summary>
|
||||
public class RecordingIndex
|
||||
{
|
||||
public const int SIZE = 8+8+2+1+1+8;
|
||||
private byte [] RTC = new byte [8];
|
||||
private byte [] TimeDate = new byte[8];
|
||||
private DateTime _dt;
|
||||
private readonly ushort ChannelId = ushort.MaxValue;
|
||||
private readonly byte DataType = 0x11;
|
||||
private readonly byte Reserved = 0x00;
|
||||
private byte[] DataPacketOffset = new byte[8];
|
||||
|
||||
private void SetRTC(long rtc)
|
||||
{
|
||||
var bits = BitConverter.GetBytes(rtc);
|
||||
Buffer.BlockCopy(bits, 0, RTC, 0, bits.Length);
|
||||
}
|
||||
|
||||
private void SetDataPacketOffset(long offset)
|
||||
{
|
||||
var bits = BitConverter.GetBytes(offset);
|
||||
Buffer.BlockCopy(bits, 0, DataPacketOffset, 0, bits.Length);
|
||||
}
|
||||
public DateTime GetDateTime(){ return _dt; }
|
||||
private void SetDateTime(DateTime dt)
|
||||
{
|
||||
_dt = dt;
|
||||
TimeDate[0] = Utils.Utils.GetBCDBytes(dt.Millisecond/10)[0];
|
||||
TimeDate[1] = Utils.Utils.GetBCDBytes(dt.Second)[0];
|
||||
TimeDate[2] = Utils.Utils.GetBCDBytes(dt.Minute)[0];
|
||||
TimeDate[3] = Utils.Utils.GetBCDBytes(dt.Hour)[0];
|
||||
TimeDate[4] = Utils.Utils.GetBCDBytes(dt.Day)[0];
|
||||
TimeDate[5] = Utils.Utils.GetBCDBytes(dt.Month)[0];
|
||||
TimeDate[6] = Utils.Utils.GetBCDBytes(dt.Year)[0];
|
||||
TimeDate[7] = Utils.Utils.GetBCDBytes(dt.Year)[1];
|
||||
}
|
||||
|
||||
public RecordingIndex(long rtc, long offset, DateTime dt)
|
||||
{
|
||||
SetDataPacketOffset(offset);
|
||||
SetRTC(rtc);
|
||||
SetDateTime(dt);
|
||||
}
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
var data = new byte[8 + 8 + 2 + 1 + 1 + 8];
|
||||
var curOffset = 0;
|
||||
Buffer.BlockCopy(RTC, 0, data, 0, RTC.Length);
|
||||
curOffset += RTC.Length;
|
||||
Buffer.BlockCopy(TimeDate, 0, data, curOffset, TimeDate.Length);
|
||||
curOffset += TimeDate.Length;
|
||||
var bytes = BitConverter.GetBytes(ChannelId);
|
||||
Buffer.BlockCopy(bytes, 0, data, curOffset, 2);
|
||||
curOffset += 2;
|
||||
data[curOffset++] = DataType;
|
||||
data[curOffset++] = Reserved;
|
||||
Buffer.BlockCopy(DataPacketOffset, 0, data, curOffset, DataPacketOffset.Length);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// this class handles the root recorder index packet,
|
||||
/// it points to recorder index packets, it's the last packet in a ch10 file
|
||||
/// (but can be broken over multiple packets)
|
||||
/// </summary>
|
||||
public class RootRecorderIndexPacket : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
//not confident on breaking out the individual properties of this dataword yet
|
||||
//(root index, file size, presence of data header, etc, so just use a
|
||||
//prefilled one for now ...
|
||||
private readonly byte [] ChannelSpecificDataWord = new byte[] {0x8D, 0x00, 0x00, 0x60};
|
||||
private long _rootPacketAddressLong;
|
||||
private readonly byte [] RootPacketAddress = new byte [8];
|
||||
public void SetRootPacketAddress(long address)
|
||||
{
|
||||
_rootPacketAddressLong = address;
|
||||
var bits = BitConverter.GetBytes(address);
|
||||
Buffer.BlockCopy(bits, 0, RootPacketAddress, 0, bits.Length);
|
||||
}
|
||||
|
||||
private List<RecordingIndexIndex> _indices = new List<RecordingIndexIndex>();
|
||||
|
||||
public void AddRecordingIndex(RecordingIndexIndex index)
|
||||
{
|
||||
_indices.Add(index);
|
||||
}
|
||||
private readonly DateTime _dt;
|
||||
public RootRecorderIndexPacket(DateTime dt)
|
||||
: base(DataFileDataTypes.ComputerGeneratedDataFormat3)
|
||||
{
|
||||
_dt = dt;
|
||||
_CheckSum = new byte[4];
|
||||
}
|
||||
public override byte[] GetBytes()
|
||||
{
|
||||
//the last index should apparently point back to this entry...
|
||||
_indices.Add(new RecordingIndexIndex(GetRTC(), _rootPacketAddressLong, _dt));
|
||||
//we just need to make sure data bytes is populated then we can let the base class do it's thing
|
||||
_dataBytes = new byte[_indices.Count * RecordingIndexIndex.SIZE + RootPacketAddress.Length +
|
||||
ChannelSpecificDataWord.Length];
|
||||
|
||||
Buffer.BlockCopy(ChannelSpecificDataWord, 0, _dataBytes, 0, ChannelSpecificDataWord.Length);
|
||||
Buffer.BlockCopy(RootPacketAddress, 0, _dataBytes, ChannelSpecificDataWord.Length, RootPacketAddress.Length);
|
||||
var offset = ChannelSpecificDataWord.Length + RootPacketAddress.Length;
|
||||
foreach (var index in _indices)
|
||||
{
|
||||
var bytes = index.GetBytes();
|
||||
Buffer.BlockCopy(bytes, 0, _dataBytes, offset, bytes.Length);
|
||||
offset += bytes.Length;
|
||||
}
|
||||
return base.GetBytes();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// here is a root recording index entry, it points at other recording indicies
|
||||
/// </summary>
|
||||
public class RecordingIndexIndex
|
||||
{
|
||||
public const int SIZE = 8+8+8;
|
||||
private byte [] RTC = new byte [8];
|
||||
private byte [] TimeDate = new byte[8];
|
||||
private byte[] DataPacketOffset = new byte[8];
|
||||
|
||||
private void SetRTC(long rtc)
|
||||
{
|
||||
var bits = BitConverter.GetBytes(rtc);
|
||||
Buffer.BlockCopy(bits, 0, RTC, 0, bits.Length);
|
||||
}
|
||||
|
||||
private void SetDataPacketOffset(long offset)
|
||||
{
|
||||
var bits = BitConverter.GetBytes(offset);
|
||||
Buffer.BlockCopy(bits, 0, DataPacketOffset, 0, bits.Length);
|
||||
}
|
||||
|
||||
private void SetDateTime(DateTime dt)
|
||||
{
|
||||
TimeDate[0] = Utils.Utils.GetBCDBytes(dt.Millisecond/10)[0];
|
||||
TimeDate[1] = Utils.Utils.GetBCDBytes(dt.Second)[0];
|
||||
TimeDate[2] = Utils.Utils.GetBCDBytes(dt.Minute)[0];
|
||||
TimeDate[3] = Utils.Utils.GetBCDBytes(dt.Hour)[0];
|
||||
TimeDate[4] = Utils.Utils.GetBCDBytes(dt.Day)[0];
|
||||
TimeDate[5] = Utils.Utils.GetBCDBytes(dt.Month)[0];
|
||||
TimeDate[6] = Utils.Utils.GetBCDBytes(dt.Year)[0];
|
||||
TimeDate[7] = Utils.Utils.GetBCDBytes(dt.Year)[1];
|
||||
}
|
||||
|
||||
public RecordingIndexIndex(long rtc, long offset, DateTime dt)
|
||||
{
|
||||
SetDataPacketOffset(offset);
|
||||
SetRTC(rtc);
|
||||
SetDateTime(dt);
|
||||
}
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
var data = new byte[SIZE];
|
||||
var curOffset = 0;
|
||||
Buffer.BlockCopy(RTC, 0, data, 0, RTC.Length);
|
||||
curOffset += RTC.Length;
|
||||
Buffer.BlockCopy(TimeDate, 0, data, curOffset, TimeDate.Length);
|
||||
curOffset += TimeDate.Length;
|
||||
Buffer.BlockCopy(DataPacketOffset, 0, data, curOffset, DataPacketOffset.Length);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// this class helps encapsulate a secondary time format header, but it's really
|
||||
/// only designed for one type of format currently
|
||||
/// </summary>
|
||||
public class SecondaryTimeFormatHeader : ISecondaryTimeFormatHeader
|
||||
{
|
||||
private byte[] _bytes = new byte[SECONDARY_TIME_HEADER_LENGTH];
|
||||
public int NanoSeconds { get; }
|
||||
public int Seconds { get; }
|
||||
public ushort Reserved { get; }
|
||||
public ushort CheckSum { get; }
|
||||
|
||||
public DateTime LocalTime
|
||||
{
|
||||
get => (new DateTime(1970, 1, 1)).AddSeconds(Seconds).AddTicks(NanoSeconds / 100).ToLocalTime();
|
||||
}
|
||||
public SecondaryTimeFormatHeader(byte[] input)
|
||||
{
|
||||
Array.Copy(input, 0, _bytes, 0, SECONDARY_TIME_HEADER_LENGTH);
|
||||
NanoSeconds = BitConverter.ToInt32(_bytes, 0);
|
||||
Seconds = BitConverter.ToInt32(_bytes, 4);
|
||||
Reserved = BitConverter.ToUInt16(_bytes, 8);
|
||||
CheckSum = BitConverter.ToUInt16(_bytes, 10);
|
||||
byte[] bytesToCompute;
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write(BitConverter.GetBytes(NanoSeconds));
|
||||
bw.Write(BitConverter.GetBytes(Seconds));
|
||||
bw.Write(Reserved);
|
||||
}
|
||||
bytesToCompute = ms.ToArray();
|
||||
}
|
||||
var computedChecksum = Utils.Utils.GetCheckSum8(bytesToCompute);
|
||||
if (computedChecksum != CheckSum)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("Secondary time header CRC does not match expectations");
|
||||
}
|
||||
}
|
||||
public static byte[] GetBytes(int nanoseconds, int seconds)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write(BitConverter.GetBytes(nanoseconds));
|
||||
bw.Write(BitConverter.GetBytes(seconds));
|
||||
bw.Write((ushort)0);
|
||||
|
||||
bw.Write(Utils.Utils.GetCheckSum8(ms.ToArray()));
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
public const int SECONDARY_TIME_HEADER_LENGTH = 12;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using DTS.Serialization.IRIGCH10.Packets;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// the TMATS packet in the ch10 file is
|
||||
/// a packet containing the setup file, a description of
|
||||
/// what channels, das, time formats, etc are used in the file
|
||||
/// </summary>
|
||||
public class TMATSPacket : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
/// <summary>
|
||||
/// whether the TMATS is in XML or ASCII format
|
||||
/// </summary>
|
||||
public bool XMLFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(BitConverter.GetBytes(ChannelSpecificDataWord));
|
||||
return b.Get(9);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// whether the setup record has changed or not
|
||||
/// </summary>
|
||||
public bool SetupRecordConfigurationChange
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(BitConverter.GetBytes(ChannelSpecificDataWord));
|
||||
return b.Get(8);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// the chapter 10 version of the TMATS file
|
||||
/// </summary>
|
||||
public RCCChapter10Versions Chapter10Version
|
||||
{
|
||||
get
|
||||
{
|
||||
var b = new BitArray(BitConverter.GetBytes(ChannelSpecificDataWord));
|
||||
var ver = Utils.Utils.BitArrayToInt32(b, 0, 7);
|
||||
switch (ver)
|
||||
{
|
||||
case 0x07: return RCCChapter10Versions.RCC_106_07;
|
||||
case 0x08: return RCCChapter10Versions.RCC_106_09;
|
||||
case 0x09: return RCCChapter10Versions.RCC_106_11;
|
||||
case 0x0A: return RCCChapter10Versions.RCC_106_13;
|
||||
case 0x0B: return RCCChapter10Versions.RCC_106_15;
|
||||
default: return RCCChapter10Versions.RESERVED;
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum RCCChapter10Versions
|
||||
{
|
||||
RESERVED,
|
||||
RCC_106_07,
|
||||
RCC_106_09,
|
||||
RCC_106_11,
|
||||
RCC_106_13,
|
||||
RCC_106_15
|
||||
}
|
||||
private const byte DataVersionNoSecondaryHeader = 0x01;
|
||||
private const byte DataVersionSecondaryHeader = 0x09;
|
||||
public TMATSPacket(int nanoseconds, int seconds, string tmatsDoc, bool secondaryHeaderPresent)
|
||||
: base(DataFileDataTypes.ComputerGeneratedDataFormat1, secondaryHeaderPresent)
|
||||
{
|
||||
var tmatsBytes = Encoding.ASCII.GetBytes(tmatsDoc);
|
||||
var dataLength = Convert.ToUInt32(4 + tmatsBytes.Length); ;
|
||||
var offset = CommonHeaderWork(0, secondaryHeaderPresent ? DataVersionSecondaryHeader : DataVersionNoSecondaryHeader,
|
||||
1, BASE_RTC, dataLength, nanoseconds, seconds);
|
||||
Buffer.BlockCopy(tmatsBytes, 0, _dataBytes, offset, tmatsBytes.Length);
|
||||
}
|
||||
|
||||
public TMATSPacket(byte[] bytes) : base(bytes)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// the TMATS document
|
||||
/// </summary>
|
||||
public string TMATSDocument
|
||||
{
|
||||
get
|
||||
{
|
||||
//the document is prepended by a ChannelSpecificDataWord and potentially a time header
|
||||
var prepend = 4; //CSDW
|
||||
if (PacketHeader.SecondaryHeaderPresent) { prepend += SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH; }
|
||||
var length = PacketHeader.DataLength - prepend;
|
||||
var bytes = new byte[length];
|
||||
Buffer.BlockCopy(_dataBytes, prepend, bytes, 0, bytes.Length);
|
||||
return Encoding.ASCII.GetString(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using DTS.Serialization.IRIGCh10.Attributes;
|
||||
using DTS.Serialization.IRIGCH10.Enums;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
/// <summary>
|
||||
/// implements a time data packet, allowing time data packets to be put into a ch 10 file
|
||||
/// </summary>
|
||||
public class TimeDataPacket : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
public TimeDataPacket()
|
||||
: base(DataFileDataTypes.TimeDataFormat1)
|
||||
{
|
||||
_dataBytes = new byte[12];
|
||||
|
||||
}
|
||||
private DateTime _dt;
|
||||
public DateTime GetDateTime()
|
||||
{
|
||||
return _dt;
|
||||
}
|
||||
public void SetTime(DateTime dt)
|
||||
{
|
||||
_dt = dt;
|
||||
_dataBytes[4] = Utils.Utils.GetBCDBytes(dt.Millisecond/10)[0];
|
||||
_dataBytes[5] = Utils.Utils.GetBCDBytes(dt.Second)[0];
|
||||
_dataBytes[6] = Utils.Utils.GetBCDBytes(dt.Minute)[0];
|
||||
_dataBytes[7] = Utils.Utils.GetBCDBytes(dt.Hour)[0];
|
||||
_dataBytes[8] = Utils.Utils.GetBCDBytes(dt.Day)[0];
|
||||
_dataBytes[9] = Utils.Utils.GetBCDBytes(dt.Month)[0];
|
||||
_dataBytes[10] = Utils.Utils.GetBCDBytes(dt.Year)[0];
|
||||
_dataBytes[11] = Utils.Utils.GetBCDBytes(dt.Year)[1];
|
||||
}
|
||||
|
||||
public void SetTimeSource(byte b)
|
||||
{
|
||||
var bitArray = new BitArray(new[] {_dataBytes[0]});
|
||||
var bitArray2 = new BitArray(new[] {b});
|
||||
bitArray[0] = bitArray2[0];
|
||||
bitArray[1] = bitArray2[1];
|
||||
bitArray[2] = bitArray2[2];
|
||||
bitArray[3] = bitArray2[3];
|
||||
|
||||
bitArray.CopyTo(_dataBytes, 0);
|
||||
}
|
||||
public void SetTimeSource(TimeSource src)
|
||||
{
|
||||
var bitArray = new BitArray(new[] {_dataBytes[0], _dataBytes[1], _dataBytes[2], _dataBytes[3]});
|
||||
var bytevalue = new BitArray(new[] {PacketHeaderValueAttribute.GetPacketHeaderValue(src)});
|
||||
//ref Chapter 10, 10-47
|
||||
|
||||
bitArray[0] = bytevalue[3];
|
||||
bitArray[1] = bytevalue[2];
|
||||
bitArray[2] = bytevalue[1];
|
||||
bitArray[3] = bytevalue[0];
|
||||
|
||||
bitArray.CopyTo(_dataBytes, 0);
|
||||
}
|
||||
|
||||
public void SetTimeFormat(TimeFormats fmt)
|
||||
{
|
||||
var bitArray = new BitArray(new[] {_dataBytes[0], _dataBytes[1], _dataBytes[2], _dataBytes[3]});
|
||||
var bytevalue = new BitArray(new[] {PacketHeaderValueAttribute.GetPacketHeaderValue(fmt)});
|
||||
//ref Chapter 10, 10-47
|
||||
bitArray[4] = bytevalue[3];
|
||||
bitArray[5] = bytevalue[2];
|
||||
bitArray[6] = bytevalue[1];
|
||||
bitArray[7] = bytevalue[0];
|
||||
|
||||
bitArray.CopyTo(_dataBytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// added an analog time format 1 packet for validating exports with EMC
|
||||
/// </summary>
|
||||
public class TimePacketFormat1 : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
public enum IRIGTimeSource
|
||||
{
|
||||
IRIG_TCG_freewheeling,
|
||||
IRIG_TCG_freewheeling_from_TIME,
|
||||
IRIG_TCG_freewheeling_from_RMM,
|
||||
IRIG_TCG_locked_to_external_IRIG,
|
||||
IRIG_TCG_locked_to_external_GPS,
|
||||
IRIG_TCG_locked_to_external_NetworkTimeProtocol,
|
||||
IRIG_TCG_locked_to_external_Precision_Time_Protocol,
|
||||
IRIG_TCG_locked_to_external_Embedded,
|
||||
RESERVED
|
||||
};
|
||||
//bits 15-12
|
||||
//this is currently not serialized to the CSDW as the EMC validation tool will return errors if the
|
||||
//field is filled in.
|
||||
//DTM 2023-10-27
|
||||
public IRIGTimeSource ITS
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public enum TimeFormats
|
||||
{
|
||||
IRIG_B = 0x00,
|
||||
IRIG_A = 0x01,
|
||||
IRIG_G = 0x02,
|
||||
RTC = 0x03,
|
||||
UTC = 0x04,
|
||||
NativeGPS = 0x05,
|
||||
RESERVED = 0x06,
|
||||
NONE = 0x0F
|
||||
}
|
||||
//bits 7-4
|
||||
public TimeFormats TimeFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
var ntf = Utils.Utils.BitArrayToInt32(bits, 4, 7);
|
||||
return (TimeFormats)ntf;
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 4, 7);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
//bits 3-0
|
||||
public enum TimeSources
|
||||
{
|
||||
Internal = 0x00,
|
||||
External = 0x01,
|
||||
InternalFromRMM = 0x02,
|
||||
Reserved = 0x03,
|
||||
None = 0x0F
|
||||
}
|
||||
public TimeSources TimeSource
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
var ntf = Utils.Utils.BitArrayToInt32(bits, 0, 3);
|
||||
return (TimeSources)ntf;
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 0, 3);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DateFormats
|
||||
{
|
||||
IRIGDayAvailable = 0x00,
|
||||
MonthAndYearAvailable = 0x01
|
||||
}
|
||||
//bit 9
|
||||
public DateFormats DateFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
var csdw = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(csdw);
|
||||
return bits[9] ? DateFormats.MonthAndYearAvailable : DateFormats.IRIGDayAvailable;
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
b.Set(9, value == DateFormats.MonthAndYearAvailable);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// the time represented by this packet
|
||||
/// </summary>
|
||||
public DateTime TimePacketTime { get; set; }
|
||||
//bit 8
|
||||
public bool IsLeapYear
|
||||
{
|
||||
get
|
||||
{
|
||||
var csdw = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(csdw);
|
||||
return bits[8];
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
b.Set(8, value);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public TimePacketFormat1(byte sequenceNumber, DateTime packetTime, long rtc, int nanoseconds, int seconds)
|
||||
: base(Enums.DataFileDataTypes.TimeDataFormat1, false)
|
||||
{
|
||||
SetDataVersion(Enums.DataTypeVersion.TG78);
|
||||
TimePacketTime = packetTime;
|
||||
IsLeapYear = DateTime.IsLeapYear(packetTime.Year);
|
||||
DateFormat = DateFormats.MonthAndYearAvailable;
|
||||
TimeSource = TimeSources.External;
|
||||
ITS = IRIGTimeSource.IRIG_TCG_locked_to_external_Embedded;
|
||||
var dataLength = Convert.ToUInt32(4 + sizeof(ushort) + 4);
|
||||
var offset = CommonHeaderWork(1, PacketHeader.DataVersion, sequenceNumber, rtc, dataLength, nanoseconds, seconds);
|
||||
|
||||
WriteMonthDayPayload(offset);
|
||||
}
|
||||
/// <summary>
|
||||
/// writes the month day payload portion of the timepacket
|
||||
/// </summary>
|
||||
/// <param name="offset"></param>
|
||||
private void WriteMonthDayPayload(int offset)
|
||||
{
|
||||
var Line1 = new BitArray(16);
|
||||
var digits = GetDigits(TimePacketTime.Second, 2);
|
||||
//tens of seconds
|
||||
var TSn = digits[0];
|
||||
Utils.Utils.SetBits(Line1, (uint)TSn, 12, 14);
|
||||
//units of seconds
|
||||
var Sn = digits[1];
|
||||
Utils.Utils.SetBits(Line1, (uint)Sn, 8, 11);
|
||||
digits = GetDigits(TimePacketTime.Millisecond, 3);
|
||||
//hundreds of ms
|
||||
var Hmn = digits[0];
|
||||
Utils.Utils.SetBits(Line1, (uint)Hmn, 4, 7);
|
||||
//tens of ms
|
||||
var Tmn = digits[1];
|
||||
Utils.Utils.SetBits(Line1, (uint)Tmn, 0, 3);
|
||||
|
||||
var temp = new byte[sizeof(ushort)];
|
||||
Line1.CopyTo(temp, 0);
|
||||
Buffer.BlockCopy(temp, 0, _dataBytes, offset, 2);
|
||||
offset += 2;
|
||||
|
||||
var Line2 = new BitArray(16);
|
||||
digits = GetDigits(TimePacketTime.Hour, 2);
|
||||
//tens of hours
|
||||
var THn = digits[0];
|
||||
Utils.Utils.SetBits(Line2, (uint)THn, 12, 13);
|
||||
//Units of hours
|
||||
var Hn = digits[1];
|
||||
Utils.Utils.SetBits(Line2, (uint)Hn, 8, 11);
|
||||
digits = GetDigits(TimePacketTime.Minute, 2);
|
||||
//tens of minutes
|
||||
var TMn = digits[0];
|
||||
Utils.Utils.SetBits(Line2, (uint)TMn, 4, 6);
|
||||
//units of minutes
|
||||
var Mn = digits[1];
|
||||
Utils.Utils.SetBits(Line2, (uint)Mn, 0, 3);
|
||||
temp = new byte[sizeof(ushort)];
|
||||
Line2.CopyTo(temp, 0);
|
||||
Buffer.BlockCopy(temp, 0, _dataBytes, offset, 2);
|
||||
offset += 2;
|
||||
|
||||
var Line3 = new BitArray(16);
|
||||
digits = GetDigits(TimePacketTime.Month, 2);
|
||||
//tens of months
|
||||
var Ton = digits[0];
|
||||
Utils.Utils.SetBits(Line3, (uint)Ton, 12, 13);
|
||||
//units of months
|
||||
var On = digits[1];
|
||||
Utils.Utils.SetBits(Line3, (uint)On, 8, 11);
|
||||
digits = GetDigits(TimePacketTime.Day, 2);
|
||||
//Tens of days
|
||||
var TDn = digits[0];
|
||||
Utils.Utils.SetBits(Line3, (uint)TDn, 4, 7);
|
||||
//units of days
|
||||
var Dn = digits[1];
|
||||
Utils.Utils.SetBits(Line3, (uint)Dn, 0, 3);
|
||||
temp = new byte[sizeof(ushort)];
|
||||
Line3.CopyTo(temp, 0);
|
||||
Buffer.BlockCopy(temp, 0, _dataBytes, offset, 2);
|
||||
offset += 2;
|
||||
|
||||
var line4 = new BitArray(16);
|
||||
digits = GetDigits(TimePacketTime.Year, 4);
|
||||
var OYn = digits[0];
|
||||
//thousands of years
|
||||
Utils.Utils.SetBits(line4, (uint)OYn, 12, 13);
|
||||
var HYn = digits[1];
|
||||
//hundreds of years
|
||||
Utils.Utils.SetBits(line4, (uint)HYn, 8, 11);
|
||||
var TYn = digits[2];
|
||||
//tens of years
|
||||
Utils.Utils.SetBits(line4, (uint)TYn, 4, 7);
|
||||
var Yn = digits[3];
|
||||
//units of years
|
||||
Utils.Utils.SetBits(line4, (uint)Yn, 0, 3);
|
||||
temp = new byte[sizeof(ushort)];
|
||||
line4.CopyTo(temp, 0);
|
||||
Buffer.BlockCopy(temp, 0, _dataBytes, offset, 2);
|
||||
offset += 2;
|
||||
}
|
||||
private int[] GetDigits(int number, int expectedDigits)
|
||||
{
|
||||
var list = new List<int>();
|
||||
var res = number;
|
||||
while (0 != res)
|
||||
{
|
||||
var digit = res % 10;
|
||||
res /= 10;
|
||||
list.Add(digit);
|
||||
}
|
||||
while (list.Count < expectedDigits)
|
||||
{
|
||||
list.Add(0);
|
||||
}
|
||||
list.Reverse();
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using DTS.Common.Utilities;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
public class TimePacketFormat2 : AbstractDataPacket, IDataPacket
|
||||
{
|
||||
public DateTime LocalTimeOfFirstSample
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public enum NetworkTimeFormats
|
||||
{
|
||||
NetworkTimeProtocolVersion3 = 0x00,
|
||||
IEEE1588_2002 = 0x01,
|
||||
IEEE1588_2008 = 0x02,
|
||||
RESERVED
|
||||
}
|
||||
//bits 7-4
|
||||
public NetworkTimeFormats NetworkTimeFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
var ntf = Utils.Utils.BitArrayToInt32(bits, 4, 7);
|
||||
switch (ntf)
|
||||
{
|
||||
case 0: return NetworkTimeFormats.NetworkTimeProtocolVersion3;
|
||||
case 1: return NetworkTimeFormats.IEEE1588_2002;
|
||||
case 2: return NetworkTimeFormats.IEEE1588_2008;
|
||||
default: return NetworkTimeFormats.RESERVED;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 4, 7);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum TimeStatuses
|
||||
{
|
||||
TimeNotValid,
|
||||
TimeValid,
|
||||
Reserved
|
||||
}
|
||||
//bits 3-0
|
||||
|
||||
public TimeStatuses TimeStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
var ts = Utils.Utils.BitArrayToInt32(bits, 0, 3);
|
||||
switch (ts)
|
||||
{
|
||||
case 0: return TimeStatuses.TimeNotValid;
|
||||
case 1: return TimeStatuses.TimeValid;
|
||||
default: return TimeStatuses.Reserved;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var b = new BitArray(channelSpecificDataWord);
|
||||
Utils.Utils.SetBits(b, (uint)value, 0, 3);
|
||||
var ret = new byte[sizeof(uint)];
|
||||
b.CopyTo(ret, 0);
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(ret, 0);
|
||||
}
|
||||
}
|
||||
public uint UnsignedSeconds
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public uint UnsignedNanoSeconds
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string PTPTime
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
private const byte DATA_VERSION_TIME_FORMAT2 = 0x08;
|
||||
public TimePacketFormat2(byte sequenceNumber, bool rtcSyncError, int nanoseconds, int seconds, long rtc,
|
||||
bool includeSecondaryHeader) : base(Enums.DataFileDataTypes.TimeDataFormat2, includeSecondaryHeader)
|
||||
{
|
||||
NetworkTimeFormat = NetworkTimeFormats.IEEE1588_2008;
|
||||
TimeStatus = rtcSyncError ? TimeStatuses.TimeNotValid : TimeStatuses.TimeValid;
|
||||
//CSDW+CRC+TimeData (32 bitsx2)
|
||||
var dataLength = Convert.ToUInt32(4 + sizeof(uint) * 2);
|
||||
var offset = CommonHeaderWork(1, DATA_VERSION_TIME_FORMAT2, sequenceNumber, rtc, dataLength, nanoseconds, seconds);
|
||||
var bytes = BitConverter.GetBytes(seconds);
|
||||
Buffer.BlockCopy(bytes, 0, _dataBytes, offset, bytes.Length);
|
||||
offset += bytes.Length;
|
||||
bytes = BitConverter.GetBytes(nanoseconds);
|
||||
Buffer.BlockCopy(bytes, 0, _dataBytes, offset, bytes.Length);
|
||||
}
|
||||
public TimePacketFormat2(byte[] bytes) : base(bytes)
|
||||
{
|
||||
var offset = IRIGCH10.PacketHeader.PACKET_HEADER_LENGTH;
|
||||
|
||||
if (PacketHeader.SecondaryHeaderPresent)
|
||||
{
|
||||
var secondaryHeaderBytes = new byte[SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH];
|
||||
Buffer.BlockCopy(bytes, offset,
|
||||
secondaryHeaderBytes, 0, SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH);
|
||||
var secondaryHeader = new SecondaryTimeFormatHeader(secondaryHeaderBytes);
|
||||
LocalTimeOfFirstSample = secondaryHeader.LocalTime;
|
||||
offset += SecondaryTimeFormatHeader.SECONDARY_TIME_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
ChannelSpecificDataWord = BitConverter.ToUInt32(bytes, offset);
|
||||
offset += sizeof(uint);
|
||||
var channelSpecificDataWord = BitConverter.GetBytes(ChannelSpecificDataWord);
|
||||
var bits = new BitArray(channelSpecificDataWord);
|
||||
var ntf = Utils.Utils.BitArrayToInt32(bits, 4, 7);
|
||||
switch (ntf)
|
||||
{
|
||||
case 0: NetworkTimeFormat = NetworkTimeFormats.NetworkTimeProtocolVersion3; break;
|
||||
case 1: NetworkTimeFormat = NetworkTimeFormats.IEEE1588_2002; break;
|
||||
case 2: NetworkTimeFormat = NetworkTimeFormats.IEEE1588_2008; break;
|
||||
default: NetworkTimeFormat = NetworkTimeFormats.RESERVED; break;
|
||||
}
|
||||
var ts = Utils.Utils.BitArrayToInt32(bits, 0, 3);
|
||||
switch (ts)
|
||||
{
|
||||
case 0: TimeStatus = TimeStatuses.TimeNotValid; break;
|
||||
case 1: TimeStatus = TimeStatuses.TimeValid; break;
|
||||
case 2: TimeStatus = TimeStatuses.Reserved; break;
|
||||
}
|
||||
|
||||
UnsignedSeconds = BitConverter.ToUInt32(bytes, offset);
|
||||
offset += sizeof(uint);
|
||||
UnsignedNanoSeconds = BitConverter.ToUInt32(bytes, offset);
|
||||
offset += sizeof(uint);
|
||||
PTPTime = PTP1588Timestamps.ToDateTimeString((decimal)UnsignedSeconds, (decimal)UnsignedNanoSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Packets
|
||||
{
|
||||
public class TransportStreamHeader : ITransportStreamHeader
|
||||
{
|
||||
/*• Transport Stream: 4-byte or 32-bit value. (Byteswap to PC format.)
|
||||
o Bit [3:0] 4-bit Message Format is set to 1.
|
||||
o Bit [7:4] 4-bit Message Type is set to 0.
|
||||
o Bit [31:8] 24-bit UDP message sequence number. It is incremented by 1 for each packet sent out in stream including Time data packet, CGPD (TMATS), and Analog data packets.
|
||||
*/
|
||||
private byte[] _bytes = new byte[4];
|
||||
public int MessageFormat { get; }
|
||||
public int MessageType { get; }
|
||||
public int SequenceNumber { get; }
|
||||
|
||||
public TransportStreamHeader()
|
||||
{
|
||||
|
||||
}
|
||||
public TransportStreamHeader(byte[] input)
|
||||
{
|
||||
if (null == input) { throw new NullReferenceException($"input is null"); }
|
||||
if (TRANSPORT_HEADER_LENGTH != input.Length) { throw new Exception($"Expected {TRANSPORT_HEADER_LENGTH} bytes, received {input.Length}"); }
|
||||
Buffer.BlockCopy(input, 0, _bytes, 0, TRANSPORT_HEADER_LENGTH);
|
||||
var int32 = BitConverter.ToInt32(input, 0);
|
||||
var bits = new BitArray(BitConverter.GetBytes(int32));
|
||||
MessageFormat = Utils.Utils.BitArrayToInt32(bits, 0, 3);
|
||||
MessageType = Utils.Utils.BitArrayToInt32(bits, 4, 7);
|
||||
SequenceNumber = Utils.Utils.BitArrayToInt32(bits, 8, 31);
|
||||
_bytes = input;
|
||||
}
|
||||
public const int TRANSPORT_HEADER_LENGTH = 4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using IRIGCh10;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.TMATS.DataConversion
|
||||
{
|
||||
public enum CoefficientsAttributes
|
||||
{
|
||||
[Description("CO\\N")]
|
||||
OrderOfCurveFit,
|
||||
[Description("CO1")]
|
||||
DerivedFromPairSet,
|
||||
[Description("CO")]
|
||||
Coefficient0,
|
||||
[Description("CO-1")]
|
||||
Coefficient1,
|
||||
[Description("CO-2")]
|
||||
Coefficient2,
|
||||
[Description("CO-3")]
|
||||
Coefficient3,
|
||||
[Description("CO-4")]
|
||||
Coefficient4,
|
||||
[Description("CO-5")]
|
||||
Coefficient5,
|
||||
[Description("CO-6")]
|
||||
Coefficient6,
|
||||
[Description("CO-7")]
|
||||
Coefficient7
|
||||
}
|
||||
/// <summary>
|
||||
/// implements the coefficient section of the tmats document
|
||||
/// </summary>
|
||||
public class CoefficientSection : TMATSSection<CoefficientsAttributes>
|
||||
{
|
||||
public CoefficientSection(int number)
|
||||
: base(AttributeIdentifiers.DataConversionAttributes, number)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// SPECIFY THE ORDER OF THE POLYNOMINAL CURVE FIT, n.
|
||||
/// </summary>
|
||||
public int? OrderOfCurveFit
|
||||
{
|
||||
get => GetIntOrNull(CoefficientsAttributes.OrderOfCurveFit);
|
||||
set => SetIntOrNull(CoefficientsAttributes.OrderOfCurveFit, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VALUE OF THE ZERO ORDER TERM (OFFSET), SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string Coefficient0
|
||||
{
|
||||
get => GetValue(CoefficientsAttributes.Coefficient0);
|
||||
set => SetValueWithLength(CoefficientsAttributes.Coefficient0, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// VALUE OF THE COEFFICIENT OF THE N-TH POWER OF X (FIRST ORDER COEFFICIENT IS
|
||||
/// THE EQUIVALENT OF BIT WEIGHT).
|
||||
/// SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string Coefficient1
|
||||
{
|
||||
get => GetValue(CoefficientsAttributes.Coefficient1);
|
||||
set => SetValueWithLength(CoefficientsAttributes.Coefficient1, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using IRIGCh10;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Attributes
|
||||
{
|
||||
public enum DataConversionAttributes
|
||||
{
|
||||
[Description("DCT")]
|
||||
ConversionType
|
||||
}
|
||||
|
||||
public enum ConversionTypes
|
||||
{
|
||||
[Description("NON")]
|
||||
None,
|
||||
[Description("PRS")]
|
||||
PairSets,
|
||||
[Description("COE")]
|
||||
Coefficients,
|
||||
[Description("NPC")]
|
||||
CoefficientsNegative,
|
||||
[Description("DER")]
|
||||
Derived,
|
||||
[Description("DIS")]
|
||||
Discrete,
|
||||
[Description("PTM")]
|
||||
PCMTime,
|
||||
[Description("BTM")]
|
||||
Time1553,
|
||||
[Description("VOI")]
|
||||
DigitalVoice,
|
||||
[Description("VID")]
|
||||
DigitalVideo,
|
||||
[Description("SP")]
|
||||
SpecializedProcessing,
|
||||
[Description("OTH")]
|
||||
Other
|
||||
}
|
||||
/// <summary>
|
||||
/// implements the data conversion section of the tmats packet
|
||||
/// </summary>
|
||||
public class DataConversionSection : TMATSSection<DataConversionAttributes>
|
||||
{
|
||||
public DataConversionSection(int number) : base(AttributeIdentifiers.DataConversionAttributes, number)
|
||||
{
|
||||
}
|
||||
public void SetConversionType(ConversionTypes type)
|
||||
{
|
||||
SetValueWithLength(DataConversionAttributes.ConversionType,
|
||||
DescriptionDecoder.GetDescription(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using IRIGCh10;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.TMATS.DataConversion
|
||||
{
|
||||
public enum MeasurandAttributes
|
||||
{
|
||||
[Description("MN1")]
|
||||
[MaxLength(64)]
|
||||
Description,
|
||||
[Description("MNA")]
|
||||
[MaxLength(32)]
|
||||
MeasurementAlias,
|
||||
[Description("MN2")]
|
||||
[MaxLength(10)]
|
||||
ExcitationVoltage,
|
||||
[Description("MN3")]
|
||||
[MaxLength(16)]
|
||||
EngineeringUnits,
|
||||
[Description("MN4")]
|
||||
[MaxLength(3)]
|
||||
LinkType
|
||||
}
|
||||
|
||||
public enum SourceDataTypeLinks
|
||||
{
|
||||
[Description("ANA")]
|
||||
FM,
|
||||
[Description("PCM")]
|
||||
PCM,
|
||||
[Description("PAM")]
|
||||
PAM,
|
||||
[Description("OTH")]
|
||||
Other
|
||||
}
|
||||
/// <summary>
|
||||
/// implements the measurand section of the tmats document
|
||||
/// </summary>
|
||||
public class MeasurandSection : TMATSSection<MeasurandAttributes>
|
||||
{
|
||||
public MeasurandSection(int number) : base(AttributeIdentifiers.DataConversionAttributes, number)
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// DESCRIBE THE PARAMETER BEING MEASURED.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get => GetValue(MeasurandAttributes.Description);
|
||||
set => SetValueWithLength(MeasurandAttributes.Description, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// ALTERNATE MEASURAND NAME
|
||||
/// </summary>
|
||||
public string MeasurementAlias
|
||||
{
|
||||
get => GetValue(MeasurandAttributes.MeasurementAlias);
|
||||
set => SetValueWithLength( MeasurandAttributes.MeasurementAlias, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// SENSOR REFERENCE VOLTAGE, IN VOLTS
|
||||
/// </summary>
|
||||
public string ExcitationVoltage
|
||||
{
|
||||
get => GetValue(MeasurandAttributes.ExcitationVoltage);
|
||||
set => SetValueWithLength(MeasurandAttributes.ExcitationVoltage, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// DEFINE THE ENGINEERING UNITS APPLICABLE TO THE OUTPUT DATA.
|
||||
/// </summary>
|
||||
public string EngineeringUnits
|
||||
{
|
||||
get => GetValue(MeasurandAttributes.EngineeringUnits);
|
||||
set => SetValueWithLength(MeasurandAttributes.EngineeringUnits, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// DEFINE THE SOURCE DATA LINK TYPE:
|
||||
/// </summary>
|
||||
/// <param name="linkType"></param>
|
||||
public void SetLinkType(SourceDataTypeLinks linkType)
|
||||
{
|
||||
SetValueWithLength(MeasurandAttributes.LinkType, DescriptionDecoder.GetDescription(linkType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using IRIGCh10;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Attributes
|
||||
{
|
||||
public enum OtherInformationAttributes
|
||||
{
|
||||
[MaxLength(32)]
|
||||
[Description("MOT1")]
|
||||
HighMeasurementValue,
|
||||
[MaxLength(32)]
|
||||
[Description("MOT2")]
|
||||
LowMeasurementValue,
|
||||
[MaxLength(32)]
|
||||
[Description("MOT3")]
|
||||
HighAlertLimitValue,
|
||||
[MaxLength(32)]
|
||||
[Description("MOT4")]
|
||||
LowAlertLimitValue,
|
||||
[MaxLength(32)]
|
||||
[Description("MOT5")]
|
||||
HighWarningLimitValue,
|
||||
[MaxLength(32)]
|
||||
[Description("MOT6")]
|
||||
LowWarningLimitValue,
|
||||
[Description("SR")]
|
||||
[MaxLength(6)]
|
||||
SampleRate
|
||||
}
|
||||
/// <summary>
|
||||
/// implements the other information section of the TMAT packet, see chapter 9
|
||||
/// </summary>
|
||||
public class OtherInformationSection : TMATSSection<OtherInformationAttributes>
|
||||
{
|
||||
public OtherInformationSection(int number)
|
||||
: base(AttributeIdentifiers.DataConversionAttributes, number)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// HIGHEST ENGINEERING UNIT VALUE DEFINED BY THE CALIBRATION DATA,
|
||||
/// SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string HighMeasurementValue
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.HighMeasurementValue);
|
||||
set => SetValueWithLength(OtherInformationAttributes.HighMeasurementValue, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// LOWEST ENGINEERING UNIT VALUE DEFINED IN THE CALIBRATION DATA,
|
||||
/// SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string LowMeasurementValue
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.LowMeasurementValue);
|
||||
set => SetValueWithLength(OtherInformationAttributes.LowMeasurementValue, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// HIGHEST ENGINEERING UNIT VALUE EXPECTED OR SAFE OPERATING VALUE OF
|
||||
/// THE PARAMETER (“RED”), SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string HighAlertLimitValue
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.HighAlertLimitValue);
|
||||
set => SetValueWithLength(OtherInformationAttributes.HighAlertLimitValue, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// LOWEST ENGINEERING UNIT VALUE EXPECTED OR SAFE OPERATING VALUE OF
|
||||
/// THE PARAMETER (“RED”), SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string LowAlertLimitValue
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.LowAlertLimitValue);
|
||||
set => SetValueWithLength(OtherInformationAttributes.LowAlertLimitValue, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// HIGHEST ENGINEERING UNIT VALUE EXPECTED OR SAFE OPERATING VALUE OF
|
||||
/// THE PARAMETER (“YELLOW”), SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string HighWarningLimitValue
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.HighWarningLimitValue);
|
||||
set => SetValueWithLength(OtherInformationAttributes.HighWarningLimitValue, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// LOWEST ENGINEERING UNIT VALUE EXPECTED OR SAFE OPERATING VALUE OF
|
||||
/// THE PARAMETER (“YELLOW”), SCIENTIFIC NOTATION MAY BE USED.
|
||||
/// </summary>
|
||||
public string LowWarningLimitValue
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.LowWarningLimitValue);
|
||||
set => SetValueWithLength(OtherInformationAttributes.LowWarningLimitValue, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// ENTER THE SAMPLE RATE IN TERMS OF SAMPLES/SECOND.
|
||||
/// </summary>
|
||||
public string SampleRate
|
||||
{
|
||||
get => GetValue(OtherInformationAttributes.SampleRate);
|
||||
set => SetValueWithLength(OtherInformationAttributes.SampleRate, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using IRIGCh10;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.TMATS.DataConversion
|
||||
{
|
||||
public enum TelemetryAttributes
|
||||
{
|
||||
[Description("BFM")]
|
||||
[MaxLength(3)]
|
||||
BinaryFormat
|
||||
}
|
||||
|
||||
public enum BinaryFormats
|
||||
{
|
||||
[Description("INT")]
|
||||
Integer,
|
||||
[Description("UNS")]
|
||||
UnsignedBinary,
|
||||
[Description("SIG")]
|
||||
SignAndMagnitudeSig,
|
||||
[Description("SIM")]
|
||||
SignAndMagnitudeSim,
|
||||
[Description("ONE")]
|
||||
OnesCompliment,
|
||||
[Description("TWO")]
|
||||
TwosCompliment,
|
||||
[Description("OFF")]
|
||||
OffsetBinary,
|
||||
[Description("FPT")]
|
||||
FloatingPoint,
|
||||
[Description("BCD")]
|
||||
BinaryCodedDecimal,
|
||||
[Description("BWT")]
|
||||
BitWeight,
|
||||
[Description("OTH")]
|
||||
Other
|
||||
}
|
||||
/// <summary>
|
||||
/// implements part of the telemetry section of the TMATS packet (see chapter 9)
|
||||
/// </summary>
|
||||
public class TelemetrySection : TMATSSection<TelemetryAttributes>
|
||||
{
|
||||
public TelemetrySection(int number) : base(AttributeIdentifiers.DataConversionAttributes, number)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetBinaryFormat(BinaryFormats format)
|
||||
{
|
||||
SetValueWithLength(TelemetryAttributes.BinaryFormat,
|
||||
DescriptionDecoder.GetDescription(format));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using IRIGCh10;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.TMATS.DataConversion
|
||||
{
|
||||
public enum TransducerInformation
|
||||
{
|
||||
[Description("DCN")] [MaxLength(32)] MeasurementName,
|
||||
[Description("TRD1")] [MaxLength(32)] Type,
|
||||
[Description("TRD2")] [MaxLength(32)] ModelNumber,
|
||||
[Description("TRD3")] [MaxLength(32)] SerialNumber,
|
||||
[Description("TRD4")] [MaxLength(2)] SecurityClassification,
|
||||
[Description("TRD5")] [MaxLength(10)] OriginationDate,
|
||||
[Description("TRD6")] [MaxLength(4)] RevisionNumber,
|
||||
[MaxLength(32)] [Description("TRD7")] Orientation,
|
||||
[MaxLength(24)] [Description("POC1")] PointOfContactName,
|
||||
[MaxLength(48)] [Description("POC2")] PointOfContactAgency,
|
||||
[MaxLength(48)] [Description("POC3")] PointOfContectAddress,
|
||||
[MaxLength(20)] [Description("POC4")] PointOfContactTelephone,
|
||||
}
|
||||
|
||||
public enum ClassificationTypes
|
||||
{
|
||||
[Description("U")] Unclassified,
|
||||
[Description("C")] Confidential,
|
||||
[Description("S")] Secret,
|
||||
[Description("T")] TopSecret,
|
||||
[Description("O")] Other
|
||||
}
|
||||
/// <summary>
|
||||
/// this class handles the transducer information section of the TMAT packet (see chapter 9.pdf)
|
||||
/// </summary>
|
||||
public class TransducerInformationSection : TMATSSection<TransducerInformation>
|
||||
{
|
||||
public TransducerInformationSection(int number) : base(AttributeIdentifiers.DataConversionAttributes, number)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GIVE THE MEASUREMENT NAME.
|
||||
/// </summary>
|
||||
public string MeasurementName
|
||||
{
|
||||
get => GetValue(TransducerInformation.MeasurementName);
|
||||
set => SetValueWithLength(TransducerInformation.MeasurementName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TYPE OF SENSOR, IF APPROPRIATE
|
||||
/// </summary>
|
||||
public string Type
|
||||
{
|
||||
get => GetValue(TransducerInformation.Type);
|
||||
set => SetValueWithLength(TransducerInformation.Type, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// IF APPROPRIATE
|
||||
/// </summary>
|
||||
public string ModelNumber
|
||||
{
|
||||
get => GetValue(TransducerInformation.ModelNumber);
|
||||
set => SetValueWithLength(TransducerInformation.ModelNumber, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// IF APPLICABLE
|
||||
/// </summary>
|
||||
public string SerialNumber
|
||||
{
|
||||
get => GetValue(TransducerInformation.SerialNumber);
|
||||
set => SetValueWithLength(TransducerInformation.SerialNumber, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// ENTER THE SECURITY CLASSIFICATION OF THIS MEASURAND.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="signalClassified"></param>
|
||||
/// <param name="measurandClassified"></param>
|
||||
public void SetSecurityClassification(ClassificationTypes type,
|
||||
bool signalClassified, bool measurandClassified)
|
||||
{
|
||||
var val = DescriptionDecoder.GetDescription(type);
|
||||
if (signalClassified && measurandClassified)
|
||||
{
|
||||
val = $"{val}B";
|
||||
}
|
||||
else if (signalClassified)
|
||||
{
|
||||
val = $"{val}R";
|
||||
}
|
||||
else if (measurandClassified)
|
||||
{
|
||||
val = $"{val}E";
|
||||
}
|
||||
|
||||
SetValueWithLength(TransducerInformation.SecurityClassification, val);
|
||||
}
|
||||
/// <summary>
|
||||
/// DATE OF ORIGINATION OF THIS DATA FILE. DD – DAY MM – MONTH
|
||||
/// YYYY – YEAR (MM-DD-YYYY)
|
||||
/// </summary>
|
||||
public DateTime? OriginationDate
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(TransducerInformation.OriginationDate);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(val, out var dt))
|
||||
{
|
||||
return dt;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (null == value)
|
||||
{
|
||||
SetValueWithLength(TransducerInformation.OriginationDate, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dt = (DateTime) value;
|
||||
SetValueWithLength(TransducerInformation.OriginationDate,
|
||||
$"{dt.Month:00}-{dt.Day:00}-{dt.Year:0000}");
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// SPECIFY THE REVISION NUMBER OF THE DATA PROVIDED.
|
||||
/// </summary>
|
||||
public string RevisionNumber
|
||||
{
|
||||
get => GetValue(TransducerInformation.RevisionNumber);
|
||||
set => SetValueWithLength(TransducerInformation.RevisionNumber, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// DESCRIBE THE PHYSICAL ORIENTATION OF THE SENSOR.
|
||||
/// </summary>
|
||||
public string Orientation
|
||||
{
|
||||
get => GetValue(TransducerInformation.Orientation);
|
||||
set => SetValueWithLength(TransducerInformation.Orientation, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// POINT OF CONTACT WITH THE ORGANIZATION THAT PROVIDED THE CALIBRATION DATA
|
||||
/// </summary>
|
||||
public POC PointOfContact
|
||||
{
|
||||
get => new POC()
|
||||
{
|
||||
Name = GetValue(TransducerInformation.PointOfContactName),
|
||||
Agency = GetValue(TransducerInformation.PointOfContactAgency),
|
||||
Address = GetValue(TransducerInformation.PointOfContectAddress),
|
||||
Telephone = GetValue(TransducerInformation.PointOfContectAddress)
|
||||
};
|
||||
set
|
||||
{
|
||||
SetValueWithLength(TransducerInformation.PointOfContactName, value.Name);
|
||||
SetValueWithLength(TransducerInformation.PointOfContactAgency, value.Agency);
|
||||
SetValueWithLength(TransducerInformation.PointOfContectAddress, value.Address);
|
||||
SetValueWithLength(TransducerInformation.PointOfContactTelephone, value.Telephone);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Point Of Contact structure
|
||||
/// </summary>
|
||||
public class POC
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Agency { get; set; }
|
||||
public string Address { get; set; }
|
||||
public string Telephone { get; set; }
|
||||
|
||||
public POC(string name, string agency, string address, string telephone)
|
||||
{
|
||||
Name = name;
|
||||
Agency = agency;
|
||||
Address = address;
|
||||
Telephone = telephone;
|
||||
}
|
||||
|
||||
public POC()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
public static class DescriptionDecoder
|
||||
{
|
||||
public static string GetDescription(Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(
|
||||
typeof(DescriptionAttribute), false);
|
||||
if (attributes.Any())
|
||||
{
|
||||
return attributes.First().Description;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MaxLengthDecoder
|
||||
{
|
||||
public static int GetMaxLength(Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
var attributes = (MaxLengthAttribute[]) fi.GetCustomAttributes(
|
||||
typeof(MaxLengthAttribute), false);
|
||||
if (attributes.Any())
|
||||
{
|
||||
return attributes.First().Length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
public enum GeneralTags
|
||||
{
|
||||
[Description("PN")]
|
||||
[MaxLength(16)]
|
||||
ProgramName,
|
||||
[Description("TA")]
|
||||
[MaxLength(64)]
|
||||
TestItem
|
||||
}
|
||||
public class GeneralInformationGroup : TMATSSection<GeneralTags>
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of program
|
||||
/// </summary>
|
||||
public string ProgramName
|
||||
{
|
||||
get => GetValue(GeneralTags.ProgramName);
|
||||
set => SetValueWithLength(GeneralTags.ProgramName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TEST ITEM DESCRIPTION IN TERMS OF NAME, MODEL, PLATFORM, OR
|
||||
/// IDENTIFICATION CODE, AS APPROPRIATE
|
||||
/// </summary>
|
||||
public string TestItem
|
||||
{
|
||||
get => GetValue(GeneralTags.TestItem);
|
||||
set => SetValueWithLength(GeneralTags.TestItem, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VERSION OF IRIG 106 STANDARD USED TO GENERATE THIS TMATS FILE
|
||||
/// </summary>
|
||||
public string IRIG106RevisionLevel
|
||||
{
|
||||
get => _information.GetValue(InformationTags.IRIG106RevisionLevel);
|
||||
set => _information.SetValueWithLength(InformationTags.IRIG106RevisionLevel, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DATE OF ORIGINATION OF THIS MISSION CONFIGURATION.
|
||||
/// </summary>
|
||||
public DateTime? OriginationDate
|
||||
{
|
||||
get => _information.GetDate(InformationTags.OriginationDate);
|
||||
set => _information.SetDate(InformationTags.OriginationDate, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// REVISION NUMBER ASSOCIATED WITH THIS MISSION CONFIGURATION
|
||||
/// </summary>
|
||||
public string RevisionNumber
|
||||
{
|
||||
get => _information.GetValue(InformationTags.RevisionNumber);
|
||||
set => _information.SetValueWithLength(InformationTags.RevisionNumber, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// DATE OF REVISION.
|
||||
/// </summary>
|
||||
public DateTime? RevisionDate
|
||||
{
|
||||
get => _information.GetDate(InformationTags.RevisionDate);
|
||||
set => _information.SetDate(InformationTags.RevisionDate, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// UPDATE NUMBER OF CURRENT CHANGE WHICH HAS NOT BEEN INCORPORATED
|
||||
/// AS A REVISION
|
||||
/// </summary>
|
||||
public string UpdateNumber
|
||||
{
|
||||
get => _information.GetValue(InformationTags.UpdateNumber);
|
||||
set => _information.SetValueWithLength(InformationTags.UpdateNumber, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// DATE OF UPDATE.
|
||||
/// </summary>
|
||||
public DateTime? UpdateDate
|
||||
{
|
||||
get => _information.GetDate(InformationTags.UpdateDate);
|
||||
set => _information.SetDate(InformationTags.UpdateDate, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// TEST IDENTIFICATION
|
||||
/// </summary>
|
||||
public string TestNumber
|
||||
{
|
||||
get => _information.GetValue(InformationTags.TestNumber);
|
||||
set => _information.SetValue(InformationTags.TestNumber, value);
|
||||
}
|
||||
|
||||
public int NumberOfPointsOfContact
|
||||
{
|
||||
get => _information.GetContactCount();
|
||||
set => _information.SetContactCount(value);
|
||||
}
|
||||
/// <summary>
|
||||
/// SPECIFY THE NUMBER OF DATA SOURCES: FOR RF TELEMETRY
|
||||
/// SYSTEMS, GIVE THE NUMBER OF CARRIERS; FOR TAPE OR STORAGE
|
||||
/// RECORDED DATA, IDENTIFY THE NUMBER OF TAPE OR STORAGE
|
||||
/// SOURCES.
|
||||
/// </summary>
|
||||
public int NumberOfDataSources
|
||||
{
|
||||
get => _information.GetDataSourceCount();
|
||||
set => _information.SetDataSourceCount(value);
|
||||
}
|
||||
/// <summary>
|
||||
/// APPROXIMATE DURATION OF TEST IN HOURS.
|
||||
/// </summary>
|
||||
public string TestDuration
|
||||
{
|
||||
get => _information.GetValue(Information.TestInformationTags.TestDuration);
|
||||
set => _information.SetValue(Information.TestInformationTags.TestDuration, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// INDICATE WHETHER A PRE-TEST REQUIREMENT IS APPLICABLE (‘Y’ OR
|
||||
/// ‘N’). PROVIDE DETAILS IN COMMENTS RECORD.
|
||||
/// </summary>
|
||||
public bool PreTestRequirement
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = _information.GetValue(Information.TestInformationTags.PreTestRequirement);
|
||||
if (string.IsNullOrWhiteSpace(val)) { return false; }
|
||||
if (val == "Y") { return true; }
|
||||
return false;
|
||||
}
|
||||
set => _information.SetValue(Information.TestInformationTags.PreTestRequirement, value ? "Y" : "N");
|
||||
}
|
||||
/// <summary>
|
||||
/// INDICATE WHETHER A PRE-TEST REQUIREMENT IS APPLICABLE (‘Y’ OR
|
||||
/// ‘N’). PROVIDE DETAILS IN COMMENTS RECORD.
|
||||
/// </summary>
|
||||
public bool PostTestRequirement
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = _information.GetValue(Information.TestInformationTags.PostTestRequirement);
|
||||
if (string.IsNullOrWhiteSpace(val)) { return false; }
|
||||
if (val == "Y") { return true; }
|
||||
return false;
|
||||
}
|
||||
set => _information.SetValue(Information.TestInformationTags.PostTestRequirement, value ? "Y" : "N");
|
||||
}
|
||||
|
||||
public void SetDataSourceField(int number, Information.DataSourceIdentificationTags tag,
|
||||
string value)
|
||||
{
|
||||
_information.SetDataSourceField(number, tag, value);
|
||||
}
|
||||
|
||||
public void SetDataSourceType(int number, Information.DataSourceTypes sourceType)
|
||||
{
|
||||
_information.SetDataSourceType(number, sourceType);
|
||||
}
|
||||
public SecurityClassifications SecurityClassification
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = _securitySection.GetValue(SecurityTags.SecurityClassification);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return SecurityClassifications.Unclassified;
|
||||
}
|
||||
|
||||
var classes = Enum.GetValues(typeof(SecurityClassifications))
|
||||
.Cast<SecurityClassifications>().ToArray();
|
||||
foreach (var item in classes)
|
||||
{
|
||||
if (DescriptionDecoder.GetDescription(item) == val)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return SecurityClassifications.Unclassified;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// PROVIDE THE ADDITIONAL INFORMATION REQUESTED OR ANY OTHER INFORMATION DESIRED.
|
||||
/// </summary>
|
||||
public string Comments
|
||||
{
|
||||
get => _comments.GetValue(CommentTags.Comments);
|
||||
set => _comments.SetValueWithLength(CommentTags.Comments, value);
|
||||
}
|
||||
|
||||
#region infrastructure
|
||||
|
||||
public override string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_information.Serialize());
|
||||
|
||||
sb.Append(base.Serialize());
|
||||
return sb.ToString();
|
||||
}
|
||||
private CommentSection _comments = new CommentSection();
|
||||
public class CommentSection : TMATSSection<CommentTags>
|
||||
{
|
||||
}
|
||||
public enum CommentTags
|
||||
{
|
||||
[Description("COM")]
|
||||
[MaxLength(1600)]
|
||||
Comments
|
||||
}
|
||||
public enum SecurityClassifications
|
||||
{
|
||||
[Description("U")]
|
||||
Unclassified,
|
||||
[Description("C")]
|
||||
Confidential,
|
||||
[Description("S")]
|
||||
Secret,
|
||||
[Description("T")]
|
||||
TopSecret,
|
||||
[Description("O")]
|
||||
Other
|
||||
}
|
||||
public enum SecurityTags
|
||||
{
|
||||
[Description("SC")]
|
||||
[MaxLength(1)]
|
||||
SecurityClassification
|
||||
}
|
||||
|
||||
public class SecuritySection : TMATSSection<SecurityTags>
|
||||
{
|
||||
}
|
||||
private SecuritySection _securitySection = new SecuritySection();
|
||||
|
||||
private Information _information = new Information();
|
||||
|
||||
public enum InformationTags
|
||||
{
|
||||
[Description("106")]
|
||||
[MaxLength(2)]
|
||||
IRIG106RevisionLevel,
|
||||
[Description("OD")]
|
||||
[MaxLength(10)]
|
||||
OriginationDate,
|
||||
[Description("RN")]
|
||||
[MaxLength(4)]
|
||||
RevisionNumber,
|
||||
[Description("RD")]
|
||||
[MaxLength(10)]
|
||||
RevisionDate,
|
||||
[Description("UN")]
|
||||
[MaxLength(2)]
|
||||
UpdateNumber,
|
||||
[Description("UD")]
|
||||
[MaxLength(10)]
|
||||
UpdateDate,
|
||||
[MaxLength(16)]
|
||||
[Description("TN")]
|
||||
TestNumber
|
||||
}
|
||||
public class Information : TMATSSection<InformationTags>
|
||||
{
|
||||
#region Contacts
|
||||
public int GetContactCount()
|
||||
{
|
||||
return _contacts.GetCount();
|
||||
}
|
||||
|
||||
public void SetContactCount(int count)
|
||||
{
|
||||
_contacts.SetCount(count);
|
||||
}
|
||||
|
||||
public void SetContactField(int number, PointOfContactTags tag, string value) => _contacts.SetValue(number, tag, value);
|
||||
public string GetContactField(int number, PointOfContactTags tag) => _contacts.GetValue(number, tag);
|
||||
|
||||
public enum PointOfContactTags
|
||||
{
|
||||
[Description("POC1-")]
|
||||
[MaxLength(24)]
|
||||
Name,
|
||||
[Description("POC2-")]
|
||||
[MaxLength(48)]
|
||||
Agency,
|
||||
[Description("POC3-")]
|
||||
[MaxLength(48)]
|
||||
Address,
|
||||
[Description("POC4-")]
|
||||
[MaxLength(20)]
|
||||
Telephone
|
||||
}
|
||||
|
||||
private TMATSectionNumberedArray<PointOfContactTags> _contacts =
|
||||
new TMATSectionNumberedArray<PointOfContactTags>("POC\\N", 9);
|
||||
#endregion Contacts
|
||||
|
||||
#region DataSourceIdentification
|
||||
|
||||
public int GetDataSourceCount() => _datasources.GetCount();
|
||||
public void SetDataSourceCount(int value) => _datasources.SetCount(value);
|
||||
|
||||
public void SetDataSourceField(int number, DataSourceIdentificationTags tag,
|
||||
string value)
|
||||
{
|
||||
if (tag == DataSourceIdentificationTags.DataSourceID)
|
||||
{
|
||||
//must be unique
|
||||
for (var i = 1; i < _datasources.GetCount(); i++)
|
||||
{
|
||||
if( i == number ){ continue; }
|
||||
|
||||
if (_datasources.GetValue(number, tag) == value)
|
||||
{
|
||||
throw new Exception($"Datasource ID must be unique");
|
||||
}
|
||||
}
|
||||
}
|
||||
_datasources.SetValue(number, tag, value);
|
||||
}
|
||||
|
||||
public void SetDataSourceType(int number, DataSourceTypes type)
|
||||
{
|
||||
SetDataSourceField(number, DataSourceIdentificationTags.DataSourceType,
|
||||
DescriptionDecoder.GetDescription(type));
|
||||
}
|
||||
|
||||
public string GetDataSourceField(int number, DataSourceIdentificationTags tag) =>
|
||||
_datasources.GetValue(number, tag);
|
||||
public enum DataSourceIdentificationTags
|
||||
{
|
||||
[MaxLength(32)]
|
||||
[Description("DSI-")]
|
||||
DataSourceID,
|
||||
[MaxLength(3)]
|
||||
[Description("DST-")]
|
||||
DataSourceType
|
||||
}
|
||||
|
||||
public enum DataSourceTypes
|
||||
{
|
||||
[Description("RF")]
|
||||
RF,
|
||||
[Description("STO")]
|
||||
Storage,
|
||||
[Description("TAP")]
|
||||
Tape,
|
||||
[Description("OTH")]
|
||||
Other
|
||||
}
|
||||
private TMATSectionNumberedArray<DataSourceIdentificationTags> _datasources =
|
||||
new TMATSectionNumberedArray<DataSourceIdentificationTags>("DSI\\N");
|
||||
|
||||
#endregion
|
||||
|
||||
public enum TestInformationTags
|
||||
{
|
||||
[Description("TI1")]
|
||||
[MaxLength(4)]
|
||||
TestDuration,
|
||||
[Description("TI2")]
|
||||
[MaxLength(1)]
|
||||
PreTestRequirement,
|
||||
[Description("TI3")]
|
||||
[MaxLength(1)]
|
||||
PostTestRequirement
|
||||
}
|
||||
|
||||
public class TestInformation : TMATSSection<TestInformationTags>
|
||||
{
|
||||
}
|
||||
|
||||
private TestInformation _testInformation = new TestInformation();
|
||||
|
||||
public void SetValue(TestInformationTags tag, string value) => _testInformation.SetValueWithLength(tag, value);
|
||||
|
||||
public string GetValue(TestInformationTags tag) => _testInformation.GetValue(tag);
|
||||
|
||||
public override string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(base.Serialize());
|
||||
sb.Append(_contacts.Serialize());
|
||||
sb.Append(_datasources.Serialize());
|
||||
sb.Append(_testInformation.Serialize());
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
531
Common/DTS.Common.Serialization/IRIGCH10/TMATS/PCM.cs
Normal file
531
Common/DTS.Common.Serialization/IRIGCH10/TMATS/PCM.cs
Normal file
@@ -0,0 +1,531 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
public enum PCMCodes
|
||||
{
|
||||
[Description("NRZ-L")]
|
||||
NRZL,
|
||||
[Description("NRZ-M")]
|
||||
NRZM,
|
||||
[Description("NRZ-S")]
|
||||
NRZS,
|
||||
[Description("BIO-L")]
|
||||
BIOL,
|
||||
[Description("BIO-M")]
|
||||
BIOM,
|
||||
[Description("BIO-S")]
|
||||
BIOS,
|
||||
[Description("RNRZ-L")]
|
||||
RNRZL,
|
||||
[Description("OTHER")]
|
||||
Other
|
||||
}
|
||||
|
||||
public enum Polarities
|
||||
{
|
||||
[Description("N")]
|
||||
Normal,
|
||||
[Description("I")]
|
||||
Inverted
|
||||
}
|
||||
|
||||
public enum PCMDataDirections
|
||||
{
|
||||
[Description("N")]
|
||||
Normal,
|
||||
[Description("R")]
|
||||
Reversed
|
||||
}
|
||||
public enum PCMAttributes
|
||||
{
|
||||
[Description("DLN")]
|
||||
[MaxLength(32)]
|
||||
DataLinkName,
|
||||
[MaxLength(6)]
|
||||
[Description("D1")]
|
||||
PCMCode,
|
||||
[Description("D2")]
|
||||
[MaxLength(9)]
|
||||
Bitrate,
|
||||
[Description("D3")]
|
||||
Encrypted,
|
||||
[Description("D4")]
|
||||
[MaxLength(1)]
|
||||
Polarity,
|
||||
[Description("D5")]
|
||||
AutoPolarityCorrection,
|
||||
[Description("D6")]
|
||||
DataDirection,
|
||||
[Description("D7")]
|
||||
DataRandomized,
|
||||
[MaxLength(1)]
|
||||
[Description("D8")]
|
||||
RandomizerLength
|
||||
}
|
||||
|
||||
public class PCM : TMATSSection<PCMAttributes>
|
||||
{
|
||||
/// <summary>
|
||||
/// IDENTIFY THE DATA LINK NAME CONSISTENT WITH THE MUX/MOD
|
||||
/// GROUP.
|
||||
/// </summary>
|
||||
public string DataLinkName
|
||||
{
|
||||
get => GetValue(PCMAttributes.DataLinkName);
|
||||
set => SetValueWithLength(PCMAttributes.DataLinkName, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DEFINE THE DATA FORMAT CODE:
|
||||
/// </summary>
|
||||
public PCMCodes? PCMCode
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(PCMAttributes.PCMCode);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var codes = Enum.GetValues(typeof(PCMCodes))
|
||||
.Cast<PCMCodes>().ToArray();
|
||||
foreach (var code in codes)
|
||||
{
|
||||
if (DescriptionDecoder.GetDescription(code) == val)
|
||||
{
|
||||
return code;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (null == value)
|
||||
{
|
||||
SetValueWithLength(PCMAttributes.PCMCode, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetValueWithLength(PCMAttributes.PCMCode,
|
||||
DescriptionDecoder.GetDescription(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DATA RATE IN BITS PER SECOND. SCIENTIFIC NOTATION MAY BE
|
||||
/// USED.
|
||||
/// </summary>
|
||||
public string BitsPerSecond
|
||||
{
|
||||
get => GetValue(PCMAttributes.Bitrate);
|
||||
set => SetValueWithLength(PCMAttributes.Bitrate, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// DATA RANDOMIZED
|
||||
/// </summary>
|
||||
public bool DataRandomized
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(PCMAttributes.DataRandomized);
|
||||
if( string.IsNullOrWhiteSpace(val) ){ return false; }
|
||||
if( val == "N" ){ return false; }
|
||||
return true;
|
||||
}
|
||||
set { SetValueWithLength(PCMAttributes.DataRandomized, value ? "Y" : "N"); }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DATA POLARITY:
|
||||
/// </summary>
|
||||
public Polarities Polarity
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(PCMAttributes.Polarity);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return Polarities.Normal;
|
||||
}
|
||||
return val == "I" ? Polarities.Inverted : Polarities.Normal;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValueWithLength(PCMAttributes.Polarity,
|
||||
DescriptionDecoder.GetDescription(value));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// TIME SEQUENCE OF DATA:
|
||||
/// </summary>
|
||||
public PCMDataDirections DataDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(PCMAttributes.DataDirection);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return PCMDataDirections.Normal;
|
||||
}
|
||||
|
||||
return val == "R" ? PCMDataDirections.Reversed : PCMDataDirections.Normal;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetValueWithLength(PCMAttributes.DataDirection,
|
||||
DescriptionDecoder.GetDescription(value));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// TYPE OF PCM FORMAT: CLASS I - ‘ONE’ CLASS II – ‘TWO’
|
||||
/// 1553 BUS - ‘1553’ BUS – ‘BUS’ ALTERNATE TAG AND DATA-‘ALTD’
|
||||
/// PACKET TELEMETRY – ‘PKTM’ OTHER - ‘OTHR’, DESCRIBE IN
|
||||
/// COMMENTS RECORD.
|
||||
/// </summary>
|
||||
public PCMTypeFormats TypeFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = _typeFormat.GetValue(TypeFormatTags.TypeFormat);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return PCMTypeFormats.Other;
|
||||
}
|
||||
var formats = Enum.GetValues(typeof(PCMTypeFormats))
|
||||
.Cast<PCMTypeFormats>().ToArray();
|
||||
foreach (var format in formats)
|
||||
{
|
||||
if (DescriptionDecoder.GetDescription(format) == val)
|
||||
{
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return PCMTypeFormats.Other;
|
||||
}
|
||||
set
|
||||
{
|
||||
_typeFormat.SetValueWithLength(TypeFormatTags.TypeFormat,
|
||||
DescriptionDecoder.GetDescription(value));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// NUMBER OF BITS IN COMMON WORD LENGTH
|
||||
/// </summary>
|
||||
public ushort NumberOfBitsInCommonWordLength
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = _typeFormat.GetValue(TypeFormatTags.CommonWordLength);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ushort.TryParse(val, out var uTmp))
|
||||
{
|
||||
return uTmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
_typeFormat.SetValueWithLength(TypeFormatTags.CommonWordLength,
|
||||
value.ToString());
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// DEFINE THE DEFAULT FOR THE FIRST BIT TRANSFERRED IN
|
||||
/// NORMAL TIME SEQUENCE:
|
||||
/// </summary>
|
||||
public PCMWordTransferOrders WordTransferOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = _typeFormat.GetValue(TypeFormatTags.WordTransferOrder);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return PCMWordTransferOrders.MostSignificantBit;
|
||||
}
|
||||
|
||||
var orders = Enum.GetValues(typeof(PCMWordTransferOrders))
|
||||
.Cast<PCMWordTransferOrders>().ToArray();
|
||||
foreach (var order in orders)
|
||||
{
|
||||
if (DescriptionDecoder.GetDescription(order) == val)
|
||||
{
|
||||
return order;
|
||||
}
|
||||
}
|
||||
return PCMWordTransferOrders.MostSignificantBit;
|
||||
}
|
||||
set => _typeFormat.SetValueWithLength(TypeFormatTags.WordTransferOrder,
|
||||
DescriptionDecoder.GetDescription(value));
|
||||
}
|
||||
|
||||
public PCMWordParities PCMWordParity
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = _typeFormat.GetValue(TypeFormatTags.Parity);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return PCMWordParities.None;
|
||||
}
|
||||
|
||||
var parities = Enum.GetValues(typeof(PCMWordParities)).Cast<PCMWordParities>().ToArray();
|
||||
foreach (var parity in parities)
|
||||
{
|
||||
if( DescriptionDecoder.GetDescription(parity) == value){ return parity; }
|
||||
}
|
||||
|
||||
return PCMWordParities.None;
|
||||
}
|
||||
set => _typeFormat.SetValueWithLength(TypeFormatTags.Parity,
|
||||
DescriptionDecoder.GetDescription(value));
|
||||
}
|
||||
|
||||
private PCMTypeFormat _typeFormat;
|
||||
|
||||
public override string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_typeFormat.Serialize());
|
||||
sb.Append(base.Serialize());
|
||||
return sb.ToString();
|
||||
}
|
||||
public PCM(int number) : base(AttributeIdentifiers.PCMFormatAttributes, number)
|
||||
{
|
||||
_typeFormat = new PCMTypeFormat(number);
|
||||
}
|
||||
}
|
||||
|
||||
public enum PCMWordTransferOrders
|
||||
{
|
||||
[Description("M")]
|
||||
MostSignificantBit,
|
||||
[Description("L")]
|
||||
LeastSignificantBit
|
||||
}
|
||||
|
||||
public enum PCMWordParities
|
||||
{
|
||||
[Description("EV")]
|
||||
Even,
|
||||
[Description("OD")]
|
||||
Odd,
|
||||
[Description("NO")]
|
||||
None
|
||||
}
|
||||
public enum TypeFormatTags
|
||||
{
|
||||
[Description("TF")]
|
||||
[MaxLength(4)]
|
||||
TypeFormat,
|
||||
[Description("F1")]
|
||||
[MaxLength(2)]
|
||||
CommonWordLength,
|
||||
[Description("F2")]
|
||||
[MaxLength(1)]
|
||||
WordTransferOrder,
|
||||
[Description("F3")]
|
||||
Parity,
|
||||
[Description("F4")]
|
||||
ParityTransferOrder
|
||||
}
|
||||
|
||||
public enum PCMTypeFormats
|
||||
{
|
||||
[Description("ONE")]
|
||||
ClassI,
|
||||
[Description("TWO")]
|
||||
ClassII,
|
||||
[Description("1553")]
|
||||
Bus1553,
|
||||
[Description("BUS")]
|
||||
Bus,
|
||||
[Description("ALTD")]
|
||||
AlternateTagAndData,
|
||||
[Description("PKTM")]
|
||||
PacketTelemetry,
|
||||
[Description("OTHR")]
|
||||
Other
|
||||
}
|
||||
public class PCMTypeFormat : TMATSSection<TypeFormatTags>
|
||||
{
|
||||
public PCMTypeFormat(int number) : base(AttributeIdentifiers.PCMFormatAttributes,
|
||||
number)
|
||||
{
|
||||
}
|
||||
}
|
||||
public enum MinorFrameTags
|
||||
{
|
||||
[Description("MF\\N")]
|
||||
[MaxLength(3)]
|
||||
NumberOfMinorFramesInMajorFrame,
|
||||
[Description("MF1")]
|
||||
[MaxLength(4)]
|
||||
NumberOfWordsInMinorFrame,
|
||||
[Description("MF2")]
|
||||
[MaxLength(5)]
|
||||
NumberOfBitsInMinorFrame,
|
||||
[Description("MF3")]
|
||||
[MaxLength(3)]
|
||||
SyncType,
|
||||
[MaxLength(2)]
|
||||
[Description("MF4")]
|
||||
SyncLength,
|
||||
[MaxLength(33)]
|
||||
[Description("MF5")]
|
||||
SyncPattern
|
||||
}
|
||||
|
||||
public class MinorFrameSection : TMATSSection<MinorFrameTags>
|
||||
{
|
||||
public ushort NumberOfMinorFramesInAMajorFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = GetValue(MinorFrameTags.NumberOfMinorFramesInMajorFrame);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ushort.TryParse(value, out var s))
|
||||
{
|
||||
return s;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
set => SetValueWithLength(MinorFrameTags.NumberOfMinorFramesInMajorFrame, value.ToString());
|
||||
}
|
||||
/// <summary>
|
||||
/// SPECIFIES THE NUMBER OF WORDS IN A MINOR FRAME, AS DEFINED IN
|
||||
/// CHAPTER 4, PARAGRAPH 4.3. (THE MINOR FRAME SYNCHRONIZATION
|
||||
/// PATTERN IS ALWAYS CONSIDERED AS ONE WORD, REGARDLESS OF ITS
|
||||
/// LENGTH.)
|
||||
/// </summary>
|
||||
public int NumberOfWordsInMinorFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(MinorFrameTags.NumberOfWordsInMinorFrame);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( int.TryParse( val, out var iTmp)){ return iTmp; }
|
||||
return 0;
|
||||
}
|
||||
set => SetValueWithLength(MinorFrameTags.NumberOfWordsInMinorFrame, value.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NUMBER OF BITS IN A MINOR FRAME INCLUDING MINOR FRAME
|
||||
/// SYNCHRONIZATION PATTERN
|
||||
/// </summary>
|
||||
public int NumberOfBitsInMinorFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(MinorFrameTags.NumberOfBitsInMinorFrame);
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( int.TryParse( val, out var iTmp)){ return iTmp; }
|
||||
return 0;
|
||||
}
|
||||
set => SetValueWithLength(MinorFrameTags.NumberOfBitsInMinorFrame,
|
||||
value.ToString());
|
||||
}
|
||||
/// <summary>
|
||||
/// SPECIFY THE MINOR FRAME SYNCHRONIZATION PATTERN LENGTH
|
||||
/// IN NUMBER OF BITS.
|
||||
/// </summary>
|
||||
public ushort SyncLength
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = GetValue(MinorFrameTags.SyncLength);
|
||||
if (string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( ushort.TryParse( val, out var s)){ return s; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
set => SetValueWithLength(MinorFrameTags.SyncLength, value.ToString());
|
||||
}
|
||||
/// <summary>
|
||||
/// DEFINE MINOR FRAME SYNCHRONIZATION PATTERN IN BITS
|
||||
/// (“1”s and “0”s) WITH THE LEFT MOST BIT
|
||||
/// AS THE “FIRST BIT TRANSMITTED”
|
||||
/// </summary>
|
||||
public string SynchronizationPattern
|
||||
{
|
||||
get => GetValue(MinorFrameTags.SyncPattern);
|
||||
set => SetValueWithLength(MinorFrameTags.SyncPattern, value);
|
||||
}
|
||||
|
||||
public MinorFrameSection(int number=1) : base(AttributeIdentifiers.PCMFormatAttributes, number)
|
||||
{
|
||||
}
|
||||
}
|
||||
public enum SynchronizationCriteriaTags
|
||||
{
|
||||
[Description("SYNC1")]
|
||||
InSyncCriteria,
|
||||
[Description("SYNC2")]
|
||||
InSyncSyncPatternCriteria,
|
||||
[Description("SYNC3")]
|
||||
NumberOfDisagrees,
|
||||
[Description("SYNC4")]
|
||||
OutOfSyncSyncPatternCriteria
|
||||
}
|
||||
|
||||
public enum MinorFrameFormatTags
|
||||
{
|
||||
[Description("MFW1-")]
|
||||
WordNumber,
|
||||
[Description("MFW2-")]
|
||||
NumberOfBitsInWord
|
||||
}
|
||||
|
||||
public enum SubFrameSynchronizationTags
|
||||
{
|
||||
SubframeIDCounterName,
|
||||
SubframeSyncType,
|
||||
SubFrameIDCounterLocation,
|
||||
IDCounterWordLength,
|
||||
IDCounterMSBStartingBitLocation,
|
||||
IDCounterLength,
|
||||
IDCounterTransferOrder,
|
||||
IDCounterInitialValue,
|
||||
InitialCountSubframeNumber,
|
||||
IDCounterEndValue,
|
||||
EndCountSubframeNumber,
|
||||
CountDirection
|
||||
}
|
||||
|
||||
public enum SubFrameDefinitionTags
|
||||
{
|
||||
SubFrameName,
|
||||
SuperCom,
|
||||
LocationDefinition,
|
||||
SubframeLocation,
|
||||
Interval,
|
||||
SubframeDepth
|
||||
}
|
||||
}
|
||||
1496
Common/DTS.Common.Serialization/IRIGCH10/TMATS/StorageInformation.cs
Normal file
1496
Common/DTS.Common.Serialization/IRIGCH10/TMATS/StorageInformation.cs
Normal file
File diff suppressed because it is too large
Load Diff
152
Common/DTS.Common.Serialization/IRIGCH10/TMATS/TMATS.cs
Normal file
152
Common/DTS.Common.Serialization/IRIGCH10/TMATS/TMATS.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
public enum AttributeIdentifiers
|
||||
{
|
||||
[Description("G")] GeneralInformation,
|
||||
[Description("T")] TransmitionAttributes,
|
||||
[Description("R")] StorageSourceAttributes,
|
||||
[Description("M")] MultiplexingAttributes,
|
||||
[Description("P")] PCMFormatAttributes,
|
||||
[Description("D")] PCMMeasurementDescription,
|
||||
[Description("B")] BusDataAttributes,
|
||||
[Description("S")] PacketFormatAttributes,
|
||||
[Description("A")] PAMAttributes,
|
||||
[Description("C")] DataConversionAttributes,
|
||||
[Description("H")] AirborneHardwareAttributes,
|
||||
[Description("V")] VendorSpecificAttributes
|
||||
}
|
||||
|
||||
public class TMATSCreationTest
|
||||
{
|
||||
public static string CreateTMATS()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var gi = new GeneralInformationGroup();
|
||||
gi.ProgramName = "netacquire-3-pcm-sine-waves";
|
||||
gi.IRIG106RevisionLevel = "07";
|
||||
gi.NumberOfDataSources = 1;
|
||||
gi.SetDataSourceField(1, GeneralInformationGroup.Information.DataSourceIdentificationTags.DataSourceID,
|
||||
"DATASOURCE");
|
||||
gi.SetDataSourceType(1, GeneralInformationGroup.Information.DataSourceTypes.Storage);
|
||||
gi.OriginationDate = new System.DateTime(2018, 11, 20);
|
||||
|
||||
sb.Append(gi.Serialize());
|
||||
|
||||
var storage = new Storage(1);
|
||||
storage.DataSourceID = "DATASOURCE";
|
||||
storage.StorageID = "DATASOURCE";
|
||||
storage.NumberOfChannels = 4;
|
||||
storage.OriginalStorage = true;
|
||||
storage.DateTimeCreated = new System.DateTime(2018, 11, 20);
|
||||
storage.NumberOfSourceBits = 0;
|
||||
storage.RecordingEventsEnabled = false;
|
||||
storage.RecordingIndexEnabled = true;
|
||||
storage.RecordingIndexType = RecordingIndexTypes.Time;
|
||||
storage.RecordingIndexTimeValue = 10000000;
|
||||
storage.SetDataSourceID(1, "Time");
|
||||
storage.SetChannelDataType(1, ChannelDataTypes.IRIGTimeInput);
|
||||
storage.SetTrackNumber(1, 65535);
|
||||
storage.SetPhysicalChannelNumber(1, 65535);
|
||||
storage.SetChannelEnabled(1, true);
|
||||
storage.SetChannelDataLinkName(1, "Time");
|
||||
storage.SetTimeChannelDataTypeFormat();
|
||||
storage.SetTimeFormat(TimeFormats.IRIGB);
|
||||
storage.SetTimeSource(TimeSources.Internal);
|
||||
|
||||
storage.SetDataSourceID(2, "sio/0/in0-10");
|
||||
storage.SetChannelDataType(2, ChannelDataTypes.PCMInput);
|
||||
storage.SetTrackNumber(2, 10);
|
||||
storage.SetPhysicalChannelNumber(2, 10);
|
||||
storage.SetChannelEnabled(2, true);
|
||||
storage.SetChannelDataLinkName(2, "sio/0/in0-10");
|
||||
storage.SetPCMDataTimeFormat(2);
|
||||
storage.SetPCMDataPackingOption(2, DataPackingOptions.ThroughputMode);
|
||||
storage.SetPCMInputClockEdge(2, InputClockEdges.ZeroDegrees);
|
||||
storage.SetPCMInputSignalType(2, InputSignalTypes.SingleEndedWithTTL);
|
||||
storage.SetPCMVideoTypeFormat(2, VideoFormats.None);
|
||||
|
||||
var pcm = new PCM(1);
|
||||
pcm.DataLinkName = "sio/0/in0-10";
|
||||
pcm.PCMCode = PCMCodes.NRZL;
|
||||
pcm.BitsPerSecond = "1000000";
|
||||
pcm.Polarity = Polarities.Normal;
|
||||
pcm.DataDirection = PCMDataDirections.Normal;
|
||||
pcm.TypeFormat = PCMTypeFormats.ClassI;
|
||||
pcm.NumberOfBitsInCommonWordLength = 16;
|
||||
pcm.WordTransferOrder = PCMWordTransferOrders.MostSignificantBit;
|
||||
pcm.PCMWordParity = PCMWordParities.None;
|
||||
|
||||
sb.Append(pcm.Serialize());
|
||||
|
||||
var minorFrameSection = new MinorFrameSection();
|
||||
minorFrameSection.NumberOfMinorFramesInAMajorFrame = 1;
|
||||
minorFrameSection.NumberOfWordsInMinorFrame = 500;
|
||||
minorFrameSection.NumberOfBitsInMinorFrame = 8000;//500*16
|
||||
minorFrameSection.SyncLength = 16;
|
||||
minorFrameSection.SynchronizationPattern = "1110101110010000";
|
||||
|
||||
sb.Append(minorFrameSection.Serialize());
|
||||
|
||||
var subframeSync = new SubframeSync();
|
||||
sb.Append(subframeSync.Serialize());
|
||||
|
||||
storage.SetDataSourceID(3, "sio/0/in1-20");
|
||||
storage.SetChannelDataType(3, ChannelDataTypes.PCMInput);
|
||||
storage.SetTrackNumber(3, 20);
|
||||
storage.SetPhysicalChannelNumber(3, 20);
|
||||
storage.SetChannelEnabled(3, true);
|
||||
storage.SetChannelDataLinkName(3, "sio/0/in1-20");
|
||||
storage.SetPCMDataTimeFormat(3);
|
||||
storage.SetPCMDataPackingOption(3, DataPackingOptions.PackedWithFrameSync);
|
||||
storage.SetPCMInputClockEdge(3, InputClockEdges.ZeroDegrees);
|
||||
storage.SetPCMInputSignalType(3, InputSignalTypes.SingleEndedWithTTL);
|
||||
storage.SetPCMVideoTypeFormat(3, VideoFormats.None);
|
||||
|
||||
var pcm2 = new PCM(2);
|
||||
pcm2.DataLinkName = "sio/0/in1-20";
|
||||
pcm2.PCMCode = PCMCodes.NRZL;
|
||||
pcm2.BitsPerSecond = "1920000";
|
||||
pcm2.Polarity = Polarities.Normal;
|
||||
pcm2.DataRandomized = false;
|
||||
pcm2.TypeFormat = PCMTypeFormats.ClassI;
|
||||
pcm2.NumberOfBitsInCommonWordLength = 16;
|
||||
pcm2.WordTransferOrder = PCMWordTransferOrders.MostSignificantBit;
|
||||
pcm2.PCMWordParity = PCMWordParities.None;
|
||||
|
||||
sb.Append(pcm2.Serialize());
|
||||
|
||||
var minorFrameSection2 = new MinorFrameSection(2);
|
||||
minorFrameSection2.NumberOfMinorFramesInAMajorFrame = 1;
|
||||
minorFrameSection2.NumberOfWordsInMinorFrame = 300;
|
||||
minorFrameSection2.NumberOfBitsInMinorFrame = 4800;//300*16
|
||||
minorFrameSection2.SyncLength = 16;
|
||||
minorFrameSection2.SynchronizationPattern = "1110101110010000";
|
||||
|
||||
sb.Append(minorFrameSection2.Serialize());
|
||||
|
||||
var subframeSync2 = new SubframeSync(2);
|
||||
sb.Append(subframeSync2.Serialize());
|
||||
|
||||
storage.SetDataSourceID(4, "sio/0/in2-30");
|
||||
storage.SetChannelDataType(4, ChannelDataTypes.MessageDataInput);
|
||||
storage.SetTrackNumber(4, 30);
|
||||
storage.SetPhysicalChannelNumber(4, 30);
|
||||
storage.SetChannelEnabled(4, true);
|
||||
storage.SetChannelDataLinkName(4, "sio/0/in2-30");
|
||||
sb.Append(storage.Serialize());
|
||||
|
||||
MessageDataType mdt = new MessageDataType(1);
|
||||
mdt.SetMessageTypeFormat(4, "0");
|
||||
mdt.SetNumberOfMessageSubChannels(4, 1);
|
||||
mdt.SetMessageSubChannelNumber(4, 1, 1);
|
||||
mdt.SetMessageSubChannelName(4, 1, "sio/0/in2-30");
|
||||
|
||||
sb.Append(mdt.Serialize());
|
||||
return sb.ToString();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
171
Common/DTS.Common.Serialization/IRIGCH10/TMATS/TMATSSection.cs
Normal file
171
Common/DTS.Common.Serialization/IRIGCH10/TMATS/TMATSSection.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
/// <summary>
|
||||
/// this is the base class for sections in the TMATS document
|
||||
/// each section is made of a bunch of attributes and values
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class TMATSSection<T> where T : Enum
|
||||
{
|
||||
private Dictionary<T, string> _values = new Dictionary<T, string>();
|
||||
/// <summary>
|
||||
/// Sets the given tag to a given value
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="value"></param>
|
||||
public void SetValue(T tag, string value) => _values[tag] = value;
|
||||
/// <summary>
|
||||
/// returns given value for a given tag
|
||||
/// value will be null if value was never set
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <returns></returns>
|
||||
public string GetValue(T tag) => _values.ContainsKey(tag)?_values[tag]: null;
|
||||
|
||||
/// <summary>
|
||||
/// sets a given tag to a given date, or an empty string if date is null
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="value"></param>
|
||||
public void SetDate(T tag, DateTime? value)
|
||||
{
|
||||
if (null == value)
|
||||
{
|
||||
SetValueWithLength(tag, string.Empty);
|
||||
}
|
||||
var dt = (DateTime) value;
|
||||
SetValueWithLength(tag, $"{dt.Month:00}-{dt.Day:00}-{dt.Year:0000}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// gets a date for a tag, or null if date isn't set
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <returns></returns>
|
||||
public DateTime? GetDate(T tag)
|
||||
{
|
||||
var value = GetValue(tag);
|
||||
if( string.IsNullOrWhiteSpace(value) ){ return null; }
|
||||
|
||||
if (DateTime.TryParseExact(value, "MM-DD-YYYY", System.Globalization.CultureInfo.InvariantCulture,
|
||||
System.Globalization.DateTimeStyles.None,
|
||||
out var dt))
|
||||
{
|
||||
return dt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// gets a int value for tag, or null if tag was never set
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <returns></returns>
|
||||
public int? GetIntOrNull(T tag)
|
||||
{
|
||||
var val = GetValue(tag);
|
||||
if( string.IsNullOrWhiteSpace(val)){ return null; }
|
||||
|
||||
if (int.TryParse(val, out var iTmp))
|
||||
{
|
||||
return iTmp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// sets tag value
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="val"></param>
|
||||
public void SetIntOrNull(T tag, int? val)
|
||||
{
|
||||
if (null == val)
|
||||
{
|
||||
SetValueWithLength(tag, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetValueWithLength(tag, ((int) val).ToString());
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// originally designed to check the length of values against the max length in spec
|
||||
/// however no longer checks length as max length is just a suggestion in the spec...
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="value"></param>
|
||||
public void SetValueWithLength(T tag, string value)
|
||||
{
|
||||
var maxLength = MaxLengthDecoder.GetMaxLength(tag);
|
||||
//apprently maxlength is just a suggestion ...
|
||||
//if (maxLength < value.Length)
|
||||
//{
|
||||
// throw new FormatException($"{tag.ToString()} must be {maxLength} characters or less");
|
||||
//}
|
||||
SetValue(tag, value);
|
||||
}
|
||||
/// <summary>
|
||||
/// returns all fields in the section for the tmats document
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual string Serialize()
|
||||
{
|
||||
var tags = Enum.GetValues(typeof(T)).Cast<T>().ToArray();
|
||||
var sb = new StringBuilder();
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
var s = Serialize(tag);
|
||||
if( string.IsNullOrWhiteSpace(s)){ continue; }
|
||||
sb.AppendLine(s);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// some attributes have to be associated with a number, like P-x\N:1 where x is
|
||||
/// the channel number, this keeps track of the number for all attributes
|
||||
/// </summary>
|
||||
private int _number = -1;
|
||||
private AttributeIdentifiers _attribute = AttributeIdentifiers.GeneralInformation;
|
||||
/// <summary>
|
||||
/// serializes just one tag/value to a string
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <returns></returns>
|
||||
protected string Serialize(T tag)
|
||||
{
|
||||
var value = GetValue(tag);
|
||||
if( string.IsNullOrWhiteSpace(value)){ return null; }
|
||||
var attrib = DescriptionDecoder.GetDescription(tag);
|
||||
var attributeIdentifier = DescriptionDecoder.GetDescription(_attribute);
|
||||
if (_number < 0)
|
||||
{
|
||||
return $"{attributeIdentifier}\\{attrib}:{value};";
|
||||
}
|
||||
return $"{attributeIdentifier}-{_number}\\{attrib}:{value};";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// generic constructor for attributes which don't need to be associated with a number,
|
||||
/// like General Information
|
||||
/// </summary>
|
||||
public TMATSSection()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// constructor for attributes that are associated with a specific number, like PCM
|
||||
/// being associated with a channel number
|
||||
/// </summary>
|
||||
/// <param name="attribute"></param>
|
||||
/// <param name="number"></param>
|
||||
public TMATSSection(AttributeIdentifiers attribute, int number)
|
||||
{
|
||||
_attribute = attribute;
|
||||
_number = number;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace IRIGCh10
|
||||
{
|
||||
public class TMATSectionNumbered<T> where T : Enum
|
||||
{
|
||||
public AttributeIdentifiers Identifier { get; set; } = AttributeIdentifiers.GeneralInformation;
|
||||
public int Number { get; set; } = -1;
|
||||
|
||||
private Dictionary<T, string> _values = new Dictionary<T, string>();
|
||||
public void SetValue(T tag, string value) => _values[tag] = value;
|
||||
public string GetValue(T tag) => _values.ContainsKey(tag)?_values[tag] : null;
|
||||
public string Serialize(int number)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var tags = Enum.GetValues(typeof(T)).Cast<T>().ToArray();
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
var s = Serialize(number, tag);
|
||||
if( string.IsNullOrWhiteSpace(s) ){ continue; }
|
||||
|
||||
sb.AppendLine(s);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string Serialize(int number, T tag)
|
||||
{
|
||||
var value = GetValue(tag);
|
||||
if( string.IsNullOrWhiteSpace(value) ){ return null; }
|
||||
var attr = DescriptionDecoder.GetDescription(tag);
|
||||
var identifier = DescriptionDecoder.GetDescription(Identifier);
|
||||
if (Number > 0)
|
||||
{
|
||||
return $"{identifier}-{Number}\\{attr}{number}:{value};";
|
||||
}
|
||||
else{ return $"{identifier}\\{attr}{number}:{value};"; }
|
||||
}
|
||||
public void SetValueWithLength(T tag, string value)
|
||||
{
|
||||
var maxLength = MaxLengthDecoder.GetMaxLength(tag);
|
||||
//maxlength is just a suggestion ...
|
||||
//if (maxLength < value.Length)
|
||||
//{
|
||||
// throw new FormatException($"{tag.ToString()} must be {maxLength} characters or less");
|
||||
//}
|
||||
SetValue(tag, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class TMATSectionNumberedArray<T> where T:Enum
|
||||
{
|
||||
private List<TMATSectionNumbered<T>> _items = new List<TMATSectionNumbered<T>>();
|
||||
private AttributeIdentifiers _attributeIdentifier = AttributeIdentifiers.GeneralInformation;
|
||||
private readonly int _number = -1;
|
||||
private readonly int _maxNumber = -1;
|
||||
|
||||
private string _numberedTag;
|
||||
public TMATSectionNumberedArray(string numberedTag)
|
||||
{
|
||||
_numberedTag = numberedTag;
|
||||
}
|
||||
|
||||
public TMATSectionNumberedArray(string numberedTag, int maxNumber)
|
||||
{
|
||||
_numberedTag = numberedTag;
|
||||
_maxNumber = maxNumber;
|
||||
}
|
||||
|
||||
public TMATSectionNumberedArray(AttributeIdentifiers identifier, int number, string numberedTag, int maxNumber)
|
||||
{
|
||||
_number = number;
|
||||
_maxNumber = maxNumber;
|
||||
_numberedTag = numberedTag;
|
||||
_attributeIdentifier = identifier;
|
||||
}
|
||||
//private AttributeIdentifiers _attribute = AttributeIdentifiers.GeneralInformation;
|
||||
|
||||
public virtual void SetValue(int number, T tag, string value)
|
||||
{
|
||||
if (number > _items.Count)
|
||||
{
|
||||
SetCount(number);
|
||||
}
|
||||
|
||||
_items[number - 1].SetValueWithLength(tag, value);
|
||||
}
|
||||
|
||||
public string GetValue(int number, T tag) => number > _items.Count ? null : _items[number - 1].GetValue(tag);
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
if (!_items.Any())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var attribute = DescriptionDecoder.GetDescription(_attributeIdentifier);
|
||||
if (_number > 0)
|
||||
{
|
||||
attribute = $"{attribute}-{_number}";
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(_numberedTag))
|
||||
{
|
||||
sb.AppendLine($"{attribute}\\{_numberedTag}:{_items.Count()};");
|
||||
}
|
||||
|
||||
var number = 1;
|
||||
foreach (var item in _items)
|
||||
{
|
||||
var s = item.Serialize(number++);
|
||||
if( string.IsNullOrWhiteSpace(s) ){ continue; }
|
||||
sb.Append(s);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public int GetCount() => _items.Count;
|
||||
|
||||
public void SetCount(int count)
|
||||
{
|
||||
if (_maxNumber > 0 && count > _maxNumber)
|
||||
{
|
||||
throw new Exception($"count exceeds maximum number for array");
|
||||
}
|
||||
if( _items.Count == count ){ return; }
|
||||
if (_items.Count > count)
|
||||
{
|
||||
var toKeep = _items.Take(count);
|
||||
_items.Clear();
|
||||
_items.AddRange(toKeep);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = _items.Count; i < count; i++)
|
||||
{
|
||||
var newItem = new TMATSectionNumbered<T>();
|
||||
_items.Add(newItem);
|
||||
if (_number > 0)
|
||||
{
|
||||
newItem.Number = _number;;
|
||||
}
|
||||
|
||||
newItem.Identifier = _attributeIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Common/DTS.Common.Serialization/IRIGCH10/Utils/Utils.cs
Normal file
113
Common/DTS.Common.Serialization/IRIGCH10/Utils/Utils.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// helper class for getting BCD values as defined in chapter 10 for things like dates,
|
||||
/// also contains 16/32 bit checksum functions
|
||||
/// </summary>
|
||||
public abstract class Utils
|
||||
{
|
||||
public static byte[] GetBCDBytes(int value)
|
||||
{
|
||||
if (value < 0 || value > 9999)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("value must be between 0, 9999");
|
||||
}
|
||||
|
||||
var bcd = 0;
|
||||
for (var digit = 0; digit < 4; ++digit)
|
||||
{
|
||||
var nibble = value % 10;
|
||||
bcd |= nibble << (digit * 4);
|
||||
value /= 10;
|
||||
}
|
||||
return new byte[] { (byte)(bcd & 0xff), (byte)((bcd >> 8) & 0xff) };
|
||||
}
|
||||
public static ushort GetCheckSum8(byte[] bytes)
|
||||
{
|
||||
ushort crc = 0;
|
||||
foreach (var v in bytes)
|
||||
{
|
||||
crc += v;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
/// <summary>
|
||||
/// computes a 16 bit checksum per ch10
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
public static ushort GetCheckSum16(byte[] bytes)
|
||||
{
|
||||
//we might want to pad with a 0 byte or drop a byte if odd number of bytes?
|
||||
//currently we only use this for packet headers, which are always of a set length, this
|
||||
//is just here for sanity
|
||||
System.Diagnostics.Trace.Assert(0 == bytes.Length % 2, "requires even length incoming data");
|
||||
|
||||
ushort crc = 0;
|
||||
var uints = new ushort[bytes.Length / 2];
|
||||
Buffer.BlockCopy(bytes, 0, uints, 0, bytes.Length);
|
||||
for (var i = 0; i < uints.Length; i++)
|
||||
{
|
||||
crc += uints[i];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
/// <summary>
|
||||
/// computes a 32 bit checksum per ch 10,
|
||||
/// we use this currently for data packet checksums
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
public static uint GetCheckSum32(byte[] bytes)
|
||||
{
|
||||
//most packets should be filled in after the data portion with filler bytes to have
|
||||
//a length divisible by 4, this is just a sanity check
|
||||
System.Diagnostics.Trace.Assert(0 == bytes.Length % 4);
|
||||
|
||||
uint crc = 0;
|
||||
var uints = new uint[bytes.Length / 4];
|
||||
Buffer.BlockCopy(bytes, 0, uints, 0, bytes.Length);
|
||||
for (var i = 0; i < uints.Length; i++)
|
||||
{
|
||||
crc += uints[i];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
/// <summary>
|
||||
/// returns an int given a bitarray and the starting and stopping point for bits of interest
|
||||
/// This allows you to return an int from 1-4 bytes
|
||||
/// </summary>
|
||||
/// <param name="ba"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <param name="endIndex"></param>
|
||||
/// <returns></returns>
|
||||
public static int BitArrayToInt32(BitArray ba, int startIndex, int endIndex)
|
||||
{
|
||||
var n = 0;
|
||||
var bit = 0;
|
||||
for (var i = startIndex; i <= endIndex; i++)
|
||||
{
|
||||
if (ba.Get(i))
|
||||
{
|
||||
n |= 1 << bit;
|
||||
}
|
||||
bit++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
public static void SetBits(BitArray b, uint value, int startIndex, int endIndex)
|
||||
{
|
||||
var ba = new BitArray(BitConverter.GetBytes(value));
|
||||
|
||||
var bit = 0;
|
||||
for (var i = startIndex; i <= endIndex; i++)
|
||||
{
|
||||
b.Set(i, ba.Get(bit));
|
||||
bit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Common/DTS.Common.Serialization/IRIGCH10/WriteTest.cs
Normal file
72
Common/DTS.Common.Serialization/IRIGCH10/WriteTest.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using IRIGCh10;
|
||||
using System;
|
||||
|
||||
namespace DTS.Serialization.IRIGCH10
|
||||
{
|
||||
//public class WriteTest
|
||||
//{
|
||||
// public static void Write()
|
||||
// {
|
||||
// var ch10File = new Chapter10File();
|
||||
|
||||
// ch10File.AddPacket(CreateTMATSPacket());
|
||||
// ch10File.AddPacket(CreateTimePacket());
|
||||
// ch10File.AddPacket(CreateUserDefinedPacket());
|
||||
// ch10File.AddPacket(CreatePCMPacket());
|
||||
// }
|
||||
|
||||
// private const long REFERENCE_RTC = 141989612500056;
|
||||
// private const long SECOND_RTC = 141989612612992;
|
||||
// private const long THIRD_RTC = 141989612606736;
|
||||
// private static IDataPacket CreateTMATSPacket()
|
||||
// {
|
||||
// var tmats = new TMATSPacket();
|
||||
// tmats.SetRTC(REFERENCE_RTC);
|
||||
// var s = TMATSCreationTest.CreateTMATS();
|
||||
// tmats.SetTMATSDocument(s);
|
||||
// return tmats;
|
||||
// }
|
||||
|
||||
// private static IDataPacket CreatePCMPacket()
|
||||
// {
|
||||
// var pcmPacket = new PCMPacket();
|
||||
// pcmPacket.SetRTC(THIRD_RTC);
|
||||
// pcmPacket.SetChannelID(BitConverter.ToUInt16(new byte[] { 0x00, 0x0A}, 0));
|
||||
// pcmPacket.SetDataVersion(Enums.DataTypeVersion.TG78);
|
||||
// pcmPacket.SetSequenceNumber(0);
|
||||
// pcmPacket.SetPacketFlags(false, false, false, false, Enums.SecondaryHeaderTimeFormat.IRIG106Chapter4,
|
||||
// Enums.DataCheckSumType.ThirtyTwoBit);
|
||||
// var data = new ushort[625];
|
||||
|
||||
// var amplitude = .25 * short.MaxValue;
|
||||
// var frequency = 1000;
|
||||
// for (var i = 0; i < data.Length; i++)
|
||||
// {
|
||||
// var val = (ushort)(0x8000 + amplitude * Math.Sin(2 * Math.PI * i * frequency) / 625);
|
||||
// }
|
||||
// //pcmPacket.SetThroughputData(data);
|
||||
// return pcmPacket;
|
||||
// }
|
||||
// private static IDataPacket CreateTimePacket()
|
||||
// {
|
||||
// var timePacket = new TimeDataPacket();
|
||||
// timePacket.SetRTC(SECOND_RTC);
|
||||
// timePacket.SetTime(new DateTime(2018, 11, 20, 22, 51, 11, 320));
|
||||
// timePacket.SetTimeFormat(Enums.TimeFormats.IRIGB);
|
||||
// timePacket.SetTimeSource(0x03);
|
||||
// timePacket.SetChannelID(BitConverter.ToUInt16(new byte[] {0xFF, 0xFF}, 0));
|
||||
// timePacket.SetDataVersion(Enums.DataTypeVersion.TG78);
|
||||
// timePacket.SetPacketFlags(false, false, false, false, Enums.SecondaryHeaderTimeFormat.IRIG106Chapter4,
|
||||
// Enums.DataCheckSumType.ThirtyTwoBit);
|
||||
// timePacket.SetSequenceNumber(1);
|
||||
// var bytes = timePacket.GetBytes();
|
||||
// return timePacket;
|
||||
// }
|
||||
|
||||
// private static IDataPacket CreateUserDefinedPacket()
|
||||
// {
|
||||
// var userPacket = new UserDefinedPacket();
|
||||
// return userPacket;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
Reference in New Issue
Block a user