diff --git a/relay/playbooks/ca3-pc1-amd-grid.ps1 b/relay/playbooks/ca3-pc1-amd-grid.ps1 new file mode 100644 index 000000000..86ce034d1 --- /dev/null +++ b/relay/playbooks/ca3-pc1-amd-grid.ps1 @@ -0,0 +1,80 @@ +# CA3 PC 1: the RX 9070 XT's first measured grid on the AMD knob (0.3.25, main's order of 8 October 2026), by job, +# unelevated, under the INSTALLED app (0.3.20 to 0.3.24: no AMD knob in the engine), driving the rebuilt +# igneum-gpu-telemetry.exe directly (--card N --set-gmax OFFSET, --set-plimit PCT, --reset; ADLX manual tuning needs no +# elevation). The card keeps mining in the app the whole time; the rate is the app's own hash_now for that card +# (api/state), the draw and the clock in force are the tool's sample line. One card at a time; the NVIDIA cards are +# never touched. Never quits, pauses or resumes the installed app and never starts its tune (the 5 October rule). +# Rows: RESULT GRID row gmax_off= plimit= gclk= watts= mhs= mhw= mark=ok| +# Env (optional): IGNEUM_GRID_TOOL (path of the rebuilt exe; default: the job folder's copy, else the installed one), +# IGNEUM_GRID_HOLD_S (default 75), IGNEUM_GRID_GMAX (default "0,-100,-200,-300,-400,-500"), IGNEUM_GRID_PLIMIT (default "0,-10,-20,-30"). +$ErrorActionPreference = 'Continue' +function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) } +$install = Join-Path $env:LOCALAPPDATA 'Programs\Igneum Miner' +$tool = $env:IGNEUM_GRID_TOOL +if (-not $tool) { $cand = Join-Path (Get-Location) 'igneum-gpu-telemetry.exe'; if (Test-Path -LiteralPath $cand) { $tool = $cand } else { $tool = Join-Path $install 'igneum-gpu-telemetry.exe' } } +if (-not (Test-Path -LiteralPath $tool)) { Write-Output ('RESULT GRID error=no_tool ' + $tool); exit 1 } +Say ("tool: " + $tool + " sha256 " + (Get-FileHash -LiteralPath $tool -Algorithm SHA256).Hash.ToLower()) +$hold = if ($env:IGNEUM_GRID_HOLD_S) { [int]$env:IGNEUM_GRID_HOLD_S } else { 75 } +$gmaxList = @((($(if ($env:IGNEUM_GRID_GMAX) { $env:IGNEUM_GRID_GMAX } else { '0,-100,-200,-300,-400,-500' })) -split ',') | ForEach-Object { [int]$_ }) +$plimitList = @((($(if ($env:IGNEUM_GRID_PLIMIT) { $env:IGNEUM_GRID_PLIMIT } else { '0,-10,-20,-30' })) -split ',') | ForEach-Object { [int]$_ }) +# the tune line: which ordinal is the 9070 XT, and what it allows +$tune = @(& $tool --tune 2>&1 | ForEach-Object { "$_" }) +$tune | ForEach-Object { Say ('tune: ' + $_) } +$line = $tune | Where-Object { $_ -match '^tune (\d+) name "([^"]*9070[^"]*)" .* ok\s*$' } | Select-Object -First 1 +if (-not $line) { Write-Output ('RESULT GRID error=no_tune_line ' + (($tune | Where-Object { $_ -match '^tune|^info' }) -join ' | ')); exit 1 } +$ord = [int]([regex]::Match($line, '^tune (\d+)').Groups[1].Value) +$gr = [regex]::Match($line, 'gmax_range (-?\d+) (-?\d+)'); $pr = [regex]::Match($line, 'plimit_range (-?\d+) (-?\d+)') +$gmin = [int]$gr.Groups[1].Value; $gmaxTop = [int]$gr.Groups[2].Value; $pmin = [int]$pr.Groups[1].Value; $pmax = [int]$pr.Groups[2].Value +Write-Output ('RESULT GRID card ordinal=' + $ord + ' gmax_range=' + $gmin + '..' + $gmaxTop + ' plimit_range=' + $pmin + '..' + $pmax) +# the app: the AMD card's key and that no tune of the app's runs on it +$urlFile = Join-Path $env:LOCALAPPDATA 'igneum\app\app.url' +if (-not (Test-Path -LiteralPath $urlFile)) { Write-Output 'RESULT GRID error=no_app_url'; exit 1 } +$base = (Get-Content -LiteralPath $urlFile -Raw).Trim() +function State() { try { return ((Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json) } catch { return $null } } +$s = State +if (-not $s) { Write-Output 'RESULT GRID error=app_not_answering'; exit 1 } +$card = $s.mining.cards | Where-Object { $_.vendor -eq 'amd' -and $_.name -match '9070' } | Select-Object -First 1 +if (-not $card) { Write-Output 'RESULT GRID error=no_amd_card_in_app'; exit 1 } +if ($card.sweep_state -eq 'running') { Write-Output 'RESULT GRID error=app_tune_running_on_card'; exit 1 } +Write-Output ('RESULT GRID app version=' + $s.version + ' card key=' + $card.key + ' hash_now=' + $card.hash_now + ' enabled=' + $card.enabled) +if (-not $card.enabled -or [double]$card.hash_now -le 0) { Write-Output 'RESULT GRID error=card_not_mining (the grid needs the app mining on it)'; exit 1 } +function Sample() { $o = @(& $tool 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match ('^amd ' + $ord + ' ') } | Select-Object -First 1; if (-not $o) { return $null }; $w = [regex]::Match($o, ' watts (-?[\d.]+)'); $g = [regex]::Match($o, ' gclk_mhz (-?[\d.]+)'); $p = [regex]::Match($o, ' plimit_pct (-?[\d.]+)'); $m = [regex]::Match($o, ' gmax_mhz (-?[\d.]+)'); return @{ watts = [double]$w.Groups[1].Value; gclk = [double]$g.Groups[1].Value; plimit = [double]$p.Groups[1].Value; gmax = [double]$m.Groups[1].Value } } +function SetPoint([int] $goff, [int] $poff) { + $a = @(& $tool --card $ord --set-gmax $goff 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1 + $b = @(& $tool --card $ord --set-plimit $poff 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1 + return @{ gmax = ($a -match ' ok\s*$'); plimit = ($b -match ' ok\s*$'); lines = ($a + ' | ' + $b) } +} +function Median($xs) { $a = @($xs | Sort-Object); if ($a.Count -eq 0) { return 0 }; return $a[[int][math]::Floor(($a.Count - 1) / 2)] } +$rows = @() +$t0 = Get-Date +foreach ($p in $plimitList) { + if ($p -lt $pmin -or $p -gt $pmax) { Say ("plimit " + $p + " outside the range, skipped"); continue } + foreach ($g in $gmaxList) { + if ($g -lt $gmin -or $g -gt $gmaxTop) { Say ("gmax offset " + $g + " outside the range, skipped"); continue } + $set = SetPoint $g $p + if (-not $set.gmax -or -not $set.plimit) { Write-Output ('RESULT GRID row gmax_off=' + $g + ' plimit=' + $p + ' mark=refused ' + $set.lines); continue } + Say ("point gmax " + $g + " plimit " + $p + ": set (" + $set.lines + "); holding " + $hold + " s") + $rates = @(); $watts = @(); $gclks = @(); $deadline = (Get-Date).AddSeconds($hold); $settle = (Get-Date).AddSeconds([math]::Min(30, $hold / 2)) + while ((Get-Date) -lt $deadline) { + Start-Sleep -Seconds 5 + $st = State; $sm = Sample + if ((Get-Date) -lt $settle) { continue } + if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $card.key } | Select-Object -First 1; if ($cc -and [double]$cc.hash_now -gt 0) { $rates += [double]$cc.hash_now } } + if ($sm) { if ($sm.watts -gt 0) { $watts += $sm.watts }; if ($sm.gclk -gt 0) { $gclks += $sm.gclk } } + } + $mhs = [math]::Round((Median $rates), 2); $w = if ($watts.Count) { [math]::Round((($watts | Measure-Object -Average).Average), 1) } else { 0 }; $gc = [math]::Round((Median $gclks), 0) + $mhw = if ($w -gt 0) { [math]::Round($mhs / $w, 4) } else { 0 } + $mark = if ($rates.Count -lt 3) { 'no_rate' } elseif ($w -le 0) { 'no_draw' } else { 'ok' } + $row = 'gmax_off=' + $g + ' plimit=' + $p + ' gclk=' + $gc + ' watts=' + $w + ' mhs=' + $mhs + ' mhw=' + $mhw + ' samples=' + $rates.Count + ' mark=' + $mark + Write-Output ('RESULT GRID row ' + $row) + if ($mark -eq 'ok') { $rows += @{ g = $g; p = $p; mhs = $mhs; w = $w; mhw = $mhw } } + } +} +$r = @(& $tool --card $ord --reset 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1 +Write-Output ('RESULT GRID reset ' + $r) +if ($rows.Count) { + $best = $rows | Sort-Object { $_.mhw } -Descending | Select-Object -First 1 + $stock = $rows | Where-Object { $_.g -eq 0 -and $_.p -eq 0 } | Select-Object -First 1 + Write-Output ('RESULT GRID best gmax_off=' + $best.g + ' plimit=' + $best.p + ' mhs=' + $best.mhs + ' watts=' + $best.w + ' mhw=' + $best.mhw + $(if ($stock) { ' stock mhs=' + $stock.mhs + ' watts=' + $stock.w + ' mhw=' + $stock.mhw } else { '' })) +} +Write-Output ('RESULT GRID done rows=' + $rows.Count + ' minutes=' + [math]::Round(((Get-Date) - $t0).TotalMinutes, 1))