Files
2026-04-17 14:55:32 -04:00

322 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using DTS.Common.DAS.Concepts;
using DTS.Common.Utilities;
using DTS.Common.Enums.Sensors;
namespace DTS.Serialization.TDM
{
public class ChannelData
{
public void GenerateChannelData(Writer writer, System.IO.TextWriter tw, bool bFiltered, double start, double end, UInt16 subSampleInterval,
out ulong practicalNumSamples, out ulong numSamples, out Test test, out DataScaler[] scalers, out double sampleRate,
out List<short[]> ChannelDataUnFiltered, out List<double[]> ChannelDataFiltered, out int preTriggerSamples)
{
test = writer.Test;
var maxRate = test.Channels.Select(ch => ch.ParentModule.SampleRateHz).Max();
sampleRate = maxRate;
var distinctRates = test.Channels.Select(ch => ch.ParentModule.SampleRateHz).Distinct().ToArray();
var testChannels = new List<Test.Module.Channel>(test.Channels);
//numSamples = Convert.ToUInt64((from ch in testChannels select ch.PersistentChannelInfo.Data.Length).Min());
numSamples = Convert.ToUInt64(testChannels.Select(ch => ch.PersistentChannelInfo.Data.Length * (maxRate / ch.ParentModule.SampleRateHz)).Min());
preTriggerSamples = 0;
var originalNumSamples = numSamples;
//note we can have extra data, so numSamples doesn't always mean number of samples
//instead use practical number of samples to be artificially constrained number of samples and number of samples to mean
//either, as the existing code did.
//7757 TTS sub sample was not working. exported data is too much.
practicalNumSamples = Convert.ToUInt64((end - start) * sampleRate + 1);
ChannelDataUnFiltered = new List<short[]>();
ChannelDataFiltered = new List<double[]>();
scalers = new DataScaler[testChannels.Count];
if (1 < subSampleInterval)
{
if (distinctRates.Length > 1) { throw new System.Exception("we are both super sampling and subsampling in this export."); }
for (var iChannel = 0; iChannel < test.Channels.Count; iChannel++)
{
var rate = Convert.ToInt32(sampleRate / test.Channels[iChannel].ParentModule.SampleRateHz);
if (0 == iChannel)
{
practicalNumSamples = Convert.ToUInt64(Math.Ceiling(practicalNumSamples / (double)subSampleInterval));
}
if (true == bFiltered)
{
var SubSample = new NHTSASubSample<double>();
SubSample.data = writer.FilteredData[iChannel].Data;
var preTriggerRequested = (ulong)Math.Max(0, (-1 * (int)Math.Truncate(start * sampleRate))); //If start is > 0, it's not pre-trigger, so return 0
SubSample.preTriggerSamples = Math.Min((test.Modules[0].TriggerSampleNumbers[0] - test.Modules[0].StartRecordSampleNumber), preTriggerRequested);
SubSample.sampleRate = sampleRate;
SubSample.subSampleInterval = subSampleInterval;
SubSample.SubSample();
if (iChannel >= ChannelDataFiltered.Count)
{
ChannelDataFiltered.Add(SubSample.data);
}
else
{
ChannelDataFiltered[iChannel] = SubSample.data;
}
//writer.FilteredData[iChannel].Data = SubSample.data;
//ChannelList[iChannel].IsSubsampled = true; // I don't think anyone will consume this, but for completeness.
numSamples = Math.Min(numSamples, (ulong)SubSample.data.Length);
//note we have extra data, so numSamples is accurate, however gives us more data than we want ...
//7757 TTS sub sample was not working. exported data is too much.
if (SubSample.iNpre > 0)
{
preTriggerSamples = SubSample.iNpre + 1;
}
}
else
{
var SubSample = new NHTSASubSample<short>();
SubSample.data = test.Channels[iChannel].PersistentChannelInfo.Data;
var preTriggerRequested = (ulong)Math.Max(0, (-1 * (int)Math.Truncate(start * sampleRate))); //If start is > 0, it's not pre-trigger, so return 0
SubSample.preTriggerSamples = Math.Min((test.Modules[0].TriggerSampleNumbers[0] - test.Modules[0].StartRecordSampleNumber), preTriggerRequested);
SubSample.sampleRate = sampleRate;
SubSample.subSampleInterval = subSampleInterval;
SubSample.SubSample();
if (iChannel >= ChannelDataUnFiltered.Count)
{
ChannelDataUnFiltered.Add(SubSample.data);
}
else
{
ChannelDataUnFiltered[iChannel] = SubSample.data;
}
//ChannelList[iChannel].IsSubsampled = true; // I don't think anyone will consume this, but for completeness.
numSamples = Math.Min(numSamples, (ulong)SubSample.data.Length);
//note we have extra data, so numSamples is accurate, however gives us more data than we want ...
//7757 TTS sub sample was not working. exported data is too much.
if (SubSample.iNpre > 0)
{
preTriggerSamples = SubSample.iNpre + 1;
}
}
}
sampleRate = sampleRate / subSampleInterval;
}
else
{
var TotalSamples = ulong.MaxValue;
//the lowest common start in samples from t0 that all modules have
var minStart = double.MinValue;
foreach (var module in test.Modules)
{
//Don't process Slice6DB modules that were created to store Temperature values from Arm checklist
if (module.Channels.Count <= 0) continue;
TotalSamples = Math.Min(TotalSamples, module.NumberOfSamples);
double mStart = module.TriggerSampleNumbers[0] - module.StartRecordSampleNumber;
minStart = Math.Max(minStart, mStart);
}
var startSample = -1D * start * test.Modules[0].SampleRateHz;
//we don't have enough data, adjust appropriately
if (startSample > minStart) { startSample = minStart; }
preTriggerSamples = (int)Math.Truncate(startSample);
}
}
public void WriteChannelData(Writer writer, System.IO.TextWriter tw, bool bFiltered, double start, double end, UInt16 subSampleInterval,
ulong practicalNumSamples, ulong numSamples, Test test, DataScaler[] scalers, double sampleRate, List<short[]> ChannelDataUnFiltered,
List<double[]> ChannelDataFiltered)
{
var bSubSampled = subSampleInterval > 1;
// var maxSamples = (ulong)(bFiltered ? writer.FilteredData[0].Data.Length : (test.Channels[0] as Test.Module.AnalogInputChannel).PersistentChannelInfo.Data.Length);
var maxSamples = 0UL;
if (bFiltered)
{
maxSamples = Convert.ToUInt64(writer.FilteredData.Select(ch => ch.Data.Length).Max());
}
else
{
maxSamples = Convert.ToUInt64(test.Channels.Select(ch => ch.PersistentChannelInfo.Data.Length).Max());
}
if (bSubSampled)
{
maxSamples = Convert.ToUInt64(bFiltered ? ChannelDataFiltered[0].Length : ChannelDataUnFiltered[0].Length);
}
var channelList = new List<Test.Module.Channel>(test.Channels);
//var parentModule = (channelList[0] as Test.Module.AnalogInputChannel).ParentModule;
//var moduleSampleRate = (double)parentModule.SampleRateHz;
//var moduleTriggerSampleNumber = (double)parentModule.TriggerSampleNumbers[0];
//var moduleStartRecordSampleNumber = (double)parentModule.StartRecordSampleNumber;
for (ulong i = 0; i < practicalNumSamples && i < numSamples; i++)
{
if (i > 0 && 0 == i % writer.IncrementLevel)
{
writer.IncrementDone((double)i / practicalNumSamples * 100);
}
// Need to be a little defensive about the end of the array for the sub-sample case where dIndex might be non-integer and then rounded up.
//var dIndex = (start * moduleSampleRate + moduleTriggerSampleNumber) / subSampleInterval + Convert.ToDouble(i);
//we now have dIndex = the start index in terms of sample count, we want to translate it to an index in our data
//dIndex = dIndex - moduleStartRecordSampleNumber / subSampleInterval;
//if (dIndex > Convert.ToDouble(maxSamples - 1))
//{
// break;
//}
var time = start + (i / sampleRate);
tw.Write(time.ToString("e5", CultureInfo.InvariantCulture));
tw.Write(",");
var ChannelCount = channelList.Count;
for (var iChannel = 0; iChannel < ChannelCount; iChannel++)
{
var channel = channelList[iChannel] as Test.Module.AnalogInputChannel;
DataScaler scaler = null;
if (channel.IsSquibVoltage())
{
continue;
}
if (iChannel < scalers.Length) { scaler = scalers[iChannel]; }
if (null == scaler)
{
scaler = new DataScaler();
scaler.IsInverted = channel.IsInverted;//channel is DTS.Common.DAS.Concepts.DAS.Channel.IInversionAware ? (channel as DTS.Common.DAS.Concepts.DAS.Channel.IInversionAware).IsInverted : false;
scaler.SetLinearizationFormula(channel.LinearizationFormula);
scaler.SetScaleFactorMv(channel.Data.ScaleFactorMv);
scaler.SetScaleFactorEU(channel.Data.ScaleFactorEU);
scaler.SetUseEUScaleFactors(channel.Data.UseEUScaleFactors);
var analogChannel = channel;
if (channel is Test.Module.AnalogInputChannel)
{
scaler.IEPE = analogChannel.Bridge == SensorConstants.BridgeType.IEPE;
scaler.Digital = analogChannel.Bridge == SensorConstants.BridgeType.DigitalInput;
scaler.SetDigitalMultiplier(analogChannel.DigitalMultiplier);
//FB 13414 Apply EU multiplier in TTS export.
scaler.Multiplier = analogChannel.Multiplier;
//FB 16222 Apply EU Offset in TTS export.
scaler.UserOffsetEU = analogChannel.UserOffsetEU;
scaler.DigitalMode = analogChannel.DigitalMode;
}
scaler.SetLinearizationFormula(channel.LinearizationFormula);
scaler.SetMvPerEu(channel.Data.MvPerEu);
scaler.UnitConversion = channel.UnitConversion;
scaler.BasedOnOutputAtCapacity = channel.AtCapacity;
scaler.CapacityOutputIsBasedOn = channel.CapacityOutputIsBasedOn;
scaler.SensitivityUnits = channel.SensitivityUnits;
try
{
scaler.SetInitialOffset(analogChannel.InitialOffset);
scaler.ZeroMethodType = analogChannel.ZeroMethod;
if (analogChannel.RemoveOffset) { scaler.SetRemovedADC(channel.RemovedADC); }
else { scaler.SetRemovedADC(0); }
scaler.SetDataZeroLevelADC(channel.DataZeroLevelAdc);
scaler.SetZeroMvInADC(channel.ZeroMvInADC);
try { scaler.SetWindowAverageADC(channel.WindowAverageADC); }
catch (Exception) { }
}
catch (Exception) { }
scaler.NominalExcitationVoltage = analogChannel.ExcitationVoltage;
if (analogChannel.MeasuredExcitationVoltageValid)
{
try { scaler.MeasuredExcitationVoltage = analogChannel.MeasuredExcitationVoltage; }
catch { };
}
if (analogChannel.FactoryExcitationVoltageValid)
{
try { scaler.FactoryExcitationVoltage = analogChannel.FactoryExcitationVoltage; }
catch { };
}
scaler.SetInitialOffset(analogChannel.InitialOffset);
scaler.ZeroMethodType = analogChannel.ZeroMethod;
scaler.ProportionalToExcitation = channel.ProportionalToExcitation;
scalers[iChannel] = scaler;
}
var dStartTime = (double)channel.ParentModule.StartRecordSampleNumber / channel.ParentModule.SampleRateHz;
if (channel.ParentModule.TriggerSampleNumbers.Count > 0)
{
dStartTime -= (double)channel.ParentModule.TriggerSampleNumbers[0] / channel.ParentModule.SampleRateHz;
}
var rate = Convert.ToInt32(Math.Ceiling(sampleRate / channel.ParentModule.SampleRateHz));
var channelOffsetStart = (int)((dStartTime - start) * channel.ParentModule.SampleRateHz);
var indexAtCurrentTime = ((double)i - channelOffsetStart) / rate;
var thisChannelsIndexAtCurrentTime = Convert.ToInt32(Math.Floor(indexAtCurrentTime));
var step = Convert.ToInt32(Math.Ceiling(indexAtCurrentTime) - thisChannelsIndexAtCurrentTime);
var d = double.NaN;
if (thisChannelsIndexAtCurrentTime >= 0)
{
if (bFiltered)
{
if (bSubSampled)
{
d = scaler.GetEU(ChannelDataFiltered[iChannel][Convert.ToUInt64(thisChannelsIndexAtCurrentTime)]);
}
else
{
var increment = 0D;
var adc = writer.FilteredData[iChannel].Data[Convert.ToUInt64(thisChannelsIndexAtCurrentTime)];
if ((thisChannelsIndexAtCurrentTime + 1) < writer.FilteredData[iChannel].Data.Length)
{
increment = (writer.FilteredData[iChannel].Data[thisChannelsIndexAtCurrentTime + 1] - adc) / rate;
}
else
{
increment = (adc - writer.FilteredData[iChannel].Data[thisChannelsIndexAtCurrentTime - 1]) / rate;
}
d = scaler.GetEU(adc + increment * step);
}
}
else
{
if (bSubSampled)
{
d = scaler.GetEU(ChannelDataUnFiltered[iChannel][Convert.ToInt64(thisChannelsIndexAtCurrentTime)]);
}
else
{
var increment = 0D;
var adc = channel.PersistentChannelInfo.Data[Convert.ToUInt64(thisChannelsIndexAtCurrentTime)];
if ((thisChannelsIndexAtCurrentTime + 1) < channel.PersistentChannelInfo.Data.Length)
{
increment = (channel.PersistentChannelInfo.Data[thisChannelsIndexAtCurrentTime + 1] - adc) / rate;
}
else
{
increment = (adc - channel.PersistentChannelInfo.Data[thisChannelsIndexAtCurrentTime - 1]) / rate;
}
d = scaler.GetEU(adc + increment * step);
}
}
}
tw.Write((d * channel.Sensitivity).ToString("e4", CultureInfo.InvariantCulture));
tw.Write(",");
}
tw.WriteLine();
}
tw.Flush();
}
}
}