fix(receiver): revert indicator to framesWithQR/accepted/needed with correct math

- framesWithQR now counts ALL frames with a valid QR (including duplicates),
  not just deduplicated ones. Moved increment before dedup check.
- acceptedPackets already correctly counts linearly-independent packets
  (rank increase), capped at K per generation by the decoder.
- neededPackets = K * sourceGenerations, computed from constants.
- Keep generation count (solvedGens/sourceGens) as secondary indicator.

This gives an honest rough progress bar: useful can approach or briefly
touch required while some generations are still partially filled, but the
generation count is the definitive completion signal.
This commit is contained in:
Hermes Agent
2026-05-04 14:36:39 +00:00
parent f4ef1bb473
commit d7fc7606ec
2 changed files with 16 additions and 10 deletions
+12 -8
View File
@@ -155,6 +155,7 @@ export function ReceiverPage() {
const [totalFrames, setTotalFrames] = useState(0);
const [framesWithQR, setFramesWithQR] = useState(0);
const [acceptedPackets, setAcceptedPackets] = useState(0);
const [neededPackets, setNeededPackets] = useState(0);
const [receivedFile, setReceivedFile] = useState<ReceivedFile | null>(null);
const [receivedText, setReceivedText] = useState('');
const [error, setError] = useState('');
@@ -181,6 +182,7 @@ export function ReceiverPage() {
setTotalFrames(msg.totalFrames ?? 0);
setFramesWithQR(msg.framesWithQR ?? 0);
setAcceptedPackets(msg.acceptedPackets ?? 0);
setNeededPackets(msg.neededPackets ?? 0);
setSolvedGens(msg.solvedGenerations ?? 0);
setSourceGens(msg.sourceGenerations ?? 0);
if (msg.dataLength) {
@@ -281,6 +283,7 @@ export function ReceiverPage() {
setTotalFrames(0);
setFramesWithQR(0);
setAcceptedPackets(0);
setNeededPackets(0);
setElapsedMs(0);
setThroughputKbps(0);
setSolvedGens(0);
@@ -354,6 +357,7 @@ export function ReceiverPage() {
setTotalFrames(0);
setFramesWithQR(0);
setAcceptedPackets(0);
setNeededPackets(0);
setElapsedMs(0);
setThroughputKbps(0);
setSolvedGens(0);
@@ -548,13 +552,13 @@ export function ReceiverPage() {
{scanning && (
<div style={S.statsBar}>
<span>
QRs{' '}
<span style={S.statValue}>
{framesWithQR}/{solvedGens}/{sourceGens}
</span>{' '}
gens
{framesWithQR}/{acceptedPackets}/{neededPackets || '?'}
</span>
</span>
<span>
pkts <span style={S.statValue}>{acceptedPackets}</span>
gens <span style={S.statValue}>{solvedGens}/{sourceGens}</span>
</span>
<span>
time <span style={S.statValue}>{formatDuration(elapsedMs)}</span>
@@ -615,13 +619,13 @@ export function ReceiverPage() {
{scanning && (
<div style={S.statsBar}>
<span>
QRs{' '}
<span style={S.statValue}>
{framesWithQR}/{solvedGens}/{sourceGens}
</span>{' '}
gens
{framesWithQR}/{acceptedPackets}/{neededPackets || '?'}
</span>
</span>
<span>
pkts <span style={S.statValue}>{acceptedPackets}</span>
gens <span style={S.statValue}>{solvedGens}/{sourceGens}</span>
</span>
<span>
time <span style={S.statValue}>{formatDuration(elapsedMs)}</span>
+4 -2
View File
@@ -112,12 +112,12 @@ function handleFrame(imageData: ImageData): void {
current.isCompressed = h.compressed;
current.stats.totalFrames++;
current.stats.framesWithQR++;
// Dedup: generationIndex:symbolIndex
const dedupKey = `${h.generationIndex}:${h.symbolIndex}`;
if (current.dedup.has(dedupKey)) return;
current.dedup.add(dedupKey);
current.stats.framesWithQR++;
// Feed to decoder
const gen = h.generationIndex;
@@ -251,12 +251,14 @@ function reconstructData(state: DecodeState): void {
function reportProgress(state: DecodeState): void {
const totalGens = state.totalGenerations;
const solvedGens = state.solvedGenerations.size;
const needed = state.sourceGenerations > 0 ? K * state.sourceGenerations : 0;
self.postMessage({
self.postMessage({
type: 'progress',
totalFrames: state.stats.totalFrames,
framesWithQR: state.stats.framesWithQR,
acceptedPackets: state.stats.acceptedPackets,
neededPackets: needed,
receivedPackets: state.receivedPackets,
solvedGenerations: solvedGens,
totalGenerations: totalGens,