Add sensors for Astral GPUs (#2168)

* Add sensors for Astral GPUs

* Clean up imports

* Code clean-up

---------

Co-authored-by: PhyxionNL <7643972+PhyxionNL@users.noreply.github.com>
This commit is contained in:
Timotej
2026-01-22 10:00:23 +01:00
committed by GitHub
co-authored by PhyxionNL
parent 24218b0703
commit 6f9ac5aece
2 changed files with 171 additions and 0 deletions
@@ -45,6 +45,21 @@ internal sealed class NvidiaGpu : GenericGpu
private readonly Sensor _coreVoltage;
private readonly Sensor[] _temperatures;
private readonly uint _thermalSensorsMask;
private readonly Sensor[] _12VHPwrPinCurrentSensors;
private readonly Sensor[] _12VHPwrPinVoltageSensors;
private readonly Sensor[] _12VHPwrPinPowerSensors;
private readonly Sensor _12VHPwrConnectorPowerSensor;
private readonly Sensor _12VHPwrConnectorCurrentSensor;
// ASUS Astral subsystem IDs for 12VHPwr pin monitoring
private static readonly uint[] AstralSubSystemIds =
[
0x89EA1043, // Astral 5090D OC
0x8A611043, // Astral 5090 Matrix
0x89EC1043, // Astral 5090 LC
0x89E31043, // Astral 5090 OC
0x89DE1043, // Astral 5080 OC
];
public NvidiaGpu(int adapterIndex, NvApi.NvPhysicalGpuHandle handle, NvApi.NvDisplayHandle? displayHandle, ISettings settings)
: base(GetName(handle),
@@ -434,6 +449,58 @@ internal sealed class NvidiaGpu : GenericGpu
_memoryTotal = new Sensor("GPU Memory Total", 2, SensorType.SmallData, this, settings);
_memoryLoad = new Sensor("GPU Memory", 3, SensorType.Load, this, settings);
// Pin power sensors for NVIDIA RTX Astral series from ASUS
if (NvApi.NvAPI_I2CReadEx != null && NvApi.NvAPI_GPU_GetPCIIdentifiers != null)
{
NvApi.NvStatus pciIdentifierStatus = NvApi.NvAPI_GPU_GetPCIIdentifiers(_handle, out _, out uint subSystemId, out _, out _);
if (pciIdentifierStatus == NvApi.NvStatus.OK && AstralSubSystemIds.Contains(subSystemId))
{
_12VHPwrPinVoltageSensors =
[
new Sensor("12VHPWR Pin 1", 0, SensorType.Voltage, this, settings),
new Sensor("12VHPWR Pin 2", 1, SensorType.Voltage, this, settings),
new Sensor("12VHPWR Pin 3", 2, SensorType.Voltage, this, settings),
new Sensor("12VHPWR Pin 4", 3, SensorType.Voltage, this, settings),
new Sensor("12VHPWR Pin 5", 4, SensorType.Voltage, this, settings),
new Sensor("12VHPWR Pin 6", 5, SensorType.Voltage, this, settings),
];
_12VHPwrPinCurrentSensors =
[
new Sensor("12VHPWR Pin 1", 1, SensorType.Current, this, settings),
new Sensor("12VHPWR Pin 2", 2, SensorType.Current, this, settings),
new Sensor("12VHPWR Pin 3", 3, SensorType.Current, this, settings),
new Sensor("12VHPWR Pin 4", 4, SensorType.Current, this, settings),
new Sensor("12VHPWR Pin 5", 5, SensorType.Current, this, settings),
new Sensor("12VHPWR Pin 6", 6, SensorType.Current, this, settings),
];
_12VHPwrPinPowerSensors =
[
new Sensor("12VHPWR Pin 1", 2, SensorType.Power, this, settings),
new Sensor("12VHPWR Pin 2", 3, SensorType.Power, this, settings),
new Sensor("12VHPWR Pin 3", 4, SensorType.Power, this, settings),
new Sensor("12VHPWR Pin 4", 5, SensorType.Power, this, settings),
new Sensor("12VHPWR Pin 5", 6, SensorType.Power, this, settings),
new Sensor("12VHPWR Pin 6", 7, SensorType.Power, this, settings),
];
foreach (Sensor sensor in _12VHPwrPinVoltageSensors)
ActivateSensor(sensor);
foreach (Sensor sensor in _12VHPwrPinCurrentSensors)
ActivateSensor(sensor);
foreach (Sensor sensor in _12VHPwrPinPowerSensors)
ActivateSensor(sensor);
_12VHPwrConnectorPowerSensor = new Sensor("12VHPWR Connector", 1, SensorType.Power, this, settings);
_12VHPwrConnectorCurrentSensor = new Sensor("12VHPWR Connector", 0, SensorType.Current, this, settings);
ActivateSensor(_12VHPwrConnectorPowerSensor);
ActivateSensor(_12VHPwrConnectorCurrentSensor);
}
}
Update();
}
@@ -681,6 +748,75 @@ internal sealed class NvidiaGpu : GenericGpu
ActivateSensor(_pcieThroughputTx);
}
}
// Astral specific
if (_12VHPwrPinCurrentSensors is { Length: > 0 } && TryReadAstral12VHPwrPinSensors(out ushort[] pinSensorValues))
{
float connectorCurrent = 0;
float connectorPower = 0;
for (int i = 0; i < 6; i++)
{
float current = pinSensorValues[i * 2] / 1000f;
float voltage = pinSensorValues[i * 2 + 1] / 1000f;
_12VHPwrPinVoltageSensors[i].Value = voltage;
_12VHPwrPinCurrentSensors[i].Value = current;
_12VHPwrPinPowerSensors[i].Value = voltage * current;
connectorCurrent += current;
connectorPower += voltage * current;
}
_12VHPwrConnectorCurrentSensor.Value = connectorCurrent;
_12VHPwrConnectorPowerSensor.Value = connectorPower;
}
}
private bool TryReadAstral12VHPwrPinSensors(out ushort[] data)
{
data = null;
if (NvApi.NvAPI_I2CReadEx == null)
return false;
byte[] dataBuffer = new byte[24];
byte[] regAddress = [0x80];
unsafe
{
fixed (byte* pData = dataBuffer)
fixed (byte* pRegAddr = regAddress)
{
NvApi.NvI2CInfo i2cInfo = new()
{
Version = (uint)NvApi.MAKE_NVAPI_VERSION<NvApi.NvI2CInfo>(3),
DisplayMask = 0,
IsDDCPort = 0,
I2CDevAddress = 0x2B << 1,
I2CRegAddress = (IntPtr)pRegAddr,
RegAddrSize = 1,
Data = (IntPtr)pData,
Size = 24,
I2CSpeed = NvApi.NVAPI_I2C_SPEED_DEPRECATED,
I2CSpeedKhz = NvApi.NvI2CSpeed.Speed100Khz,
PortId = 0x1,
IsPortIdSet = 1
};
uint readData = 0;
if (NvApi.NvAPI_I2CReadEx(_handle, ref i2cInfo, ref readData) != NvApi.NvStatus.OK)
return false;
}
}
// Parse 12 x u16 big-endian (as mV and mA) values in reverse order
data = new ushort[12];
for (int i = 0; i < 12; i++)
{
int pinIndex = 11 - i;
data[i] = (ushort)((dataBuffer[pinIndex * 2] << 8) | dataBuffer[pinIndex * 2 + 1]);
}
return true;
}
public override string GetReport()
+35
View File
@@ -23,6 +23,8 @@ internal static class NvApi
public const int THERMAL_SENSOR_RESERVED_COUNT = 8;
public const int THERMAL_SENSOR_TEMPERATURE_COUNT = 32;
public const uint NVAPI_I2C_SPEED_DEPRECATED = 0xFFFF;
private const string DllName = "nvapi.dll";
private const string DllName64 = "nvapi64.dll";
@@ -46,6 +48,7 @@ internal static class NvApi
public static NvAPI_GPU_GetUsagesDelegate NvAPI_GPU_GetUsages { get; internal set; }
public static NvAPI_GPU_SetCoolerLevelsDelegate NvAPI_GPU_SetCoolerLevels { get; internal set; }
public static NvAPI_GPU_GetThermalSensorsDelegate NvAPI_GPU_GetThermalSensors { get; internal set; }
public static NvAPI_I2CReadExDelegate NvAPI_I2CReadEx { get; internal set; }
public static NvAPI_GPU_ClientVoltRailsGetStatusDelegate NvAPI_GPU_ClientVoltRailsGetStatus { get; internal set; }
private static NvAPI_GetInterfaceVersionStringDelegate _nvAPI_GetInterfaceVersionString;
@@ -91,6 +94,7 @@ internal static class NvApi
NvAPI_GPU_ClientFanCoolersSetControl = GetDelegate<NvAPI_GPU_ClientFanCoolersSetControlDelegate>(0xA58971A5);
NvAPI_GPU_ClientPowerTopologyGetStatus = GetDelegate<NvAPI_GPU_ClientPowerTopologyGetStatusDelegate>(0x0EDCF624E);
NvAPI_GPU_GetThermalSensors = GetDelegate<NvAPI_GPU_GetThermalSensorsDelegate>(0x65FE3AAD);
NvAPI_I2CReadEx = GetDelegate<NvAPI_I2CReadExDelegate>(0x4D7B0709);
NvAPI_GPU_ClientVoltRailsGetStatus = GetDelegate<NvAPI_GPU_ClientVoltRailsGetStatusDelegate>(0x465F9BCF);
IsAvailable = true;
@@ -160,6 +164,9 @@ internal static class NvApi
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvStatus NvAPI_GPU_SetCoolerLevelsDelegate(NvPhysicalGpuHandle gpuHandle, int coolerIndex, ref NvCoolerLevels NvCoolerLevels);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvStatus NvAPI_I2CReadExDelegate(NvPhysicalGpuHandle gpuHandle, ref NvI2CInfo i2cInfo, ref uint readData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvStatus NvAPI_GPU_ClientVoltRailsGetStatusDelegate(NvPhysicalGpuHandle gpuHandle, ref NvGpuClientVoltRailsStatus status);
@@ -194,6 +201,17 @@ internal static class NvApi
BusInterface // Bus
}
public enum NvI2CSpeed : uint
{
Default = 0,
Speed3Khz = 1,
Speed10Khz = 2,
Speed33Khz = 3,
Speed100Khz = 4,
Speed200Khz = 5,
Speed400Khz = 6
}
public static bool IsAvailable { get; private set; }
[DllImport(DllName, EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl, PreserveSig = true)]
@@ -628,6 +646,23 @@ internal static class NvApi
public bool IsPresent => (_isPresent & 1) != 0;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
internal struct NvI2CInfo
{
public uint Version;
public uint DisplayMask;
public byte IsDDCPort;
public byte I2CDevAddress;
public IntPtr I2CRegAddress;
public uint RegAddrSize;
public IntPtr Data;
public uint Size;
public uint I2CSpeed;
public NvI2CSpeed I2CSpeedKhz;
public byte PortId;
public uint IsPortIdSet;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate NvStatus NvAPI_InitializeDelegate();