-
Notifications
You must be signed in to change notification settings - Fork 4
fix: separate IEC 61850 process feedback and smooth analyzers #311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e12be23
889fdad
16df052
ad18bdc
de828cb
063f05c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| namespace ArIED61850Tester.Services; | ||
|
|
||
| /// <summary> | ||
| /// Allocation-free presentation easing helpers. These functions are intentionally presentation-only: | ||
| /// raw COMTRADE/IEC 61850 engineering values remain untouched and authoritative. | ||
| /// </summary> | ||
| public static class PresentationEasingMath | ||
| { | ||
| public static double ExponentialAlpha(double elapsedMilliseconds, double timeConstantMilliseconds) | ||
| { | ||
| if (!double.IsFinite(timeConstantMilliseconds) || timeConstantMilliseconds <= 0.0) | ||
| return 1.0; | ||
| if (double.IsPositiveInfinity(elapsedMilliseconds)) | ||
| return 1.0; | ||
| if (!double.IsFinite(elapsedMilliseconds) || elapsedMilliseconds <= 0.0) | ||
| return 0.0; | ||
|
|
||
| var alpha = 1.0 - Math.Exp(-elapsedMilliseconds / timeConstantMilliseconds); | ||
| return Math.Clamp(alpha, 0.0, 1.0); | ||
| } | ||
|
|
||
| public static double Smooth(double current, double target, double elapsedMilliseconds, double timeConstantMilliseconds) | ||
| { | ||
| if (!double.IsFinite(target)) return current; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a later COMTRADE result contains a non-finite magnitude but has the same channel topology, returning AGENTS.md reference: AGENTS.md:L60-L66 Useful? React with 👍 / 👎. |
||
| if (!double.IsFinite(current)) return target; | ||
| var alpha = ExponentialAlpha(elapsedMilliseconds, timeConstantMilliseconds); | ||
| return current + ((target - current) * alpha); | ||
| } | ||
|
|
||
| public static double ShortestAngleDeltaDegrees(double currentDegrees, double targetDegrees) | ||
| { | ||
| if (!double.IsFinite(currentDegrees) || !double.IsFinite(targetDegrees)) | ||
| return 0.0; | ||
| var delta = (targetDegrees - currentDegrees) % 360.0; | ||
| if (delta >= 180.0) delta -= 360.0; | ||
| if (delta < -180.0) delta += 360.0; | ||
| return delta; | ||
| } | ||
|
|
||
| public static double NormalizeAngleDegrees(double degrees) | ||
| { | ||
| if (!double.IsFinite(degrees)) return 0.0; | ||
| var normalized = degrees % 360.0; | ||
| if (normalized >= 180.0) normalized -= 360.0; | ||
| if (normalized < -180.0) normalized += 360.0; | ||
| return normalized; | ||
| } | ||
|
|
||
| public static double SmoothAngleDegrees(double currentDegrees, double targetDegrees, double elapsedMilliseconds, double timeConstantMilliseconds) | ||
| { | ||
| if (!double.IsFinite(targetDegrees)) return NormalizeAngleDegrees(currentDegrees); | ||
| if (!double.IsFinite(currentDegrees)) return NormalizeAngleDegrees(targetDegrees); | ||
| var alpha = ExponentialAlpha(elapsedMilliseconds, timeConstantMilliseconds); | ||
| return NormalizeAngleDegrees(currentDegrees + (ShortestAngleDeltaDegrees(currentDegrees, targetDegrees) * alpha)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| using ArIED61850Tester.Services; | ||
| using Xunit; | ||
|
|
||
| namespace ARSAS.Tests; | ||
|
|
||
| public sealed class PresentationEasingMathTests | ||
| { | ||
| [Fact] | ||
| public void ExponentialSmoothing_IsTimeBased_NotFrameCountBased() | ||
| { | ||
| const double tau = 80.0; | ||
| var halfStep = PresentationEasingMath.Smooth(0.0, 1.0, 16.67, tau); | ||
| var twoSteps = PresentationEasingMath.Smooth(halfStep, 1.0, 16.67, tau); | ||
| var oneStep = PresentationEasingMath.Smooth(0.0, 1.0, 33.34, tau); | ||
| Assert.Equal(oneStep, twoSteps, 12); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void AngleSmoothing_UsesShortestPathAcrossPlusMinus180() | ||
| { | ||
| Assert.Equal(2.0, PresentationEasingMath.ShortestAngleDeltaDegrees(179.0, -179.0), 10); | ||
| Assert.Equal(-2.0, PresentationEasingMath.ShortestAngleDeltaDegrees(-179.0, 179.0), 10); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void InfiniteElapsedTime_SnapsToTarget() | ||
| { | ||
| Assert.Equal(1.0, PresentationEasingMath.ExponentialAlpha(double.PositiveInfinity, 80.0)); | ||
| Assert.Equal(42.0, PresentationEasingMath.Smooth(10.0, 42.0, double.PositiveInfinity, 80.0), 10); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a fast scrub ends, the final request calls
ShowPhasorsonly once and the composition pump then stops, so these eased arrays remain between the previous and requested frame indefinitely; no render callback advances them to the target. The phasor labels/vectors therefore report values from neither frame after the user releases the cursor, andComtradeHarmonicsWorkstationView.ShowSpectrahas the same issue. Bypass easing for the final request or continue animation until committing the exact target.AGENTS.md reference: AGENTS.md:L103-L106
Useful? React with 👍 / 👎.