Skip to content

Web workbench

The web workbench is the live, no-install face of antennaknobs: a panel of knobs per design, with the radiation pattern, SWR, and impedance re-solving as you drag. The ? in the header opens this page.

Terminal window
pip install "antennaknobs[web]"
uvicorn antennaknobs.web.server:app # http://127.0.0.1:8000

The [web] extra pulls in uvicorn[standard], which provides the WebSocket support the live-solve channel (/ws) needs — plain uvicorn fails that handshake.

The server sizes its BLAS/OpenMP thread pools itself (physical-core count). On multi-core boxes you can optionally prefix the command with OMP_WAIT_POLICY=PASSIVE GOMP_SPINCOUNT=0 to park idle solver threads between solves — worth ~15% on live knob-drag latency. These two are read once at process start, so they only take effect as launch environment.

A local instance has no login and none of the hosted instance’s solve-size limits, and it runs any design files you’ve allowed with your user privileges — keep it on the default 127.0.0.1 bind rather than exposing it to a network you don’t fully control.

A hosted simulator is running at app.antennaknobs.dev (a single FastAPI process serving the API, the /ws live-solve channel, and the built React SPA). It’s deployed as a container on Fly.io; the repo’s docs/deploy.md is the runbook.

The hosted app can run on more than one machine, in different regions, so that a page talks to one near you. A page keeps to the machine it first reached for everything it does: its live solves, its sweeps and an opened deck all go to that one machine, so “one solve at a time” still holds for your tab, and a deck you opened is parsed once. If that machine goes away, the page moves to another and carries on; the new one starts with a cold cache.

Each machine has its own limits: the opened-deck slot, the new-decks-per-minute count and the solve cache are per machine, not shared across them. A region with no visitors sleeps, and the first request wakes it, usually in well under a second; just after a release it starts cold instead, which takes a few seconds.

On a local workbench none of this applies: there is one process, and nothing is pinned or sent.

To see which designs and engines get used, the hosted app counts, per machine:

  • solves: each live solve the server actually ran (not a cached answer), by design, engine and ground model;
  • sessions: each live-solve connection, by whether its first solve was of a catalog design, an opened deck or a design from a designs folder;
  • opened decks: each Open…, by the NEC dialect it was read in and whether it opened, was refused or was turned away for the rate limit.

The design is named only when it is a catalog design; an opened deck is counted as deck and a designs-folder design as user, never by file name. Nothing that identifies a person is counted or kept: no address, no session or browser identifier, no deck contents and no knob values. The counts are plain totals, which the hosting provider’s own monitoring reads; they are not served to the public. A local workbench counts nothing.

If the page itself breaks, it shows Something broke on this page and a Reload button instead of going blank, and the hosted app writes one line to its server log: the error’s message, the list of page components it broke in, and the app version. Nothing else is sent: no address, no browser details, no deck and no knob values, and anything shaped like a link’s query (where an opened deck travels) is cut out before the line is written. A few reports at most per page load, and a local workbench never sends one.

Open… beside the design picker opens your own model: a NEC card deck (.nec, which MMANA-GAL, 4nec2 and EZNEC all export), a SimNEC .ssn, or an MMANA-GAL .maa (read as MMANA). It becomes the tab’s design, under Opened decks in the picker, read by the same importer as antennaknobs @file.nec — see Loading NEC decks. A deck’s SY constants are its knobs, as there; a deck with none still solves, sweeps and draws, frozen.

Nothing is installed or stored. The page’s address carries the deck itself, compressed:

/?deck=<the file, compressed>&name=my_vertical.nec

so copying the address shares the model: whoever opens the link gets the same design in their own browser, on the hosted app or a local workbench. A deck whose compressed form is over about 8 KB still opens, but the link cannot carry it, and the page says so; send the file instead.

A .nec deck says which NEC dialect it was read in as the first line of its note — NEC-2 and NEC-4 put sources and loads at segment centres, NEC-5 at segment ends, and the importer reads NEC-5 only when the deck says so (EZNEC’s NEC-5 stamp, CM NEC-5, or an EX at a segment end; a GN … NOFILE alone reads NEC-5 by default and says so) and NEC-4 when EZNEC’s NEC-4.2 stamp says so. If it guessed wrong, Read as under the deck’s name chooses NEC-2, NEC-4 or NEC-5 and re-opens it; the link carries the choice (&dialect=nec5). See Loading NEC decks.

On the hosted app an opened deck is bounded, so one visitor’s model cannot take the server from everyone else:

LimitHosted
File size64 KB
Segments (after GM / GR / GX copies, and at any knob value)3000
Wires200
One solve (a live solve, a sweep point, an optimizer step)60 s
One several-band optimize run120 s
One sweep (frequency, knob, density or held)120 s
Opened-deck solves at once, across all visitors1
New decks opened per minute, per visitor10

A deck over a structure limit is refused when it opens, naming the card that crossed it. A solve that runs past 60 s is stopped and says so. A several-band optimize run that reaches 120 s stops and keeps the best point it has solved, saying so under its table. A sweep that reaches 120 s stops at its next point and keeps the points it drew, saying stopped at the time limit (120 s): partial. While another visitor’s deck is solving, yours answers busy at once rather than queueing behind it — try again in a minute. PyNEC is not offered for an opened deck on the hosted app (it cannot be stopped mid-solve); the momwire solvers are.

None of this applies to your own machine: for a larger model, run the workbench locally (pip install "antennaknobs[web]", above) and put the file in your designs folder (~/.antennaknobs/designs). A local workbench still opens a deck the same way, within the file-size and structure limits, with no time limit and no queue.

Each parameter in a design is a knob (the big one is the measurement-frequency VFO dial; the rest are smaller). The dial is normally locked to the design frequency; off-band designs — an antenna cut for one band and worked on another through a tuner (see Cut for one band, worked on another) — open with the lock open and the dial already parked on their operating band. Three ways to change a knob:

  • Drag — press on the knob and move the mouse vertically (up to increase, down to decrease). Horizontal motion is ignored, so a natural hand motion won’t fight you.
  • Keyboard / physical dial — click a knob (or tab to it) to focus it; it shows a highlight ring. While focused, ↑ / → and ↓ / ← step by one increment, Page Up / Down by ten, Home / End jump to the range ends, and Enter opens the value to type a number exactly. A physical USB dial that emits arrow keys drives the focused knob the same way — the knob keeps focus until you click elsewhere, so twisting the dial keeps adjusting it.
  • Right-click menu — right-click a knob for its settings:
    • Turn step — how much one drag-notch / arrow press moves the value.
    • Display range — the min/max the knob sweeps between.
    • Optimize this knob + Optimize range — mark it as a free variable for the optimizer and bound its search (see Optimizing below). The same range bounds Keep the target while I drag: a marked knob is the one the hold moves, and it will not leave this range to do it.

The measurement-frequency dial’s own right-click menu is different: its Display range is the sweep range, so it opens the range editor described in The measurement dial is the sweep range below instead of a plain min/max.

Every turn re-solves and redraws live (when Live is on — see below).

The design-frequency and measurement-band pickers both end with Custom…, for a design that sits between the amateur bands (a 300 MHz dipole, say). Enter a centre frequency and, optionally, a span; the default is ±1.5 % of the centre. The picker keeps that band for the session:

  • the design-frequency slider sweeps its span;
  • a custom design band locks the measurement dial to the design frequency, so the design is measured where it is cut (unlock the dial to move it again);
  • the measurement dial, the lock and the sweeps treat it like any other band.

A .nec deck whose FR card names a single frequency opens on the same ±1.5 % band around it, so its dial can still move. An FR sweep sets the band to the swept range instead.

There is one range, not two: the span the measurement dial travels is the span a new sweep chart’s frequency sweep solves over, on its Smith, SWR and S11 views. Set it from the dial’s own right-click menu (long-press on a touch screen):

  • Sweep range lo / hi (MHz) — the dial’s endpoints.
  • Step (MHz), with linear spacing, showing the resulting point count; or Points with log spacing (Base points when adaptive resolution is on, with a hint that refinement adds points where the curve bends).
  • lin / log spacing.
  • ↺ design range reverts a session edit back to wherever the range would otherwise come from.

A field applies when you press Enter or leave it, not on each keystroke, so typing 14.35 sweeps once, at 14.35. An entry that grids the same frequencies as the range already in force (re-typing the file’s own value, say) sweeps nothing again.

A bad entry — a step of zero or less, fewer than two points, or a high end at or below the low end — is refused visibly: the field marks itself invalid and keeps the last good value rather than accepting the edit.

Where the range comes from, first match wins:

  1. your edit in this menu;
  2. else the file’s own sweep — a .nec deck’s FR card, or a SimNEC Generator sweep (start : stop : step, lin or log);
  3. else a Python design’s ui_params["sweep_range"], or its meas_freq_range;
  4. else the design’s sweep_policy (a band lock, or multiplicative factors);
  5. else ×0.8–×1.25 of the design frequency, log-spaced.

Your edit is session-only — it lives in that tab, never in settings.toml — and picking a different band or switching design clears it, falling back through the same list. A band-locked design’s dial now travels only that band, and narrowing the range clamps an unlocked measurement frequency that had drifted outside it back in.

The Settings menu’s switches, which analyses a pick runs, the Antenna view’s orientation, the ground slots and the solver slots start from a file, so a workbench can open the way you use it, with the frequency sweep off, say. The file is settings.toml in the .antennaknobs folder in your home directory, the folder that holds designs: ~/.antennaknobs/settings.toml, or %USERPROFILE%\.antennaknobs\settings.toml on Windows. The packaged workbench also takes --settings PATH, and a pip install reads the ANTENNAKNOBS_SETTINGS variable.

[switches]
live = true
freq_sweep = false # a new frequency chart's "auto re-run"
convergence_sweep = false # a new knob or density chart's "auto re-run"
pattern_renorm = true # "norm check"
refine = true # "adaptive resolution"
heatmap_currents = true
current_waveforms = false
wire_labels = false
feed_labels = true
[antenna_view]
orientation = "iso" # auto, top, front, side or iso
[grounds.X] # ground slot X; [ground] is its older spelling
enabled = true
type = "finite" # finite, pec or terrain
method = "sommerfeld" # fast, sommerfeld or mininec
soil = "average" # a soil preset, or eps_r = 13 and sigma = 0.005
terrain_preset = "levee" # cliff, hillside, levee or one of your own
[grounds.Y] # free space in stock; this makes it PEC
enabled = true
type = "pec"
[slots.A]
backend = "bspline"
n_per_wire = 15
model = { degree = 2 }
[workbench.run_on_pick] # does picking an analysis start it?
frequency = true # frequency sweep / band SWR
pattern = true # pattern analyses
knob = false # sweeps of a knob
held = false # sweeps with an.Hold
convergence = false # density ladders
map = false # 2-D maps
[engines] # edited by hand only; the page never writes these
nec5_exe = 'C:\EZNEC 7.0\Docs\NEC5CL_x13.exe'
nec2_exe = 'C:\4nec2\exe\nec2dxs11.exe'
nec42_exe = 'C:\nec42\nec42cl.exe' # your own licensed NEC-4.2, if you have one
nec42_sommerfeld = 3 # optional: the NEC-4.2 slot starts on GN 3 (default 2)
[capture]
dir = 'C:\ak-captures' # every NEC-5 / NEC-2 deck and printout

Note the SINGLE quotes on the Windows paths. TOML reads a single-quoted string literally, so the path goes in exactly as Explorer gives it. Double quotes make it an escape-processing string, where \E and \D are not valid escapes — and because a parse error rejects the whole file, getting that wrong makes every engine and the capture dir disappear at once rather than reporting a bad path. If you prefer double quotes, every backslash has to be doubled.

Every entry is optional; anything the file leaves out starts where it always did. The soil presets are very-poor, poor, average, good, very-good, fresh-water and salt-water. A slot’s backend is any solver the slot picker offers, and its model knobs are the ones that solver’s options panel shows. Naming a different solver starts that slot from the solver’s own defaults.

The solver slots are [slots.A], [slots.B] and [slots.C], and up to two more, [slots.D] and [slots.E], which the slot strip’s + adds. A [slots.D] table, even an empty one, makes slot D, which starts on the first solver the slot picker lists. Like the ground slots they run without gaps, so a [slots.E] with no [slots.D] is named as a mistake and left out.

The ground slots are [grounds.X], [grounds.Y] and [grounds.Z], one table per ground slot, each taking the same five settings. A table changes only what it names, over that slot’s stock ground: slot X the built-in ground (on, finite, Sommerfeld, the served soil), slot Y free space, slot Z the reflection-coefficient model over average soil. Past Z the slots run in threes, each read forwards: U, V, W, then R, S, T, and so on down to F, G, H, so they never meet the solver slots’ letters. A [grounds.U] adds a fourth slot, which starts at the built-in ground; the slots run without gaps, so a [grounds.W] with no [grounds.V] is named as a mistake and left out. A file from before the ground slots has a [ground] table, and it still works: it is slot X. A file with both [ground] and [grounds.X] uses [grounds.X] and says so.

