diff --git a/ShimmerBLE/ShimmerBLEAPI/Sensors/SensorGSR.cs b/ShimmerBLE/ShimmerBLEAPI/Sensors/SensorGSR.cs
index 0bccda9c..860663ab 100644
--- a/ShimmerBLE/ShimmerBLEAPI/Sensors/SensorGSR.cs
+++ b/ShimmerBLE/ShimmerBLEAPI/Sensors/SensorGSR.cs
@@ -22,7 +22,16 @@ public class SensorGSR : Sensor
protected SensorSetting GSROversamplingRateSetting = Sensor.UnknownSetting;
public const double LIMIT_FOR_MINIMUM_VALID_GSR_CONDUCTANCE_US = 0.03;
public const int GSR_UNCAL_LIMIT_RANGE3_SR62 = 683;
- public const int GSR_UNCAL_LIMIT_RANGE3_SR68 = 1134;
+ ///
+ /// Range-3 codes below this are raised to it before calibration so that an open circuit
+ /// reads as open, which only works if the limit is above the amplifier reference. 1138 is
+ /// the first code above 0.5 V at the SR68's 1.8 V full scale (0.5 V = code 1137.5), so it
+ /// also clears the 0.4986 V that CalibrateGsrDataToKOhmsUsingAmplifierEq divides by (code
+ /// 1134.3); the Java driver divides by 0.5 V, and 1138 is correct under both. It was 1134,
+ /// the last code below 0.4986 V: that decoded to a negative resistance, nudged to 8 kOhm,
+ /// so an open circuit read 125 uS and counted as Connected (DEV-1067).
+ ///
+ public const int GSR_UNCAL_LIMIT_RANGE3_SR68 = 1138;
///
/// GSR range setting
diff --git a/ShimmerBLE/ShimmerBLETests/Sensors/SensorGSROpenCircuitTest.cs b/ShimmerBLE/ShimmerBLETests/Sensors/SensorGSROpenCircuitTest.cs
new file mode 100644
index 00000000..2a7019e5
--- /dev/null
+++ b/ShimmerBLE/ShimmerBLETests/Sensors/SensorGSROpenCircuitTest.cs
@@ -0,0 +1,82 @@
+using NUnit.Framework;
+using shimmer.Sensors;
+using ShimmerAPI;
+using static ShimmerBLEAPI.Devices.VerisenseDevice;
+
+namespace ShimmerBLETests
+{
+ ///
+ /// An open circuit on GSR range 3 of a Verisense Pulse+ (SR68) has to decode as open (DEV-1067).
+ /// With nothing across the electrodes the amplifier output settles on its reference, so the ADC
+ /// reads a few codes either side of it: an SR68-9 open-circuit recording (DEV-793 dataset B6)
+ /// peaks at codes 1126-1136. The range-3 limit was 1134, itself below the 0.4986 V reference
+ /// (code 1134.3), so every clamped sample decoded to a negative resistance, which the auto-range
+ /// nudge floored at 8 kOhm: an open circuit read 125 uS and counted as Connected.
+ ///
+ public class SensorGSROpenCircuitTest
+ {
+ const int Range3 = 3;
+ /// Top of range 3, 4.7 MOhm - an open circuit should read at least this.
+ const double Range3MaxKOhms = 4700.0;
+
+ [Test]
+ public void TestOpenCircuitOnRange3ReadsOpenInAutoRange()
+ {
+ var sensor = CreateSR68Sensor(SensorGSR.GSRRange.Range_Auto);
+ for (int code = 1134; code <= 1138; code++)
+ {
+ AssertReadsOpen(sensor, code, "auto-range");
+ Assert.That(sensor.GetGSRConnectivityLevel(), Is.EqualTo(SensorGSR.GSRConnectivityLevel.Disconnected), "auto-range, code " + code);
+ }
+ }
+
+ ///
+ /// A fixed range pins the reading to that range's limits, so here an open circuit reads
+ /// 4.7 MOhm (0.213 uS, still above the 0.03 uS connectivity threshold) rather than the
+ /// 680 kOhm bottom of the range that 1134 produced.
+ ///
+ [Test]
+ public void TestOpenCircuitOnRange3ReadsOpenInFixedRange3()
+ {
+ var sensor = CreateSR68Sensor(SensorGSR.GSRRange.Range_3);
+ for (int code = 1134; code <= 1138; code++)
+ {
+ AssertReadsOpen(sensor, code, "fixed range 3");
+ }
+ }
+
+ ///
+ /// The Java driver divides by 0.5 V where this API uses 0.4986 V, so the limit is the first
+ /// code above 0.5 V: correct under both references.
+ ///
+ [Test]
+ public void TestLimitIsTheFirstCodeAboveHalfAVolt()
+ {
+ int limit = SensorGSR.GSR_UNCAL_LIMIT_RANGE3_SR68;
+ Assert.That(CalibrateADCValueToVolts(limit, HardwareIdentifier.VERISENSE_PULSE_PLUS), Is.GreaterThan(0.5));
+ Assert.That(CalibrateADCValueToVolts(limit - 1, HardwareIdentifier.VERISENSE_PULSE_PLUS), Is.LessThan(0.5));
+ }
+
+ private static SensorGSR CreateSR68Sensor(Sensor.SensorSetting range)
+ {
+ var sensor = new SensorGSR();
+ sensor.SetDeviceHardwareIdentifier(HardwareIdentifier.VERISENSE_PULSE_PLUS);
+ sensor.SetGSREnabled(true);
+ sensor.SetGSRRange(range);
+ return sensor;
+ }
+
+ private static void AssertReadsOpen(SensorGSR sensor, int code, string rangeLabel)
+ {
+ // One GSR sample as the firmware sends it: range in bits 15-14 over the 12-bit code, LSB first.
+ int raw = (Range3 << 14) | code;
+ var ojc = sensor.ParseSensorData(new byte[] { (byte)(raw & 0xFF), (byte)(raw >> 8) }, new ObjectCluster("", ""));
+ double kOhms = ojc.GetData(SensorGSR.ObjectClusterSensorName.GSR, ShimmerConfiguration.SignalFormats.CAL, ShimmerConfiguration.SignalUnits.KiloOhms).Data;
+ double uS = ojc.GetData(SensorGSR.ObjectClusterSensorName.GSR, ShimmerConfiguration.SignalFormats.CAL, ShimmerConfiguration.SignalUnits.MicroSiemens).Data;
+
+ string context = rangeLabel + ", code " + code;
+ Assert.That(kOhms, Is.GreaterThanOrEqualTo(Range3MaxKOhms), context);
+ Assert.That(uS, Is.GreaterThan(0).And.LessThanOrEqualTo(1000.0 / Range3MaxKOhms), context);
+ }
+ }
+}