すべてが立っているタイミングの格子
M0 の報告書。作業の後に書いた。
走行スタンプ: 2026-09-01、このリポジトリの最初のコミット、rustc stable
(rustup のツールチェーン)、cargo test --workspace は 16 テスト緑、clippy も
綺麗。以下のどの数字も、その走行か、引いた出典から貼ったもので、仕様書から
打ち直したものではない。M5 がそれらを check-self-counts のスキャンの下に置く。
閉じたもの
ntsc-grid が存在する: 厳密な有理数のサンプルレート (f_sc は
39,375,000/11 Hz として保持し、格子は 472,500,000/11 Hz)、Phase、
phase_at と line_len を備えた二つの Geometry プロファイル、
CompositeFrame の型、そして仕様 3.4 節の CompositeSource/Stage トレイト。
位相は 3.3 節どおりジオメトリの純関数で、自由なパラメータは
phase_at_origin だけだ。
剰余: 3.2 節の主張はすべて確認した
tests/residues.rs、12 テスト。写しではなく、テストで確認したもの:
- NES の 1 行: 2728 サンプル、剰余 4 (120 度)。各行は
phase_at自身を通して 4 だけ後ろにずれて始まる。 - NES のフルフレーム: 714,736 サンプル、剰余 4。Even のフレーム三つは原点の 位相に戻り、一つは戻らない (描画を切ったときの三フレーム周期)。
- NES の短いフレーム: 714,728 サンプル、剰余 8。Even のあと OddShort なら 12 個の開始位相すべてから原点に戻り、Even だけならどこからも戻らない。
- 放送プロファイルの 1 行: 2730、剰余 6 (180 度)。フレームは 1,433,250、 剰余 6。二フレーム (四フィールド) で原点に戻り、一フレームでは戻らない。
- 閉形式の
phase_atは、両プロファイルの全行にわたる総当たりの累積和と 一致し、積算した合計はsamples_per_frameと一致する。 - 放送プロファイルでの
OddShortはline_lenとphase_atの両方で名指しで 拒まれる (should-panic テスト)。
レート、厳密に
| レート | 厳密値 | 近似 |
|---|---|---|
| NES フルフレーム | 29,531,250 / 491,381 Hz | 60.09848 Hz |
| NES 短いフレーム | 8,437,500 / 140,393 Hz | 60.09915 Hz |
| NES 二フレーム平均 (描画あり) | 39,375,000 / 655,171 Hz | 60.09881 Hz |
| 放送のフレーム | 30,000 / 1,001 Hz | 29.97003 Hz |
| 放送のフィールド | 60,000 / 1,001 Hz | 59.94006 Hz |
仕様との食い違い、v0.3 向け: 3.2 節は「フィールドレート:
(6 x f_sc) / (4 x 341 x 262) = フルフレームで 60.0988 Hz」と述べている。式の
形は正しいが、引かれている値は正しくない: 計算すると 60.09848 Hz になる。
60.0988 は短いフレームが交互に入る二フレーム平均、つまり描画を有効にした
ときにプレイヤーが実際に見るレートで、文献がふつう引くのもこの数字だ。
field_rates_exact が NES の三つのレートを別々に釘付けするので、二つを再び
取り違えることはできない。放送のレートがちょうど 30,000/1,001 と
60,000/1,001 として出てくること自体が、315/88 とジオメトリが正しく配線されて
いることの検査になっている。
データファイルと転記のゲート
data/nes-levels.toml: 終端した状態での実測表 (lidnariq、75 オーム、
+/-2 IRE)、そのページの平均エンファシス減衰係数 0.816328、そして波数について
の事実。出典は nesdev wiki の "NTSC video" リビジョン 23864
(2026-06-01) で、生の wikitext から転記した。この wiki はサイト全体のライセンス
を宣言していない (API の rightsinfo は空、フッタに告知も無い)。ファイルの
ヘッダと NOTICE.md に記録してある。
閉じるゲート、2026-09-01 の走行: 二人目のエージェントが独立に同じリビジョンを
取得し、最初の写しを見ないまま同じスキーマへ転記した。
tools/diff-transcriptions.py が 43 個の数値を比較し、不一致は 0。片方の
写しの 1 桁をわざと狂わせるとゲートは exit 1 になるので、ゲートは違いを言える。
二人の転記者は独立に同じ判断を一つした: $3x の high のスロットは $2x の行
を写す、というもので、ページ自身の levels[16] の表と「$20 と $30 はまったく
同じ」という記述に従っている。転記 B は
data/nes-levels-transcription-b.toml として残し、証拠と札を付け、どのクレート
からも読まない。
data/yuv-matrix.toml: SMPTE 170M の基本行列、縮小係数
0.492111 / 0.877283、記載されている逆行列の係数、33 度の I/Q 回転、そして
復調の定数。二次資料だ: ITU-R BT.1700 のダウンロードが 2026-09-01 に
404 だったので、これは同じ wiki のリビジョンからの転記であり、あちらは
BT.1700 の 4、16、17 ページを引いている。M2 で一次資料に照らして確認するよう、
ヘッダに印を付けてある。
tests/data.rs、4 テスト。転記をそれ自身に照らして保つ:
- (ボルト、印字された IRE) の組はすべて、$1D を 0 IRE の基準として 1 IRE = 7.14 mV を通して相互検査される。最悪の行は CBH で、計算 29.69 に 対して印字 30。ページ自身の +/-2 IRE が正当化する許容差の内側だ。
- ページの 0.816328 という平均は、実測された七つの減衰の組から 1e-4 以内で 再計算できる (重複している $3x の行は除く。入れると二重に数えてしまう)。
- 逆行列の係数はすべて、基本行列と縮小係数から 1e-4 以内で再導出できる (実際の一致は 5e-6 以内)。
MUTATE=1、赤の証明
MUTATE=1 cargo test --workspace --no-fail-fast: 1 行あたり 2 サンプル長い
ジオメトリと、狂わせたレベル電圧。結果は 11 テストが赤 (ジオメトリの剰余と
レートのテスト 8 個すべて、IRE の相互検査、減衰の再計算、行列の再導出)。
5 個は設計どおり緑で、テストの docstring に名指ししてある: 格子レートの
同一性 (ジオメトリが入っていない)、閉形式と累積和の一致 (定数ではなく位相の式
を検査している)、二つの OddShort の拒否、そして波数の範囲の検査。
持ち越し
- 仕様 9 節の未解決の問いには、2026-09-01 に監督が答えた: blargg の
nes_ntscは 0.2.2 (最後のリリース) に固定、composite のプリセット、M1 で ハッシュとともに記録する。M2 の RGB 往復には D65 下の CIEDE2000。 ラング D (時間方向のコム) は M2 で欲しい。 - yuv-matrix の一次資料での確認は M2 の義務だ。
- 上に書いたフルフレームのフィールドレートの訂正は v0.3 に入る。
ビルド時に ntsc-crt/docs/m0-report.md から取り込んでいる。写しはリポジトリの一つだけだ。報告には私たちの間の言葉がいくつか残っている: 報告書で使われる言葉。