using SSPCTester.Devices.Interfaces; using SSPCTester.Logic.Calculation; using Xunit; namespace SSPCTester.Tests; public class CurveMetricsTests { /// 构造一段 V(t) = Vmax*(1-exp(-t/tau)) 形状的上升曲线。 private static CurveData MakeRising(double vMax = 28, double tauSec = 0.0005, int rate = 100_000, double duration = 0.01, double trigger = 0.0025) { int n = (int)(rate * duration); var v = new double[n]; for (int i = 0; i < n; i++) { double t = (double)i / rate - trigger; v[i] = t <= 0 ? 0 : vMax * (1 - System.Math.Exp(-t / tauSec)); } return new CurveData { SampleRateHz = rate, TriggerTimeSec = trigger, Voltage = v }; } private static CurveData MakeFalling(double vMax = 28, double tauSec = 0.0005, int rate = 100_000, double duration = 0.01, double trigger = 0.0025) { int n = (int)(rate * duration); var v = new double[n]; for (int i = 0; i < n; i++) { double t = (double)i / rate - trigger; v[i] = t <= 0 ? vMax : vMax * System.Math.Exp(-t / tauSec); } return new CurveData { SampleRateHz = rate, TriggerTimeSec = trigger, Voltage = v }; } [Fact] public void OnTimeMs_ReachesNinetyPercent_AtAboutMinusLogPointOneTau() { var c = MakeRising(); double onMs = CurveMetrics.OnTimeMs(c, 28, 0.9); // 90% 时刻 t = -tau * ln(0.1) ≈ 1.151 ms Assert.InRange(onMs, 1.1, 1.2); } [Fact] public void RiseTimeMs_TonToNinety_AboutNinetyPercentTime() { var c = MakeRising(); double rt = CurveMetrics.RiseTimeMs(c, 28); // ln(0.9/0.1) = ln 9 ≈ 2.197;rt = tau * 2.197 ≈ 1.099 ms Assert.InRange(rt, 1.1, 1.2); } [Fact] public void AnalyzeRising_ReturnsTonToNinetyIndexesAndDuration() { var c = MakeRising(); var mark = CurveMetrics.AnalyzeRising(c, 28); int triggerIndex = (int)Math.Round(c.TriggerTimeSec * c.SampleRateHz); int expectedEnd = CurveMetrics.FirstReachIndex(c.Voltage, 28 * 0.9, rising: true, triggerIndex); int expectedStart = triggerIndex; Assert.True(mark.IsValid); Assert.Equal(expectedStart, mark.StartIndex); Assert.Equal(expectedEnd, mark.EndIndex); Assert.Equal(0, mark.StartThresholdV, 6); Assert.Equal((c.TimeAt(expectedEnd) - c.TimeAt(expectedStart)) * 1000.0, mark.DurationMs, 6); Assert.Equal(mark.DurationMs, CurveMetrics.RiseTimeMs(c, 28), 6); } [Fact] public void OffTimeMs_FallingToTenPercent() { var c = MakeFalling(); double off = CurveMetrics.OffTimeMs(c, 28, 0.1); Assert.InRange(off, 1.1, 1.2); } [Fact] public void AnalyzeFalling_ReturnsStableHighToTenIndexesAndDuration() { var c = MakeFalling(); var mark = CurveMetrics.AnalyzeFalling(c, 28); int triggerIndex = (int)Math.Round(c.TriggerTimeSec * c.SampleRateHz); int expectedEnd = CurveMetrics.FirstReachIndex(c.Voltage, 28 * 0.1, rising: false, triggerIndex); int expectedStart = triggerIndex; for (int i = triggerIndex; i <= expectedEnd; i++) { if (c.Voltage[i] >= 28 * 0.98) expectedStart = i; } Assert.True(mark.IsValid); Assert.Equal(expectedStart, mark.StartIndex); Assert.Equal(expectedEnd, mark.EndIndex); Assert.Equal(28, mark.StartThresholdV, 6); Assert.Equal((c.TimeAt(expectedEnd) - c.TimeAt(expectedStart)) * 1000.0, mark.DurationMs, 6); Assert.Equal(mark.DurationMs, CurveMetrics.FallTimeMs(c, 28), 6); } [Fact] public void AnalyzeTransition_ReturnsInvalidWhenThresholdsAreNotReached() { var risingCurve = new CurveData { SampleRateHz = 100_000, TriggerTimeSec = 0, Voltage = Enumerable.Repeat(0.0, 100).ToArray() }; var fallingCurve = new CurveData { SampleRateHz = 100_000, TriggerTimeSec = 0, Voltage = Enumerable.Repeat(28.0, 100).ToArray() }; var rising = CurveMetrics.AnalyzeRising(risingCurve, 28); var falling = CurveMetrics.AnalyzeFalling(fallingCurve, 28); Assert.False(rising.IsValid); Assert.False(falling.IsValid); Assert.Equal(-1, rising.StartIndex); Assert.Equal(-1, falling.EndIndex); Assert.True(double.IsNaN(CurveMetrics.RiseTimeMs(risingCurve, 28))); Assert.True(double.IsNaN(CurveMetrics.FallTimeMs(fallingCurve, 28))); } [Fact] public void EstimateVMax_NearActualMax() { var c = MakeRising(); double v = CurveMetrics.EstimateVMax(c.Voltage); Assert.InRange(v, 27, 28.001); } } public class PowerLossTests { [Fact] public void VoltageDrop_Subtracts() => Assert.Equal(1.5, PowerLoss.VoltageDrop(28, 26.5), 3); [Fact] public void Loss_PinMinusPout() => Assert.Equal(28 * 1.0 - 26.5 * 1.0, PowerLoss.LossWatts(28, 1, 26.5, 1), 3); [Fact] public void Efficiency_Percentage() { double e = PowerLoss.EfficiencyPct(28, 1, 26.5, 1); Assert.InRange(e, 94, 95); } [Fact] public void Efficiency_ZeroInputReturnsZero() => Assert.Equal(0.0, PowerLoss.EfficiencyPct(0, 0, 0, 0)); }