Before the slots had letters they had numbers, [grounds.1], [grounds.2] and so on. A file that numbers them still loads: each number is the slot at that place, so 1, 2, 3 are X, Y, Z and 4 is U, and the note under the header says how each was read. The next save as my defaults writes the letters. A file that names one slot both ways, say [grounds.1] and [grounds.X], uses neither and says so, rather than guess which you meant.

The Antenna view’s orientation is auto unless the file says otherwise: each design opens on its own best view, Top, Front or Side, guessed from its shape. Any other value wins over that guess every time a design loads, not only the first. The view’s own Top / Front / Side / Iso switch still changes it for the design on screen, until the next design loads. The Settings menu’s View choice (under Antenna chart) sets the same thing for the session.

  • When it applies. The server reads the file at every page load, so edit it and reload; there is no need to restart. Whatever you change during a session changes as usual, and the next load starts from the file again.
  • A mistake never stops the workbench. An unknown key, a wrong type or a file that is not valid TOML shows once as a note under the header, naming the entry, and that entry keeps its built-in default. The server log says the same.
  • Save as my defaults. The Settings menu’s save as my defaults writes the session’s switches, which analyses a pick runs, antenna view on load, ground slots and solver slots to the file, but only where they differ from the built-in defaults. A ground slot is compared with its own stock ground, so an untouched slot Y writes nothing, and the ground slots are written as [grounds.X], [grounds.Y] and so on. A slot you added with + is written even when it changes nothing, as an empty [grounds.U] or [slots.D], since the table is what makes it exist; one you removed is left out. Everything you left alone stays out of the file, so it follows the defaults of whichever version you run next. A soil that matches a preset is written by its name. The previous file is kept beside it as settings.toml.bak. Hand edits to the saved file are fine, though a line that only repeats a default is dropped at the next save.
  • Engines and the capture folder. [engines] and [capture] do what NEC5_EXE, NEC2_EXE, NEC42_EXE and ANTENNAKNOBS_CAPTURE_DIR do, for the workbench and the command line alike. A variable, or the workbench’s --nec5-exe, --nec2-exe or --capture-dir flag, wins over the file, and the file wins over a NEC5_EXE.txt beside the workbench. Write Windows paths in single quotes, as above, so the backslashes need no escaping. The page never writes these two tables: a path the server runs is never set from a browser. An [engines] path with no program at it shows as a note like any other mistake, and the entry stays in the file.
  • freq_sweep and convergence_sweep were the Smith chart’s freq sweep and param sweep switches. The workbench no longer has either: each sweep chart has its own auto re-run switch, and these two keys set where that switch starts on a new chart, freq_sweep for a frequency sweep and convergence_sweep for a knob or density sweep. An older file that names them still loads without a note. Flipping a chart’s switch never changes them, and save as my defaults writes back what the file said.
  • [workbench.run_on_pick] says, per kind of analysis, whether picking it in a chart’s analysis list starts it. Off, the pick only selects it: the chart shows the analysis with its run button and solves nothing until you press it, so you can change its range or the knobs first. A frequency sweep and a pattern start at once, as they always have, because they take seconds; a knob sweep, a held one, a density ladder and a 2-D map default to waiting, because each point is a rebuild, an optimisation or a re-mesh and the whole sweep can take minutes. A study counts as the kind of analysis it is. The Settings menu’s picking an analysis runs it switches set the same thing for the session. A link with run=1 always runs. After the pick, the chart’s own auto re-run switch decides as before whether a knob change re-runs it.
  • The hosted simulator reads no file and offers no save.

The output stage — views, pins, and layout

Section titled “The output stage — views, pins, and layout”

The right-hand stage shows one primary view at full size with your other views as thumbnails beside it. The roster is:

ViewWhat it draws
Antennathe wires, current heat-map, standing-wave envelope
Azimuth (xy)far-field polar cut in azimuth
Elevation (yz)far-field polar cut in elevation
Sweepthe sweep chart: a frequency sweep on the Smith chart to start, or on SWR or S11 (dB) against frequency; or a knob sweep as R and X against the knob, or its trail on the Smith chart
Schematicthe feed network as a chain
Filesthe design’s source, and the deck an external engine ran with the report it printed

Click a thumbnail to promote it to primary.

The Sweep view is the workbench’s analysis chart. It opens on a frequency sweep on the Smith chart, over the measurement dial’s range, where the Smith view used to be. Its header, on the chart, holds what it computes and how it draws it:

  • analysis — what the chart runs. First what runs here: freq sweep (the design’s band), the design’s analyses the chart can run (under their group headings, such as Tuning and General, when the design lists more than three in more than one group: see --list; a shorter list has no headings except General over the generic analyses every design is offered, and each entry’s tooltip, like the analyses as Python list, says this design or every design), and Sweep a knob, which runs the knob in the chart’s sweep list (the last knob you swept, else the design’s first) over its own range, as Sweep this knob… does. Then, under Studies, the studies this design is in: the module-level ones that name it, and its own Builder’s comparisons with its references, each by its short name. Picking one selects it just as an analysis that crosses designs is selected, and it runs on the same terms (see run_on_pick). Last, under Not in the workbench yet, the analyses it cannot run, greyed out, each with its reason;
  • view — Smith, SWR, S11 (dB), R / X or Table for a frequency sweep, all drawn from the one sweep, so switching re-solves nothing; R / X, Smith or Table for a knob sweep, where Smith draws the knob sweep as a trail (what the old param sweep switch drew), and then the pattern views, which draw the knob’s values as a family of patterns. R / X of a frequency sweep is the knob sweep’s R / X chart with frequency across the bottom and the dashed guide at the measurement frequency. Table prints the numbers (see The Table);
  • from / to and ↺, run, and auto re-run;
  • A · X, the engines and grounds it compares (see Comparing engines and grounds);
  • ⧉ duplicates the chart, and × closes a duplicate (see More than one chart);
  • on the Smith view, measured .s1p…, the measured overlay.

auto re-run is what the freq sweep switch was, moved onto the chart. With it on, the curve re-sweeps once the knobs have been still for half a second. With it off, a change leaves the curve drawn but faded as stale, and run · re-run? runs it again. Either way the live point, the marker at the measurement frequency, moves with every knob at once; only the curve waits. It starts on for a frequency sweep and off for a knob or density sweep, unless settings.toml says otherwise. The chart’s pick, switch and range last for the session.

An analysis with a hold, such as the inverted vee’s resonance vs angle and match vs height, runs the optimizer at every point of its sweep, on the server, exactly as antennaknobs analyze does. Each point is several solves, so picking it only selects it (unless held = true in [workbench.run_on_pick]) and it runs when you press run, and auto re-run starts off for it whatever settings.toml says. Turn it on and the chart re-runs after the dwell like any other. stop cancels it. Its views add Knobs: the held knobs against the swept one, the first knob on the left axis and a second on the right. A point the optimizer did not converge at is drawn as a gap, a break in the line with an × on the axis. Hover it for the reason. It is never drawn as a value. The Table adds a column per held knob, and a gap’s row gives its reason. The held knobs start from the design’s defaults, not from their sliders, so moving one does not make the curve stale. Moving any other knob does.

The Table view prints what antennaknobs analyze prints for an analysis’s an.Table() view, with the same columns and number formats: for a frequency sweep MHz, then R, X and SWR against the session’s Z0; for a knob sweep the knob, then R and X; for the density ladder nominal_N, then the achieved segment count, R, X and |ΔΓ| against the finest rung. There is one row per swept value and one group of columns per curve, headed with the legend’s name for it, so a chart comparing engines, grounds, designs or a family reads side by side. Where the curves were swept at different values (two designs on their own bands, say) the table has a row for each, and a curve with no point there leaves its cells blank. copy puts the table on the clipboard as tab-separated text, which a spreadsheet pastes as columns, or select the cells and copy them. Download CSV saves the same table as a .csv file named for the design and what the chart sweeps. On a phone the table scrolls sideways and down inside the chart’s square rather than widening the page. The table lists the points the chart swept: on a frequency sweep with adaptive resolution on, the curves on the other views are refined between those points, and the table shows the base grid.

The standalone Smith, S11 (dB) vs freq and VSWR vs freq views are gone; they are this chart’s views now. Pins saved in the browser that named them become the chart: a Smith pin opens it on Smith, a VSWR pin on SWR and an S11 pin on S11, each in the old pin’s place. Several of them together become one chart; ⧉ makes more.

The A · X button on a chart’s header names the solver slots and ground slots the chart draws, and opens their checkboxes: one curve per ticked engine on each ticked ground. It starts on the active slot and ground alone, the one curve the chart always drew, and follows them as you switch until you tick a box. On a phone the boxes open over the page from that one button, so the header still fits.

Each curve solves on its slot’s engine and its ground slot’s ground, exactly as the chart would with that slot and that ground active, and no slot changes: the active ones stay what the live solve and every other view use. The curves draw in their own colours on the Smith, SWR and S11 views and on a knob sweep’s R / X (each curve’s R solid and its X dashed) and Smith trail, with a legend naming each one, as antennaknobs analyze names its curves. The marker at the measurement frequency is the live solve’s, and moves with every knob; the curves wait for auto re-run or run as one curve does.

On a phone the legend starts folded into one small chip in the chart’s corner, 3 curves ▾, with · 1 refused after it whenever a curve is refused, so folding it never hides a refusal. Tap the chip to open the legend and ▴ to fold it again. On a desktop it starts open. Each chart keeps its own choice for the session, and a duplicate starts from its source’s.

A chart draws at most six curves, the command line’s cap: three engines on two grounds draw, three on three are refused (“REFUSED: 3 engines x 3 grounds = 9 curves, over the cap of 6”), and nothing is dropped to fit.

When an analysis you pick names its engines or grounds (a cross in its build_analyses()), their slots are ticked for you. An engine that no slot holds is skipped: it draws no curve, and a quiet note under the legend’s curves names it, e.g. “skipped: razor-2p, NEC-5, which no slot holds. Put one in a slot to include it.” With B-spline, B-spline d=1 and PyNEC in your slots, the inverted vee’s convergence (which names B-spline, razor-2p and NEC-5) draws the one B-spline curve and that note. If none of the engines it names is in a slot, the chart draws every slot instead, and the note says so. Load an engine into a slot to draw it; the chart never rewrites a slot, and the boxes still choose among your slots. A skipped engine is the workbench’s call about your slots only: antennaknobs analyze runs exactly the engines the analysis names. A cells= analysis skips a cell whose engine no slot holds the same way; if every cell is skipped and they differ only by engine, the chart draws your slots instead, and otherwise draws only the note.

A ground that no slot holds is still a refused curve: the legend names it with the reason, e.g. “free space: not drawn: no ground slot holds free. Add one with + to include it.”, with no colour sample beside it, since nothing is drawn for it; the rest draw. While the boxes are still the ones a pick ticked, the legend also says which slots it ticked and that the session still solves on the active one, e.g. “This analysis compares 3 grounds, so picking it ticked the ground slots that hold them (X: Sommerfeld · average; Y: free space). The session still solves on X.” So is a curve on a slot you have that cannot run the design at all, or is a poor match for it (which only the active slot’s Solve anyway can override), or an engine that declines this design’s feed: these are curves you asked for, so they are named rather than skipped.

An analysis can also compare measurement planes, designs, named knob settings (states), or values of a second knob (a family), with an an.Cross(planes=…), an.Cross(designs=…), an.Cross(states=…) or an.Cross(step=an.Sweep(knob, values=…)) in its build_analyses(). Picking one draws a curve for each, as antennaknobs analyze does; there are no boxes to tick for these, since they come from the analysis rather than from the session’s slots, and the legend names every curve:

  • a plane curve is the design measured at that port, exactly what the readout’s plane selector shows when you pick it there. A plane the design does not have is a refused curve, in the command line’s words (“no plane ‘nowhere’ on this design; it offers rig, C1, …”);
  • a design curve is that design at its own defaults, as a fresh session would open it, on the chart’s solver slot and ground slot. The design you have loaded is drawn at its defaults too, so your knob settings do not leak into the comparison. An engine that cannot build a design is a refused curve in its own words: NEC-2 cannot feed dipoles.invvee_apex’s apex knot, so the feed spelling (E7) study, under Studies on both of its designs, names that curve with NEC-2’s reason and draws the rest. In a frequency analysis each design sweeps its own band, on the same frequencies antennaknobs analyze sweeps for it, so a 7 MHz and a 28 MHz design each stay on theirs; the chart’s from / to moves only the curves of the design you have loaded;
  • a family curve is your design with the second knob set to that value, on top of your other knobs, labelled angle_deg = 30 as the command line labels it, while the swept knob moves along the x axis as usual;
  • a state curve (an.Cross(states=…), see States) is a design with a few named knobs set over its defaults, labelled by the state’s name. Your own knob settings do not leak into it, so the inverted vee’s height states draws its 5 m, 7 m and 12 m masts wherever you have dragged the base. A state that names another design is that design’s curve. A knob the design lacks is a refused curve, in the command line’s words. States come from the .py; the chart does not author them yet.

These multiply with the engines and grounds you tick: two designs on three engines are six curves, labelled dipoles.invvee, momwire:bspline and so on, in the order the analysis writes its crosses. The six-curve cap covers the whole product, in the command line’s words (“REFUSED: 4 values x 2 engines = 8 curves, over the cap of 6”). A family over the segment density stays refused, as on the command line.

