diff --git a/RunCat365/TemperatureRepository.cs b/RunCat365/TemperatureRepository.cs index ecbe1a0..311d33b 100644 --- a/RunCat365/TemperatureRepository.cs +++ b/RunCat365/TemperatureRepository.cs @@ -69,22 +69,40 @@ internal sealed class TemperaturePerformanceCounters : InstancedPerformanceCount } } + internal sealed class TemperatureHighPrecisionPerformanceCounters : InstancedPerformanceCounters + { + protected override string CategoryName => "Thermal Zone Information"; + protected override string CounterName => "High Precision Temperature"; + + internal static TemperatureHighPrecisionPerformanceCounters? TryCreate() + { + var instance = new TemperatureHighPrecisionPerformanceCounters(); + return instance.TryInitialize() ? instance : null; + } + } + internal class TemperatureRepository { private const float KELVIN_TO_CELSIUS_OFFSET = 273.15f; private const float MIN_VALID_TEMPERATURE_CELSIUS = -50.0f; private const float MAX_VALID_TEMPERATURE_CELSIUS = 150.0f; private const int REFRESH_INTERVAL_TICKS = 30; + private const int STALE_READ_THRESHOLD = 24; private readonly TemperaturePerformanceCounters? counters; + private readonly TemperatureHighPrecisionPerformanceCounters? highPrecisionCounters; + private readonly List baselineRawValues = []; private TemperatureInfo? temperatureInfo; private int ticksSinceLastRefresh; + private int identicalReadCount; + private bool hasEverVaried; internal bool IsAvailable => counters is not null; internal TemperatureRepository() { counters = TemperaturePerformanceCounters.TryCreate(); + highPrecisionCounters = TemperatureHighPrecisionPerformanceCounters.TryCreate(); } internal void Update() @@ -96,9 +114,13 @@ internal void Update() { ticksSinceLastRefresh = 0; counters.RefreshInstances(); + highPrecisionCounters?.RefreshInstances(); } var rawValues = counters.ReadValues(); + var variationValues = highPrecisionCounters is null ? rawValues : highPrecisionCounters.ReadValues(); + TrackValueVariation(variationValues); + var temperaturesCelsius = new List(rawValues.Count); foreach (var temperatureKelvin in rawValues) { @@ -108,7 +130,7 @@ internal void Update() temperaturesCelsius.Add(temperatureCelsius); } - temperatureInfo = temperaturesCelsius.Count == 0 + temperatureInfo = temperaturesCelsius.Count == 0 || IsLikelyPlaceholder() ? null : new TemperatureInfo { @@ -117,6 +139,48 @@ internal void Update() }; } + // Some firmware exposes an ACPI thermal zone that is not wired to a real + // sensor and always reports the same fixed value (e.g. 301 K). Track whether + // the readings have ever changed so such zones can be treated as unavailable + // instead of showing a misleading constant temperature. Variation is tracked + // on the "High Precision Temperature" counter (0.1 K granularity) when it is + // available, because a live sensor jitters at that resolution even when the + // whole-Kelvin "Temperature" counter stays flat on an idle machine; the + // tracking source is fixed at construction so values are never compared + // across the two counters' different scales. + private void TrackValueVariation(List rawValues) + { + if (hasEverVaried || rawValues.Count == 0) return; + + var sortedValues = new List(rawValues); + sortedValues.Sort(); + + if (baselineRawValues.Count != sortedValues.Count) + { + baselineRawValues.Clear(); + baselineRawValues.AddRange(sortedValues); + identicalReadCount = 1; + return; + } + + for (var i = 0; i < sortedValues.Count; i++) + { + if (baselineRawValues[i] != sortedValues[i]) + { + hasEverVaried = true; + baselineRawValues.Clear(); + return; + } + } + + identicalReadCount += 1; + } + + private bool IsLikelyPlaceholder() + { + return !hasEverVaried && STALE_READ_THRESHOLD <= identicalReadCount; + } + internal TemperatureInfo? Get() { return temperatureInfo; @@ -125,6 +189,7 @@ internal void Update() internal void Close() { counters?.Close(); + highPrecisionCounters?.Close(); } } }