CodeRigor

GUIDED LESSON / 3 OF 6

Suppress repeats without losing new events

Use a monotonic cooldown and a bounded set of recent marker labels.

What moves through this step
  1. 01Decoded text
  2. 02Full-match validation
  3. 03Recent-label map
  4. 04Accept or ignore

A frame is not an event

The camera may decode the same label on many consecutive frames. The journal needs a policy for turning those observations into events. This implementation remembers accepted labels for five seconds and limits the set to sixty-four entries.

Python
"""A small, bounded QR event policy; no camera library required."""
import math
import re


class Journal:
    def __init__(self, cooldown=5.0, capacity=64):
        if cooldown <= 0 or not math.isfinite(cooldown) or capacity < 1:
            raise ValueError("positive cooldown and capacity required")
        self.cooldown, self.capacity = cooldown, capacity
        self.seen = {}
        self.last_time = None

    def accept(self, payload, now):
        if not isinstance(payload, str) or not re.fullmatch(r"CRIGOR:[A-Z0-9][A-Z0-9_-]{0,23}", payload):
            return None
        if not isinstance(now, (int, float)) or isinstance(now, bool) or not math.isfinite(now):
            raise ValueError("finite monotonic timestamp required")
        if self.last_time is not None and now < self.last_time:
            raise ValueError("clock moved backwards")
        self.last_time = now
        self.seen = {key: t for key, t in self.seen.items() if now - t < self.cooldown}
        if payload in self.seen or len(self.seen) >= self.capacity:
            return None
        self.seen[payload] = now
        return {"label": payload.removeprefix("CRIGOR:"), "elapsed_s": now}

At exactly the cooldown boundary, an expired label is allowed again. A second label can be recorded immediately. When the recent set is full, an additional label is ignored until space expires; memory use does not grow with arbitrary QR input.

last_time rejects a backwards timestamp. Tests can supply explicit times, while the real adapter calls time.monotonic(). A clock value is not inferred from frame number because camera frame rates vary.

Run a controlled replay

Python
from journal import Journal
j = Journal()
print(j.accept("CRIGOR:BOX_A", 1.0))
print(j.accept("CRIGOR:BOX_A", 2.0))
print(j.accept("CRIGOR:BOX_A", 6.0))

The middle result is None. The first and third are event dictionaries. Change the second label to CRIGOR:BOX_B and explain why it is accepted.

Try it

Run the supplied tests, then add a test for the longest allowed label. Decide whether a two-second cooldown would fit a different project and change the constructor argument, rather than scattering timing constants throughout capture code.