A pattern analysis (sweep=None) draws far-field cuts instead of curves. Pick one and each cell is solved once, at its measurement frequency, with the same solve the live far-field views use. The chart’s view menu offers the analysis’s own views, in its order:

  • Elevation @ 0° az or Azimuth @ 10° el: that cut on the far-field charts’ polar grid, one trace per cell in the legend’s colours. Switching between cuts re-cuts the solves already made, so nothing solves again;
  • Table: one row per cell with the pattern compare table’s columns (peak, take-off, F/B, azimuth beamwidth, RDF), computed by the same function. A refused cell has a row too, with its reason.

The inverted vee’s height patterns draws the elevation cut and the table for its three mast heights. run, Stop and auto re-run work as for a sweep, and the cells cross engines, grounds, designs and states in the same way, under the same cap. A pattern’s cells are not pinned: its header has no pin.

On the Sweep a knob chart the view menu goes on past the knob sweep’s own views to Elevation @ 0° az, Azimuth @ 10° el and Pattern table. Picking one turns the chart’s knob and range into a family: one pattern per value, each solved once as a pattern cell is, drawn as the patterns above are and labelled knob = value as the command line labels a family. Any knob the chart can sweep works, an opened deck’s SY symbols included (Dan’s hgh), so a height family needs no height detection; the inverted vee’s height patterns is the same idea written into the design as three named heights.

The header holds the family’s values:

  • over — the knob, or frequency (MHz): patterns across the band, one per measurement frequency, starting from the bottom, middle and top of the band the measurement frequency is in;
  • from, to — the range’s ends;
  • step — a linear range’s step. The values are from, from + step, … up to to and never past it: a step that does not divide the range moves to down onto the last value it reaches (4 to 14 by 3 is 4, 7, 10, 13, and to reads 13);
  • values — how many, linear or with log spacing (a fixed ratio, no step). An integer knob is rounded to whole values, as the knob sweep is.

Beside the view menu, an elevation or azimuth view shows its cut: at n ° az (the bearing of an elevation cut, 0–359, wrapping round) or at n ° el (the elevation of an azimuth cut, 1–89). Type an angle and press Enter: every pattern in the family redraws at that cut, and the view menu’s label follows it (Elevation @ 90° az). at peak sets the cut through the live design’s 3-D maximum.

At most 6 values, the chart’s curve cap: more overlapping patterns are unreadable. A knob sweep of more points is clamped to 6 on the way in, and the values and step boxes refuse a count over 6. Ticking more solver or ground slots multiplies the family, and a product over the cap is refused whole in the command line’s words, with run disabled until it fits.

What picking a pattern view does follows run_on_pick’s pattern (it runs by default; off, it waits for run), and after that the chart waits for run as the knob sweep does, unless auto re-run is on. R / X, Smith or Table goes back to the knob sweep of the same knob and range (a frequency family goes back to your last knob, since the knob sweep never sweeps frequency). copy and keep write the family as an.patterns(cross=an.Cross(step=an.Sweep("base", 4, 14, points=6))), with an.FREQUENCY for a frequency family, spacing="log" for log spacing, and the exact values= only when an integer knob’s rounding took them off that range, and the cut angles you set as views= when they differ from the views’ own. The chart’s link carries the family as family= and a cut angle off the view’s own as cut= (see Linking to a chart).

A map sweeps two knobs over a grid and colours each node by how well it matches: |Γ| on the session’s Z₀, the colours the command line draws (yellow is a match, dark violet is |Γ| = 1). The inverted vee’s tuning map is length_factor against the apex angle, 33 × 25 = 825 solves, about six seconds. Picking a map shows it with its run button and solves nothing until you press it (unless map = true in [workbench.run_on_pick]). The grid fills a row at a time as the nodes land, each node its own cell, so a partial map reads as partial.

  • Contours are the analysis’s reference lines, drawn from the grid as the command line draws them: X = each x (solid), R = each r (dashed), and an swr threshold as a dotted line where |Γ| reaches it. With no r or x, X = 0 and R = Z₀. A line the grid never crosses is listed in the legend as (not reached). Under the legend is the grid’s best node, worded as the command line prints it: least |Γ| 0.0247 (SWR 1.05) at length_factor 0.975, angle 30.
  • The ring is where the two knobs are now, filled with the live solve’s colour. Off the grid it becomes an arrow on the edge. Dragging either knob moves the ring and leaves the map as it is: every node sets both knobs itself. Any other change (another knob, the solver, the ground, the frequency) fades the map as stale, and run · re-run? runs it again. A new Z₀ only re-colours it.
  • Hover (or tap) a cell for that node’s values: the two knobs, R, X, SWR and |Γ|, with no interpolation between nodes. Click a cell to select it: its readout stays, with set knobs here, which sets both knobs to that node, so the live solve lands on it (a knob the optimizer holds is handed back to you, as a drag would). Click it again to let it go.
  • x and y edit each axis’s range, points and spacing, and ↺ goes back to the analysis’s own. colour switches between |Γ| and 1 − 1/SWR, the SWR chart’s two scales.
  • solver and ground pick one slot each: each slot is another whole grid, so a map compares nothing in this version. A map that crosses engines, grounds or anything else is listed greyed out, as is a map with a frequency or density axis. antennaknobs analyze draws those.
  • Beside run, the cost: 825 solves · ~6 s at the pace of the live solve. stop keeps the nodes solved so far. auto re-run starts off for a map.
  • A map has no pin yet, and its Table view is left out: hover gives the numbers, and antennaknobs analyze prints the table.
  • copy and keep work as for any chart. An axis you edited is written as a range, an.Sweep("angle_deg", 10.0, 50.0, points=9), never as a list of its values; an axis you left alone stays as the analysis wrote it.

On the hosted simulator a map is limited to 1000 nodes, and run says so over that. One that is still running after two minutes stops at the next node and keeps what it drew, saying stopped at the time limit (120 s): partial.

pin on a chart’s header freezes what it draws: one pin per curve, holding the curve’s R and X at each point, the Z0 it was drawn at, and a label naming the design, its changed knobs, the engine, the ground and the plane. A pin never re-solves. It draws dashed, in its own colour, beside the live curves, so you can change a knob, an engine, a ground or the design itself and see what moved.

  • A pin draws on any chart that sweeps the same thing: a frequency pin on every frequency chart where the ranges overlap, a knob pin on any chart sweeping a knob of the same name (in another design too), a density pin on density charts. It draws on every view; on SWR and S11 it keeps the Z0 it was taken at, and its label says so when that differs from the chart’s.
  • Pins belong to the session, not the tab: pin in one design tab and compare in another. They are gone when the page reloads.
  • The legend lists every pin with show/hide, csv (x, R, X and SWR at the pin’s Z0) and ×. A pin that cannot draw on this chart stays listed, greyed, with the reason.
  • pin waits while a curve is running, refused or stale.

Pins are gone when the page reloads. To keep what you built, turn it into Python, the only thing the workbench keeps:

  • copy on a chart’s header (copy as analysis): the picked analysis as the Python that builds it, with the engines and grounds it drew (a skipped engine is not among them) and an edited range, to paste into the design’s build_analyses(). Only for a chart about this design; a chart that compares named designs is kept as a study.
  • keep on a chart’s header (keep as study): the same chart as a study function. A study names its designs, so the tab’s design (and the knobs you moved on it) becomes a named state.
  • keep as study above the pinned sweeps in a chart’s legend, and in the pinned-pattern table’s actions: the shown pins as a study. Each pin becomes one cell, read from the exact request its curve was solved with: its design and variant, the knobs that differ from that variant’s defaults, the engine, the ground and the plane. Pins that are a product are written as the plain cross; pins that are not are written as cells= (see Cells). Every sweep pin must sweep the same thing, and at most six are kept.

Each opens a dialog that shows the Python before it goes anywhere, with a name to edit. copy puts it on the clipboard. On a local workbench, save as study writes it as a new file under ~/.antennaknobs/studies/ (type feeds/e7 for a subfolder) and allows it with your edits allowed, so it is in the Studies group of every tab it names at once; an existing file is replaced only when you say so. The hosted instance has no save: copy it instead.

The address bar follows the tab you are on: its design, and the first chart’s analysis and view. link on a chart’s header copies the same for that chart. Opening such a link opens the design and selects the analysis:

/?design=dipoles.invvee&analysis=resonance%20vs%20angle&view=Knobs
/?design=dipoles.invvee&analysis=feed%20spelling%20(E7)
  • design= is the design’s family.name, with :variant after it for another variant than its first. A bare name works when one family has it, as on the command line.
  • analysis= is one of the design’s analyses or a study that includes it. A study goes by its full source:name, or by its source or short name when only one study answers to it: the same rule as analyze --study.
  • view= is the chart’s view (Rx, Smith, Table, Knobs, Metric, Swr, S11, or a pattern view such as pattern:0); optional.
  • family= is a pattern family’s knob and range, beside a pattern view=: knob:from:to:points, with :log for log spacing (family=angle_deg:0:60:6). The knob is its name, not its label.
  • cut= is that family’s cut angle in degrees: the bearing of an elevation view, or the elevation of an azimuth view. Absent, the view’s own (0° az, 10° el).
  • run=1 presses run once the analysis is selected, whatever [workbench.run_on_pick] says. Without it, the link picks the analysis as the analysis list does: a frequency sweep or a pattern runs, and a knob sweep, a held one or a density ladder waits for you to press run, unless run_on_pick says otherwise. A copied link never carries it.

A name the workbench does not know is reported by name above the knobs and otherwise ignored. A link to a user design (user.…) opens only on the machine that has that design. The link is one tab’s: the other tabs (D2, …) are not in it.

⧉ on a chart’s header opens another like it, up to four charts: its own analysis, auto re-run, range, view, scales and engines and grounds, and its own sweeps, so one chart can watch SWR across the band while another sweeps a knob. The copy opens on the stage, and in the grid the charts take the four cells first. × closes a copy. Copies last for the session and are never among the pins the browser remembers.

The SWR and S11 views’ vertical range is set by clicking the chart’s y axis (tap it on a phone), and a choice shows on the chart at once, with the menu still open. The menu opens inside what is on screen, a pinch-zoomed phone included.

The VSWR chart starts on its 1–∞ scale, which shows every SWR at once, none pegged: it plots 1 − 1/SWR (which is 2|Γ|/(1+|Γ|)), so 1.5:1 sits a third of the way up, 2:1 half way, 3:1 two thirds, 10:1 at 90 %, and ∞ is the top edge. The ticks are still labelled in SWR. It spends the chart’s height where the match is and squeezes the bad end together. ρ (EZNEC) is the same 1 to ∞ on EZNEC’s scale instead: linear in the reflection coefficient ρ = (SWR − 1)/(SWR + 1), still labelled in SWR, so 1.5:1 sits a fifth of the way up, 2:1 a third, 3:1 half way and 10:1 at 82 %. For a close look, VSWR also offers 1–1.5, 1–2, 1–3, 1–5 and 1–10, and a custom min and max; above such a top the curve leaves the chart at the edge and the off-scale ticks mark where.

The S11 chart offers floors of −10, −20, −30 and −40 dB, a custom min and max, and Auto, its default, which fits the dip: the shallowest floor the dip clears by 5 dB that still shows the threshold line. So that the axis never rescales under your hand, Auto only deepens while a knob is moving or a sweep is landing, and fits again once things have been still for half a second. Each view remembers its own choice, in the browser, as the pins are.

Both charts draw a dashed SWR threshold line, 2:1 unless you change it in the same menu (on S11 it is the matching return loss, −9.5 dB for 2:1). The band where the sweep is below it is shaded, and the title row reads its width, e.g. 2:1 BW 176 kHz, with the edges interpolated between sweep points rather than taken at the nearest one. ≥ means the band runs off the end of the sweep, so it may be wider. When the sweep dips below the line more than once, the readout gives the band holding the measurement frequency (else the widest) and says (1 of 2).

The Files view shows the text behind a solve, each tab with Copy and Download. Source is the file the design is built from: a catalog or user .py, or a bare .nec/.ssn as written. SimNEC is the design as a .ssn circuit — the antenna on screen at its frequency and ground, written for every design and needing no engine, which is how the round trip to SimNEC runs without a terminal. When the slot’s solve ran through an external binary (NEC-5 or NEC-2), the last two tabs show the exact deck that engine was given and the output it printed, with a run picker beside them: the NEC overlay’s own far-field pass is a separate pattern run against the same engine, and once you’ve fetched a pattern its deck and printout are a second entry in that picker, named apart from the solve’s own. A momwire or PyNEC slot runs no deck and says so instead. The texts are fetched only while Files is the view on the stage, never for a thumbnail.

Minimizing the readout. The – on the floating solve readout collapses it to a one-line R · X · SWR pill; click the pill to restore the card. The choice is remembered per view, and the Files view starts minimized, since its printout carries the numbers.

