Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/comtrade-viewer-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ on:
- "ComtradeWorkspaceWindow.xaml"
- "ComtradeWorkspaceWindow.xaml.cs"
- "ComtradeWorkspaceWindow.Analysis.cs"
- "ComtradeWorkspaceWindow.Disturbance.cs"
- "Controls/ComtradeWaveformView.cs"
- "Controls/ComtradeDisturbanceView.cs"
- "Controls/ComtradePhasorView.cs"
- "Controls/ComtradeHarmonicsView.cs"
- "Services/ArdIrecNativeBridge.cs"
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/installer-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ on:
- "FaultRecordWindow.ComtradeOpen.cs"
- "ComtradeWorkspaceWindow.xaml"
- "ComtradeWorkspaceWindow.xaml.cs"
- "ComtradeWorkspaceWindow.Analysis.cs"
- "ComtradeWorkspaceWindow.Disturbance.cs"
- "Controls/ComtradeWaveformView.cs"
- "Controls/ComtradeDisturbanceView.cs"
- "Controls/ComtradePhasorView.cs"
- "Controls/ComtradeHarmonicsView.cs"
- "Services/ArdIrecNativeBridge.cs"
- "Services/ArdIrecViewerLauncher.cs"
- "Services/ComtradeNavigationMath.cs"
- "Services/ComtradeTimeMath.cs"
- "Services/ComtradeAbsoluteViewportMath.cs"
- "Services/ComtradeRangeDecimator.cs"
- "Services/ComtradeDecimatedSeriesBuilder.cs"
- "tests/ARSAS.Tests/ArdIrecNativeBridgeIntegrationTests.cs"
- "tests/ARSAS.Tests/ComtradeNavigationMathTests.cs"
- "tests/ARSAS.Tests/ComtradeTimeMathTests.cs"
- "tests/ARSAS.Tests/ComtradeAbsoluteViewportMathTests.cs"
- "tests/ARSAS.Tests/ComtradeRangeDecimatorTests.cs"
- "tests/ARSAS.Tests/ComtradeDecimatedSeriesBuilderTests.cs"
- "ArIED61850Tester.csproj"
Expand All @@ -43,16 +50,23 @@ on:
- "FaultRecordWindow.ComtradeOpen.cs"
- "ComtradeWorkspaceWindow.xaml"
- "ComtradeWorkspaceWindow.xaml.cs"
- "ComtradeWorkspaceWindow.Analysis.cs"
- "ComtradeWorkspaceWindow.Disturbance.cs"
- "Controls/ComtradeWaveformView.cs"
- "Controls/ComtradeDisturbanceView.cs"
- "Controls/ComtradePhasorView.cs"
- "Controls/ComtradeHarmonicsView.cs"
- "Services/ArdIrecNativeBridge.cs"
- "Services/ArdIrecViewerLauncher.cs"
- "Services/ComtradeNavigationMath.cs"
- "Services/ComtradeTimeMath.cs"
- "Services/ComtradeAbsoluteViewportMath.cs"
- "Services/ComtradeRangeDecimator.cs"
- "Services/ComtradeDecimatedSeriesBuilder.cs"
- "tests/ARSAS.Tests/ArdIrecNativeBridgeIntegrationTests.cs"
- "tests/ARSAS.Tests/ComtradeNavigationMathTests.cs"
- "tests/ARSAS.Tests/ComtradeTimeMathTests.cs"
- "tests/ARSAS.Tests/ComtradeAbsoluteViewportMathTests.cs"
- "tests/ARSAS.Tests/ComtradeRangeDecimatorTests.cs"
- "tests/ARSAS.Tests/ComtradeDecimatedSeriesBuilderTests.cs"
- "ArIED61850Tester.csproj"
Expand Down
35 changes: 22 additions & 13 deletions ComtradeWorkspaceWindow.Analysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ protected override void OnContentRendered(EventArgs e)
_analysisEventsAttached = true;
SignalList.SelectionChanged += SignalList_AnalysisSelectionChanged;
Closed += AnalysisWindow_Closed;
InitializeDisturbanceWorkspace();
ApplyAnalysisModeVisuals();
UpdateAnalysisAvailability();
}
Expand Down Expand Up @@ -60,15 +61,16 @@ private void SetAnalysisMode(AnalysisMode mode)
mode = AnalysisMode.Waveform;

_analysisMode = mode;
WaveformView.Visibility = mode == AnalysisMode.Waveform ? Visibility.Visible : Visibility.Collapsed;
WaveformWorkspaceHost.Visibility = mode == AnalysisMode.Waveform ? Visibility.Visible : Visibility.Collapsed;
WaveformView.Visibility = Visibility.Collapsed;
PhasorView.Visibility = mode == AnalysisMode.Phasor ? Visibility.Visible : Visibility.Collapsed;
HarmonicsView.Visibility = mode == AnalysisMode.Harmonics ? Visibility.Visible : Visibility.Collapsed;
ResetViewButton.Visibility = mode == AnalysisMode.Waveform ? Visibility.Visible : Visibility.Collapsed;
NavigationTextBlock.Text = mode switch
{
AnalysisMode.Phasor => "Fundamental RMS phasors • native one-cycle DFT • cosine-referenced phase",
AnalysisMode.Harmonics => "Full-cycle native DFT • magnitude shown as % of fundamental • click a bar for details",
_ => NavigationHint
AnalysisMode.Phasor => "Cursor A = phasor reference • native one-cycle DFT • select an analog signal family on the left",
AnalysisMode.Harmonics => "Cursor A = harmonic reference • full-cycle native DFT • click a bar for details",
_ => "Shared trigger-relative timeline • click Cursor A • Ctrl/right-click Cursor B • digital ↑/↓ = transition"
};
ApplyAnalysisModeVisuals();

