This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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)
{
}
}
}
}

View 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;
}
}

View 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;
}
}
}

View 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
}
}

View File

@@ -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;
}
}
}

View 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;
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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;
// }
//}
}
}

View 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);
}
}
}

View 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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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()
{
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View 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
}
}

File diff suppressed because it is too large Load Diff

View 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();
}
}
}

View 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;
}
}
}

View File

@@ -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;
}
}
}
}
}

View 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++;
}
}
}
}

View 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;
// }
//}
}