Wires below the ground plane and the polar cuts. Currents above the plane are read out through an image in it, Fresnel-corrected. Currents inside the soil reach the air a different way — as the transmitted wave, refracted at the interface and attenuated on the way up — and that is how the cuts place them. Every buried design gets a pattern: the buried dipole, whose wires are all below the plane, is its transmitted field alone; a vertical over buried radials or an elevated feed over a buried counterpoise adds it to what the above-ground wires radiate, with nothing below the plane entering the image. The polar views label the share of the current moment that lies below ground, which says how much of the antenna is down there.

  • Pin the views you watch. The stage carries a pinned set, not the whole roster — Antenna, Azimuth, Elevation, Sweep to start, up to six pins. Six is a hard cap: more thumbnails than that and none of them is legible.
  • The rest live in the picker. All views ⌄ +N at the foot of the thumbnail strip opens every view in the roster; the ⊕ / ⊖ beside each row pins or unpins it, and a view added to the roster since you last looked wears a NEW badge. At the cap, pinning is blocked with “Unpin a view first” rather than silently dropping someone else’s pin.
  • Rail or grid. The ▤ / ⊞ toggle on the stage switches between the rail (one primary + thumbnails) and a 2×2 grid of equal cells over your first four pins, for watching several live at once. Each grid cell has a maximize button back to the rail. Grid is desktop-only.
  • On a phone the output pages are the pinned set — swipe between them, with a trailing Info page for the solve readout — and the ⋯ button opens the same roster as a sheet, so pinning a view gives it a page and unpinning takes it away.
  • Pins are yours, not the design’s: the set (and the layout mode) is stored in the browser and shared by every design session, since which views you care about is a habit, not a property of the antenna. Stored in the browser means stored against the address the workbench answers at, port included — which is why the packaged workbench serves on a fixed port, 8000, and not on a free one: a launch at another address is a first visit as far as the browser is concerned, and starts from the default set. --port, or 8000 being busy, moves it, and the console window says so. These never go into settings.toml: that file holds where the workbench starts, and pins, layout and theme are the browser’s own. Since v0.43.0 the picker’s ▲/▼ buttons reorder the pinned set — pin order IS rail order, grid-cell order, and phone-page order, so one reorder serves all three — and a second browser window picks up pin/order/layout changes live instead of on reload.
  • Analyses follow the views (v0.43.0): the sweep chart’s sweep and the NEC pattern overlay only run while a view that renders them is pinned or open — a sweep chart that is neither costs nothing, and pinning it starts it. The norm check is the deliberate exception (its number lives in the always-present solve readout).

The Antenna view draws the wires (with the current heat-map and standing-wave envelope overlays) in one of four projections — Top (xy), Front (xz), Side (yz), and an isometric Iso (the classic corner view: x and y recede left and right, z stays up) — switched by the buttons in the top-right overlay. The ground reference line appears on the two elevation views.

Each design opens on its own best view. A design that lies in a plane opens on that plane: an inverted V on Side, a Yagi on Top. One with real depth in all three directions (a vertical over radials, an inverted L, a quad, a helix) opens in Iso, so no single flat projection hides part of it. The Tools menu’s View choice overrides this for every design, and settings.toml can make it the default (see settings.toml).

The view auto-fits the whole antenna, and you can navigate from there, map-style:

  • Zoom — scroll wheel (desktop) or pinch (touch), anchored at the cursor or pinch centre, up to 10 000×. Fine construction detail — a cage of wires millimetres apart on an antenna metres across, typical of imported NEC decks — is inspectable without touching any knob.
  • Pan — drag, once zoomed in. At fit zoom a touch drag stays with the page (on a phone that’s the swipe between output screens); after a pinch the canvas owns the gesture until you re-fit.
  • Re-fit — double-click / double-tap, the Fit button in the bottom-right HUD (next to the live zoom readout), or just zoom all the way back out. Switching design also re-fits — the old viewport means nothing for a new geometry.
  • Turning the view keeps your place — switching projection carries the zoomed viewport over: the world point at the centre of the canvas stays centred (its depth along the old camera ray taken from the geometry’s midpoint), at the same on-screen magnification. Zoom into a feed region in Front view and flip to Side or Iso to see the same region from the other angle. Fit is one double-click away whenever the carried view isn’t what you wanted.

The scale bar under the antenna reads λ/4 at fit zoom; once zoomed it switches to a round metric length (1/2/5 × 10ᵏ m — like a map’s) sized to about a quarter of the canvas, so it stays readable and true at any magnification. Zooming magnifies geometry only: wire strokes, labels, the feed dot, and the envelope amplitude keep their size.

A Live toggle sits next to the frequency dial. It looks depressed when on and raised when off:

  • On — every knob turn triggers a solve; the plots track your hand.
  • Paused — knob turns just move the values; nothing solves until you turn Live back on. Useful when you want to set several knobs before paying for a solve, or when a heavy design makes continuous solving sluggish.

The frequency sweep and the far-field cuts sample where the curve actually bends, not just on a fixed grid. Hold the design still for a moment after a sweep settles and the workbench quietly buys extra samples where the drawn polyline still misses the true curve — a sharp SWR notch that a uniform grid would step right over resolves down to sub-pixel, and a pattern lobe’s edge gets angles a uniform circle wouldn’t spend there.

What you’ll notice:

  • Sweeps start faster. With refinement on, the base grid’s job is only to detect features, so it opens at 17 log-spaced points instead of 41 and the S11/VSWR/Smith views fill in sooner; the refinement rounds then sharpen whatever the base grid straddled.
  • The Smith locus is a connected curve — once it has earned it. While refinement is still landing points, the Smith, VSWR and |Γ| views draw unconnected dots: a polyline through a half-refined set shows kinks that are artifacts of sampling, not physics. When the refinement pass settles, the views switch to the connected stroke. (Toggling refinement off mid-run deliberately keeps the dots — the accumulated set is uneven, and the charts keep saying so.)
  • Only what’s on screen refines. Like the sweeps themselves (view-residency gating, above), refinement spends solves only on the projections and cuts whose views are pinned or open.
  • It’s a setting. The gear menu’s adaptive resolution toggle (default on, stored per browser like the theme) turns the whole machinery off, and the base grids revert to their historical sizes (41-point sweeps) and become final. For unusually difficult designs the budgets can be nudged via localStorage overrides (antennaknobs.sweepBaseN, antennaknobs.sweepRefineBudget, antennaknobs.cutRefineBudget, antennaknobs.refineTolerance) — deliberately not knobs in the menu.

Relatedly, the S11 chart no longer clamps at 0 dB: a driven-array port’s active reflection is not bounded by |Γ| ≤ 1 (mutual coupling can push more power into a detuned element than its own generator supplies), so when any drawn value crosses zero the axis top rises to show it, with the 0 dB line kept as the tick a healthy passive port never crosses.

Some decks are solvable but sit somewhere the answer is a strong function of something you may not have thought about. When that happens the solver says so, and the note appears under the design’s description in the catalog panel.

These are advisories, not errors. Nothing was refused and nothing was re-meshed: the solve ran with exactly the settings you gave it and produced exactly the number shown. A deck the solver will not take is refused instead, in the backend picker, before it runs.

Three come from momwire and carry that deck’s own measured figures:

  • a conductor within a few radii of the ground — the impedance is a strong function of the stand-off, and the note quotes the deck’s actual height and h/a. On a sparse surface screen a millimetre of grass is worth tens of ohms;
  • an unresolved crossing node — the mesh where a wire crosses the interface is coarser than its convergence class wants, with the measured bar;
  • razor-2p’s far mesh — that basis is first order in the far mesh, so its default-mesh answer is not converged. This one fires on every razor-2p solve, by design: momwire measured that no solve-free predictor of the error correlates, so there is no honest threshold to gate it behind.

Each of the three leads with a plain summary for the deck in front of you: where (a wire meeting the ground at x, y), roughly how much (a few ohms of reactance), and what to do (add segments near the joint). momwire’s own text, written for its developers, is one click away behind Details. The command line prints the summaries of the first two the same way, as an advisory: line on stderr.

Because the last one is unconditional, it is collapsed to a count you can expand while the deck-specific notes are always shown in full. An advisory that appears every single time trains the eye to skip the whole panel, which would take the deck-specific ones down with it.

One advisory is the app’s own rather than the solver’s:

  • single-valued soil constants, on a deck with buried conductors solved at 14 MHz or above. The soil presets are one (εr, σ) pair each, and real soil disperses with frequency. On the 48-radial buried screen the fixed average soil sits 17.7 % from the frequency-dependent value at 14 MHz and 31.9 % at 28.5 MHz, against 5.9 % at 1.8 MHz — so above 14 MHz treat a buried deck’s impedance as indicative. Elevated antennas are not affected the same way (1–3 % on the same comparison), which is why the note is scoped to buried decks and to the higher bands rather than shown everywhere.

The channel carries whatever the solver raises rather than a fixed list, so an advisory added upstream appears here without a client change — an unfamiliar one is shown in full rather than collapsed.

Next to Live is an Optimize toggle (same depressed-when-on look), with a gear menu beside it for the objective. The optimizer continuously tunes the knobs you’ve marked to hit a target. It solves on the slot’s own engine, so an optimization run on a NEC-5 slot tunes against NEC-5’s answer, not the built-in B-spline one:

  1. Pick an objective in the gear menu — SWR, Resonance or Match Z₀. They differ in more than the target: see Which objective.
  2. Mark the knobs to vary — right-click each knob you’ll let the optimizer move, check Optimize this knob, and set its Optimize range (the search bounds). It starts at the range the design declares for the knob, else ±20 % of the knob’s value when you mark it (inside its slider), the same default as optimize --bands on the command line. A marked knob is visually flagged. To flip the flag from the keyboard, focus a knob (click or tab to it) and press o — the same toggle as the menu checkbox, without leaving the home row.
  3. Turn on Optimize. While Live is also on, the optimizer runs reactively: any time you change a fixed knob, it re-tunes the marked knobs (a short debounce, then a few dozen solves) and writes the best values back, so the antenna stays on target as you explore.

The three are not flavours of the same thing. Two of them are roots — an exact condition with an exact answer — and one is a minimisation, and that difference decides how many knobs each needs and what the readout shows.

  • SWR — minimize SWR against the design’s reference impedance (Z₀, 50 Ω by default). A best compromise rather than an exact target, searched by Nelder–Mead. It is the only one of the three that works with any number of marked knobs and on a multi-feed design, which is why it stays the default choice for anything three-knob or many-port.
  • Resonance — drive the feed-point reactance to zero (X = 0) exactly. With exactly one marked knob this is a scalar root and is solved as one, by a bracketed secant: typically three to five solves where the general search takes ten to sixteen.
  • Match Z₀ — drive both R = R₀ and X = 0 exactly. With exactly two marked knobs this is a two-component root, solved by Newton on (R − R₀, X) with a finite-difference Jacobian, started from a short survey of the knob box. Typically eleven to thirteen solves against Nelder–Mead’s thirty to fifty.

The gear menu says which is which in a line each. The knob counts are what the root methods need, not a restriction: mark two knobs with Resonance selected, or a number other than two with Match Z₀, and the run falls back to Nelder–Mead on the same objective rather than refusing. A multi-feed design does the same, because a minimax over several ports has no single root.

The readout shows the residual for the root objectives — |X| for Resonance, |Z − Z₀| for Match Z₀ — rather than an eval count and an SWR. A root method’s residual falls monotonically, so it is worth watching; a simplex’s best-so-far is not, and showing one where the other is expected reads as a fault.

A run shows its work. Progress streams back per evaluation: the VFO panel’s readout ticks through each candidate’s eval count, impedance, and SWR as the search moves, and the Smith chart follows the run live — its dot walks the Γ-plane with every trial. On a multi-feed design it draws one hollow ring per feed, so you watch the array come into balance rather than one element of it; the feed the objective is currently chasing (the worst one) is drawn bright and the others recede. Every other view keeps describing the design you started from (the knobs aren’t touched until the run finishes), so those views dim for the duration — the same stale-fade a mid-solve view wears — and light up when the result lands. The schematic stays bright: it’s drawn from the current knob values and stays right throughout.

The solve readout keeps moving too. Its solve field shows the last full solve inside the run, rtt gives way to a per eval figure — the gap between updates, which is what you are actually waiting through, rather than the interactive round trip the optimizer’s own request never makes — and a solves row counts the work the run has paid for, so forty solves stop looking like one.

Under the hood, SWR is a derivative-free Nelder–Mead search, and Nelder–Mead is also the finisher whenever a root method cannot apply (see Which objective above); Resonance and Match Z₀ with the right knob counts are solved as roots instead. Every evaluation is a full MoM solve, bounded by your Optimize ranges, and it always runs on the fast momwire engine — never PyNEC, which is too slow for an interactive loop. It’s a tuning aid, not a global optimizer: give it sensible ranges and a couple of free knobs, not a dozen.

Evaluations and solves are different numbers. Nelder–Mead keeps only its current simplex, so it re-probes points it has already paid for; those are answered from within the run instead of re-solved. A run reports both, and on a one-knob run the gap is about a fifth — 24 evaluations for 19 solves on a 12-radial buried screen. The match is exact, never approximate: a point that differs in the last bit is a different point and gets its own solve, so the run is the run it would have been, only cheaper.

