Refactor indicators to support optional time step in Prime method

- Updated the Prime method signature in multiple indicators (Jma, Kama, Lsma, Mama, Mgdi, Pwma, Rma, Sma, Ssf, Super, T3, Tema, Trima, Usf, Vidya, Wma, Atr) to accept an optional TimeSpan parameter for improved flexibility.
- Added unit tests for Lsma to verify Dispose functionality, ensuring proper unsubscription from the source and thread safety.
- Enhanced Mama and Wma classes to handle non-finite inputs gracefully and added checks for valid parameters in constructors.
- Introduced additional tests for T3 to validate constructor behavior with invalid volume factors.
- Ensured all indicators maintain consistent behavior when handling edge cases, such as empty buffers and non-finite values.
This commit is contained in:
Miha Kralj
2025-12-28 15:14:07 -08:00
parent af7abea6e7
commit 5c3b3fbab4
43 changed files with 661 additions and 131 deletions
+5 -1
View File
@@ -8,9 +8,13 @@ public class BesselTests
{
Assert.Throws<ArgumentException>(() => new Bessel(0));
Assert.Throws<ArgumentException>(() => new Bessel(-1));
Assert.Throws<ArgumentException>(() => new Bessel(1));
var bessel = new Bessel(14);
var bessel = new Bessel(2);
Assert.NotNull(bessel);
var bessel14 = new Bessel(14);
Assert.NotNull(bessel14);
}
[Fact]
+10 -14
View File
@@ -45,16 +45,14 @@ public sealed class Bessel : AbstractBase, IDisposable
/// <summary>
/// Creates Bessel filter with specified length.
/// </summary>
/// <param name="length">Cutoff length (must be > 0, internally clamped to at least 2).</param>
/// <param name="length">Cutoff length (must be >= 2 for 2nd-order filter stability).</param>
public Bessel(int length)
{
if (length <= 0)
throw new ArgumentException("Length must be greater than 0", nameof(length));
if (length < 2)
throw new ArgumentException("Length must be at least 2 for 2nd-order Bessel filter", nameof(length));
int safeLength = Math.Max(length, 2);
double a = Math.Exp(-Math.PI / safeLength);
double b = 2.0 * a * Math.Cos(1.738 * Math.PI / safeLength);
double a = Math.Exp(-Math.PI / length);
double b = 2.0 * a * Math.Cos(1.738 * Math.PI / length);
_c2 = b;
_c3 = -a * a;
_c1 = 1.0 - _c2 - _c3;
@@ -91,7 +89,7 @@ public sealed class Bessel : AbstractBase, IDisposable
public override bool IsHot => _state.IsHot;
public override void Prime(ReadOnlySpan<double> source)
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
{
if (source.Length == 0)
return;
@@ -288,8 +286,8 @@ public sealed class Bessel : AbstractBase, IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Calculate(ReadOnlySpan<double> source, Span<double> output, int length)
{
if (length <= 0)
throw new ArgumentException("Length must be greater than 0", nameof(length));
if (length < 2)
throw new ArgumentException("Length must be at least 2 for 2nd-order Bessel filter", nameof(length));
if (source.Length != output.Length)
throw new ArgumentException("Source and output must have the same length", nameof(output));
@@ -297,10 +295,8 @@ public sealed class Bessel : AbstractBase, IDisposable
if (source.Length == 0)
return;
int safeLength = Math.Max(length, 2);
double a = Math.Exp(-Math.PI / safeLength);
double b = 2.0 * a * Math.Cos(1.738 * Math.PI / safeLength);
double a = Math.Exp(-Math.PI / length);
double b = 2.0 * a * Math.Cos(1.738 * Math.PI / length);
double c2 = b;
double c3 = -a * a;
double c1 = 1.0 - c2 - c3;