Adom Fields
Public Made by Adomby adom
Adom Fields: where the current and the heat go on your board.
RF review on the AM/FM superhet: 8 bugs, and the noise, EMC, ground and decoupling screens miss a receiver's couplings
Adom Fields 0.7.15 on a discrete AM/FM superhet: the board in adom/amfm-receiver-molecule, kicad/amfm_receiver.kicad_pcb. It is a 4-layer JLC04161H-7628 stack: GND on F.Cu, In1 and B.Cu, the +5VA and +3V3 planes on In2. The 0.7.15 release notes say this board was the test case. I reviewed the RF findings as the board's designer. The spec and my check scripts are attached.
Spec: the published docs/fields/spec.json plus:
- all 17 rail voltages;
clocks: Y101 40 MHz on HSE_OUT, Y501 10.24 MHz on XO_X, the FM LO collector FMLO_C at 108.7 MHz, the AM LO tank AMLO_T at 11.8 MHz;crosstalkMv: 5;signals: the USB pair and four In2 plane-pair capacitance checks;- a GND load (see bug 3).
adom-fields analyze --board kicad/amfm_receiver.kicad_pcb --spec spec.json --out fields
Summary. The screens are built around a switching converter and logic edges. On a receiver they mostly report on the wrong things:
- The two crosstalk findings are false alarms, about 89 dB and 50 dB too high.
- The cavity check looked at the wrong layer pair.
- EMC, decoupling and PDN have nothing to say about the RF stages.
The couplings that set this board's performance are fF to pF, at µV to mV levels, through the circuit as well as the copper. None of them are screened:
- LO to antenna;
- crystal harmonics landing in band;
- an MCU GPIO line routed past the IF and tuning lines;
- B.Cu RF traces whose nearest plane is a supply plane.
Bugs (reproducible on this board)
The cavity check examines only the first two layers over 15 % copper (
noise.rs,real[0]/real[1]). Here those are F.Cu and In1. So the 11 "cavities" it reports are F.Cu slivers of rails over In1 (25–65 mm², modes at 2–18 GHz, all withgapMm0.2). The real planes on In2 are never examined. I ran the same Neumann method on them (plane_modes.py, attached):Plane Overlap with In1 / B.Cu (the tool's Signals check) Modes, over In1 (1.065 mm core) Modes, over B.Cu (0.2 mm 7628) +5VA 7,330 / 6,820 mm² 689, 878 MHz 657, 854 MHz +3V3 1,840 / 1,490 mm² 688 MHz 559, 969 MHz The FM LO's 5th harmonic sweeps 494–594 MHz across the band, so it crosses the 559 MHz mode near a 101 MHz station. Fix: examine every plane pair (any net poured over GND on an adjacent layer), with h and εr per pair.
The Noise view hardcodes the crosstalk budget.
ui/noise.js:13setsconst budget = 50, ignoringcrosstalkMv. With 5 mV in the spec:- the solver lists AM_AUDIO (10 mV) as an issue, but the view colours it green, "under budget";
- the narration says "the fifty millivolt budget";
- the narration talks about "the switch node" and "this converter's harmonics" on a board with neither.
Please export the budget in
review.noise, and use converter-free wording when no converter is modelled.The Ground view is silently empty unless GND is in
loads.ground.jsreturns the "No ground net" stub when GND is not a flow net. The page keeps the temperature legend, and ground offset and ground-loop risk are skipped. A user who only lists supply loads, as the published spec does, gets a blank view. Please derive the return current from the supply loads, or state it in the view and the Review hint. I added"GND": {"amps": 0.085}to get the results below.The GND current solve did not converge, and the solve published anyway. The log shows
GND: 0.085 A, 4 layer(s), 6000 iterations, residual 1.7e-1; every other net reached about 1e-7. The Review hint then reports a 0.03 mV ground offset, and the Ground panel shows 0.06 mV. The thermal solve refuses to publish on a failed residual; the current solve should too, or at least flag the net.Overlays and tags leak between views. When I switched views with
ui view --quantity …(Noise → EMC → Ground → Power → Signals → EMC), each view's tags stayed on screen, and the Signals plate stayed lit in EMC. The EMC screenshot shows the Ground, Power and Signals tags stacked over its own. The Ground panel also scrolls sideways in a 1278 px Hydrogen tab.Stitching counts vias only. Three of the four edge gaps it reports are the corners where MP2–MP4 sit: large plated GND machine pins 3.25 mm from the edge. Plated GND pads and holes should count as fence posts.
The crosstalk victim's capacitance to ground ignores the capacitors on the net. NRST "picks up 94 mV" from HSE_OUT through 0.10 pF. But NRST carries C115, 100 nF: 0.1 pF / 100 nF × 3.3 V ≈ 3 µV. AM_AUDIO "10 mV" has C619 (1 nF), so about 30 µV, and it is a 5 kHz audio net. Neither is a finding.
Smaller crosstalk and noise issues:
Cguses the whole stack thickness (1.465 mm) for an F.Cu victim that is 0.2 mm over In1 GND, about 7× low.- The overlap term only compares
real[0]withreal[1], which is always GND on In1 here, so it is always 0. - εr is fixed at 4.3 in
noise.rsand in the stitching code, ignoring the stackup. - Every clock is a 3.3 V step (
rail("VCC")fallback), whatever its real swing. rail_from_name("+3.3VA")returns 3.0.
The RF findings, one by one
Cavity resonance. The physics is right: the Neumann eigenproblem on the real overlap, as the rectangle test shows. But the cavities sit at 560 MHz and up, while the receiver works at 0.5–2 MHz, 88–108 MHz and its IFs. So plane resonance here is an EMC footnote, not a reception problem. The tool reached "no finding" for the wrong reason (bug 1). What is missing:
- A clock is one frequency, while an LO tunes ±10 %. Allow
{minHz, maxHz, harmonics, amplitudeV}, and test overlap with the swept band rather than ±5 % of a point. - Report each plane's lowest mode against the highest strong tone as a margin.
Crosstalk. This is where the gap is widest. The model is a quasi-static step divider, ΔV·Cm/(Cm+Cg), within 1 mm on one layer, against a mV budget. A receiver needs the coupled voltage at the aggressor's frequency into the victim's impedance: V = Va·ωCm·|Zv(f)|. Zv comes from the parts on the net. It can be 25 Ω at the antenna, 3.7 kΩ at a tank at resonance, or 100 nF on a reset line, so the divider is off by orders of magnitude in both directions.
The "oscillator's own circuit" exclusion also hides the main path. FM_MIX_B is one hop from FMLO_C (C321, 2.2 pF), and FMRF_C, the RF amplifier's tank, is two hops away (C313, 4.7 pF). So LO → mixer base → RF tank → RF amplifier reverse isolation → antenna is never examined. My nodal estimate (lo_leak.py; hybrid-π, 1 V-peak LO, design.py values) puts the LO at the SMA at −56 to −52 dBm across the band. FCC 15.111's antenna-terminal limit is 2 nW (−57 dBm). On top of that, a direct LO-to-antenna coupling of about 26 fF would reach the limit by itself.
What a receiver crosstalk check needs:
- Frequency-plan arithmetic before any geometry: does each tone or harmonic fall in an RF band, an image band (RF + 2·IF) or an IF passband? On this board that alone finds:
- the 10.24 MHz crystal's 9th and 10th harmonics at 92.16 and 102.4 MHz, inside the FM band (the tool stops at the 5th for crystals);
- the 40 MHz crystal's 3rd harmonic at 120.0 MHz, the image when tuned to 98.6 MHz.
- Victim classes with receiver budgets:
- the antenna port, in dBm against 2 nW and against sensitivity;
- RF and IF inputs, in µV referred to the antenna through the gain ahead of them (FM sensitivity is about 1–2 µV);
- varactor tuning lines, in Hz of deviation (Kvco × pickup);
- reset and logic lines, in volts.
- The circuit path, not just the copper. Trace from each oscillator through the netlist to the antenna and to the IF inputs, using ngspice AC, which the PDN cross-check already uses. Don't exclude the oscillator's neighbours; report the path.
- Aggressors beyond named clocks:
- The USB pair: broadband edge energy across the FM band, and the 48 MHz USB clock's 2nd harmonic at 96 MHz.
- GPIO and PWM lines. CAL_MARK runs 97 mm from the MCU to the antenna injector (47 k + 100 p into ANT), passing 0.37 mm from VT_RF, 0.73 mm from AMF_IN and 0.82 mm from IF2_OUT on F.Cu.
- The 455 kHz limiter outputs, whose 2nd and 3rd harmonics land in the AM band.
- A reach beyond 1 mm. At µV levels, fF couplings over several mm count. With a solid plane 0.2 mm below, Cm falls roughly as (h/s)², so model that rather than cutting off at 1 mm.
Emissions. The screen exists only for a converter's hot loop, so this board reports "not screened". For a receiver, the emission that matters is the LO:
- conducted out of the antenna port (FCC 15.111: 2 nW);
- radiated by the antenna and by the LO tank and its traces (FCC 15.109; CISPR 32's broadcast-receiver local-oscillator requirements);
- MCU and USB noise carried out on the cable as common mode.
A useful screen would reuse the LO-to-antenna path above, plus each oscillator's tank loop area and trace length. On this board the FM LO collector net is 24 mm of F.Cu, and its tank capacitor C319 sits 7 mm from the transistor.
Return path (no-reference). It checks only nets that carry a spec load, on the top layer. So it flagged +3V3 (34 %, 5.5 mm²) and +5VA (26 %, 10 mm²) supply stubs, which carry no RF current. It never looks at the nets whose return current matters. On this board many RF and clock nets run on B.Cu, whose adjacent plane is In2, the supply planes, so their returns ride +5VA and +3V3 through bypass capacitors:
| Net | On B.Cu | Plane above it |
|---|---|---|
| VT_RF | 47 mm | +5VA |
| USB_DM | 42 mm (D+ stays on F.Cu) | +3V3 |
| VT_FM | 39 mm | +5VA |
| IF2_OUT | 25 mm | +5VA |
| IF_MIX | 15.5 mm | +5VA |
| AMLO_T | 15 mm (AM LO tank) | +5VA |
| CAL_INJ | 13.5 mm | +5VA |
Please check every non-rail net (or at least clocks, signals, and nets marked RF) for the plane it actually references, and flag a supply plane, a void, or a layer change with no ground via nearby.
Ground. The DC offset (0.03–0.06 mV at 85 mA) says nothing at RF. ground-loop-risk lists nine connectors and picks J301–MC17 (1,179 mm²) as the largest loop. For a USB-powered radio the loop that matters is the USB cable's ground (J101) to the antenna coax shield (J301), 95 mm apart at opposite ends of the board. The PC's common-mode noise crosses the plane and drives the coax shield, which is part of the antenna. A receiver check would:
- name the cable pair whose loop includes the antenna feed;
- estimate the plane's transfer impedance between the two entries at the RF bands;
- suggest the usual fixes: a common-mode choke on the USB lines and a ferrite on VBUS.
Stitching. λ/20 at 1 GHz gives a 9.2 mm pitch: a digital rule of thumb. This board's strongest tone (the LO 5th) is under 600 MHz, so the edge fence is not the issue, and three of the four gaps are bug 6. What matters in a superhet:
- via fences between blocks (LO and RF amplifier, mixer output and IF filter output, IF output and input);
- the via at each RF shunt part's ground pad;
- set
emcFmaxHzfrom the spec's tones by default.
On this board the RF shunt parts each have a ground via within 0.9 mm, and each IF filter has one at 1.1–1.2 mm. Both look fine, but the tool should be the one saying so.
Decoupling and PDN. Only chips (U*) are checked. The 3 mm rule with 100 nF is fine for the MCU (C111 at 9.3 mm is a fair finding). For U201, it's a fair finding for a different reason: U201 buffers the varactor tuning voltages, so its supply noise frequency-modulates the LO. But none of the 17 discrete RF and IF transistors is checked, and the PDN check skips every stage feed ("no load pin: no chip").
The receiver needs, per stage feed (47 Ω or 22 Ω + 100 nF + 1 nF/10 µF):
- the HF bypass at the tank inductor's cold end, with its own via;
- the bypass capacitor's self-resonance against the stage frequency;
- the isolation between stages through the shared rail at the aggressor's frequency, for example V_LO1 → +5V_FM → V_RF1 at the LO, and V_IF1…V_IF4 at the IF, against a dB target.
The 5 % load-step target is a digital check.
Antenna input. esd-unprotected on ANT is fair: the FM branch (C303 → HPF/BPF → C307) leads to Q301's base with nothing to clamp a fast edge. A useful fix needs a capacitance budget (≤ 0.5 pF at 100 MHz into 50 Ω) and a linearity note (junction intermodulation from strong stations), not just "low capacitance".
Signals. The USB pair check honestly refused: vias, a layer change, branches, and 7.4 mm of total-length skew. It would help to print each net's length per layer, so the reason is visible: D+ runs on F.Cu and D− runs 42 mm on B.Cu over +3V3, which is not a pair at all. Outer-layer microstrip only means the In2 planes can only be screened as plate capacitors: +5VA/In1 280 pF, +5VA/B.Cu 1,329 pF, +3V3/In1 70 pF, +3V3/B.Cu 291 pF. Those numbers look sane.
Attached (amfm_rf_review.zip and the screenshots)
spec.json, the spec used.plane_modes.py: In2 plane modes by the tool's own method.lo_leak.py: the LO-to-antenna nodal estimate.rf_geometry.py: the same-layer spacings and B.Cu routing over the In2 planes.- Screenshots of the Noise, EMC, Ground, Power and Signals views.
These are screening numbers by hand, not measurements. The LO level at the SMA is the first thing I'll measure when the boards arrive.