Seed from a survey is an off-by-default switch in the same gear menu, under Search. With it on, the optimizer first samples the whole knob box, fits a surface to the impedance it measured, and hands the most promising point to the search as a starting position — to Nelder–Mead for SWR, and to the Newton root for Match Z₀, which starts from the surface’s predicted crossing rather than its best sample. The search is still the finisher — the survey proposes a place to start, it never decides the answer, and every value reported is a point that was actually solved.

It is off by default because it is only worth its cost some of the time. From a design’s shipped tuning, where the answer is already near the start, the survey spends evaluations to arrive at about the same place. From knobs you have dragged well away from a good answer it is the difference between converging and not: on the Moxon, started from a corner of its ranges, plain Nelder–Mead sticks at SWR 1.60 at every budget — it walks into a local basin and parks a knob on its bound — while the seeded run reaches 1.0006. Turn it on when you are exploring rather than refining.

What it costs is visible: the survey’s points are spread across the whole box, so their SWR jumps around and none of them need be an improvement. The readout says seeding 3/6 through that phase rather than showing an eval count and a wandering SWR, because a working search should not look like a failing one. The survey’s evaluations come out of the same budget, so switching it on never makes a run more expensive. One free knob skips it entirely — a one-knob search converges quickly enough that surveying would cost more than it saves.

Multi-feed designs score their worst feed. On a design with several independently driven ports (a bowtie array, a pair of phased verticals), the objective is evaluated per feed and the optimizer minimizes the worst one — so “SWR 1.4” after a run means no element sits worse than 1.4, and one bad feed can’t hide behind several good ones. The SWR shown while optimizing is that worst feed’s, and it is the bright ring on the Smith chart — which feed that is can change from one evaluation to the next as the search rebalances them. A design that feeds its elements from a single source through a network is different: there the match that matters is the network’s input, so the optimizer scores the driven plane — the same impedance the readout shows. The CLI’s optimize aggregates feeds the same way (it differs only in scoring |Z − Z₀| distance rather than SWR).

Several bands at once. The gear menu’s Bands section tunes for more than one frequency in the same run. Tick Several bands at once, which seeds the list with the measurement frequency. Then add each band in MHz, up to eight; × removes one. Each evaluation builds the design once and solves it at every band on the same mesh, and the optimizer minimizes

(1 − w) · worst band’s SWR + w · mean of the bands’ SWR

with Balance setting w (default 0.5). At 0 it improves the worst band only, which lets it drag the easy bands up to the hardest one’s SWR; the mean term stops that. On UR0GT’s three-band vertical (160/80/40 m), w = 0.2 settles on SWR 1.86 on all three bands, and w = 0.5 on 1.93 / 1.93 / 1.34. While it runs, the readout shows each band’s SWR and the worst. When it finishes, a table gives each band’s SWR before and after, plus the worst and the mean. Under it, a note says when a knob ended at the edge of its range (the best value may lie past it: widen the range and run again) or when no band is near a match. A range that leaves out a knob’s current value is refused before the run, naming the knob and its unit. While the run is going, each band’s impedance moves on the Smith chart in its own colour. Opening the gear menu pauses Optimize; closing it resumes, with one run for all the edits. Knobs inside a group (a fan dipole’s per-band lengths) can be marked like any other. The command line’s optimize --bands does the same, and adds the per-band objective, feed and Z₀, and the root and band-by-band modes; see Optimizing across bands.

Keeping a band run. Under a finished band run’s table, Keep… keeps it as a study: where the run started (the knobs you had set, the marked ones put back at their start), the marked knobs and their ranges, the bands, the balance, the engine and ground, and the answer with its before/after table. It opens the keep dialog charts use: copy the Python, or (on a local workbench) save it to the studies folder, trusted. A deck is kept by its file name. The kept run is then listed under Studies in the analysis picker of the tabs of its design, marked (jump to): picking it sets the knobs to its stored answer, marks its knobs over its ranges, sets its bands and balance, and shows its stored table. Run again from its start puts the knobs back where it began and runs it in its own form: the same mode (minimax, root or sequential) and each band’s own objective, feed and Z₀, whatever the session’s Z₀ is. Keeping that run keeps the form. Editing the bands in the gear menu leaves the kept form behind, and the run is the SWR minimax again. A run kept on another variant says to switch to that variant first.

A design whose network holds a tuner that tunes itself (tune_to) is refused when it is measured at the tuner’s tune frequency: the tuner presents its target on every solve there, so SWR, Resonance and Match Z₀ are already met and cannot respond to any knob. The message names the ways out — measure at another frequency, give the tuner fixed part values and mark them as knobs, or optimise something other than the match. The drag-hold below refuses the same case, with the same message.

The optimizer tunes for you and hands back an answer. This is the other half: you drag a knob and the marked knobs follow, holding the target while you explore.

The switch is in the same gear menu, under While dragging. Turn it on and drag any knob you have not marked — the marked ones move under your hand to keep the objective satisfied, and the chart and readout stay on target throughout. It costs one solve per drag tick, the same solve the app already owed you to redraw at the new knob position: the hold predicts where the marked knobs go and the tick’s own solve usually confirms it.

It needs the objective’s exact knob count — one marked knob for Resonance, two for Match Z₀ — because holding an exact target is a root problem. Where the optimizer would fall back to a general search, the hold cannot: it refuses, and the menu entry says why, with the count it found. SWR is refused for the same reason: it is a best compromise, with no target to hold.

Marked knobs still belong to the optimizer. Grab one by hand and the hold switches off, exactly as it switches an optimizer run off — the knob is yours again, and the dragged knob is never promoted into the search.

When the target runs out. The hold stops and says which of the two things happened, because they have different fixes. If a marked knob has reached the end of its Optimize range — a limit you set — it names the knob: “Resonance not held: length_factor is at the end of its optimize range — widen it to keep going”, naming both knobs when Match Z₀ has run both out. If instead there is no longer a target to hold at all, which happens where a resonance simply stops existing as the geometry changes, it says “Resonance not held: none within reach — drag back to recover it”. Match Z₀ says Match in place of Resonance.

Either way the marked knobs stay at their last good values rather than drifting, and the hold re-acquires the moment the target is reachable again — whether you carry on in the same direction or drag back the way you came.

What restarts it. Changing a marked knob’s Optimize range, or the measurement frequency, restarts the hold from a fresh root find rather than continuing: the surface it was tracking is no longer the surface it measured. Switching the mode off and on again does the same, which is also the way out of a hold you would rather not drag out of.

Loading a design pauses Optimize. Switching antenna or picking a variant turns Optimize off — its objective and marks belong to the design you left — and briefly says so. Switching antenna keeps that design’s marks (they’re remembered per design, so coming back restores them); loading a variant instead clears its marks, because their ranges were scaled to the values the variant just replaced. Re-mark the knobs and turn Optimize back on to resume.

A solver selector offers a few preset slots so you can flip between engines without re-entering options — e.g. a fast dense basis, an accelerated array engine, and the PyNEC reference. The available engines are the momwire bases (Sinusoidal, Sin-Galerkin, B-spline, Harrington (pulse)), the accelerators (H-matrix (ACA), Array-block), the optional PyNEC backend — see The solver & accuracy for what each is good at — and, on a machine with a licensed binary, NEC-5 (setup and terms): the slot appears exactly when the server resolves NEC5_EXE, which is why the hosted simulator never shows it while your own local instance can — and, the same way, NEC-2 (setup) when the server resolves NEC2_EXE to any NEC-2 console binary (nec2c, nec2++, the one 4nec2 ships), and NEC-4.2 (setup and terms) when it resolves NEC42_EXE to your own licensed NEC-4.2 binary. The list is served by the backend you’re pointed at, so a server without an optional engine simply doesn’t offer it, rather than offering a slot that fails on the first solve. NEC-5, NEC-2 and NEC-4.2 solves are one external run per request — right for A/B snapshot checks against momwire in the next slot, heavier than the in-process engines for live dragging.

There are three slots, A, B and C, to start. The + at the end of the tabs adds slot D, then E: a copy of the active slot, made active with its gear menu open, so “the same solver with one change” is one click and one edit. An added slot has remove slot in its gear menu; A, B and C stay, and only the last slot can go, the same rule as the ground slots. An added slot lasts the session unless you save as my defaults, which writes it as [slots.D] in settings.toml.

Under the tab strip in that gear menu, each engine states what it is made of in one line — basis, testing, kernel, quadrature, solve strategy, feed model — for instance B-spline · degree 2 · Galerkin · reduced kernel · converged quadrature · dense · point gap. It exists because three of the tabs differ in one word: B-spline, H-matrix (ACA) and Array-block are the same physics with a different solve strategy, and Sin-Galerkin is the Sinusoidal basis with Galerkin testing in place of point matching. Every control below the line rewrites its one segment in place, a segment the preset pins is marked (pinned), and PyNEC and NEC-5 say External engine, not described compositionally rather than inventing one. The vocabulary is momwire’s own, served with the roster — the browser carries no engine names.

The controls themselves are drawn from the served option catalogue rather than a hand-written panel per engine, so a knob appears exactly when the engine behind the tab takes it, with the server’s own bounds and captions. When the loaded design cannot be solved with a tab’s current settings — a buried deck under hmatrix’s ACA solve, say — the tab says so inline with momwire’s own refusal sentence and the issue it cites, and there is no Solve anyway: momwire would raise, so an override would buy an error dialog rather than a result.

On the buried designs, that gate now covers the external engines too, and each answer is measured rather than assumed. PyNEC is greyed out: NEC-2’s Sommerfeld-Norton ground is formulated for sources above the interface and has no below-interface case, and nec2++ does not refuse such a deck — it solves it as though the wire were still in air and returns a plausible number. Measured, moving a buried dipole from 5 cm above the interface to 5 cm below moves PyNEC’s impedance by 12 % where the physics moves it by a factor of ten. That is a worse failure than a refusal, so the tab refuses on PyNEC’s behalf. NEC-5 serves buried decks and is not gated; a conductor that stops exactly on the plane is refused, because the ground flag a card deck needs differs between a buried wire and one bonded to ground. Sinusoidal-Galerkin serves the buried classes since momwire 0.52.0 — wholly buried, mixed, and the connected screen where a buried radial meets the mast at a node in the plane — a second basis sharing no below-interface code with the default. The one spelling it greys, with momwire’s own sentence, is N coincident rises meeting at the node (the catalog’s retired bundle variant), which stays on the B-spline lane. Razor (2-point) serves the buried and crossing classes too, since momwire 0.62.0. Sinusoidal (point-matched) serves the wholly buried and mixed classes since momwire 0.66.0, and since 0.67.0 the catalog’s buried radial vertical too, where a near-normal rise meets its hub below the plane. An oblique crossing, or radials meeting the mast at the plane itself, stays greyed with momwire’s own sentence.

The solver’s gear menu also exposes segments per λ/4 (N), the mesh density: each wire gets N segments per quarter wavelength at the design frequency, so a long wire gets proportionally more. More segments = more accurate (up to convergence) but a larger, slower solve. A design loaded from a .nec or .ssn file keeps the segment counts it was written with, so N does not apply to it and the gear says so. See Segments & convergence for what N means and how to find “enough.”

Switching a slot to a different solver sets N to that solver’s own default, and a note under the knob says which one it came from — segments set to Razor (2-point)‘s default, 40. What N a solver needs to be converged is a property of its basis, so carrying a number across the swap would run the new engine at a mesh nobody measured for it. Changing the degree tab does the same, for the same reason: the degree is the basis. Type your own N and the note goes away; it stays gone until the next swap. The stock slots are unchanged (A at 15, B at 20, C at 21), a settings file’s saved n_per_wire still wins at startup, and the convergence sweep sets its own N per point regardless.

Beside wire radius, every momwire slot’s gear menu carries an extended kernel (EK) check — NEC’s extended thin-wire kernel, the EK card. It changes how the solve treats the wire’s radius on-axis: instead of collapsing the current to a filament, it integrates NEC’s O(a²) expansion over the tube. That only matters when a wire is fat relative to its own segments — the Δ/a ratio. Above Δ/a ≈ 10 it moves the impedance a fraction of a percent; below Δ/a ≈ 3 it moves it several percent, in NEC’s direction. It costs about 1.0–1.3× the ordinary solve.

Turn it on when you’re modelling thick elements (tubing, cages, a fat-wire imported deck) or cross-checking a NEC model whose deck carries an EK card — the flag is per slot, so the natural use is A against B: the same basis and mesh, one slot with the kernel and one without, and the readouts side by side. A slot running it is labelled +EK on its chip (e.g. B-spline d=2 +EK), so the pair stays tellable apart.

