diff --git a/wrapper-tests/ThresholdTests.cs b/wrapper-tests/ThresholdTests.cs
new file mode 100644
index 00000000..aa672695
--- /dev/null
+++ b/wrapper-tests/ThresholdTests.cs
@@ -0,0 +1,101 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+
+namespace wrapper_tests
+{
+ [TestClass]
+ public class ThresholdTests
+ {
+ ///
+ /// This test checks for "jumps" in output when crossing the sharpness = 16 threshold.
+ /// It compares the Driver's optimized logic (which clamps at 16) with a
+ /// pure mathematical simulator that does NOT clamp.
+ ///
+ [TestMethod]
+ public void SynchronousAccel_ThresholdCrossing_IsContinuous()
+ {
+ double syncSpeed = 20;
+ double gamma = 0.5;
+ double motivity = 1.3;
+
+ // smooth = 0.03125 exactly results in sharpness = 16
+ // We test values slightly above and slightly below this threshold
+ double[] smoothValues = { 0.03126, 0.03125, 0.03124, 0.0 };
+
+ foreach (double smooth in smoothValues)
+ {
+ var profile = new Profile();
+ profile.outputDPI = 1000;
+ profile.argsX.mode = AccelMode.synchronous;
+ profile.argsX.gain = false;
+ profile.argsX.syncSpeed = syncSpeed;
+ profile.argsX.gamma = gamma;
+ profile.argsX.motivity = motivity;
+ profile.argsX.smooth = smooth;
+
+ var accel = new ManagedAccel(profile);
+
+ // Pure Math Simulator (Ignores the 16 threshold)
+ var pureSimulator = new UnclampedSynchronousSimulator(syncSpeed, motivity, gamma, smooth);
+
+ // Test with various input counts (1 to 100)
+ for (int input = 1; input < 100; input += 5)
+ {
+ Tuple output = accel.Accelerate(input, 0, 1, 10);
+
+ double inputSpeed = (double)input / 10.0;
+ double expectedOutput = (double)input * pureSimulator.Accelerate(inputSpeed);
+
+ // We expect the difference to be very small (continuity check)
+ // Precision of 0.01% is usually safe for mouse input
+ double delta = Math.Abs(output.Item1 - expectedOutput);
+ double relativeError = delta / expectedOutput;
+
+ Assert.IsTrue(relativeError < 0.001,
+ $"Discontinuity found at smooth={smooth}, input={input}. " +
+ $"Driver: {output.Item1}, Pure Math: {expectedOutput}, Error: {relativeError:P}");
+ }
+ }
+ }
+ }
+
+ ///
+ /// A simulator that follows the raw math WITHOUT the sharpness >= 16 optimization.
+ /// This allows us to see what the "true" curve would look like without the clamp.
+ ///
+ public class UnclampedSynchronousSimulator
+ {
+ public UnclampedSynchronousSimulator(double syncSpeed, double motivity, double gamma, double smooth)
+ {
+ SyncSpeed = syncSpeed;
+ Motivity = motivity;
+ Gamma = gamma;
+ Sharpness = smooth <= 0 ? 100 : 0.5 / smooth; // Use a very high number instead of clamping
+ }
+
+ public double SyncSpeed { get; }
+ public double Motivity { get; }
+ public double Gamma { get; }
+ public double Sharpness { get; }
+
+ public double Accelerate(double inputSpeed)
+ {
+ if (inputSpeed == SyncSpeed) return 1.0;
+
+ double logSyncSpeed = Math.Log(SyncSpeed);
+ double logMotivity = Math.Log(Motivity);
+ double gammaConst = Gamma / logMotivity;
+
+ double logX = Math.Log(inputSpeed);
+ double logDiff = logX - logSyncSpeed;
+
+ double logSpace = Math.Abs(gammaConst * logDiff);
+ double exponent = Math.Pow(Math.Tanh(Math.Pow(logSpace, Sharpness)), 1 / Sharpness);
+
+ if (logDiff < 0) exponent = -exponent;
+
+ return Math.Exp(exponent * logMotivity);
+ }
+ }
+}
diff --git a/wrapper-tests/wrapper-tests.csproj b/wrapper-tests/wrapper-tests.csproj
index d3032755..a9330758 100644
--- a/wrapper-tests/wrapper-tests.csproj
+++ b/wrapper-tests/wrapper-tests.csproj
@@ -72,6 +72,7 @@
+