Files
DP44/enriched-partialglm/DTS Viewer/DTS.Viewer.Modules/DTS.Viewer.Graph/Model.md
2026-04-17 14:55:32 -04:00

220 lines
12 KiB
Markdown

---
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<ITestDataSeries> 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<List<ITestDataSeries>> GetTestDataAsync(List<ITestChannel> 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<ITestDataSeries> GetTestData(List<ITestChannel> 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<ITestDataSeries> 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.