Every momwire basis serves it — the Galerkin family joined with momwire 0.27.0 (momwire#246/#287/#299: every ground model, bent and stepped geometry included). Some combinations are refused, and the check greys out and says which, in momwire’s words: the reasons are served with the roster from momwire’s own table of couplings, never retyped here.

  • K≥3 junction singular enrichment (the validation-only knob on the B-spline family) cannot run alongside it: the enrichment degrees of freedom bypass the very kernels the extended kernel corrects (momwire#249). The two grey each other out, so you can always back out of either.
  • A buried wire keeps the standard kernel. On the B-spline tab (since momwire 0.78, momwire#1170), razor-2p (since momwire 0.82, momwire#1614) and Sin-Galerkin (since momwire 0.83, momwire#1626) the extended kernel acts on the wires above the ground and the buried wires keep the standard one, which the solve’s advisory says; the point-matched Sinusoidal tab does not serve it beside a buried wire at all (momwire#1636).

A deck read as NEC-4 or NEC-5 opens with the check on. NEC-5’s kernel behaves as the extended one, and NEC-4’s single thin-wire model is equivalent to it, so a deck in either dialect — detected, or chosen with Read as — is solved the way its own program solved it; the gear says so under the check. Turning it off is honoured, and changing Read as to NEC-2 turns it back off unless you set it yourself (a NEC-2 deck’s EK card is the card’s word, as before). A NEC-4 deck’s EK card is ignored, as NEC-4 ignores it, and the import note says so; a VC card is noted as not modelled. On a solver that cannot run the kernel here — the reduced-only Pulse tab, a buried wire on the Sinusoidal tab — the default quietly stays off and the solve’s advisory names the reason, rather than refusing a solve you never asked to be extended.

PyNEC has no such check — the toggle drives momwire’s kernel. Changing a slot’s solver resets the check along with that solver’s other options, so an armed kernel never rides silently onto a basis you just switched to.

Sin-Galerkin and the B-spline family (B-spline, H-matrix, Array-block) carry a feed model control — how the source gap itself is modelled:

  • NEC-compatible (the default) — NEC’s segment-wide gap. The readout reproduces NEC/EZNEC behaviour, including the familiar reactance drift as the mesh refines. Use it when you’re cross-checking against NEC results.
  • Converged — a zero-width (point) gap instead: the impedance converges to the B-spline answer, and the port admittance is exactly reciprocal.

On near-open, high-Q feeds the choice is worth a lot, and the gear menu says so: those designs (wire.lazy_h, wire.vbeam and their class) show a hint recommending Converged, which removes two to three orders of magnitude of the apparent disagreement between solver bases (momwire#213). What it does not do is reduce the mesh such a design needs — budget fine segments either way; see the near-open feed. A Sin-Galerkin slot running the non-default setting carries a suffix on its chip, so two such slots stay tellable apart at a glance — and which value is the deviation flipped with momwire#654. The point gap is the solver’s default now, so a plain Sin-Galerkin chip is the converged one and the chip reading Sin-Galerkin (NEC gap) is the slot asking for NEC’s source. The B-spline family defaults to the point gap too; its chip carries the degree instead, and the composition line is where its feed model reads.

The plain Sinusoidal solver does not carry the control: it cannot express a zero-width gap under point matching (momwire#212), which is why its composition line says segment gap where Sin-Galerkin’s says point gap — the one difference that follows from the other.

Everything a workbench tab computes — the live solve, the frequency sweep, the convergence ladder, the norm check, the NEC pattern — runs through a single per-session solve lane on the server, one computation at a time, with the live solve always first in line. You’ll notice it as steadiness on heavy designs: background sweeps never compete with the solve that’s drawing the heatmap, and a knob turn preempts stale background work at the solver’s next internal checkpoint (milliseconds to one sweep point, not the rest of the batch). Abandoning the tab mid-sweep stops the computation the same way.

When the selected solver is a poor match for the design (a dense engine on a benchmark-class mesh), the workbench warns and withholds the solve; the server holds background batches to the same answer, so a sweep of minutes-per-point solves only runs once you’ve clicked Solve anyway.

Real antennas hang over real ground, so the workbench starts there: the ground plane checkbox is on by default, solved with the Sommerfeld method over average soil, with free space one click away. Sommerfeld has been the default since v0.95.0: across the catalog it costs a few milliseconds per knob drag at the median, while the reflection-coefficient model is off by 10 % at the median for antennas whose lowest point is under 0.05 wavelength. The command line uses the same default ground.

The grounds work like the solver slots: the ground slot tabs under the solver slots hold one whole ground each, and one click switches the ground every solve, sweep and chart uses. Each slot’s settings (the ground plane, its type and method, the soil or terrain, all described below) sit behind the ⚙ on its tab, which edits that slot whether or not it is the active one; the tab itself shows a one-line summary. Notes about the active slot (a ground taken from the file, a buried design’s Sommerfeld requirement) show on a line under the tabs. The ground slots are lettered X, Y and Z, their own family beside the solver slots’ A, B and C. The three stock slots are:

  • X: the design’s own ground, or the session default — the checkbox, type, method and soil described below, where the workbench starts;
  • Y: free space;
  • Z: the reflection-coefficient model over average soil — the quick approximation, for a comparison or a large array where speed matters more than the last few percent.

NEC-5 has no reflection-coefficient model (its only finite ground is a full Sommerfeld solution), so on a NEC-5 solver slot a reflection-coefficient ground slot, stock slot Z included, is refused rather than quietly solved as Sommerfeld, as --engine nec5 --ground finite-fast is on the command line. Its tab carries ⊘, the line naming the solving pair says why, and one click switches to the first Sommerfeld ground slot. A chart that crosses NEC-5 with that ground names the cell as refused in its legend.

Slot X is active when the workbench starts, so if you never click another slot, the workbench behaves as it did before ground slots. A design that brings its own ground, a deck’s GE / GN cards or a buried design’s Sommerfeld requirement (below), puts it in slot X when it loads and makes slot X active, so the design opens on the ground its file models. Slots Y and Z keep their settings across a design switch. A design with no ground of its own leaves the active slot where it was. It also leaves slot X alone, unless slot X still holds the previous design’s ground untouched: then the session default comes back. The stock set and any extra slots come from settings.toml.

The + at the end of the ground slot tabs adds a slot: a copy of the active one, under the next letter (U, then V, W, R, …), made active with its settings open, so “the same ground with one change” is one click and one edit. An added slot has remove slot in its settings. The stock X, Y and Z stay, and only the last slot can go, because the slots run without gaps and charts name them by letter; the settings of an earlier added slot say which to remove first. An added slot lasts the session unless you save as my defaults.

The over-ground picture — takeoff angle, the ground-lobed elevation pattern, the shifted feed-point impedance — is usually the one your design decisions actually depend on.

The selector describes what the ground is, independent of solver:

  • finite — real earth, the default. Its two constants are yours to set: εr (relative permittivity, 1–81) and σ (conductivity, S/m, on a log scale from 1e-4 to 5), with named presets — very poor, poor, average, good, very good, fresh water, salt water, each a row of the ARRL Antenna Book’s ground-constants table (25th edition, Table 3.1) — that fill both at once. The active preset is read off the two numbers, so dragging a knob off a preset simply deselects it; the default is the average row (εr 13, σ 0.005). The soil travels with every solve — sweeps, patterns, cuts, near fields and the exported NEC deck’s GN 2 card — and a changed soil is a new sweep, not a cache hit. Each preset is a single (εr, σ) pair, while real soil disperses with frequency: on a buried deck above about 14 MHz that is worth tens of percent in impedance, and the solve says so in an advisory (antennaknobs#1188 tracks making the presets frequency-dependent). An antenna clear of the ground is barely affected;
  • PEC — a perfect reflector, mainly for apples-to-apples engine comparisons; or
  • terrain — a faceted height profile around the site (levee/cliff presets), for antennas where the ground is not flat. See Faceted terrain below.

Every solver then offers the same method sub-choice — full Sommerfeld/Norton (most accurate, the reference below ~0.1λ heights) vs. the reflection-coefficient approximation (much faster per solve, and fine above ~0.1λ; Sommerfeld’s first solve of a session builds an interpolation grid, but since momwire 0.15.0 that grid is reused across a band’s frequencies — a sweep pays a few fills on its first pass, then every repeat sweep and knob-drag tick runs warm in tens of milliseconds). Since momwire 0.8.0 the choice is uniform: every momwire solver honours both models (Sommerfeld validated against an independent NEC-2 implementation down to 0.02λ — within ~2.4 Ω on the B-spline bases, ~0.1 Ω on the sinusoidal basis, and the accelerators solve it on their fast paths), and PyNEC honours both natively — with one caveat: PyNEC’s (nec2++‘s) Sommerfeld solve is known-unreliable for conductors within 0.1λ of the ground that don’t touch it (low radials, half-squares, slopers — the engine warns; use a momwire engine as the reference in that configuration). The far-field pattern uses the real εr/σ on every basis. Whatever runs, the solve readout’s ground row reports the model that was actually used, and over a finite ground the norm check readout becomes a radiated percentage — the share of your input power that actually leaves as sky wave. The rest is absorbed power, not error.

The third method, MININEC, is a different ground rather than a cheaper solve of the same one; see The MININEC-type ground below.

One class of designs picks its own method: the buried-wire designs (verticals.buried_radial_vertical, verticals.elevated_buried_counterpoise, specialty.buried_dipole) declare that they require the Sommerfeld model — a conductor below the surface only exists under a Sommerfeld half-space, and the reflection-coefficient approximation refuses it by name. Loading one auto-selects finite ground with the Sommerfeld method in ground slot X and notes it on the line under the ground-slot tabs; you can still flip the radio back in the slot’s ⚙, but the solver’s refusal is the answer you’ll get.

MININEC is EZNEC’s “Real, MININEC type” ground. The currents and the feed-point impedance are the ones a perfect ground gives. Only the far field sees the soil: the pattern reflects off the εr and σ you set, just as it does over a finite ground. On AC6LA’s EZNEC 4-square it moves the pattern by 5.4 dB: 5.40 dBi at 25° elevation over the MININEC-type ground, 10.83 dBi on the horizon over the perfect ground. Both grounds give the same impedance, because this ground’s currents are the perfect ground’s.

It is valid only for antennas well clear of the ground. The 4nec2 manual puts the limit at no horizontal wire below about 0.2 λ. It suits verticals and elevated arrays, where the MININEC heritage came from; for low horizontal wires, radials near the surface, or anything buried, use the Sommerfeld method.

Over this ground the readout’s radiated (incl. ground) row shows a dash. That row is the pattern’s power over the input power, and here the two come from different grounds: the input power from the perfect one, the pattern from the soil. So the ratio is not a fraction of anything. A low wire whose perfect-ground image cancels it can read over 100 %. Pick Sommerfeld or the fast reflection-coefficient method for that number.

Every engine serves it in its own terms:

  • momwire solves over the PEC image and reflects the far field off the soil;
  • NEC-5 is sent GE 1 and a bare GD 0 0 0 0 εr σ 1 0 — EZNEC’s own NEC-5 spelling;
  • NEC-2 (the NEC-2 tab and PyNEC) is sent GN 1 plus a GD circular cliff at radius 0 and height 0, and asked for its pattern with RP 3, the one mode that reads that medium everywhere. This is how 4nec2 runs its GN 3.

The NEC rp overlay therefore reflects off the same soil as the app’s own cut. A deck that carries this ground opens with it selected; see NEC import.

The terrain ground type models a site whose ground is not an infinite flat plane: a piecewise-linear height profile per azimuth sector, each facet carrying its own medium. Three presets cover the common cases, with every number a live knob:

  • levee — a raised crest with two sloped sides: crest width, slope, drop to water on the water bearing side, drop to land opposite. Crest and slopes are earth; the water medium starts at the water-side toe.
  • cliff — flat earth out to the cliff edge, then a sheer drop to water; arc < 360° restricts the cliff to a sector facing the bearing.
  • hillside — a flat bench on a slope: the ground rises at the uphill slope on one side and falls at the downhill slope on the other (facing the downhill bearing), all earth. There is no bottom to reference and none is needed — the slope itself is the reflector, so the effective height grows continuously as the elevation drops. Below the uphill slope angle the real sky is shadowed by the hill — which the specular composer cannot express and the diffracted one can; see Two fields below.

Media are fixed for these three (water εr=80 σ=0.001, land and crest εr=13 σ=0.005 — shown read-only in the panel).

Your own terrain. On a local workbench, a file in your designs folder (~/.antennaknobs/designs) can add a terrain to this list: its own sliders, its own profile and its own media. One terrain per file; the file’s name is the name in the list, and it cannot be levee, cliff or hillside. The file binds a TerrainPreset (from antennaknobs.terrain) to the name TERRAIN and defines no Builder. It is offered for every design, runs under the same permission as a design file, and a file with an error in it is shown in the “designs that failed to load” panel. The list of terrains is read when the page loads, so reload the page after adding or renaming one. The worked example and the rules are in Writing designs with Claude Code; TERRAIN_TEMPLATE.py in the designs folder is a copy to start from.

How it solves: the impedance/current solution runs over a flat Sommerfeld ground with the crest medium (near-field ground interaction is crest-local; soil and water cannot be wire-gridded), and the far field reflects each ray off the facet its specular point lands on — tilted incidence, that facet’s Fresnel coefficients, and the facet’s height folded into the reflected-path phase, which is what lets a modest mast act electrically tall toward a drop-off. A single flat facet reproduces the plain finite ground exactly, and the solve readout’s ground row reports terrain (crest Somm.). The worked example — Antennas on a levee — shows what it changes on a real site and where each flat model fails.

Two fields, and the chart says which. The description above is the specular composer: geometric optics, lobe positions and direction-dependent asymmetry. There is a second one that adds what a profile with an edge in it actually implies — shadowing, the source imaged across each facet’s own plane rather than a horizontal mirror, and wedge diffraction at the facet breaks — and where the two disagree, it is the one to believe.

It is too slow to draw while you drag: about a second per pattern against 17 ms, and paid per direction, so no warm-up helps. So the polar charts draw the specular field while a knob is moving and compose the diffracted one a moment after it settles. The bottom-left corner of each chart says which you are looking at — specular while dragging or with diffraction — and it reads the trace that is actually drawn, not the request that fetched it.

Nothing else on the page changes: the impedance, the SWR, the sweep and the efficiency ledger are all the flat crest-medium solve either way, so the readout numbers do not jump when the lobe does. What moves is the far field, and it moves most where the specular model was least believable. On the hillside preset with its uphill slope set to 45°, the uphill band below that angle collapses by 4 to 36 dB: geometric optics was reporting a healthy +5.7 dBi straight through 45° of hillside, having no notion of the hill being in the way. Just above the slope angle, where the sky is clear, the same cut gets up to 6 dB brighter — a different mechanism, the reflection imaged across the facet’s own tilted plane rather than a horizontal mirror. Peak gain moves up, by +0.4 to +2.6 dB across the three presets at their defaults.

Two limits worth knowing. Near the zenith both composers are computing a number close to zero for most antennas, and they reach it by different arithmetic — a vertical’s zenith null reads −319 dBi one way and −322 the other, which is agreement, not a 3 dB discrepancy. And a mast standing right on a crest edge has structure in the edge’s near zone, where a ray model of that edge is the weakest thing in the calculation; move the mast a metre back from the break and the question goes away.

Engine notes: momwire applies the facet far field natively. PyNEC runs a hybrid — NEC-2 has no facet model, so NEC solves the currents over the crest-medium Sommerfeld ground (exactly what the terrain recipe feeds the current solve anyway) and the server applies the facet reflection to those currents; the two engines agree to engine tolerance. The NEC rp overlay switch (on the NEC engines’ lanes) is greyed out over terrain, because NEC’s own rp_card pattern is flat-ground-only and would silently disagree with the facet traces. Download .nec exports the crest medium as a flat GN card — a NEC deck cannot carry the facets.

A wire that ends at exactly z = 0 connects to the ground plane — the return path a design like wire.terminated_longwire (fed and terminated against ground through its vertical legs) depends on. NEC-2 makes that connection physical over PEC ground only (finite-ground contact is a NEC-4 feature), so solve ground-connected designs with PEC selected; elevated designs are unaffected.

Designs can add their own rows to the solve readout (v0.43.0): a builder that computes physical diagnostics — the catenary inverted vee reports its rigging tension in N and lbf, sag, and derived rope cut length — sends them as self-describing rows the readout renders generically, so a new design idea (including a user design in ~/.antennaknobs/designs/) gets its numbers on screen with no frontend change.

A ⟳ button beside the design picker is always there. When the design you’re iterating is a user design — editor in one window, workbench in the other — it re-reads that file and re-solves in place: your tuned knob values always survive the reload; a parameter the edited file just grew appears with the file’s default, and the Files view follows it: the source, the SimNEC circuit and the engine’s deck are the edited file’s. One click instead of a page reload per edit cycle. With anything else loaded, the same button instead rescans ~/.antennaknobs/designs for new or changed files, so a design dropped into an empty folder shows up in the picker without a page reload.

Designs with a lossy feed network — a real coax or ladder-line run, a matching network with a finite-Q coil, a lossy balun, a terminating resistor — get a power budget table in the solve readout: one row per network branch with the fraction of the source’s input power it dissipates, plus an antenna (accepted) row for what actually reaches the wires. The rows come straight from the MNA network solve (each branch current is an explicit unknown, so the watts are read off the solution, not modelled separately — see Station modelling for the network vocabulary). Rows from a station box are grouped under a header naming its instance and indented beneath it — one indent step per nesting level — and a design may retitle rows for display via ui_params["budget_labels"]. The same accounting drives the reported radiation efficiency: every dissipative branch counts, including resistive coupling and matching elements, not just explicit Loads. Gain is normalised by input power, so network loss already shows up in dBi; the budget tells you where it went. Lossless networks hide the branch rows.

Below the budget sits the honest bottom line: a radiated (incl. ground) row — the fraction of input power that leaves as far-field radiation after the ground has taken its share, the third of the three efficiency ledgers. It comes from the dwell-triggered norm check pattern integral, so it greys to — the moment any knob moves and fills in once you settle — never costing the live drag path anything. Expect a shock the first time: a “95% efficient” portable vertical over average ground radiates ~30%; a 7 m-high inverted vee on 20 m about 70%. Over PEC ground or free space (nothing to absorb) it collapses back onto the structural efficiency.

Designs that declare a real wire material (a wire_type knob over the WIRES catalog, e.g. dipoles.pota_invvee) additionally get a wire loss (I²R) row: the skin-effect power burned in the antenna conductor itself, read from the solve’s current distribution. It counts toward the reported efficiency the same way the network rows do, and the Info pane shows the matching wire length and wire weight rows.

The Schematic view draws the design’s feed network — feedline, tuner, balun, and the port the source sits on — as a circuit chain, the same drawing the CLI writes with schematic. The server renders it to themed SVG on every knob change; no solve is involved, so component labels (lengths, C and L values, turns ratios) always match the knobs on screen.

  • The power budget is folded into the picture. Each box carries the fraction of input power it burns, drawn where the loss happens, so the budget rows and the circuit are one artifact rather than two. Elements that dissipate nothing — an ideal TL, a bypass() — appear here and nowhere else.
  • Balanced sections are drawn as two conductors. Past a floating balun’s secondary or along a balanced line the return rides the partner wire, so there is a second rail, the isolation barrier crosses the balun, and no ground symbol appears beyond it.
  • What isn’t a chain isn’t faked. A trap in a dipole leg or a curtain’s risers draw beneath the antenna labelled with the nodes they bridge; a parallel second antenna is noted rather than drawn in series; a 16-source array summarizes its feeds instead of drawing sixteen identical branches.
  • Roughly two-thirds of the catalog is a bare antenna with no feed circuit at all. Those say so — “No feed circuit — this design is the bare antenna” — instead of showing an empty box.

A local install needs the optional extra for this view: pip install 'antennaknobs[schematic]'.

Every number in the readout and every impedance chart is referenced to one port. For a bare antenna that’s the feedpoint; for a station design whose chain runs from the rig through a tuner and feedline, it’s the rig end — what a sweep taken in the shack sees.

When a design’s chain has more than one named port, the readout grows a plane selector, and the picked plane is marked on the schematic with the disconnected part of the chain dimmed. A plane inside a box is drawn there: a manual L tuner’s inside tuner (L–C) plane sits between its capacitor and coil, with only the part upstream of it dimmed, and the rig’s own terminals are marked when you pick the rig. Picking a plane re-solves the design as a VNA clipped on at that port would see it: the chain upstream of the pick is unscrewed, not merely re-driven — leaving a length of open-ended coax dangling in parallel with the antenna would be neither what you asked for nor anything you could measure. Attachments that hang off the structure (traps, stubs wired into the antenna) are upstream of nothing, so they always stay.

A design can name its planes for what they are: the doublet into a remote tuner (wire.doublet_remote_tuner) lists rig (tuner input), inside tuner (L–C) and tuner output / balun input, source to antenna.

Tuning on the Smith chart. A design can also mark a target circle at a plane: the circle the remaining tuner part moves the point along, through the chart’s centre. At the doublet tuner’s inside tuner (L–C) plane it is R = 50 Ω (r = 1) when the capacitor sits across the balun, since the series coil left to turn moves the point along constant resistance, and G = 20 mS (g = 1) when it sits across the rig. That is the graphical L-network tune: turn the part nearest the antenna until the point lands on the circle (Sweep this knob… traces its whole path), then pick the rig plane and turn the other part to slide along the circle to the centre. The Y button on the Smith chart adds the admittance grid (constant-G circles and constant-B arcs, dashed) that the g = 1 circle belongs to; the browser remembers the choice.

The frequency sweep, convergence ladder, and chart titles all follow the pick, so a measurement overlay is compared at the plane you actually calibrated at. The CLI has the same control on fit via --plane.

To check that your chosen N is converged — i.e. adding more segments no longer moves the impedance — run a convergence sweep. It re-solves the current antenna across a ladder of densities (N = 8, 12, 17, 24, 34, 48, 68 segments per λ/4) on the active slot’s engine, and extrapolates Z to N → ∞ (the diamond Z∞, by the same estimator as the command line’s density study, against the segment count each rung actually meshed), so you can see where the curve flattens out. Basics: Segments & convergence; the full method (ladders, cross-basis validation with a second solver slot, and what a non-settling curve is telling you): How many segments?.

It is drawn two ways, from one sweep:

  • As R and X against N in the Z vs parameter view, with the parameter set to density. This is the chart to read convergence from: R and X each on their own axis, a log N axis, and any ladder you like (10 … 500 in 20 steps, say).
  • As a trail on the Smith chart: the same chart’s Smith view (the param sweep switch under the old Smith view drew it): a ring at the coarsest N, a disc at the finest, the end values beside them, and Z∞ as a diamond. A knob sweep’s trail is labelled with its values.

The sweep runs only while the chart is on screen (v0.43.0’s view-residency gating).

A knob sweep on the sweep chart (its R / X view) plots the feed impedance against one parameter, the way AC6LA’s SimNEC convergence charts do: R in red on the left axis, X in blue on the right, each on its own range, with a dot at every point and the values boxed at both ends. On a phone the curves are drawn as lines, with no dots, and the boxes start hidden, since they would cover a small plot: values at the chart’s lower right shows or hides them, for the session. When a chart draws three or more curves the dots shrink, and the boxes appear on the main view only, never in the rail’s thumbnails.

The header at the top of the chart picks what to sweep:

  • sweep — density (N per λ/4), or any of the design’s numeric knobs. Knobs that set a frequency are not offered: the measurement frequency stays where it is for the whole sweep.
  • from / to / points — the range and how many solves. A knob starts at its own slider range and 11 points; density starts at the convergence ladder above. ↺ puts the parameter’s default back, or, while an analysis is picked, that analysis’s own range.
  • log spacing — points spaced by a fixed ratio (SimNEC’s logStep) instead of a fixed step. A whole-number parameter (density, a segment count, a number of radials) is rounded to whole values and repeats are dropped, so 10 … 500 in 20 steps solves 20 distinct segment counts. The points field’s tooltip lists the values actually solved.

Right-click a knob and pick Sweep this knob… to jump straight here with that knob over its range. To reach the density ladder, pick the convergence analysis.

auto re-run is the chart’s own switch for re-running after a change. With it on, the chart re-sweeps once the knobs have stopped moving (half a second). With it off, a change leaves the curve dimmed as stale, and the button offers run · re-run?. For a knob or density sweep it starts off (convergence_sweep in settings.toml turns it on), because a knob sweep rebuilds the design at every point: it runs when you ask, through Sweep this knob…, an analysis pick, Sweep a knob, a knob chosen in the sweep list (choosing what to sweep is a pick, and a pick runs), the header’s run, or an edit to its own from, to, points or spacing. Reloading the design or coming back to the view does not start one. run always runs the sweep again, including a finished one. Switching to another design or variant puts the chart back on the design’s own frequency sweep, keeping the view you had it on and a switch you flipped. While a sweep runs, the button shows its progress and stops it; the points so far stay, marked partial.

On the chart:

  • A dashed guide marks the knob’s current value, labelled at its top (“N = 40 (now)”, “height = 9.5 (now)”), with the live solve’s R and X on it. Turning the swept knob slides the guide along the curve; it does not re-run the sweep, since every point sets that knob itself. Any other change re-runs a density sweep, and marks a knob sweep stale.
  • Hover (or tap) reads the nearest point: its value, R and X.
  • Click the R or X axis for its range: Auto fits that trace, or type a min and max — for instance, to put two engines’ charts on the same scale.
  • lin x / log x (bottom right) switches the x axis. It follows the spacing until you choose.
  • On a density sweep, Z∞ is drawn as a dotted line on each axis and read out at the top. The readout ends with · p 0.98 when the ladder is asymptotic and the observed order p was used, or · rough when it is not yet (first order assumed, and the dotted lines are drawn fainter). When the feed mesh is the reason, the readout names the step, e.g. · rough: fed segment 100.0→33.3 mm at N 65→95. See Where the extrapolated value comes from.

The sweep solves on the active slot’s engine. Tick more slots on the chart to draw another engine’s curve beside it (see Comparing engines and grounds).

The sweep chart is the workbench’s analysis chart. It has its own analysis list, run and auto re-run switch on the chart, and it draws whatever it picked in place, whether that is a knob sweep or a frequency sweep. The list holds the sweeps the design names as worth running: the same list as antennaknobs analyze --list (see Analyses). Picking a frequency analysis or a pattern runs it; picking a knob sweep, a held one or the density ladder selects it and waits for run, as [workbench.run_on_pick] in settings.toml can change. A knob analysis sets the header’s parameter, range, points and spacing. On the inverted V, height sweeps base from 2 to 20 m in 37 points and convergence runs the density ladder, the same values the command line solves. When the chart shows a knob sweep and the design has an analysis that sweeps exactly that, the list names it.

An analysis that compares engines or grounds draws one curve per slot that holds one: height is crossed over three grounds, and picking it ticks the ground slots that hold them, naming any that none does in the legend (see Comparing engines and grounds). A deck’s own segment knob runs as a knob sweep over the density ladder, with Z∞ on the command line only for now.

A frequency analysis, such as band SWR, draws in the same chart, on the analysis’s first view: SWR, S11, Smith, R / X or Table. The view list offers the analysis’s views first, then the other frequency views, R / X and Table included whether or not the analysis lists them. A knob analysis opens on the view on screen when it lists that view, else on its first, so an analysis that lists only an.Table() opens on its table. The chart runs its frequency sweep over the analysis’s range, from the rule the command line uses (see Frequency analyses). A deck’s own sweep or a design’s declared range sweeps the design’s range, and on a band-policy design that follows the band you are on. from / to on the chart edit its range and ↺ puts the analysis’s back. None of this moves the measurement dial. The SWR scale is the analysis’s (an.Swr(scale="rho") is EZNEC’s), and the threshold line and the bandwidth readout move to its an.Ref(swr=...), for this chart only: a scale you pick on the chart’s axis is remembered in the browser as the VSWR chart’s was, an analysis’s is not. auto re-run starts on (freq_sweep in settings.toml can turn it off), so the curve follows the knobs after the usual half second. Either way the marker at the measurement frequency, the live solve, moves with every knob.

A frequency analysis given explicit frequencies, an.Sweep(an.FREQUENCY, values=(...)), sweeps exactly those frequencies, as the command line does, with no adaptive points added between them; the header says 3 values beside from / to, and its tooltip lists them. Editing from or to makes it a range between the new ends with as many points as the list had. The analysis stays picked and the list reads (edited). ↺ puts the list back.

What you pick, the switch, the range and the scale last for the session. Only the design’s .py is remembered: to keep a chart’s setup, put it in the design’s build_analyses().

What the workbench cannot run yet is listed greyed out, with the reason as its tooltip: a map with a cross, or with a frequency or density axis. Crosses over planes, designs, states and families draw (see Planes, designs, states and families).

A note the chart has for what it draws (a view it leaves out, or a word on cost) sits on the one line under the header as ⓘ and its first words; tap it to read the whole note over the chart. That line is there with a note or without one, so a note never takes height from the plot.

analyses as Python, under the header, shows every analysis as the Python that makes it, with copy, ready to paste into a design’s build_analyses().

Measured overlay — your VNA on the Smith chart

Section titled “Measured overlay — your VNA on the Smith chart”

On the sweep chart’s Smith view, measured .s1p… in the chart’s header (in the Tools menu on a phone) loads a one-port Touchstone file — a NanoVNA export, or any VNA’s — and draws the antenna you actually measured as a dashed violet locus against the modeled one. It is the “did my model match reality?” view, and the same overlay the CLI draws with --measured.

  • Capture is a local CLI step. python -m antennaknobs capture --out bench.s1p --start 27 --stop 30 sweeps an attached NanoVNA into a file you then load here. The server never opens a serial port — its ports are not yours when the backend runs elsewhere.
  • Your file stays on your machine. The browser reads it and posts the text to be parsed; nothing is stored server-side. That also means it works unchanged when the backend runs somewhere else while the VNA is plugged in here.
  • Reference impedance is handled. The measurement travels as impedance and is converted at the chart’s own reference, so a 75 Ω calibration lands correctly on a 50 Ω chart.
  • Bands are clipped to the frequency sweep so both loci cover the same frequencies; the label under the chart reports the span actually drawn, and says (clipped) when the measurement reaches past the swept band. A measurement with no overlap at all says so rather than silently drawing nothing — turn the freq sweep onto that band, or move the measurement frequency there.
  • Calibrate at the plane you’re comparing. The chart shows the feedpoint, so a measurement taken at the feedpoint is the like-for-like one; a shack-end sweep includes feedline the model may not have.

Expect some irreducible disagreement — common-mode current on a real feedline perturbs a measurement in ways a differential model doesn’t reproduce. A structured gap (the same offset across the band) usually points at something physical: line length, ground, a connector.

The sidebar is a notebook: the tabs across its top (D1, D2, …) are independent design sessions, each with its own geometry, knob values, design and measurement frequency, ground setting, solver slot, and results. Click + to open a new session — it starts fresh and solves on its own — switch by clicking a tab, and close one with the ✕ (the last remaining tab can’t be closed).

  • Sessions are fully independent: changing a knob, the solver, or the ground model in one leaves every other session exactly as you left it.
  • Hover a tab for its summary — design, solver, segment count, and ground model — e.g. dipoles.invvee · B-spline d=2 N=15 · reflection-coef ground.
  • Switching to a session re-solves it, which is near-instant because the server caches recent solves (see How a knob turn works).
  • The light/dark theme and pinned patterns are shared across all sessions; everything else is per-session.

Open the same design in two tabs to compare tunings, or load two different antennas — then pair it with pattern pinning to overlay one session’s radiation pattern on another’s.

The far-field views are calibrated azimuth and elevation polar cuts — the numbers-first presentation you read gain, takeoff angle, and beamwidth straight off. (The solver computes the full sphere on every basis; the cuts are how the workbench chooses to show it.)

A combined Az + El view, the overlay EZNEC calls its combined 2D plot, draws both cuts on one polar plot. Colour says which cut is which (azimuth in the live pattern’s hue, elevation in its own), and pinned patterns keep their cut’s colour, dashed. The azimuth trace reads from 0° at +x; the elevation trace from 0° at the cut bearing’s horizon to 90° at the zenith. Click a row in the legend to focus that design and dim the rest; show and hide stay in the compare table beneath. It is off by default: add it from the view picker.

On the azimuth and elevation pattern views a 📌 Pin pattern button (top-left of the plot) freezes the current radiation pattern as a dimmed, dashed ghost overlaid on the live one. Pin it, then change knobs — or switch to a completely different design, or another session tab — and the live lobe redraws over the pinned ghost so you can see the effect directly.

  • Pins are shared across every design session: pin in one tab and the ghost (and its table row) is there in all the others, so you can overlay one antenna’s pattern on another’s — a Yagi’s beam against a dipole’s figure-8, say — not just two tunings of the same design. A pin is a frozen snapshot: it survives switching designs and even closing the tab that made it.
  • Each pinned trace recomputes for whichever cut (azimuth or elevation) and cut-angle you’re viewing, so it always shares the live plot’s geometry.
  • A compare table appears alongside with a row per pattern — peak gain (dBi), takeoff angle, front-to-back, −3 dB azimuth beamwidth, and RDF (the receiving directivity factor: peak gain over the pattern’s average gain, normalised by the whole sphere; see the CLI reference) — so the overlaid shapes come with the numbers that matter.
  • Show or hide a pin without losing it: click a pinned row’s colored swatch-and-name in the compare table. The ghost disappears from the plot and the row dims, but its metrics stay readable for the side-by-side numbers; click the name again to bring the ghost back in the same color. Handy when several pins crowd the plot and you want to declutter one at a time.
  • Removing pins: the ✕ on a row deletes that pin (everywhere — pins are shared); clear above the table removes them all. The – button minimizes the table to a compact n pinned chip — ghosts stay on the plot — and clicking the chip reopens it.

The readout at the top right of a pattern view says where this cut’s own maximum is: peak +6.8 dBi @ el 0° on an elevation cut (reading up and over, 0–180°, like EZNEC’s “Slice Max Gain”), or @ az on an azimuth cut. That costs nothing; it is the peak of the trace on screen.

The whole pattern’s maximum needs a full far-field solve, so it is found on request: find 3-D max fetches it for the design as it stands, and shows 3-D max … @ az …, el …. Click that to aim both cuts through it: the elevation cut turns to its bearing and the azimuth cut rises to its elevation, and the slice peak becomes the 3-D maximum. A re-solve offers the button again rather than keep an answer for a design that has changed. With a pattern pinned, the compare table already fetches the maximum, so it appears without asking. The × beside it hides it until you find it again. The search samples elevations 0–90° over ground (0–180° in free space, so a lobe pointing into the lower hemisphere is not missed), then refines the peak it finds to 0.01° — a lobe sitting exactly on the horizon reads there, not one degree off, and the front-to-back and beamwidth figures are measured through that refined peak (#1669).

The plot’s captions (which cut this is, the peak, the terrain field, the NEC rp legend) sit in the control stacks rather than printed in the plot’s corners, so no control covers them on a small or zoomed display.

On the azimuth and elevation pattern views a norm check checkbox (top-right of the plot) draws a second, dotted radiation curve over the solid live one — a built-in “should I trust this pattern?” gauge.

The two curves are the same pattern normalised two different ways. The solid line scales it by the input power the feed delivers (the circuit side — what the impedance solve says went in). The dotted line renormalises by the pattern’s own integrated radiated power (the field side — what the far-field integral says came back out). For a lossless PEC antenna those two must be equal, so:

  • The curves overlap ⇒ the solve conserves power: the mesh is fine enough that the currents and the radiated field agree.
  • A visible gap ⇒ discretisation error — too few segments (or too stiff a basis) for this geometry. Add segments and the gap closes.

Beside the checkbox a Δ readout gives that gap as one number in decibels — 0 dB is perfect power balance. A few tenths of a dB is typical and harmless; a large value means the pattern and its gain figures should not be trusted until you refine the mesh. This is exactly NEC’s classic “average gain” sanity check, which most tools make you compute by hand. It’s cheap (a closed-form integral for free space and PEC ground, a small reference-grid quadrature over finite ground, either way evaluated once the knob settles), so it’s on by default — uncheck it to hide the overlay and the readout.

Over a finite ground the gap is not supposed to be zero — it’s physics, so the readout switches to its honest form: radiated NN%, the fraction of input power that actually leaves as far-field radiation. The pattern integral only counts power that leaves upward — what the lossy ground absorbs never comes back — so the shortfall from 100% is structural loss plus real ground absorption, exactly like NEC’s average-gain value over real ground (hover the readout for the raw Δ dB). The same number fills the power budget’s radiated (incl. ground) row, so it follows you to every view. It’s still a mesh check: what should be small is how much the reading moves as you add segments, and switching the ground to PEC (or off) should send it back toward ~100% (Δ 0 dB).

SWR, the Smith chart, the readout and the optimizer’s SWR and Match Z₀ all measure against one reference impedance, Z₀: the design’s own (a SimNEC Generator’s Zo, or what the design file names), else 50 Ω. To change it, click the Smith chart’s Z₀ = … corner or the readout’s SWR (… Ω) line, or open the gear menu (⚙, top of the sidebar): it is the first thing in its this design section. Each design tab keeps its own, and reset returns to the design’s. The optimizer’s menu shows the value it is tuning against, with change… leading to the same field.

The gear menu is split by a line: above it, this design (the reference impedance, Copy params, the deck downloads); below it, all designs, the display switches every tab shares.

The gear menu (⚙, top of the sidebar) has Copy params (Python), which copies the current knob values to the clipboard as a paste-ready default_params = {...} block (a <variant>_params block when you’re on a named variant). Drop it straight into a design file to bake in whatever you dialed in — no more transcribing values off the screen by hand.

The same gear menu has three deck downloads: Download NEC-2 .nec, for xnec2c / 4nec2 / EZNEC / nec2c; Download NEC-4 .nec, for a NEC-4.2 binary and EZNEC Pro/4 (the NEC-4 deck); and Download NEC-5 .nec, for NEC5CL / EZNEC Pro+. All are always offered, whatever engines the machine running the workbench has — the deck writers need none, and the person who most wants the file is usually the one without the engine there.

What varies is the design, not the engines. A NEC-2 deck cannot carry a buried wire, a wire lying in the ground plane, or a graded mesh (a card deck numbers wires by tag and a graded expansion would shift every EX/LD/NT reference), so those designs refuse the NEC-2 download with a sentence pointing at the NEC-5 and NEC-4 ones — which do carry them. A design whose network is a transmission line or a virtual driver refuses both: the app solves those by a multiport-Y reduction over one deck per driven port, and no single deck says that in either dialect.

The NEC-5 deck is the same file the corpus tool’s catalog-nec5/ ships for that design at that mesh and ground — one writer, so the download and the published set cannot disagree.

The reverse — bringing a .nec deck someone published into the workbench — is Loading NEC decks.

On phones, the gear menu also has a full screen check (under display): it hides the system status and navigation bars so the whole screen is workbench — uncheck it or use the back gesture to exit. The control appears only in the mobile layout (desktop already has F11), and only on browsers with full-screen support (so not iPhone Safari).

A knob change sends one message over the /ws WebSocket; the server re-solves in a worker thread and sends the result back. Perceived latency is dominated by the solve time (free-space dipole-class solves are tens of milliseconds), not the network — so a regional server feels responsive for live tuning. Repeated solves of the same request hit a server-side cache, so flicking a knob back to a prior value is instant.