Skip to content

Commit 2445ce0

Browse files
ShaeOJclaude
andcommitted
Add dynamic hashrate scaling and estimated efficiency for high-power miners
- Dynamic gauge scaling for miners up to 10+ TH/s (NerdAxeQ++, Magic Miner, Canaan Nano S) - Device specs database for estimated efficiency when miners don't report expectedHashrate - Supported devices: Magic Miner BG01/BG02, NerdAxeQ++, Canaan Nano S, BitDSK N5.Rex, Piaxe, Jade, Lucky Miner - Estimated efficiency shown with ~ suffix (e.g., "95.2% eff~") - Bump version to 1.2.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent e093071 commit 2445ce0

8 files changed

Lines changed: 211 additions & 12 deletions

File tree

.github/workflows/release.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,21 @@ jobs:
5555
with:
5656
tagName: ${{ github.ref_name }}
5757
releaseName: 'AxeOS Live! ${{ github.ref_name }}'
58-
releaseBody: 'Building for cross platforms - V1.0'
58+
releaseBody: |
59+
## What's New in V1.2
60+
61+
### Features
62+
- **Dynamic hashrate gauge scaling** - Properly displays high-power miners (NerdAxeQ++, Magic Miner BG01/BG02, Canaan Nano S) up to 10+ TH/s
63+
- **Estimated efficiency for all miners** - Devices that don't report expected hashrate now show estimated efficiency based on known device specs
64+
65+
### Supported Devices for Estimated Efficiency
66+
- Magic Miner BG01/BG02
67+
- NerdAxeQ++
68+
- Canaan Nano S
69+
- BitDSK N5.Rex (BM1397)
70+
- Piaxe, Jade, Lucky Miner, NerdAxe
71+
72+
Estimated efficiency is shown with a `~` suffix (e.g., "95.2% eff~")
5973
releaseDraft: true
6074
prerelease: false
6175
args: ${{ matrix.args }}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nextn",
3-
"version": "0.1.0",
3+
"version": "1.2.0",
44
"private": true,
55
"scripts": {
66
"dev": "next dev -p 9002 -H 0.0.0.0",

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "app"
3-
version = "0.1.0"
3+
version = "1.2.0"
44
description = "A Tauri App"
55
authors = ["you"]
66
license = ""

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
33
"productName": "AxeOS Live!",
4-
"version": "1.0.0",
4+
"version": "1.2.0",
55
"identifier": "com.axeos.live",
66
"build": {
77
"frontendDist": "../out",

src/components/miner-card.tsx

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -710,22 +710,66 @@ export function MinerCard({ minerConfig, onRemove, isRemoving, state, updateMine
710710

711711
const hashrateDisplay = useMemo(() => {
712712
if (hashrateInGhs >= 1000) {
713-
return { value: hashrateInGhs / 1000, unit: 'TH/s', isFloat: true, max: 3 };
713+
// Convert to TH/s for high-power miners
714+
const valueInThs = hashrateInGhs / 1000;
715+
// Dynamic max scaling for different miner tiers:
716+
// - Standard BitAxe: up to 1.5 TH/s
717+
// - NerdAxeQ++, Magic Miner BG01/BG02: up to 7 TH/s
718+
// - Canaan Nano S and larger: up to 10+ TH/s
719+
let max: number;
720+
if (valueInThs <= 1.5) {
721+
max = 2; // Small TH/s miners (standard BitAxe overclocked)
722+
} else if (valueInThs <= 3) {
723+
max = 4; // Mid-range TH/s miners
724+
} else if (valueInThs <= 5) {
725+
max = 6; // Higher-end miners like some Magic Miner models
726+
} else if (valueInThs <= 7) {
727+
max = 8; // NerdAxeQ++, Magic Miner BG01/BG02
728+
} else if (valueInThs <= 10) {
729+
max = 12; // Canaan Nano S range
730+
} else if (valueInThs <= 20) {
731+
max = 25; // Larger miners
732+
} else {
733+
// For very high hashrates, round up to next 10 TH/s
734+
max = Math.ceil(valueInThs / 10) * 10 + 5;
735+
}
736+
return { value: valueInThs, unit: 'TH/s', isFloat: true, max };
737+
}
738+
// GH/s mode - also make max dynamic for better gauge utilization
739+
let max: number;
740+
if (hashrateInGhs <= 500) {
741+
max = 600; // Low-power miners
742+
} else if (hashrateInGhs <= 800) {
743+
max = 1000; // Standard BitAxe range
744+
} else {
745+
max = 1200; // High-end GH/s range (approaching TH/s)
714746
}
715-
return { value: hashrateInGhs, unit: 'GH/s', isFloat: false, max: 3000 };
747+
return { value: hashrateInGhs, unit: 'GH/s', isFloat: false, max };
716748
}, [hashrateInGhs]);
717749

718750
const freq = state.info?.frequency ?? 0;
719751
const cardTitle = minerConfig.name || state.info?.hostname || minerConfig.ip;
720752
const cardDescription = minerConfig.name ? minerConfig.ip : (state.info?.hostname ? `v${state.info.boardVersion}` : '');
721753

722754
// Calculate efficiency percentage (actual vs expected hashrate)
723-
const efficiencyPercent = useMemo(() => {
724-
if (state.info?.hashRate && state.info?.expectedHashrate && state.info.expectedHashrate > 0) {
725-
return ((state.info.hashRate / state.info.expectedHashrate) * 100).toFixed(1);
755+
// Uses device-reported expectedHashrate if available, otherwise falls back to estimated value
756+
const efficiencyData = useMemo(() => {
757+
if (!state.info?.hashRate) return null;
758+
759+
// Prefer device-reported expectedHashrate, fall back to estimated
760+
const expectedHashrate = state.info.expectedHashrate || state.info.estimatedExpectedHashrate;
761+
const isEstimated = !state.info.expectedHashrate && !!state.info.estimatedExpectedHashrate;
762+
763+
if (expectedHashrate && expectedHashrate > 0) {
764+
return {
765+
percent: ((state.info.hashRate / expectedHashrate) * 100).toFixed(1),
766+
isEstimated
767+
};
726768
}
727769
return null;
728-
}, [state.info?.hashRate, state.info?.expectedHashrate]);
770+
}, [state.info?.hashRate, state.info?.expectedHashrate, state.info?.estimatedExpectedHashrate]);
771+
772+
const efficiencyPercent = efficiencyData?.percent ?? null;
729773

730774

731775
const StatusBadge = useMemo(() => {
@@ -815,8 +859,14 @@ export function MinerCard({ minerConfig, onRemove, isRemoving, state, updateMine
815859
<div className="flex items-center gap-2">
816860
{StatusBadge}
817861
{efficiencyPercent && (
818-
<Badge variant="outline" className="text-xs">
819-
{efficiencyPercent}% eff
862+
<Badge
863+
variant="outline"
864+
className="text-xs"
865+
title={efficiencyData?.isEstimated
866+
? `Estimated efficiency based on typical ${state.info?.ASICModel} specs`
867+
: 'Efficiency based on device-reported expected hashrate'}
868+
>
869+
{efficiencyPercent}% eff{efficiencyData?.isEstimated && '~'}
820870
</Badge>
821871
)}
822872
{(state.info?.blockFound === 1 || blockFoundCelebration) && (

src/components/miner-dashboard.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ThemeSwitcher } from './theme-switcher';
1515
import { GlitchText } from './glitch-text';
1616
import { useIsMobile } from '@/hooks/use-mobile';
1717
import { getMinerData } from '@/lib/tauri-api';
18+
import { getExpectedHashrate } from '@/lib/device-specs';
1819

1920
const FETCH_INTERVAL = 15000; // 15 seconds
2021
const MAX_HISTORY_LENGTH = 360; // Keep 90 minutes of history (360 * 15s)
@@ -46,6 +47,15 @@ export function MinerDashboard() {
4647
info.coreVoltage = parseFloat((info.coreVoltage * 1000).toFixed(0));
4748
}
4849

50+
// If device doesn't report expectedHashrate, try to estimate from device specs
51+
if (!info.expectedHashrate && info.ASICModel) {
52+
const estimatedHashrate = getExpectedHashrate(info.ASICModel);
53+
if (estimatedHashrate) {
54+
info.estimatedExpectedHashrate = estimatedHashrate;
55+
info.isEstimatedHashrate = true;
56+
}
57+
}
58+
4959
const hashrateInGhs = info.hashRate ? info.hashRate : 0;
5060
const infoInGhs = { ...info, hashRate: hashrateInGhs };
5161

src/lib/device-specs.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Device specifications for miners that don't report expectedHashrate.
3+
* These values are used to estimate efficiency when the device doesn't provide its own.
4+
*
5+
* All hashrate values are in GH/s.
6+
*/
7+
8+
export interface DeviceSpecification {
9+
model: string | RegExp; // Model identifier (exact string or regex pattern)
10+
expectedHashrate: number; // Expected max hashrate in GH/s
11+
description: string; // Human-readable name
12+
}
13+
14+
/**
15+
* Known device specifications.
16+
* Add new devices here as they become available.
17+
*
18+
* Note: Regex patterns are case-insensitive and match partial strings.
19+
* Order matters - more specific patterns should come before generic ones.
20+
*/
21+
export const DEVICE_SPECIFICATIONS: DeviceSpecification[] = [
22+
// Magic Miner devices
23+
{
24+
model: /Magic[- ]?Miner[- ]?BG02/i,
25+
expectedHashrate: 6800, // Magic Miner BG02 ~6.8 TH/s
26+
description: 'Magic Miner BG02'
27+
},
28+
{
29+
model: /Magic[- ]?Miner[- ]?BG01/i,
30+
expectedHashrate: 5500, // Magic Miner BG01 ~5.5 TH/s
31+
description: 'Magic Miner BG01'
32+
},
33+
{
34+
model: /Magic[- ]?Miner/i,
35+
expectedHashrate: 5500, // Generic Magic Miner fallback
36+
description: 'Magic Miner'
37+
},
38+
39+
// NerdAxe devices
40+
{
41+
model: /NerdAxe[- ]?Q\+\+/i,
42+
expectedHashrate: 5000, // NerdAxeQ++ ~5 TH/s
43+
description: 'NerdAxeQ++'
44+
},
45+
{
46+
model: /NerdAxe/i,
47+
expectedHashrate: 500, // Standard NerdAxe ~500 GH/s
48+
description: 'NerdAxe'
49+
},
50+
51+
// Canaan devices
52+
{
53+
model: /Canaan[- ]?Nano[- ]?S/i,
54+
expectedHashrate: 8000, // Canaan Nano S ~8 TH/s
55+
description: 'Canaan Nano S'
56+
},
57+
{
58+
model: /Nano[- ]?S/i,
59+
expectedHashrate: 8000, // Canaan Nano S (short name)
60+
description: 'Canaan Nano S'
61+
},
62+
63+
// Piaxe devices
64+
{
65+
model: /Piaxe/i,
66+
expectedHashrate: 500, // Piaxe ~500 GH/s
67+
description: 'Piaxe'
68+
},
69+
70+
// Jade devices
71+
{
72+
model: /Jade/i,
73+
expectedHashrate: 500, // Jade ~500 GH/s
74+
description: 'Jade Miner'
75+
},
76+
77+
// Lucky Miner devices
78+
{
79+
model: /Lucky[- ]?Miner/i,
80+
expectedHashrate: 500, // Lucky Miner ~500 GH/s
81+
description: 'Lucky Miner'
82+
},
83+
84+
// BitDSK devices (BM1397 chip)
85+
{
86+
model: /BitDSK[- ]?N5[- ]?Rex/i,
87+
expectedHashrate: 300, // BitDSK N5.Rex ~300 GH/s (BM1397)
88+
description: 'BitDSK N5.Rex'
89+
},
90+
{
91+
model: /N5[- ]?Rex/i,
92+
expectedHashrate: 300, // N5.Rex ~300 GH/s (BM1397)
93+
description: 'BitDSK N5.Rex'
94+
},
95+
{
96+
model: /BitDSK/i,
97+
expectedHashrate: 300, // Generic BitDSK fallback
98+
description: 'BitDSK'
99+
},
100+
];
101+
102+
/**
103+
* Look up device specification by ASIC model string.
104+
* Returns null if no matching specification is found.
105+
*/
106+
export function getDeviceSpecification(asicModel?: string): DeviceSpecification | null {
107+
if (!asicModel) return null;
108+
109+
return DEVICE_SPECIFICATIONS.find(spec => {
110+
if (typeof spec.model === 'string') {
111+
return asicModel.toLowerCase() === spec.model.toLowerCase();
112+
}
113+
return spec.model.test(asicModel);
114+
}) || null;
115+
}
116+
117+
/**
118+
* Get expected hashrate for a device, returning null if unknown.
119+
*/
120+
export function getExpectedHashrate(asicModel?: string): number | null {
121+
const spec = getDeviceSpecification(asicModel);
122+
return spec?.expectedHashrate ?? null;
123+
}

src/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export type MinerInfo = {
2626
nominalVoltage?: number;
2727
hashRate?: number; // MHS
2828
expectedHashrate?: number;
29+
estimatedExpectedHashrate?: number; // Estimated from device-specs when device doesn't report
30+
isEstimatedHashrate?: boolean; // Flag indicating if expectedHashrate is estimated
2931
errorPercentage?: number;
3032
bestDiff?: string;
3133
bestSessionDiff?: string;

0 commit comments

Comments
 (0)