--- source_files: - DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.Graph/Model/TestDataSeries.cs - DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.Graph/Model/TestDataSeriesModel.cs generated_at: "2026-04-16T11:14:33.699820+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "1630e6f30be700ba" --- # Documentation: DTS.Viewer.Graph Module - Data Series Models ## 1. Purpose This module provides the data models for representing and transforming test channel data into graphable series within the DTS Viewer application. `TestDataSeries` serves as the primary data transfer object holding X/Y values, metadata, and computed statistics for a single graphable channel. `TestDataSeriesModel` acts as a factory/processor that transforms raw `ITestChannel` data into `TestDataSeries` objects, handling time-series, FFT (Fast Fourier Transform), and PSD (Power Spectral Density) conversions with optional filtering and windowing. Together, they bridge the gap between raw binary sensor data and the visualization layer. --- ## 2. Public Interface ### TestDataSeries Class **Namespace:** `DTS.Viewer.Graph.Model` **Inherits:** `Common.Base.BasePropertyChanged` **Implements:** `ITestDataSeries` #### Properties | Property | Type | Description | |----------|------|-------------| | `HIC` | `bool` | Indicates if Head Injury Criteria data is present. | | `HICValue` | `string` | Formatted HIC value. | | `T1Time`, `T2Time` | `string` | T1/T2 timestamp strings for HIC calculations. | | `TestGroup` | `string` | Group identifier for the test. | | `TestId` | `string` | Unique test identifier. | | `TestSetupName` | `string` | Name of the test setup configuration. | | `ChannelId` | `string` | Channel identifier. | | `Xvalue` | `double[]` | Array of X-axis values (time or frequency). | | `Yvalue` | `double[]` | Array of Y-axis values (amplitude, magnitude, or PSD). | | `GraphColor` | `Brush` | WPF brush for graph rendering; getter creates new `SolidColorBrush` from internal `byte[]`. | | `HardwareChannel` | `string` | Hardware channel name (auto-property). | | `GroupName` | `string` | Channel group name (auto-property). | | `SWAAF` | `string` | Software anti-aliasing filter setting (auto-property). | | `Bridge` | `string` | Bridge type identifier (auto-property). | | `HWAAF` | `string` | Hardware anti-aliasing filter rate (auto-property). | | `SampleRate` | `string` | Sample rate in Hz (auto-property). | | `ISOCode`, `ISOChannelName` | `string` | ISO-related identifiers (auto-properties). | | `UserCode`, `UserChannelName` | `string` | User-defined identifiers (auto-properties). | | `ChannelName`, `Description` | `string` | Channel naming and description (auto-properties). | | `SensorSN` | `string` | Sensor serial number (auto-property). | | `SensorSNDisplay` | `string` | Display-friendly serial number; returns `"N/A"` if test-specific embedded per `SensorConstants.IsTestSpecificEmbedded()`. | | `EngineeringUnits` | `string` | Engineering units string (auto-property). | | `Excitation` | `string` | Excitation voltage (auto-property). | | `Polarity` | `string` | Sensor polarity (auto-property). | | `MinY`, `MaxY`, `AvgY`, `StdDevY` | `string` | Statistical values; default to `Strings.Table_NA`. | | `PeakMagnitude` | `double` | Peak magnitude for FFT data; default `0`. | | `PeakFrequency` | `double` | Frequency of peak magnitude; default `0`. | | `GRMS` | `double` | Root-mean-squared acceleration for PSD; default `0`. | | `FFT` | `bool` | Indicates if series contains FFT/PSD data; default `false`. | | `T0EUValue` | `string` | T0 value string; default empty. | | `RecordingMode` | `string` | Recording mode description (auto-property). | | `IsSaved` | `bool` | Read-only auto-property (getter only). | #### Methods ```csharp public void SetStatsFromYValues() ``` Sets `AvgY`, `StdDevY`, `MinY`, `MaxY`, `T0EUValue` from internal `Yvalue` array. Formats using `"G5"` format string. ```csharp public void SetStatsFromYValues(double[] values) ``` Overload that accepts an external `double[]` array. Handles null/empty arrays by setting all stats to `NaN` (displayed as `Table_NA`). ```csharp public void SetStatsFromChannel(ITestChannel channel) ``` Copies pre-calculated statistics from an `ITestChannel` instance using `channel.MinY`, `channel.MaxY`, `channel.AveY`, `channel.StdDevY`, `channel.T0Value`. --- ### TestDataSeriesModel Class **Namespace:** `DTS.Viewer.Graph` **Implements:** `IBaseModel` #### Properties | Property | Type | Description | |----------|------|-------------| | `Parent` | `IGraphViewModel` | Parent view model reference. | | `_eventAggregator` | `IEventAggregator` | Prism event aggregator for publishing progress events. | | `ErrorMessage` | `string` | Error message string with property change notification. | | `IsSaved` | `bool` | Read-only auto-property. | #### Methods ```csharp public Task GetTestDataAsync(ITestChannel channel, IChartOptionsModel chartOptions, bool bVolts, IPSDReportSettingsModel psdSettings = null) ``` Async wrapper returning `GetTestData()`. **Warning:** Lacks `await` operators (runs synchronously per compiler warning suppression). ```csharp public Task> GetTestDataAsync(List channels, IChartOptionsModel chartOptions, bool bVolts, IPSDReportSettingsModel psdSettings = null) ``` Async wrapper for batch channel processing. Catches `OutOfDataException` and re-throws with context. Optionally adds envelope channel if `psdSettings.ShowEnvelope` is true. ```csharp public List GetTestData(List channels, IChartOptionsModel chartOptions, bool bVolts, IPSDReportSettingsModel psdSettings = null) ``` Synchronous batch processing. Maps each channel through `GetTestData()`. Handles `OutOfDataException` with sample index context. ```csharp public ITestDataSeries GetTestData(ITestChannel channel, IChartOptionsModel chartOptions, bool bVolts, IPSDReportSettingsModel psdSettings = null) ``` Returns `AddTestChannelToChart()` result. ```csharp public TestDataSeries AddTestChannelToChart(ITestChannel channel, IChartOptionsModel chartOptions, bool bVolts, IPSDReportSettingsModel psdSettings = null) ``` **Primary processing method.** Reads binary channel data via `Serialization.SliceRaw.File.Reader.ReadChannelsBinaryData()`, then branches based on `chartOptions.UnitType`: - **FFT mode** (`ChartUnitTypeEnum.FFT` with `psdSettings == null`): Sets `FFT = true`, populates `PeakFrequency`, `PeakMagnitude`, calculates stats. - **Regular time-series** (`psdSettings == null`): Handles HIC data if present, applies time unit multiplier, copies stats from channel. - **PSD mode** (`psdSettings != null`): Trims data to selected range, applies optional low/high pass filters, computes PSD via `FftSharp.Transform.PSD_Welch()`, calculates GRMS. Returns `null` if `channel.ErrorMessage` is not empty. ```csharp private ITestDataSeries GetEnvelopeChannel(List data) ``` Creates a synthetic "envelope" series by taking the maximum Y-value at each frequency index across all input series. Sets `FFT = true`, color to black. ```csharp private double CalculateGRMS(double[] freq, double[] psd) ``` Calculates Grms using numerical integration of PSD. Uses logarithmic interpolation formula; handles `N = -1` edge case separately. Returns `Math.Sqrt(aRMS.Sum())`. --- ## 3. Invariants 1. **Array Length Consistency**: `Xvalue` and `Yvalue` arrays should have matching lengths after processing by `TestDataSeriesModel`. 2. **FFT Flag Consistency**: When `FFT == true`, `Xvalue` represents frequency (Hz) and `Yvalue` represents magnitude or PSD. When `FFT == false`, `Xvalue` represents time. 3. **Statistics Default**: `MinY`, `MaxY`, `AvgY` default to `Strings.Table_NA` (not null or empty string). 4. **GraphColor Thread Safety**: `GraphColor` getter creates a new `SolidColorBrush` on each call; the underlying color data is stored as `byte[] _graphColorARGB` to ensure thread safety (per comment referencing issue #34455). 5. **FFT/PSD Filter Bypass**: When `chartOptions.UnitType` is `FFT` or `PSD`, `channel.SoftwareFilter` is forcibly set to `"none"` before reading data. 6. **PSD Data Length**: PSD calculations require input length to be an even power of 2; `Utils.GetEnclosingPower2()` is used to resize the array with zero-padding if necessary. 7. **HIC Validity**: `HIC` is only set to `true` when `channel.HIC != 0` AND `channel.T2Sample > 0`. --- ## 4. Dependencies ### TestDataSeries Dependencies **External Dependencies:** - `System.Windows.Media` - `Brush`, `SolidColorBrush`, `Color`, `Colors` - `DTS.Common.Enums.Sensors` - `SensorConstants` for serial number validation - `DTS.Common.Interface` - `ITestDataSeries` interface - `DTS.Common.Strings` - Localized string constants (`Table_NA`) - `DTS.Common.Base` - `BasePropertyChanged` for INPC implementation **Dependents (Inferred):** - `TestDataSeriesModel` - Primary consumer/factory - Graph view models and charting components (via `ITestDataSeries`) ### TestDataSeriesModel Dependencies **External Dependencies:** - `System.Windows.Media` - Color types - `Prism.Events` - `IEventAggregator` for event publishing - `FftSharp` - FFT/PSD transforms (`Transform.PSD_Welch`, `Transform.FFTfreq`, `WindowType`, `WindowAveragingType`) - `Exocortex.DSP` - `PassFilter.LowPass`/`HighPass` filtering **Internal Dependencies:** - `DTS.Common.Interface` - `ITestChannel`, `ITestDataSeries`, `IGraphViewModel`, `IChartOptionsModel`, `IPSDReportSettingsModel`, `IBaseModel` - `DTS.Common.Enums.Viewer` - `ChartUnitTypeEnum`, `TimeUnitTypeEnum`, `Reports.WindowType`, `Reports.WindowAveragingType` - `DTS.Common.Enums.Sensors` - `SensorConstants.BridgeType.DigitalInput` - `DTS.Common.Enums.DASFactory` - `DFConstantsAndEnums.RecordingMode` - `DTS.Common.Converters` - `EnumDescriptionTypeConverter` - `DTS.Common.Exceptions` - `OutOfDataException` - `DTS.Common.Events` - `GraphChannelReadCalcProgressChangedEvent`, `GraphChannelReadCalcProgressChangedEventArgs` - `DTS.Common.Utilities.Logging` - `APILogger` - `DTS.Common.Utils` - `Utils.GetEnclosingPower2()` - `DTS.Common.Strings` - Localized strings - `Serialization.SliceRaw.File.Reader` - `ReadChannelsBinaryData()` for binary data deserialization - `ChannelFilter` - `AdHoc` filter constant **Dependents (Inferred):** - Graph view models implementing `IGraphViewModel` - PSD report generation components --- ## 5. Gotchas 1. **Async Methods Are Not Truly Async**: Both `GetTestDataAsync` overloads have suppressed compiler warning CS1998 and run synchronously. The `async` keyword is misleading—these methods will block the calling thread. 2. **GraphColor Getter Allocates on Every Call**: The `GraphColor` property getter instantiates a new `SolidColorBrush` on every access. Frequent access in UI binding scenarios could cause unnecessary allocations. 3. **PSD Frequency Array Mutation**: In PSD processing, `freq[0] = 1` forcibly sets the first frequency bin to 1 Hz, potentially overwriting the actual DC component value from `FftSharp.Transform.FFTfreq()`. 4. **CalculateStdDev Uses Population Formula Incorrectly**: The method divides by `(values.Length - 1)` (sample standard deviation) but does not check for single-element arrays, which would cause division by zero. 5. **T0EUValue Naming Is Misleading**: Per the source comment: *"this is the T0 value regardless of units ... it's not really EU but since it's already in use I'm not going to change it"* — historical naming debt. 6. **Envelope Channel Returns Empty Series on Null Input**: `GetEnvelopeChannel()` returns a default `TestDataSeries()` if input list is null or empty, rather than returning null or throwing. 7. **IEPE Bridge Hardcoded**: The constant `IEPE_BRIDGE = "IEPE"` is used for special-casing excitation display, but the value is not sourced from a shared constant. 8. **Digital Channel Detection Uses String Prefix**: `channel.Bridge.StartsWith(SensorConstants.BridgeType.DigitalInput.ToString())` relies on string matching rather than enum comparison. 9. **GRMS Calculation Has Index Bounds Check**: The loop condition `i < psd.Length - 2 && i < freq.Length - 2` skips the last two elements, which is correct for the pairwise calculation but may silently drop data if arrays have different lengths.