Skill: design a module¶
For: an AI agent asked to design an RF module — a transmitter, a receiver, or a transmit/receive front end — on someone's behalf.
The shape of the job: one HTTP call returns a complete module. There is no session, no job id and nothing to poll. Your work is on either side of that call: collecting inputs only your user can give you, and reporting a result without smoothing it over.
This is the agent-facing procedure. The same design is told as a narrative for a human reader in Design a 9.4 GHz T/R module from Claude Code — read that one if you want the story; read this one if you are making the call.
Every figure here was re-measured against the production API on 2026-09-09.
Step 0 — pick the engine¶
Transmit chain, receiver, T/R module and link budget are four different engines with four different endpoints. Pick the engine is one page and settles it. The rest of this skill uses the T/R module as the worked case, because it is the one with the most decisions in it; the procedure is identical for the others.
Step 1 — collect what only your user can tell you¶
These engines compute faithfully from whatever you send. They have no way to tell whether a number came from your user or from you. A value you invented produces a precise, authoritative-looking, wrong answer — and it is wrong because of your input, not our maths.
For a T/R module, exactly two fields are required:
| Field | Unit | Why only your user knows it |
|---|---|---|
frequencyGhz |
GHz | The band is the requirement |
targetOutputPowerDbm |
dBm | Transmit power is a system decision |
Everything else is optional, and every optional field you leave out is disclosed
rather than assumed. That is a feature. Leave out transmitDutyCyclePercent and
the engine does not silently pick one — it says so, in analysis.disclosures:
"No transmit duty cycle was stated, so the transmit stages and the shared element assume continuous wave. A pulsed design dissipates less in both, in proportion to its duty cycle. The receive branch does not derate — its bias is continuous."
That is a question for your user, and it moves the thermal answer.
Worth asking for, because each one changes an answer:
application(radar,wifi, …) — moves both the part selection and the power verdict.transmitDutyCyclePercent— a pulsed design derates; without it you get a CW assumption, disclosed.antennaReturnLossDb— see step 4. This one field flips a verdict.prfHzandpulseWidthS— without both, thetimingbudget is absent entirely frombudgets. Notnull, notnot_evaluated: absent. Measured — the response below carriestopology,isolation,leakageandstress, and notimingkey at all. AddprfHz: 1000, pulseWidthS: 1e-6to the same design point andtimingappears,verdict: "ok", with a 0.9989 ms receive window and the switching time taken from the selected switch's own record (txRxSwitchingBasis: "switching_speed_ns"). When a part states no switching speed, the budget comes backnot_evaluatedwith the missing input named rather than assumed.
If you do not have a required field, ask. The refusal already names it for you:
curl -X POST https://api.pcbgenerator.com/api/tr-module/design \
-H "Content-Type: application/json" -d '{}'
Supply the frequency alone and the next refusal explains itself too:
422 {"detail": "targetOutputPowerDbm is required: the transmit half of a T/R
module is designed to a stated output power, and every safety budget below
is measured against it"}
Those are sentences you can hand straight to your user. They are never a reason to pick a frequency or a power yourself.
Step 2 — make the call¶
curl -X POST https://api.pcbgenerator.com/api/tr-module/design \
-H "Content-Type: application/json" \
-d '{
"frequencyGhz": 9.4,
"targetOutputPowerDbm": 40,
"application": "radar",
"transmitDutyCyclePercent": 10
}'
No account, no API key, no Authorization header. Rate limit 15 requests per
minute per IP.
Step 3 — read what came back¶
Top-level keys: ok, topology, frequencyGhz, transmit, receive,
sharedParts, budgets, sharedPathLoss, needsPartFlags, warnings,
analysis, bom.
It chose a topology and recorded the alternative it rejected. In
budgets.topology: selected: "tr_switch", a reason, and a rejected block
naming circulator_limiter and why it lost. The choice is auditable rather than
magic — quote the reason, not just the name.
It returned 15 catalogue parts across three groups:
| Group | Parts |
|---|---|
| Shared | QPC2040 (the T/R switch) |
| Transmit | HMC465 → QPA2811 → ABF-9G+ → SMA-J-P-H-ST-EM1 |
| Receive | TGL2209-SM, HMC8412, B095MB1S, CMD183C4, ADF5611, CBP2-1090C+, PHA-23LN+, HMC1119, SMA-J-P-H-ST-EM1, RO3003-50ohm-MS |
needsPartFlags: [] — every slot filled from the catalogue. Nothing invented.
Headline figures:
| Figure | Field | Value |
|---|---|---|
| Transmit target | transmit.targetOutputDbm |
40.0 dBm |
| Transmit, as built | transmit.asBuiltOutputDbm |
40.65 dBm |
| Receive gain | receive.totalGainDb |
25.65 dB |
| Receive noise figure | receive.cascadedNfDb |
2.597 dB |
| Shared-path insertion loss | sharedPathLoss.insertionLossDb |
1.035 dB |
| Delivered at the antenna | sharedPathLoss.txDeliveredAtAntennaDbm |
39.615 dBm |
| Receive NF at the antenna | sharedPathLoss.rxCascadedNfAtAntennaDb |
3.632 dB |
| BOM | bom.summary |
29 lines, 43 items, $939.99, 15 of 29 priced |
⚠ Note sharedPathLoss.fedBack: false. The switch's loss is stated beside the
as-designed figures, not folded back into them — neither chain was re-run against
a target your user never gave. Quote both: the chain figure and the at-the-antenna
figure. They answer different questions.
Thirteen entries in warnings. They are the part worth reading, and the part
a summary usually deletes. Three of the thirteen:
- The receive budget's verdict is
lna_compressed—HMC8412compresses at 3.50 dBm input, below the 4.55 dBm reaching it during transmit. The disclosure carries the finding, a census of the alternatives ("6 of the 33 LNAs here that declare a computable input P1dB stay out of compression at this level"), an engineering judgement about severity ("the receiver is deaf, not damaged"), and the input that would settle it — a PRF and pulse width, so the timing budget can answer whether it recovers before the first range bin. TGL2209-SM"never conducts on this branch" — the limiter is present and idle, and no limiter covering this band clamps low enough to change that.- Three parts are inside their average rating but the filing states no peak/pulse rating: "The average is fine; the peak is UNVERIFIED — check the datasheet for a pulse rating."
And analysis.unresolved names heat the thermal model declines to attribute,
rather than absorbing it into a tidier total.
Read a design result is the companion skill for this step
— which field carries the power verdict, why needsPartFlags is the honesty flag,
and why a null verdict is more dangerous than a false one.
Step 4 — the second call, when a disclosure names its own missing input¶
The most useful thing these engines do is tell you what they are holding back.
The switch verdict came back safe:
| Field | Value |
|---|---|
budgets.stress.switch.powerAtThePartDbm |
40.65 dBm |
budgets.stress.switch.ratingDbm |
44.5 dBm |
budgets.stress.switch.ratingBasis |
abs_max_pulsed_85c_dbm |
budgets.stress.switch.stressDb |
−3.85 |
budgets.stress.switch.verdict |
safe |
With a disclosure attached to it:
"No antenna return loss was supplied, so the load QPC2040 drives is unknown and the verdict above is measured against
abs_max_pulsed_85c_dbm. Its record also declaresabs_max_3to1_vswr_85c_dbm= 43.00 dBm… against that rating this design's 40.65 dBm is -2.35 dB of stress and the verdict would bemarginalrather thansafe. SupplyantennaReturnLossDb(6.02 dB or lower) to have it govern."
So supply it. One field, same design point:
| Without | With antennaReturnLossDb: 6.0 |
|
|---|---|---|
ratingBasis |
abs_max_pulsed_85c_dbm 44.5 dBm |
abs_max_3to1_vswr_85c_dbm 43.0 dBm |
stressDb |
−3.85 | −2.35 |
verdict |
safe |
marginal |
Same parts, same transmit power, asBuiltOutputDbm unchanged at 40.65. The engine
held the more binding rating the whole time and told you it was holding it,
because applying it without knowing the load would have over-derated a matched
design.
The procedure this teaches: when a disclosure names the input that would
resolve it, that is a question for your user, and often a second call. Do not
answer it yourself with a plausible number — antennaReturnLossDb: 6.0 from you
rather than from your user turns a safe into a marginal on your authority.
Step 5 — report¶
Report in this order, because it is the order a reviewer needs:
- What it built — topology, part count,
needsPartFlags. - The headline figures — as-built transmit, receive NF and gain, BOM total. Name the at-the-antenna figures separately.
- What it caught — the warnings, as findings, in the engine's own words.
- What it wants you to decide — the disclosures that name a missing input, each one as a question.
Do not compress step 3 into "the design looks good". A lna_compressed verdict
and a marginal stress verdict are handed back as findings for a human to rule
on, and that ruling is the one thing in this whole procedure that is not yours to
make.
What it will not do¶
- It will not invent a part. An unfillable slot is named in
needsPartFlagsand left empty. - It will not check numbers you supply. Figures in the request are computed faithfully whether or not anyone said them. Pass through what your user actually stated.
- It will not evaluate a budget you did not ask for. Without a PRF and pulse
width the
timingbudget is absent entirely — not null, not a guess. - It will not decide for you.
lna_compressedandmarginalcome back as findings, with the evidence, for your user to rule on. - It will not remember. Every call is independent. If a second call needs a figure from the first, carry it yourself.
Related¶
- Pick the engine — transmitter vs receiver vs T/R vs link budget, with paths and rate limits.
- Read a design result — the field-by-field reading guide.
- Design a 9.4 GHz T/R module from Claude Code — the same design, told for a human reader.
- API reference for LLMs — every path, limit and error code.