Skip to content

feat(rf): FCC RF-exposure (MPE/SAR) estimates and an exposure dialog - #14

Merged
leavesofgrass merged 1 commit into
claude/sleepy-edison-2492mmfrom
claude/sleepy-edison-2492mm-exposure
Sep 25, 2026
Merged

leavesofgrass merged 1 commit into
claude/sleepy-edison-2492mmfrom
claude/sleepy-edison-2492mm-exposure

Conversation

@leavesofgrass

Copy link
Copy Markdown
Owner

PR 2 of 3. This PR is stacked on #13 and targets that branch, so the diff shows only the exposure work. Merge #13 first; this PR will then retarget to main.

Why

US amateurs must evaluate RF exposure under 47 CFR § 97.13(c) and § 1.1307(b). abax had no tools for this. The requirement was to use only rule text verified from primary sources, so every number built in here comes from rule text I could read:

Built in Source
MPE limits: E, H and S for both tiers; 6 min / 30 min averaging 47 CFR § 1.1310(e)(1) Table 1
SAR limits: whole-body, 1 g, 10 g extremity § 1.1310(b), (c)
Exemption-threshold ERP, valid only at R ≥ λ/2π § 1.1307(b)(3)(i)(C) Table 1
Household members may be evaluated against controlled limits § 97.13(c)(1)
Sites with multiple transmitters: shared responsibility above 5 % § 1.1307(b)(5)

What

11 formula functions:

  • MPELIMIT, MPEEFIELD, MPEHFIELD
  • PWRDENSITY, MPEDIST, MPEPERCENT
  • AVGPOWER
  • EXEMPTERP, EXEMPTMINDIST
  • SARLIMIT, SAR

Frequencies are in Hz, as in all abax RF functions. Power density is in mW/cm², the FCC table unit.

Ground reflection is a power-density factor F in S = F·EIRP/(4πR²):

  • 1 = free space.
  • 4 = full in-phase reflection: the reflected wave doubles the field.
  • Any other value you enter yourself.

Two independent checks on the table transcription, both in the tests:

  1. Wherever § 1.1310 gives both columns, E²/377 Ω equals the power-density limit.
  2. Every row of the § 1.1307 exemption table equals the general-population limit × πR² / 1.64, which is the ×4 model applied to ERP, to the rule's rounding.

Tools → Radio → RF exposure estimate (MPE)

  • Inputs: frequency, PEP, feed-line loss, gain (dBi), duty cycle, transmit time in the averaging period, reflection, and distance to the nearest person.
  • Outputs:
    • average power, EIRP and ERP
    • both tiers' limits and compliance distances
    • percent of each limit at your distance, stated in words ("below the limit" / "ABOVE the limit")
    • the § 1.1307 exemption check
  • The log-log density-versus-distance graph has these accessibility features:
    • it is keyboard-focusable
    • its accessible description is written from the numbers and updates on every change
    • limits are labeled with text, not color alone
    • it comes with a data table and Data → new sheet
  • A plain-language caveat is on the dialog: this is an estimate and not a substitute for a station evaluation.

Deliberately not included

These could not be verified from primary text; the proxy blocked fcc.gov, ecfr.gov, arrl.org and icnirp.org:

  • The P_th SAR-based threshold for 0.5–40 cm (§ 1.1307(b)(3)(i)(B)). It is an image in the eCFR.
  • The OET-65 Supplement B per-mode duty factors. Sources conflict, for example 40 % versus 50 % for processed SSB.
  • The OET-65 ground-reflection factor.
  • ICNIRP 2020 reference levels.

The duty cycle is therefore an input (default 100 %, the worst case), and no reflection factor between 1 and 4 is preset. The docs say this and list these items as future work.

Also in this PR

  • GraphPanel / rows_to_new_sheet move from the circuit dialog into a shared dialogs/_graphpanel.py.
  • Vertical reference-line labels move to the bottom of the plot. They had collided with the threshold labels, which I found by rendering the chart and looking at it.

Verification

  • ruff check . passes.
  • pytest -n auto: 4744 passed, 196 skipped. The skip count is unchanged.
  • mkdocs build --strict passes.
  • python make_pyz.py builds, and abax.pyz --version runs.
  • Coverage ratchet (CI command): core 84.54% (floor 83), engine 63.26% (floor 62).
  • All 77 =formula → value examples in the formula reference's RF section were evaluated against the engine, and all match. A test also pins the worked exposure example in rf-toolkit.md; while writing it I found and corrected two wrong numbers in my first draft of that example.

🤖 Generated with Claude Code

https://claude.ai/code/session_0157FDmhHjtSvT3pWABUTvmX


Generated by Claude Code

Only rule text verified from primary sources is built in:
47 CFR 1.1310 Table 1 (MPE, both tiers, 6/30-minute averaging), the
1.1310(b)-(c) SAR limits, and the 1.1307(b)(3)(i)(C) exemption table
with its lambda/2pi applicability limit.

- 11 formula functions: MPELIMIT, MPEEFIELD, MPEHFIELD, PWRDENSITY,
  MPEDIST, MPEPERCENT, AVGPOWER, EXEMPTERP, EXEMPTMINDIST, SARLIMIT, SAR
  (frequencies in Hz, power density in the FCC's mW/cm2)
- Ground reflection is a power-density factor: 1 (free space), 4 (full
  in-phase reflection) or user-entered. The tests show the 1.1307
  exemption table is exactly the x4 model applied to ERP, and that the
  1.1310 E-field and power-density columns agree via E^2/377.
- Duty cycle and transmit time are inputs; OET-65's per-mode factors
  and reflection factor, the 1.1307(b)(3)(i)(B) P_th formula, and
  ICNIRP levels could not be verified and are documented as omitted.
- Tools > Radio > RF exposure estimate (MPE): whole-station evaluation,
  verdicts stated in words, an accessible log-log density-vs-distance
  graph with limits and compliance distances marked, data table, and
  "Data -> new sheet". The estimate caveat is shown on the dialog.
- GraphPanel/rows_to_new_sheet move into a shared dialogs module;
  vertical reference-line labels move to the plot bottom to avoid
  colliding with threshold labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157FDmhHjtSvT3pWABUTvmX
@leavesofgrass
leavesofgrass merged commit 5af6f18 into claude/sleepy-edison-2492mm Sep 25, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants