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 ChannelDataUnFiltered, out List 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.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(); ChannelDataFiltered = new List(); 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(); 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(); 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 ChannelDataUnFiltered, List 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.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(); } } }