Broadcast playout · Karhu TV
Karhu TV builds Rytmi — a software playout engine that turns a stock PC and a Blackmagic DeckLink card into a TAI-disciplined SDI playout chain, with a tamper-evident as-run log built into the timing core rather than bolted on afterwards. The free opx-* tools below are the instruments we wrote to prove it correct. Between 6 and 20 August 2026 the first unit produced 61,088,850 frames in one unbroken 339-hour run, with none late on the SDI output.
What we actually make
There are two conventional places to take playout timing from, and both leave the card's own clock unmeasured. Pace from the host and you assume the SDI card agrees with it; lock to house sync and you assume the reference will always be there. Either way nobody is watching the crystal in the card, and it is a few parts per million off — ours by about −3.7 ppm through 1 Aug 2026, which is one slipped frame every hour and a half or so if nothing corrects for it.
Syke paces from TAI — a monotonic timescale with no leap seconds — and measures the card's drift continuously rather than assuming it away. Rytmi's timing authority is Syke's, and Syke's is TAI. Every schedule item airs against an absolute instant, not an offset from whenever the process happened to start — with three known exceptions. RET-122, in the register below: the head of a looping region airs twice per loop, and displaces what follows; the first unit has carried a fix since 3 Oct 2026, not accepted as of 6 Oct, and the second unit has none. And RET-142, found off air on 4 Oct 2026: a schedule whose first item lay more than a minute ahead of the engine’s start aired that item at once, early by almost the whole lead. Neither unit’s schedules start that far ahead. Under the first unit’s fix, since 5 Oct 2026, such an item waits, and airs late by however much of a second the engine’s start left before its next whole second — 110 to 757 ms in eight tests without a card on 11 Oct 2026, up to a second in all — which is still late. A fix for that, made on 11 Oct 2026, is on neither unit as of that day; the second unit has neither fix. And RET-146, found on 8 Oct 2026: on the first unit every item aired a constant 41–58 ms after its instant (the median of each of its eleven runs of 5–9 Oct 2026 before the fix; some items a further 40 ms later), the figure set partly by when each run started — frames were stamped a frame late, and handed to the output a moment after the tick that should have aired them. The first unit has carried a fix since 00:33 UTC on 9 Oct 2026, not accepted as of that day. Across its first four runs on the fix, 77 of the 80 items of a schedule’s first pass started on their instants to within a millisecond, and three — the same item each time — 20 ms early. That was a second fault, found once the fix was on air: a looping region never took a fresh time from the schedule, so the part-frames left over where a 29.97 clip was cut added up until one of its airings ended 20 ms short. It was fixed on the first unit at 20:13 UTC the same day, and in that run’s first pass all 20 started on their instants. Putting the fix on air also showed that, on a schedule with no times, it cut two frames from each of the first eleven items after a start; that was fixed on the first unit at 11:20 UTC. The second unit runs the code from before all of this, with none of these fixes, and was not measured.
Which is why, by design, losing house sync would not cost you your time base — a design we first put to the test on 27 Sep 2026, as far as our own instruments reach; the register below has what that showed, what locking cost here, and what it did not cover. Take the second of those approaches and the reference is your timing authority, so when it goes you are left on an uncorrected crystal. Syke's authority is TAI — and Rytmi's is Syke's — so the card's rate error is a measured quantity rather than an assumption; we predict its consequences from a measured figure — to within 1 % at the median over the twelve-hour runs of July 2026 (individual runs −8 %/+19 %), and to 14 % over the 339-hour run of August, where the sheds arrived sooner than the model said because the drift number we publish is a run-cumulative mean that lags the crystal (median observed ÷ predicted 0.863 over 213 intervals, re-derived 21 Aug 2026). Genlock is something the card can take and we do not depend on: the design rule from the start was that the clock must not rely on it, and until 27 Sep 2026 the first unit had never been genlocked — through the 339-hour soak that ended on 20 August 2026 the card reported no late and, after start-up, no dropped frames of its own, and the engine's own late-frame count is carried in the panel above while a soak is running and explained, when it is not zero, in the table below.
The as-run log is a hash chain. Each record commits to the one before it, blocks are sealed and fsynced, and rotated day files are verifiable across boundaries. It is written as the engine plays, not assembled afterwards, so changing an earlier record breaks the chain for anyone who already holds a hash from later in it. Since 3 Oct 2026 the first unit’s log also records each airing of a schedule item, and signs each seal with a key made on that machine. As of 6 Oct 2026 nobody outside Karhu TV can check those signatures, the log carries no anchor outside itself, and our backups of it are mirrors that a later copy overwrites: the hashes are plain SHA-256, so someone holding the day files could rewrite a record and recompute every hash after it, in that file and every later one, and only a hash kept out of their reach would show the change. What verifying the log proves, and what it does not.
And it is software, not an appliance. The machine behind every figure on this page is one of two machines built around stock Intel desktop processors, each with a DeckLink card any broadcast dealer stocks. Their motherboards, memory and drives are standard retail parts too, listed part by part with the software versions each machine runs. The nanoseconds come from measurement, not from custom silicon — the engine treats every clock in the box as something to verify rather than trust, which is why it holds time on hardware you can buy, replace, and keep spares for. When a card fails, you swap a card — not a channel appliance.
One price, everything enabled, bought once. There is no feature matrix, no per-channel tier, no per-output unlock and no annual renewal: the licence is perpetual and the software you install is the whole software. We price this way because the alternative — charging separately for the things a playout system already has to do — makes the vendor's incentives point away from the operator's. It also means the number we quote is the number, which is not universally true in this market.
Four independent SDI outputs per card — first driven from one process on 5 Sep 2026, on the second unit, which has since run all four unattended — 221 hours unbroken, 18–27 Sep 2026, every output playing the same mixed-source schedule at one output rate, the engine counting no late frame. On 14 Sep 2026 a 220 ms stall during a test of ours cost three of its four outputs their card buffer, and an ordinary clip change twelve minutes later began an hour of frames dropped at the card; they did not recover without a restart (open). A source faster than the channel’s output rate airs slow, by the ratio of the two rates (0.834× for a 59.94 source on a 50 channel, 4 Aug 2026, RET-128). The engine does not refuse it, and flags it in its log only for the items it checks at start-up — by default the first 30, and any due within 30 minutes. Runs on Ubuntu. Binary-only, under NDA — the timing core is the company, so it isn't published.
Evidence
Nothing on this page is a target, a projection, or a figure from a datasheet. Each row is something we measured on hardware and can show you the capture for.
| Measurement | Result | How it was established |
|---|---|---|
| Late engine frames, pre-registered null test | 0 | adjudicated 30 Jul 2026. 35.8 h — three consecutive epochs spanning two scheduled restarts; 1 Hz sampling, 99.98 % coverage; criteria frozen on 29 Jul, after the first epoch had ended and before the third began — the twelve hours the test was decided on. That was a different and shorter test; for the 14-day campaign, which has since concluded, see the row below |
| Late engine frames, 14-day campaign — concluded | 2 | 2 across the full fourteen days, and zero late frames on the SDI output — two counters, and the card reported none of its own. The campaign ran to its frozen end and the engine was then stopped deliberately, at 2026-08-20T23:16:43 UTC, after 339 h unbroken. Both events: 11 Aug, 03:25 UTC (567 µs) and 15 Aug, 08:50 UTC (570 µs). The panel had the first by 03:33 — eight minutes later, unattended, because that panel was generated from the engine's own counters every fifteen minutes rather than written by hand. We did not notice the second for two days. The panel published it within minutes, as it is built to; nobody was reading the panel. That is worth saying plainly, because the automation and the attention are different claims and only the first one held. The criterion for this campaign was frozen before it started and allows zero, so the campaign has failed its own test — and that is the row, rather than the clean days either side of it. The second event does not change that verdict; the first had already decided it. What the counter actually means: on one tick out of the fifty in one second, the clock thread woke 567 µs after the instant it was scheduled for. The frame was still built and the card's output buffer absorbed it, so nothing reached the SDI output late, missing or out of order. This is an internal timing miss, not a dropped frame, and we would rather explain the difference than let the word do work it hasn't earned. Re-derived over the whole campaign after it ended, the two are members of a final population of 109 excursions of 380 µs or more — not the nine this row reported through 12 Aug, and not the 67 it reported through 17 Aug while the run was still going. They are all in the same direction (the clock thread always wakes late, never early), and 106 of the 109 sit in a tight band of 380–450 µs, median 413. This corrects what we published here before. On nine samples we said the 500 µs line that trips this counter sat inside the cluster; on 109 it does not. The two late frames stand 116 µs clear of the highest of the other 107, with nothing in between — so they are not simply the members that happened to cross an arbitrary line, and the threshold is not what made them count. That is a worse result than the one it replaces, in the sense that it removes an easy explanation, and it is the one the data supports. Ruled out, each by a measurement that could have said otherwise: the time daemon's own steering — we read TAI from the kernel and NTP disciplines that clock, so a correction applied to it was a live candidate and had to be excluded by measurement rather than by assertion — and the as-run block seal, thermal, and any engine stall or restart. Not ruled out, and not ruled in: clip junctions — they run typically 25 s apart (median) on this schedule, so proximity to one carries no information either way, and saying we had excluded them would be dressing an untestable up as a result. Ruled in: the ~113 s recurrence (measured 112.68 s), and it is now much harder to argue with than it was on nine samples. The 109 arrived in 36 bursts of one to five, and every gap inside a burst is 112 or 113 s, or exactly twice that where a beat is missed — 73 gaps, no other value, none. A metronome that regular is not local load, and the magnitudes are indistinguishable between the bursts that fall in working hours and those at 02:00 and 05:00 local with nobody logged in (median 412 against 413 µs). Re-derived after the campaign ended, and it did not hold: the observation that the original nine all fell inside a long MXF decode was established on those nine, and the full population broke it — events land inside items as short as six seconds, and in more than one format, so “long MXF” was a property of the first nine rather than of the class. There is a grouping that does separate the population cleanly, and it is about how an item is handled on its way to the output rather than what the item is; that is still an association and it still does not name a cause. Cause not determined. We publish it undetermined rather than attach the tidiest available story to it — and this row has now had to correct itself twice, which is what publishing an open question costs compared with waiting for a closed one |
| Frame underruns, after start-up priming | — | withdrawn 4 Aug 2026, and the empty cell is the point. This row read 0, from the 30 Jul null test. On 4 Aug we found the instrument wanting: it published a starvation run-length that reset the moment a frame arrived, so a recovered underrun — nearly all of them — was structurally invisible to it. That 0 was not so much false as unearned; the counter could not have reported anything else. It is cumulative now and reads whatever decoder priming cost a run — the panel above carries the figure whenever a soak is running — so the bar has moved: what would earn this row back is a completed run in which the counter does not grow after start-up. A run has now completed, and that bar is met: across the 14-day run the counter read 71 — all of it decoder priming, inside the first eight seconds — and then did not move again for 339 hours, in all 20,363 samples of it. The cell stays empty. The fix has since run on the second unit, and neither unit has stayed flat: the same bench's next run grew by 33 on 21 Aug 2026, while we were running our own work on the machine alongside it, and on 24 Sep 2026 one of the second unit's four channels grew by 3, unexplained, in a 221-hour run. A clean result would still have to hold on more than one bench, because the value it would publish is a zero and a zero is exactly what this row was wrong about the first time. Earning it back needs one more thing than a good result, and note that this row stays empty while the campaign row above it has already failed, which is the point of scoring them separately |
| Clock phase, typical (mean magnitude) | 53 ns | mean of |phase| per tick across the first unit’s 14-day run of 6–20 Aug 2026, sampled one second in every minute: 20,363 seconds. Its runs of 9–27 Sep 2026 read 54 ns the same way. Until it was re-derived on 28 Sep 2026 this row read 45 ns: one live reading on 2 Aug 2026, which it attributed to a 4,552 s window without a derivation. The panel above reports this same quantity for single seconds whenever a soak is running; when its label reads “hour’s worst”, it is the worst such second in the last hour |
| Clock phase, median of the per-second peak | 192 ns | a different quantity from the row above: worst tick in each second, then the median of those; p99 1,061 ns, across the first unit’s runs of 9–27 Sep 2026, sampled one second in every minute. For the 14-day run of 6–20 Aug 2026 the panel above draws this quantity for every second, as a distribution, whenever no soak is running. This row used to give 175 ns, p99 1,060, from one 4,552 s window of one run on 2 Aug 2026 — true of that window, and replaced by a longer and later measurement |
| Worst single tick, per run | 944–3,091 ns | the typical band on 42 of the 51 runs between the fsync fix of 28 Jul and 18 Aug 2026, a run being counted wherever our once-a-minute log shows the frame counter going back; most of them were under an hour. Every run from 19 Aug to 5 Sep 2026 exceeded it, the worst at 904,629 ns. The three runs of 6–9 Sep peaked at 326,072, 1,217 and 89,932 ns, and every run on the first unit from 9 to 27 Sep 2026 stayed at or below 4,043 ns, including runs of 176 and 195 hours. We changed the host and the engine on 8 and 9 Sep; the excursions' cause is not determined, so we credit neither change. Every run that exceeded it, through 18 Aug 2026: 39 µs, 198 µs, 331 µs, 434 µs, 570 µs, 1.21 ms, 1.35 ms, 4.93 ms and 23.45 ms — nine, and this list was checked against the log by re-derivation on 12 Aug after its previous version claimed “every” while missing one (198 µs, an eight-minute run during the 4 Aug deploy restarts). Two further runs on 28 Jul itself exceeded the band before the fix landed at 22:52 UTC that day and are the previous era’s numbers, not this row’s. That last one is longer than a frame, and on a page whose first line is that a frame is 20 milliseconds it is the number you should want from us: it happened on 4 Aug, it came with two starved decoder frames rather than a clock fault — the phase figure is the consequence of a repeated frame, not its cause — and it is the reason the underrun instrument three rows up was rebuilt and that row now shows no value at all. Until 12 Aug this row listed only the first four and said local build load was the common thread. Both were wrong: the list was nine days stale and omitted the two largest, and the 14-day campaign produced 109 excursions of 380 µs or more, in both attended and unattended hours and at indistinguishable magnitudes, so local load was a property of those first four rather than the cause of the class. The entry above moved 567 → 570 µs on 15 Aug: it is that one run's latched worst tick, and 570 µs is where it finished. Exactly two of the 109 exceeded 500 µs, which is the threshold the late-frame counter itself uses — so the two counters agree without being wired together. Cause open. Host load was recorded alongside throughout, and from 12 Aug so was every interrupt the clock core took |
| Effect of moving one fsync off the clock thread | 578× | the fix deployed 28 Jul 2026. Median worst tick per run, 865,037 → 1,496 ns; 9 runs before, 7 after, no overlap between the two sets. Runs were a uniform 12 h at the time, which is what makes them comparable; that cadence has since been retired. Observational, not a controlled A/B — the controlled test on that fix measured late frames, not phase. Restricted to full-length runs on both sides — the figure is a latched maximum, so mixing in short ones flattered it to 693× — and the after-set excludes one further full-length run, 2 Aug, still running when this figure was first computed, whose 434 µs peak is one of the nine the “Worst single tick” row lists; leaving it in moves the median to 1,920 ns and the factor to ~450× — which is why the exclusion is stated rather than silent, and saying “no overlap” without saying so would be a quiet cherry-pick |
| As-run records checked for tampering | ≥ 120,483,920 | re-verified 21 Aug 2026 over the whole archive after the 14-day run: 47 day files, 120,483,920 records, 13 chain segments, 0 false positives, six forgery vectors closed. One broken boundary, 20→21 June 2026, and breaks inside day files — among them 20 and 21 June and 17 July 2026, the last where two engines wrote into one file — all three known and pre-dating that run. They are real findings, and they are why the archive reads INVALID rather than clean. Cumulative, so this is a floor too |
| Card crystal error vs TAI | −3.7 ppm | median over 26 Jul–1 Aug 2026 on the first unit; over 9–14 Sep 2026, free-running, the medians were −2.9 ppm on its card and −7.2 ppm on the second unit’s. Dated because it is not a constant: the crystal is temperature-sensitive, and this is measured against CLOCK_TAI, which NTP disciplines — so a clock correction is arithmetically indistinguishable from crystal drift in it. Two further caveats, found on 21 Aug 2026 when the fortnight's logs were re-read: the instrument is a run-cumulative mean rather than an instantaneous rate, so it lags, and after 339 hours it understated the true value by about 16 %; and the true instantaneous drift moved between −2.2 and −4.2 ppm across that run, tracking case temperature at roughly −0.34 ppm/°C |
| Time-to-slip prediction from that drift | 1.007× | observed ÷ predicted, median over 14 runs to 31 Jul 2026. A model fit, so it moves as runs accumulate — what is claimed is that the model tracks, not that this digit is fixed. It moved: over the 339-hour run of August 2026 the same fit reads 0.863 across 213 shed intervals, because the drift figure it divides by is a run-cumulative mean that lags the crystal over a run that long. Corrected for that lag it reads 1.0025. It also held with the cards locked to a house reference on 27 Sep 2026, where the drift it divides by is the reference’s rather than the crystal’s — the register’s holdover row has the figures |
| Longest continuous engine run | 339 h | 6–20 Aug 2026 — a final figure, not a floor: the run ended at 2026-08-20T23:16:43Z when the engine was stopped deliberately at the close of the campaign. Derived three independent ways that agree to within twenty seconds — one part in 61,000: the service manager (systemd) reports no restart across 2026-08-06T19:53:26Z–2026-08-20T23:16:43Z (339.3881 h), the engine’s own frame counter reached 61,088,850 at 50 fps (339.3825 h, read 14 s before the stop), and the 1 Hz diagnostic line spans 339.3861 h. Rounded down to whole hours |
The campaign missed its own criterion on 11 August, and a failed test deserves more time than a passing one would have got. When the run ended we re-read the fortnight from eight independent directions — the clock's own record, the compliance log, the host's counters and the archive — and most of the value is in what that ruled out.
The two late frames turned out not to be two events. They are the largest members of a population the instruments had been recording all along and nobody had counted, because the counter that trips at half a millisecond only ever saw the two that crossed it. They are all in one direction, they are all close to one size, the clock is back inside its ordinary range within the following second every time, and every one of them is independently visible in the as-run log — which is worth knowing about a compliance log, because it was not built to be a timing instrument.
What that buys: it is a repeatable mechanism rather than noise; it is bounded — under 3 % of one frame at its worst over the 339-hour run of August 2026, 4.5 % on the 228-hour run that ended 4 Sep 2026 — and none of it reached the SDI output; and it is not caused by the things it would be convenient to blame. Around twenty candidate causes have now been tested and rejected, several of them ours — and on 21 August the last two that were still standing went the same way. What is left is not a shorter list of the same kind of suspect, and the next step is still a measurement rather than a patch.
The same logs carry both halves of this run: the junctions that were exact and the wakes that were not. We still have not found the cause, and this section will not carry a story in place of one. What changed is that the question got small enough to answer, on instruments that were already running.
The register
In April we decided internally not to ship HEVC. The website went on advertising it for three more months. Nobody caught it, because nothing was watching — and a claim nobody is watching is indistinguishable from a lie to the customer who relies on it.
This is what we keep instead of that habit. Every row is a thing we could plausibly say and don't. It is on the public site rather than in an internal file, because a register that lives where customers can't read it is the same failure with better manners.
Licensing
Out of scope on patent-licensing grounds since April 2026. Not claimed, not tested, not marketed.
Would earn it: an MPEG-LA licence and a hardware test pass. Neither is planned.
Measured — but only as far as our own instruments reach
We measured the engine-side component over 200 trials on 30 Jul 2026. It is a lower bound: it stops at the point the frame enters the handoff queue, and the card pipeline beyond that point adds at least 60 ms this number does not include — the card buffer measured 8 frames, 160 ms, unchanged across 1,824 samples over 30 h to 1 Aug 2026. Scheduled-event accuracy on the wire is likewise unmeasured; engine-side, the first frame of a scheduled item left our converter 22–36 ms after its due instant at the junctions measured on 30 Jul 2026. Quoting it as glass-to-glass would understate our own latency by an unknown margin.
Would earn it: a capture rig on the SDI output measuring the round trip end to end.
The card's crystal ran about 3.7 ppm slow against TAI over 26 Jul–1 Aug 2026, so the engine produces marginally faster than the card consumes. A surplus frame is discarded from the handoff queue roughly every 90 minutes at the drift measured through 1 Aug 2026 — by design, and at a rate we predicted from the measured drift to within 1 % at the median over July 2026's twelve-hour runs (individual runs −8 %/+19 %) — and 14 % over the 339-hour August run, median observed ÷ predicted 0.863 across 213 shed intervals, re-derived 21 Aug 2026. The card itself reported zero late and zero dropped frames after start-up, across the whole 339-hour run. Locked to a house reference, discards follow the reference's drift rather than the crystal's, and on our bench on 27 Sep 2026 they came more often; the holdover row below has the figures. Whether a discarded frame is visible on the wire is engine-side accounting, not a wire measurement.
Would earn it: the same capture rig. Until then we report what our counters see and say which side of the card they sit on.
First locked on 27 Sep 2026, and the reference pulled the same day. Until then the card's genlock state had read unlocked in every one of 78,132 samples on the first unit since it began publishing on 2 Aug 2026. Set to 1080i50, a Blackmagic Mini Converter Sync Generator was detected by the cards in both our units, the first and a second, and in 4.5 minutes of watching neither locked to it. Set to 720p50, both locked to it from 13:59 UTC — five running outputs between them, every one at 1080p50. Whether an output running 1080i50 locks to a 1080i50 reference is untested. Two cards whose crystals free-run more than 4 ppm apart then agreed to within 0.038 ppm (medians over 3.57 h). Locking to this generator had a cost, and we publish it. Syke keeps to TAI; a locked card keeps to its reference; and this generator ran about 11.8 ppm slow against TAI, further out than either card's own crystal. That is measured the same way as the crystal figure in the evidence table and carries the same caveats; Blackmagic publishes no accuracy figure for the generator to set it against (checked 16 Sep 2026). The difference arrives as surplus frames, so the engine discarded one about every 29 minutes while locked (median interval, both units) against 28.2 predicted from that drift — nearly four times as often as the first unit shed them free-running, where the median interval was 110 minutes over 9–14 Sep 2026. That cadence belongs to this generator; another reference would give another. While locked, neither the engine nor the card counted a late frame. Then we pulled it. At about 17:44 UTC we cut the generator's power with every channel on both units running. Both units read unlocked at the next once-a-minute sample; the engine counted no late frames, and its worst clock tick of the run did not change; each card was back at its own crystal's rate within the first full minute; the second unit's intervals between discards went back to a median of 47 minutes across its four outputs that evening, against 46 predicted for its crystal; and the loss paged the operator's phone from both units — the first time that alarm had had anything to fire on. All of that is our own instruments. What the SDI output did across either edge is not measured. Acquiring the reference stepped every output's hardware clock, as the card reports it, by more than a frame against TAI, while the engine's clock was undisturbed and our late, discard and dropped-frame counters did not move — so none of our instruments can tell you whether a viewer would have seen it. Losing the reference showed no such step. And it was one loss, once: enough to say what happened that afternoon, not to promise it every time. The genlock-source setting in our configuration is still read by nothing, as of 27 Sep 2026: the cards locked without any instruction from our software.
Would earn it: part of this is earned — lock observed, then the reference pulled on running channels and the edge watched by our own instruments, 27 Sep 2026. What remains is the wire: the same capture rig on the SDI output while a reference is gained and lost — and more than one loss.
For sixty days, from a change we made on 18 Jul 2026, the engine fed the card audio at the wrong rate after any clip change where the source frame rate changed but the audio channel count and sample rate did not. Fed too fast, the card refused the surplus; fed too slow, it ran short. Recorded on the wire on 16 Sep 2026, the worst case put 5.2 % silence, in short scattered gaps, into programme audio that had none. The defect was in the software that ran the 339-hour run above; what it did to that run’s audio was not measured, and that run’s frame and junction figures are video figures. Fixed on both units on 16 Sep 2026. Over two runs of the fixed build on the second unit totalling 256 hours, 16–27 Sep 2026, the card refused none of the engine’s audio on any of its four channels, by the engine’s own count.
Would earn it: the output’s audio recorded across every junction of a full pass and compared with the source, rather than a counter that stayed at zero. The counters that found this one lit up within three minutes of first being switched on.
At 08:58 UTC on 7 Oct 2026 the first unit’s engine crashed, and both of its channels were off air for at least 13 seconds until the service manager restarted it. Nothing paged us; we found it a day and a half later, reviewing our own instruments. It crashed inside Intel’s video driver, which does not keep the fetch of a decoded frame from the graphics memory apart from the teardown of the decoder that made it — and our engine let the two overlap at the end of a clip. A probe of ours, outside the engine, crashed the same way in ten runs out of ten, and not once in 35,000 tries with the two kept apart. The same day the same fetch failed once without a crash, and one interlaced frame aired built from one source picture instead of two. Since 16:18 UTC on 9 Oct 2026 the first unit’s engine keeps the two apart, and since that day a crash on the first unit pages us, with the kernel’s own line for the fault. The second unit has neither, and whether its engine has ever met the race was not checked.
Would earn it: the fix through a week of the first unit’s airing without a crash or a failed fetch, which is how we will accept it — a week that would show the fix holds, not that the engine never stops. That would take the 90 days the uptime row below asks for.
Since 3 Oct 2026 the first unit measures each airing’s integrated loudness and true peak, to BS.1770 and EBU R128, on the audio it hands on towards the card — after the output mix, not from the source file — and writes both into the as-run log’s record of that airing. Against FFmpeg’s own measurement of the same stretch of the same file, without a card, on 3 Oct 2026, every airing we could compare agreed within the tolerances of EBU Tech 3341. It is measured inside the engine, not at the connector; no operator’s screen shows it; and the second unit has none of it. Until then this row said we had “a full BS.1770 loudness meter in the codebase”. It was not full: its “true peak” was a sample peak, and it gave the 5.1 surround channels the wrong weight. Both were fixed on 3 Oct 2026, and no figure we published came from it.
Would earn it: the figure on an operator’s screen while the item airs, and the same figure read by a meter on the SDI output.
Structural, as the system stands today
The as-run is a tamper-evident hash chain and we stand behind it: 120,483,920 records checked as of 21 August 2026, no false positives, six forgery vectors closed, known breaks from before the 339-hour run of August 2026 — one between two day files (20→21 June 2026), and others inside day files, among them 20 and 21 June and 17 July 2026. It records one entry per source frame, and that entry is not exact about what played or when: a fault in our log, found in July 2026 and open as of 6 Oct 2026. Each entry is written as the engine hands its frame on towards the card, and stamped with the time of the engine clock tick the frame was made for rather than when the frame leaves the SDI output (the as-run page has the detail). In the log as it was written until 3 Oct 2026, and as the second unit still writes it, there is no item boundary — so two consecutive plays of the same file read as one continuous run of double the length — and a frame the engine then discards to absorb the card’s drift keeps any entry it had, so the log can say a frame played that never reached the card. That is not hypothetical: RET-122 airs the head of a looping region twice per loop, as it did on the first unit through the 339-hour run of August 2026, and that log cannot show it. Since 3 Oct 2026 the first unit’s log records each airing of a schedule item, so a second play is a second airing, and withdraws the entry of a discarded frame whose picture does not air; that every discard is accounted for is not established. The chain proves its records are the ones written, in the order written; it does not prove they were true, or that nobody rewrote them all — and we would rather tell you which is which than let the first imply the rest.
Would earn it: an item-boundary record — on the first unit since 3 Oct 2026, through the versioned format change this row asked for. Still to earn: frame entries written from what the card reports it played rather than from the hand-off, and a run of 50p material only, under a test written down beforehand, in which every discard is withdrawn.
Mixed source rates into one output rate are supported and normal, and the picture is proven on hardware — the sound across a change of source rate carried a defect for sixty days, found and fixed on 16 Sep 2026 (the audio-continuity row above). Until 5 Oct 2026 the engine bound one output rate per box; from then the first unit’s engine runs rates that divide one another from one tick, and the second unit’s still binds one. As of 27 Sep 2026 no 1001-family (NTSC) frame had been on the wire from it. This row is about one specific edge, so here is the whole map rather than a caveat that sounds bigger than it is.
The genlocked cross-family cell is an inference, not a measurement: one reference input feeds the whole card, and whether two of its outputs can lock in different families has not been tried. A house reference has now been on this bench — on 27 Sep 2026 every running output on both units locked to one, as the holdover row above sets out — but all of them at one rate, 1080p50, since the engine then bound one output rate per box; two rates from one tick under a reference have not been tried. Until 8 Oct 2026 this row said the same-family cells were not built and that the engine binds one output rate per box; the first unit had run them since 5 Oct. The free-running cell is our limit, not the card's: below the master clock the pipeline is already per-channel, and two Duo 2 cards have each accepted 50 and 59.94 on two outputs at once (14 and 17 Sep 2026), one of them completing frames at both rates together, 50.25 and 60.24 a second by its own count (18 Sep 2026) — the card’s own callbacks, not a receiver on the wire. What is single is our master tick, and one tick has to divide exactly into every rate it serves. 59.94 is not 60 — it is 60000⁄1001, and 1001 shares no factor with 60000, so nothing cancels. The smallest tick that lands on both is 60 kHz (120 kHz once 23.976, which we also admit, is on the grid — ruled 18 Aug 2026): 1,200 ticks per 50 Hz frame, 1,001 per 59.94 — twelve hundred times today's grid resolution, but only 2.2× its wake rate, because you visit a grid point only where a frame boundary falls: 109.9 wakes a second against 50 today, derived 7 Aug 2026 and re-derived 16 Sep 2026. (Were the second rate an exact 60, it would be 300 Hz and this row would not exist; the whole cost is the NTSC 1001.) That is an engineering choice of ours, not a law of physics — and on 16 Sep 2026 we ruled mixing 50 with 59.94 out of our next clock, with the conditions for re-opening it written down. The obstacle is spacing: at 50 and 59.94 two frame boundaries can fall 16.7 µs apart, far closer than our clock’s own timing margin, and copying one frame’s worth of picture in memory took about 243 µs when we measured it that day. A separate cadence source per channel is the route we know, and it is deferred; rates that share a denominator — 25 with 50, 29.97 with 59.94 — are what that clock is scoped to mix. We would rather say that than let a decision of ours read as a hardware limit.
Would earn it: genlocked cross-family — a second card with its own reference, and a clock that can run two rates in one box. Free-running cross-family — a clock per channel, which we have deferred; a finer shared tick is not the route, for the reason above. Ask if you need it; the answer is a scope, not a no.
Not yet earned
Our internal threshold for saying “reliable” is 90 days continuous. The longest run we could evidence from the as-run chain as of 21 August 2026 is 339 hours — just over fourteen days. That figure is dated on purpose: runs get longer, and a number stated as current would be wrong the next time one does. It said 39 hours until 21 August, which was true when written on 1 August and had been beaten twice before anyone re-derived it. What does not move is the argument — 339 hours is not 90 days, and until it is we do not use the word.
Would earn it: 90 days. There is no shortcut and we won't paraphrase our way to one.
A/85 assesses the anchor element — dialogue — not integrated programme loudness, which is what our meter measures. For dialogue-sparse content the two differ materially. The 2026 revision also requires true peak to be measured before encoding, which a delivered file no longer permits. Our tool therefore returns INCONCLUSIVE for A/85, and it is impossible for it to return a pass: the refusal is enforced in code, before any number is compared.
Would earn it: dialogue-gated measurement. A different algorithm, not a threshold change.
If a claim you need isn't here and isn't on the evidence table, ask — the answer will be a measurement, a date, or “we don't know yet”.
Tools
The opx-* tools exist because we needed them to prove Rytmi correct. They're free and binary-only, for Linux; the two released ones list their known issues, dated, on the tools page. We withdrew the Windows builds on 29 Sep 2026: they bundled a GPL-licensed FFmpeg without its licence text or an offer of its source. Published checksums; --version on every one, because that's the first thing a support conversation asks. The last card is neither an instrument nor finished — it is here because some of its timing code began as a copy of Rytmi's, and we would rather show it early than announce it late.
What is this file? Container, codec, exact rational frame rate, audio config, an MXF's XML sidecar (matched by file name). And --house pre-flights frame rate, audio sample rate and stream start offset against a target format — not resolution or scan type — exiting non-zero on a mismatch.
Is its timeline sane? PTS drift and A/V sync drift from exact rational arithmetic — 29.97 is 30000/1001, and rounding it is how drift gets designed in.
Is it deliverable? Designed to check EBU R128 and Free TV OP-59, with the thresholds read from the issuing bodies' own documents rather than a summary, and to say INCONCLUSIVE where it can't honestly say pass. The BS.1770 loudness meter exists in Rytmi's codebase; this tool does not.
Is the compliance chain intact? Verifies a hash-chained as-run archive across day-file boundaries and restarts. A single file can't check what it continues.
A media player for people who have to trust the timecode. Built for broadcast use rather than adapted to it, with EBU R128 loudness metering in the engine rather than bolted on. Its frame-rate arithmetic and handoff ring are Rytmi's — copied verbatim from a dated engine snapshot, each with a provenance record naming its origin; its TAI sourcing and precision sleep are new code built to the same design, and the record says which is which in the engine.
Work in progress and not available — no download, no date. It is listed because it exists and because a player that says 29.97 when the file says 30000/1001 is the problem it was written to stop being.