322 lines
18 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|