diff --git a/README.md b/README.md index 96bf338..75581e3 100644 --- a/README.md +++ b/README.md @@ -109,8 +109,9 @@ docker run --rm bitaxe-benchmark 192.168.2.26 --set-values -v 1150 -f 780 ## **Configuration** -The script includes several configurable parameters. These can be adjusted in the bitaxe_hashrate_benchmark.py file: +The script includes several configurable parameters. These can be adjusted in the UI or in the bitaxe_hashrate_benchmark.py file: +* Max Acceptable Error Percentage: 1%. * Maximum chip temperature: 66°C * Maximum VR temperature: 86°C * Maximum allowed voltage: 1400mV @@ -136,15 +137,16 @@ The benchmark results are saved to `bitaxe_benchmark_results__ - frontend + Bitaxe Hashrate Benchmark
diff --git a/frontend/src/components/ConfigEditor.tsx b/frontend/src/components/ConfigEditor.tsx index e76a183..9385519 100644 --- a/frontend/src/components/ConfigEditor.tsx +++ b/frontend/src/components/ConfigEditor.tsx @@ -265,6 +265,30 @@ export function ConfigEditor({ config, onSave, disabled }: ConfigEditorProps) { + {/* Analysis */} +
+

Analysis

+
+
+ + handleChange('analysis', 'max_error_percent', e.target.value)} + disabled={disabled} + className="mt-1 w-full bg-gray-700 border border-gray-600 rounded px-2 py-1 text-white text-sm disabled:opacity-50" + /> + Default: 1% +
+
+
+ {error && (
{error}
)} diff --git a/frontend/src/components/ProgressDisplay.tsx b/frontend/src/components/ProgressDisplay.tsx index d5711b9..dde5f78 100644 --- a/frontend/src/components/ProgressDisplay.tsx +++ b/frontend/src/components/ProgressDisplay.tsx @@ -19,6 +19,11 @@ export function ProgressDisplay({ progress, voltageStep }: ProgressDisplayProps) const { sample, core_voltage, frequency, sample_number, total_samples, progress_percent, running_stddev } = progress; + // Try to get error percent from sample if present (for live display) + // If not present, display "--" + // If you have a better source for live error %, replace here + const errorPercent = (progress as any).error_percent; + return (

Live Progress