Expand All @@ -83,13 +85,13 @@ private void UpdateAnalysisAvailability()
HarmonicsModeButton.IsEnabled = analog;
if (!analog)
{
PhasorModeButton.ToolTip = "Phasor analysis requires an analog channel.";
HarmonicsModeButton.ToolTip = "Harmonic analysis requires an analog channel.";
PhasorModeButton.ToolTip = "Select an analog signal first. Cursor A on the waveform becomes the phasor reference.";
HarmonicsModeButton.ToolTip = "Select an analog signal first. Cursor A on the waveform becomes the harmonic reference.";
}
else
{
PhasorModeButton.ToolTip = "Show same-unit fundamental RMS phasors at the current analysis reference.";
HarmonicsModeButton.ToolTip = "Show native ArdIrec harmonic spectrum for the selected analog channel.";
PhasorModeButton.ToolTip = "Show same-unit fundamental RMS phasors at Cursor A (or viewport center when Cursor A is unset).";
HarmonicsModeButton.ToolTip = "Show native ArdIrec harmonic spectrum at Cursor A (or viewport center when Cursor A is unset).";
}
}

Expand Down Expand Up @@ -117,12 +119,16 @@ private async Task RefreshNativeAnalysisAsync()
_analysisLoadCts?.Dispose();
_analysisLoadCts = new CancellationTokenSource();
var token = _analysisLoadCts.Token;
var referenceFrame = ResolveAnalysisReferenceFrame();
var cursorReference = TryResolveDisturbanceCursorFrame(out var cursorFrame);
var referenceFrame = cursorReference ? cursorFrame : ResolveAnalysisReferenceFrame();
var timeMs = await TryReadReferenceTimeMillisecondsAsync(referenceFrame, token).ConfigureAwait(true);
if (token.IsCancellationRequested) return;
var triggerMs = ResolveTriggerMilliseconds();
var relative = timeMs is { } absolute && triggerMs is { } trigger ? absolute - trigger : (double?)null;
AnalysisReferenceTextBlock.Text = timeMs is { } ms
? $"Analysis reference: frame {referenceFrame:N0} • {ms:G7} ms"
: $"Analysis reference: frame {referenceFrame:N0}";
? $"Analysis reference: {(cursorReference ? "Cursor A" : "viewport center")} • frame {referenceFrame:N0} • " +
(relative is { } rel ? ComtradeDisturbanceTimelineMath.FormatRelativeTime(rel) : $"{ms:G7} ms")
: $"Analysis reference: {(cursorReference ? "Cursor A" : "viewport center")} • frame {referenceFrame:N0}";

try
{
Expand All @@ -141,7 +147,7 @@ private async Task RefreshNativeAnalysisAsync()
var subtitle = BuildAnalysisSubtitle(selectedMetadata, referenceFrame, result.Count,
"fundamental RMS • same-unit channels");
PhasorView.ShowPhasors("Phasor diagram", subtitle, result);
StatusTextBlock.Text = $"Native ArdIrec phasor analysis • frame {referenceFrame:N0} • {result.Count} vector(s)";
StatusTextBlock.Text = $"Native ArdIrec phasor analysis • {(cursorReference ? "Cursor A" : "viewport center")} • frame {referenceFrame:N0} • {result.Count} vector(s)";
return;
}

Expand Down Expand Up @@ -170,7 +176,7 @@ private async Task RefreshNativeAnalysisAsync()
var harmonicSubtitle = BuildAnalysisSubtitle(metadata, referenceFrame, spectrum.Bins.Count,
$"orders H1…H{spectrum.Bins[^1].Order}");
HarmonicsView.ShowSpectrum("Harmonic spectrum", harmonicSubtitle, display);
StatusTextBlock.Text = $"Native ArdIrec harmonics • THD {spectrum.ThdPercent:G5}% • " +
StatusTextBlock.Text = $"Native ArdIrec harmonics • {(cursorReference ? "Cursor A" : "viewport center")} • THD {spectrum.ThdPercent:G5}% • " +
(spectrum.DominantOrder > 1
? $"dominant H{spectrum.DominantOrder} {spectrum.DominantPercent:G4}%"
: "no meaningful distortion harmonic");
Expand All @@ -193,6 +199,9 @@ private async Task RefreshNativeAnalysisAsync()

private ulong ResolveAnalysisReferenceFrame()
{
if (TryResolveDisturbanceCursorFrame(out var cursorFrame))
return cursorFrame;
Comment on lines +202 to +203

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve the fallback from the visible disturbance viewport

When Cursor A is unset after the user zooms or pans the disturbance view, this falls through to ResolveAnalysisReferenceFrame, which still derives its center from the hidden legacy waveform's _requestedSourceViewport/_loadedSourceViewport. Those fields are never synchronized with either the disturbance source viewport or its local time zoom, so Phasor/Harmonics can be calculated at the full-record center while the UI labels the reference as the viewport center. Derive the fallback frame from the current DisturbanceView time bounds and disturbance source data instead.

Useful? React with 👍 / 👎.


var total = _record.Info.FrameCount;
if (total == 0) return 0;
var viewport = _requestedSourceViewport.FrameCount > 0
Expand Down
Loading
Loading