mri · all four parts at once
Four pages, four isolated effects. They are not really separate: they compose into one image, and one number. Every control below belongs to one of the parts — colour-coded to it — and the panel on the right shows which knob paid for what.
That is the whole instrument in one line. The first term is part one's — the sensor is a coil, so it pays twice for field. The middle two are part two's: resolution is bought with signal, and signal is bought with time. The last is part one's again, through reciprocity. What each tissue does with that signal is part three, and what the timing costs your ears is part four.
The noise is added at the receiver, not painted on afterwards. Every scan above puts complex Gaussian noise into k-space before reconstructing, which is where thermal noise actually enters — and that has a visible consequence people notice without being told: the background of an MR image is not black. Taking a magnitude turns zero-mean complex noise into a Rician distribution with mean σ√(π/2) ≈ 1.253σ, so empty space glows faintly. The engine measures that on a reconstruction of pure noise and checks it against the closed form.
Two more things worth doing deliberately. Swing the coil to 90° and watch the image die — part one's deaf coil, now costing you a whole scan. And push the matrix to 256²: the picture gets sharper and grainier at once, because you just cut the voxel volume fourfold and only bought back a factor of two in sampling time. That trade is not an engineering limitation anybody is working on. It is the scaling law.
The law and the simulation are separate code paths that must agree:
console.rs computes the law arithmetically from the protocol, while scan()
adds receiver noise and lets the reconstruction propagate it. The tests measure SNR in a reconstructed image and assert it tracks
the law through voxel volume, averaging, and field strength — and that the Rician background mean
matches σ√(π/2). Writing those tests caught a real bug in this page's
model: voxel volume was being applied to the signal *and* implied by the reconstruction's own
scaling, counting it twice.
cd sci/engine-rs && cargo test --release — 52 known-answer tests.
engine: rust → wasm · no dependencies · 52 tests · one · two · three · four