MLX90396 Stray Field Immune (SFI) Joystick
Kinematics, Raw Signals, Polar Radar & SFI Battle MatrixInjects 5.0 mT external uniform stray field via SCPI GPIO on physical rig (Melexis IO A0).
How the Desmos calibration works here
This page runs the shared angle_calib.js module (the same engine the Thumbstick page
uses). There are two engines, and the readouts tell them apart:
-
Legacy Standard 3D Hall — 1px mode, a single pixel’s absolute field
(Bx, By, Bz, units mT). Feeds
computeSfi1Px→ the “std” radar α/β readouts. It is the uncompensated reference — fully exposed to stray fields and to the field’s falloff. -
MLX90396 SFI — 2px mode, the four differential channels between neighboring pixels
(dBx/dx = X02, dBz/dx = Z02,
dBy/dy = Y13, dBz/dy = Z13, units
mT/mm, i.e. counts ÷ (20 × pixel pitch) — the pitch is set in
Calibration Tuning). Feeds
computeSfi2Px→ the “MLX” radar α/β readouts. Uniform external fields cancel in the subtraction, so this pair rejects stray field; the graph tabs plot these gradients directly.
Both run the same model. For 1px, x1 = Bx, y1 = By, z1 = Bz:
a1 = atan2( √(z1² + (k1·(y1 − o11·z1))²), x1 − o12·z1 )
b1 = atan2( √(z1² + (k2·(x1 − o21·z1))²), y1 − o22·z1 )
The default 2px selection runs the reviewer’s normal formula (their step 1), each in its own differential axis:
α = ATAN2(dBx/dx, dBz/dx) = ATAN2(X02, Z02)
β = ATAN2(dBy/dy, dBz/dy) = ATAN2(Y13, Z13)
The radar plots that α on the X axis and β on the Y axis in the same circular polar style as the legacy radar (centre = (90°, 90°)), and every readout (summary, radar labels, Tilt X/Y) uses the same two numbers.
Select Enhanced in Calibration Tuning to apply the reviewed cross-axis formula:
α = atan2( √(X02² + (k1·(Z13 − o11·X02))²), Z02 − o12·X02 )
β = atan2( √(Y13² + (k2·(Z02 − o21·Y13))²), Z13 − o22·Y13 )
The selected formula feeds the α/β readouts, radars and live gimbal.
Inputs and parameters are normalised against NaN/Infinity, so a bad sample can never break the loop.
Frame Health
Everything on this tab is the unmodified reply from the three transfers the SFI loop issues each cycle: 0xFC000 and 0x03F00 for the twelve absolute pixels, 0x000AC for the four 2px gradient channels. Nothing is averaged, scaled or clamped.
| Source | -- |
|---|---|
| Status byte | -- |
| Function | -- |
| Data ready | -- |
| Flags | -- |
| CRC | -- |
| Loop period | -- |
Absolute Pixels — raw LSB
Signed 12-bit counts, full scale ±2048. The averages are the four-pixel means the dome uses for the absolute heading.
| Channel | Count |
|---|---|
| X0 | -- |
| Y0 | -- |
| Z0 | -- |
| X1 | -- |
| Y1 | -- |
| Z1 | -- |
| X2 | -- |
| Y2 | -- |
| Z2 | -- |
| X3 | -- |
| Y3 | -- |
| Z3 | -- |
| Average X | -- |
| Average Y | -- |
| Average Z | -- |
| Peak |count| | -- |
Differential Channels — raw LSB
The chip's own inter-pixel gradients, read in a single transfer (mask 0x000AC: X02, Z02, Y13, Z13). They feed the SFI radar and the dome. The counts are converted to mT/mm by 20 × pixel pitch (the Pixel pitch field in Calibration Tuning); a magnet sitting centred reads a small gradient — that is the null, not a missing magnet.
| Channel | Count |
|---|---|
| X02 | -- |
| Z02 | -- |
| Y13 | -- |
| Z13 | -- |
Dome Output
What the Idle / Auto-Pattern tab is drawing, after the 2px gradient → tilt mapping is applied.
| Position source | Differential 2px (dBz/dx, dBz/dy) |
|---|---|
| Tilt X | -- |
| Tilt Y | -- |
| Press magnitude | -- |
| Press state | -- |