Files
DP44/Common/DTS.Common.Serialization/SliceRaw/SliceRaw.File.BinaryChannelHeader.cs
2026-04-17 14:55:32 -04:00

431 lines
15 KiB
C#

/*
* SliceRaw.File.BinaryChannelHeader.cs
*
* Copyright © 2009
* Diversified Technical Systems, Inc.
* All Rights Reserved
*/
using System;
using System.Collections.Generic;
using System.Text;
using DTS.Common.Utilities;
using DTS.Common.Utilities.Logging;
using DTS.Common.Utils;
namespace DTS.Serialization.SliceRaw
{
// *** see SliceRaw.File.cs ***
public partial class File
{ ///
/// <summary>
/// Representation of the information found in a binary channel header.
/// </summary>
///
public class BinaryChannelHeader
: Exceptional,
IChannelHeader
{ ///
/// <summary>
/// "Magic Key" required for our binary file type.
/// </summary>
///
public static uint RequiredMagicKey => 0x2C36351F;
public static uint CurrentVersionNumber => 0x04;
/// <summary>
/// Header version number required for our binary file type.
/// </summary>
public static List<uint> KnownHeaderVersionNumbers => new List<uint>(new uint[] { 0x01, 0x02, 0x03, 0x04 });
/// <summary>
/// "Magic Key" relatively-UID <see cref="UInt32"/> for our binary file type.
/// </summary>
public uint MagicKey
{
get;
set;
}
/// <summary>
/// <see cref="UInt32"/> header version number for our binary file type.
/// </summary>
public uint HeaderVersionNumber
{
get;
set;
}
/// <summary>
/// Get/set offset of sample data start value.
/// </summary>
public ulong OffsetOfSampleDataStart
{
get;
set;
}
/// <summary>
/// Get/set number of samples.
/// </summary>
public ulong NumberOfSamples
{
get;
set;
}
/// <summary>
/// Get/set number of bits per sample.
/// </summary>
public uint NumberOfBitsPerSample
{
get;
set;
}
/// <summary>
/// Get/set signed sample indicator.
/// </summary>
public uint AreSamplesSigned
{
get;
set;
}
/// <summary>
/// Get/set sample rate value.
/// </summary>
public double SampleRate
{
get;
set;
}
/// <summary>
/// Get/set number of triggers.
/// </summary>
public ushort NumberOfTriggers
{
get;
set;
}
/// <summary>
/// Get/set the trigger sample numbers
/// </summary>
public ulong[] TriggerSampleNumbers
{
get;
set;
}
/// <summary>
/// the number of adjustment samples for the channel
/// The nature of level triggered is such that a number of qualification samples must pass before the
/// trigger is issued and then backdated to where the trigger started, which requires all other units
/// all be adjusted
/// </summary>
public int TriggerAdjustmentSamples
{
get;
set;
}
/// <summary>
/// Get/set pre test zero level counts.
/// </summary>
public int PreTestZeroLevelCounts
{
get;
set;
}
/// <summary>
/// the number of Analog Data Counts (ADC) removed during any hardware
/// zeroing on the device.
/// In the case of SLICE the firmware will remove as much offset as possible and zero the DAC
/// </summary>
public int RemovedADC
{
get;
set;
}
/// <summary>
/// Get/set pre test diagnostics level counts.
/// </summary>
public int PreTestDiagnosticsLevelCounts
{
get;
set;
}
/// <summary>
/// the Analog Data Counts (ADC) when 0mV is injected
/// (SLICE 1 can not inject 0mV and so will always return 0)
/// </summary>
public int ZeroMvInADC
{
get;
set;
}
/// <summary>
/// The average ADC over the Zero Window specified for the channel
/// </summary>
public int WindowAverageADC
{
get;
set;
}
/// <summary>
/// Initial Offset in ADC
/// </summary>
public int OriginalOffsetADC
{
get;
set;
}
/// <summary>
/// Get/set pre test noise percentage of full scale.
/// </summary>
public double PreTestNoisePercentageOfFullScale
{
get;
set;
}
/// <summary>
/// excitation on this channel
/// with SLICE2 excitation can differ on each channel, so this gives a firm placeholder for information
/// </summary>
public double Excitation
{
get;
set;
}
/// <summary>
/// Get/set post test zero level counts.
/// </summary>
public int PostTestZeroLevelCounts
{
get;
set;
}
/// <summary>
/// Get/set post test diagnostics level counts.
/// </summary>
public int PostTestDiagnosticsLevelCounts
{
get;
set;
}
/// <summary>
/// Get/set data zero level counts.
/// </summary>
public int DataZeroLevelCounts
{
get;
set;
}
/// <summary>
/// Get/set scale factor MV.
/// </summary>
public double ScaleFactorMv
{
get;
set;
}
/// <summary>
/// Get/set sensitivity MV/EU
/// </summary>
public double MvPerEu
{
get;
set;
}
/// <summary>
/// Get/set EU field length (with terminator).
/// </summary>
public ushort EuFieldLengthWithTerminator
{
get;
set;
}
/// <summary>
/// Get/set engineering units.
/// </summary>
public char[] EngineeringUnit
{
get;
set;
}
/// <summary>
/// Get/set ISO code.
/// </summary>
public char[] IsoCode
{
get;
set;
}
private const int HEADER_VERSION_REMOVEDADC = 2;
private const int HEADER_VERSION_3 = 3;
private const int HEADER_VERSION_4 = 4;
/// <summary>
/// Calculate the CRC32 for this binary channe header.
/// </summary>
///
/// <param name="stripEuPadding">
/// <see cref="bool"/> option enables/disables automatic stripping of EU padding.
/// </param>
///
/// <returns>
/// The CRC32 of the binary channel header.
/// </returns>
///
private uint CalculateCrc32(bool stripEuPadding, bool bKeepEuLength)
{
try
{
var data = new List<byte>();
data.AddRange(BitConverter.GetBytes(MagicKey));
data.AddRange(BitConverter.GetBytes(HeaderVersionNumber));
data.AddRange(BitConverter.GetBytes(OffsetOfSampleDataStart));
data.AddRange(BitConverter.GetBytes(NumberOfSamples));
data.AddRange(BitConverter.GetBytes(NumberOfBitsPerSample));
data.AddRange(BitConverter.GetBytes(NumberOfSamples));
data.AddRange(BitConverter.GetBytes(AreSamplesSigned));
data.AddRange(BitConverter.GetBytes(SampleRate));
data.AddRange(BitConverter.GetBytes(NumberOfTriggers));
for (var i = 0; i < TriggerSampleNumbers.Length; i++)
data.AddRange(BitConverter.GetBytes(TriggerSampleNumbers[i]));
data.AddRange(BitConverter.GetBytes(PreTestZeroLevelCounts));
//data.AddRange(BitConverter.GetBytes(PreTestZeroLevelMv));
if (HeaderVersionNumber >= HEADER_VERSION_REMOVEDADC) { data.AddRange(BitConverter.GetBytes(RemovedADC)); }
if (HeaderVersionNumber >= HEADER_VERSION_3)
{
data.AddRange(BitConverter.GetBytes(Excitation));
data.AddRange(BitConverter.GetBytes(TriggerAdjustmentSamples));
data.AddRange(BitConverter.GetBytes(ZeroMvInADC));
data.AddRange(BitConverter.GetBytes(OriginalOffsetADC));
}
if (HeaderVersionNumber >= HEADER_VERSION_4)
{
data.AddRange(BitConverter.GetBytes(WindowAverageADC));
}
data.AddRange(BitConverter.GetBytes(PreTestDiagnosticsLevelCounts));
data.AddRange(BitConverter.GetBytes(PreTestNoisePercentageOfFullScale));
data.AddRange(BitConverter.GetBytes(PostTestZeroLevelCounts));
data.AddRange(BitConverter.GetBytes(PostTestDiagnosticsLevelCounts));
data.AddRange(BitConverter.GetBytes(DataZeroLevelCounts));
data.AddRange(BitConverter.GetBytes(ScaleFactorMv));
data.AddRange(BitConverter.GetBytes(MvPerEu));
// Some data sets have an erroneous EuFieldLengthWithTerminator value that's +1 what it should be,
// so we'll want to make an allowance for these ones too.
var plusOneFieldLengthBugErrorPresent = EuFieldLengthWithTerminator > EngineeringUnit.Length + 1;
if (bKeepEuLength) { plusOneFieldLengthBugErrorPresent = true; }
var padAdjustedEu = new string(EngineeringUnit).Trim().ToCharArray();
if (!stripEuPadding)
{ //
// If we want to calculate the CRC of the header with leading/trailing
// whitespace intact/added.
//
if (padAdjustedEu.Length % 2 != 0)
padAdjustedEu = (new string(padAdjustedEu) + ' ').ToCharArray();
}
var euBA = Encoding.UTF8.GetBytes(padAdjustedEu);
var euFieldLength = plusOneFieldLengthBugErrorPresent ? EuFieldLengthWithTerminator : (ushort)(euBA.Length + 1);
data.AddRange(BitConverter.GetBytes(euFieldLength));
for (var i = 0; i < padAdjustedEu.Length; i++)
data.AddRange(BitConverter.GetBytes(padAdjustedEu[i]));
for (var i = 0; i < IsoCode.Length; i++)
data.AddRange(BitConverter.GetBytes(IsoCode[i]));
// We need to pad the offset so DIAdem .dat file word alignment will work. DIAdem .dat files reference the .chn files directly.
if (data.Count % 2 > 0) data.Add(0x0);
var byteDataArray = data.ToArray();
//APILogger.Log($"[DTM]: data array for CRC: {BitConverter.ToString(byteDataArray)}");
ushort crc = 0xFFFF;
for (var i = 0; i < data.Count; i += 2)
crc = Utils.Math_DoCRC16Step(BitConverter.ToUInt16(byteDataArray, i), crc);
//APILogger.Log($"[DTM]: crc {crc}");
return crc;
}
catch (System.Exception ex)
{
throw new Exception("encountered problem calculating CRC 32", ex);
}
}
public uint UnpaddedEuStringPaddedEuLengthCrc32
{
get
{
try { return CalculateCrc32(true, true); }
catch (System.Exception ex)
{
throw new Exception("encountered problem getting CRC32 for binary header channel with unpadded EU string and padded EU Length", ex);
}
}
}
/// <summary>
/// Get The CRC for the current state of the header (autostrip padding from EU first).
/// </summary>
public uint UnpaddedEuCrc32
{
get
{
try
{
return CalculateCrc32(true, false);
}
catch (System.Exception ex)
{
throw new Exception("encountered problem getting CRC32 for binary header channel with unpadded EU string", ex);
}
}
}
/// <summary>
/// Get the CRC for the current state of the header.
/// </summary>
public uint Crc32
{
get
{
try
{
return CalculateCrc32(false, false);
}
catch (System.Exception ex)
{
throw new Exception("encountered problem generating CRC for binary channel header information", ex);
}
}
}
}
}
}