@@ -119,6 +124,13 @@ export function ProgressDisplay({ progress, voltageStep }: ProgressDisplayProps) color="text-gray-300" /> )} + {/* Error % in lower right */} +
); diff --git a/frontend/src/components/ResultsTable.tsx b/frontend/src/components/ResultsTable.tsx index 3ed7e6f..ab81bf6 100644 --- a/frontend/src/components/ResultsTable.tsx +++ b/frontend/src/components/ResultsTable.tsx @@ -122,6 +122,7 @@ export function ResultsTable({ Temp Power Efficiency + Error % Status @@ -185,6 +186,11 @@ export function ResultsTable({ J/TH + + {typeof result.error_percent === 'number' && !isNaN(result.error_percent) + ? result.error_percent.toFixed(2) + : '--'} + {result.error_reason ? ( diff --git a/frontend/src/types.ts b/frontend/src/types.ts index d5dae9f..fe511a0 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -45,6 +45,7 @@ export interface SampleProgress { frequency: number; sample: SampleData; running_stddev: number; + error_percent?: number | null; timestamp: string; } @@ -60,6 +61,7 @@ export interface IterationResult { efficiency_jth: number; hashrate_within_tolerance: boolean; error_reason: string | null; + error_percent?: number | null; } export interface IterationComplete { @@ -145,6 +147,7 @@ export interface AnalysisConfig { trim_outliers: number; warmup_samples: number; min_samples: number; + max_error_percent?: number; // Maximum acceptable error percentage (default 1%) } export interface BenchmarkConfig { diff --git a/src/benchmark/core.py b/src/benchmark/core.py index 911b048..9b6bd28 100644 --- a/src/benchmark/core.py +++ b/src/benchmark/core.py @@ -282,6 +282,28 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="SYSTEM_INFO_FAILURE", + error_percent=None, + ) + + # --- Error Percentage Check --- + error_percentage = info.get("errorPercentage", 100.0) # Default to 100% if not provided + max_error = getattr(self.config.analysis, "max_error_percent", 1.0) + if error_percentage is not None and error_percentage > max_error: + self._log( + "error", + f"Error percentage {error_percentage:.2f}% exceeds threshold ({max_error:.2f}%)" + ) + return IterationResult( + core_voltage=voltage, + frequency=frequency, + average_hashrate=0, + hashrate_stddev=0, + average_temperature=info.get("temp", 0), + average_power=info.get("power", 0), + efficiency_jth=float("inf"), + hashrate_within_tolerance=False, + error_reason="ERROR_PERCENTAGE_EXCEEDED", + error_percent=error_percentage, ) temp = info.get("temp") @@ -303,6 +325,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="TEMPERATURE_DATA_FAILURE" if temp is None else "TEMPERATURE_BELOW_5", + error_percent=error_percentage, ) if temp >= self.config.safety.max_temp: @@ -316,6 +339,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="CHIP_TEMP_EXCEEDED", + error_percent=error_percentage, ) if vr_temp is not None and vr_temp >= self.config.safety.max_vr_temp: @@ -330,6 +354,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="VR_TEMP_EXCEEDED", + error_percent=error_percentage, ) if input_voltage < self.config.safety.min_input_voltage: @@ -343,6 +368,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="INPUT_VOLTAGE_BELOW_MIN", + error_percent=error_percentage, ) if input_voltage > self.config.safety.max_input_voltage: @@ -356,6 +382,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="INPUT_VOLTAGE_ABOVE_MAX", + error_percent=error_percentage, ) if hash_rate is None or power is None: @@ -369,6 +396,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="HASHRATE_POWER_DATA_FAILURE", + error_percent=error_percentage, ) if power > self.config.safety.max_power: @@ -382,6 +410,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="POWER_CONSUMPTION_EXCEEDED", + error_percent=error_percentage, ) # Record sample @@ -413,6 +442,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) fan_speed=fan_speed, ), running_stddev=self._running_stddev(sample_num + 1, s1, s2), + error_percent=error_percentage, ) ) @@ -432,6 +462,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="NO_DATA_COLLECTED", + error_percent=error_percentage, ) # Trim outliers @@ -469,6 +500,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=float("inf"), hashrate_within_tolerance=False, error_reason="ZERO_HASHRATE", + error_percent=error_percentage, ) hashrate_within_tolerance = average_hashrate >= expected_hashrate * self.config.analysis.hashrate_tolerance @@ -485,6 +517,7 @@ async def _run_iteration(self, voltage: int, frequency: int, iteration_num: int) efficiency_jth=efficiency_jth, hashrate_within_tolerance=hashrate_within_tolerance, error_reason=None, + error_percent=error_percentage, ) async def _check_pause(self) -> bool: diff --git a/src/config.py b/src/config.py index b0168fc..8d95e73 100644 --- a/src/config.py +++ b/src/config.py @@ -43,6 +43,7 @@ class AnalysisConfig(BaseModel): trim_outliers: int = Field(default=3, description="Number of outliers to trim from each end") warmup_samples: int = Field(default=6, description="Number of warmup samples to exclude from temp averaging") min_samples: int = Field(default=7, description="Minimum samples required for valid benchmark") + max_error_percent: float = Field(default=1.0, description="Maximum acceptable error percentage before marking iteration as bad") class BenchmarkConfig(BaseModel): diff --git a/src/models.py b/src/models.py index e868b39..0ecc85b 100644 --- a/src/models.py +++ b/src/models.py @@ -60,6 +60,7 @@ class SampleProgress(BaseModel): frequency: int = Field(description="Current frequency in MHz") sample: SampleData = Field(description="Sample measurement data") running_stddev: float = Field(description="Running standard deviation of hashrate") + error_percent: float = Field(default=None, description="Current error percentage reported by device (if available)") timestamp: datetime = Field(default_factory=datetime.now) @@ -77,6 +78,7 @@ class IterationResult(BaseModel): efficiency_jth: float = Field(description="Efficiency in J/TH") hashrate_within_tolerance: bool = Field(description="Whether hashrate met expected threshold") error_reason: Optional[str] = Field(default=None, description="Error if iteration failed") + error_percent: Optional[float] = Field(default=None, description="Error percentage reported by device (if available)") class IterationComplete(BaseModel):