Repository navigation
Releases: striderZA/Tiny-RF-Simulator
Releases · striderZA/Tiny-RF-Simulator
Release list
v0.28.2
[0.28.2] - 2026-10-12
Added
- Swap placed library components in place (#200) — the Properties panel's Part dropdown replaces a placed library component with another part, taking its parameters from the new part's library definition. The successor keeps its id, canvas position, selection, links (reattached by port index), probes, group membership, and Network Analyzer and Power Meter pins; a swapped PFB keeps its IQ Plot and Channelizer Grid visibility for the current session. A swap is refused, with the reason shown and the circuit unchanged, when the new part lacks a connected, probed, or instrument port, when a link would break the physical link policy, or when the part cannot be built.
- Active project Git status in the header (#206) — the menu header shows the active circuit project's commit and counts of staged, unstaged, untracked, and conflicted paths; hover for the branch and full status. The status is read from the saved project's repository, or the working directory for an untitled project. Git queries run off the UI thread and fail soft when Git or the status is unavailable.
- User manual, part 1 of 3 (#192) — MkDocs manual source under
manual/pages/with home, install, and guided tutorial pages, built under a strict gate.
Changed
- README download and build sections (#192) — Quick Start points to the release archives, developer build steps move under their own heading, and the feature list covers receiver requirements and IIP3 checks.
Testing
- Cover the swap command and its app-level carry-over, including PFB visibility and the Network Analyzer and Power Meter pin remap (
test_component_swap,test_component_swap_app) (#200). - Restore the window flags
test_project_jsonchanges in the sharedapp.ini, sotest_uino longer depends on the saved layout (#200). - Add Git parsing, process, monitor, and header-width regression coverage (#206).
v0.28.1
[0.28.1] - 2026-10-12
Fixed
- Test flow results show applied condition values (#197) — result rows and JSON exports report the value a component actually applied after normalization. A requested ADC decimation of 3, for example, now reports the applied value 2.
- Test flow restores components on applied-state failures (#197) — if an engine throws while the run reads back its applied state, the run restores the components to their pre-run state and returns a
serialize_failederror.
Testing
- Cover normalized condition values in rows and JSON, rollback after an applied-state readback failure, and agent error mapping for
serialize_failed(#197). - Add
perf_app, an opt-in timing harness (EXCLUDE_FROM_ALL, not a ctest target) for frame CPU, bounded inputs, startup, project load, and agent tool calls (#196).
v0.28.0
[0.28.0] - 2026-10-11
Added
- Agent interface (MCP) — an MCP client such as Claude Code can read and edit the circuit in an open RF Simulator window. Keep the window running, open
View > Agent, and enableAgent Server, then point the client at therf-sim-mcpbridge that ships beside the simulator. Agents can read the circuit and component parameters, apply up to 64 edits per call with checkpoints, discover component types and library parts, run port measurements, network-analyzer sweeps, receiver measurements, and test flows, and read contained data files. Agents cannot open, save, or name files. The Agent panel lists recent agent activity and checkpoints, and reverts to a checkpoint only after confirmation. (#190)
Changed
- Release archives include
rf-sim-mcp— the Linux tarball now holdsrf-sim-mcpbesiderf-simulator-linuxat its root, and the Windows zip addsrf-sim-mcp.exebesidetiny-rf-simulator.exe.
Fixed
- Out-of-range values stay as loaded (#182) — numeric fields in the inspector and the analyzer and generator widgets show a value outside their limits as it is, flagged with an amber field and a tooltip naming the range, and log it once. Previously the limit replaced the value on the next frame or edit. Edits are still clamped into the limits.
- Ideal filter cutoffs are required per filter type (#181) — low-pass and high-pass library definitions must include
fc_low_Hz, and band-pass and band-stop definitions must include bothfc_low_Hzandfc_high_Hzwith the low cutoff below the high one. Definitions that omit them are now refused instead of silently using the 100 and 200 MHz engine defaults. The New/Edit Component form shows only the cutoffs the chosen type uses. - S-parameter picker in the component form (#184) — the component form now exposes the S-parameter picker.
- Legacy S-parameter entries load their file (#190) — projects and libraries saved before the
sparam_filepathrename use their S-parameter file in amplifier, equalizer, and ideal filter components instead of the manual model. A loaded file without a forward index now uses S21, not S11. - Agent edits no longer abort on collapsed groups (#190) — an agent edit that checkpoints a project with a collapsed group no longer aborts the app.
Testing
- Add agent protocol, API, bridge, and app-host coverage (
ctest --test-dir build -R 'test_agent_|test_mcp_bridge'), plus the standalonetest_sparam_legacy_keycase for legacy S-parameter keys. - Pin component parameter serialization mappings (#174).
v0.27.0
[0.27.0] - 2026-10-06
Added
- Receiver output power and IIP3 requirements — add optional output-power limits at a selected reference tone and two-tone IIP3 tests with configurable tone spacing and input-level sweeps. The Receiver Requirements panel evaluates and reports both alongside gain and noise-figure limits, with configuration persisted in project files.
Fixed
- Network Analyzer and Receiver Requirements through switched filter banks — a 1:2 switch feeding parallel branches that rejoin at a 2:1 switch is now measured instead of reporting no data (#169). Analyzer gain follows the selected switch throws (or, where the selected filter passes no tone, any leakage from the other branch), and its noise figure includes the unselected branch's noise leaking through isolation; receiver output power and IIP3 measure the same circuit. Combiners, merges fed by a source outside the measured path, and a Point A component that feeds the bank through more than one output port still report no data.
Testing
- Cover receiver output-power and IIP3 measurement/evaluation, partial IIP3 sweeps, project persistence, and panel integration.
- Add switched-filter-bank coverage: analyzer gain and noise figure against the live engines (matched, flipped, and mismatched throws; low-isolation noise), an
IdealFilterbank whose gain reads the other branch's leakage in the selected filter's stopband, the shared path discovery and runner (both branches wired in topological order, rejected topologies, malformed paths), receiver output power and IIP3 through a bank, and the production app clone adapter. - Make the Receiver Requirements pause/resume UI test deterministic in optimized builds: it caps the receiver engine's chain runs per frame, because a Release build otherwise finished the whole sweep before the test hid the view. Unit coverage pins the per-update cap.
v0.26.1
[0.26.1] - 2026-10-04
Fixed
- Component Library Edit action — clicking Edit on a user-defined component opens it in the editor without inserting a new instance into the circuit.
v0.26.0
[0.26.0] - 2026-10-02
Added
- Receiver Requirements panel — configure a frequency band, acceptable gain range, and maximum noise figure, then compare the analyzer sweep against those limits with per-metric and overall pass, fail, or incomplete status. Requirements persist with the project.
Changed
- UI-independent circuit runtime — move graph/component ownership, topology edits, and signal updates into
CircuitRuntime, separating editor-specific actions intoGraphEditorActions. Project loading routes topology changes through the runtime.
Testing
- Add receiver-requirements evaluation, project-persistence, and panel workflow coverage, plus headless circuit-runtime and graph-editor-action tests.
v0.25.1
[0.25.1] - 2026-10-01
Added
- PFB integrated channel noise and SNR — the PFB inspector reports the active channel's integrated noise power and effective noise bandwidth, and its hover SNR compares the strongest channel tone against that integrated noise, independent of analyzer RBW.
Fixed
- Network Analyzer through 2:1 RF switches — follow the exact linked input and output ports along the measured path, so a singly-fed switch reports the response of the connected throw. Dual-fed switches and combiner paths remain unsupported and report no data.
Testing
- Add Network Analyzer coverage for both switch throws, unsupported dual-fed switch paths, and the production app clone adapter.
- Add PFB coverage for channel-integrated noise and hover SNR in
test_node_hover_snrandtest_pfb_filter_design.
v0.25.0
[0.25.0] - 2026-09-25
Added
- Test Flow authoring — the Test Flow panel (
View > Test Flow) no longer only runs a hand-written flow; it writes one, closing issue #155. Pick a component and one of its realserialize()keys from a combo that lists every key with its JSON type and current value, seed the values from the live circuit, add/update/remove conditions and measurements,Save Flow...it, andReloadto pick up a hand edit without re-picking the file.New from Circuitscaffolds a draft from the live circuit — a measurement on the first component with an output port, port 0,power_dBm, the form pre-filled from the first sweepable key and its current value — so a working file is one Add-condition click away, and every id and path in the form is read from the circuit rather than invented. A non-numeric key is listed but disabled with its JSON type as the reason, and a condition whosepathdoes not resolve is marked red and answered with the sweepable keys that engine'sserialize()does expose (up tokMaxPathHints), because a mistypedserialize()key, never an inspector label, is the likeliest authoring mistake.
Changed
- Flow authoring rules live in the harness, not the panel —
test_flow/gainsdescribeConditionPaths()(every scalar leaf of a component'sserialize()snapshot with its JSON type and current value, containers descended and never listed; only the entries it marks numeric are pathsresolveConditionSlot()accepts, so a picker built on it cannot offer a pathapplyConditionValue()would refuse),buildFlowDocument()/writeFlowFile()(the documentLoadFlowFile()reads back, ids and paths verbatim, written atomically via temp + rename so a failed write cannot truncate a hand-authored flow), andparseConditionValues()/formatConditionValues()(the values field's grammar and its shortest round-trip inverse, refusing rather than skipping a token that is not a complete finite number). The panel keeps adding no second copy of the document shape, value grammar, or applicability rules, and the harness keeps its no-ImGui/no-simulator::apprule. - A draft is the single thing edited, validated and run — edits land in an in-tool draft and
spec()is the one accessorpreview(),run()andsaveFlow()read, so the panel cannot validate one flow and run another;addCondition()/updateCondition()/addMeasurement()check each candidate throughValidateFlow()and report its own wording, plus the loader's duplicate-target/duplicate-reading rules thatValidateFlow()does not check, so a refusal at the form is the verdict Run would give later and a draft the panel runs is also a file that loads back. A load clears the draft (the file is the truth again); a circuit reload keeps it andpreview()revalidates it against the replacement circuit. app/AGENTS.mdandtest_flow/AGENTS.mddocument the authoring contract: thespec()/draft boundary, circuit-sourced references, the form's use ofValidateFlow(), and the values-field buffer growth.
Fixed
- A refused authoring edit no longer creates a draft — the duplicate-target and duplicate-reading checks and the remove/update index checks now run against the effective spec before any draft is materialised, so
draftActive()keeps meaning "unsaved edits exist" and a rejected click cannot flip the panel into that state. - The values field no longer truncates a long sweep — its ImGui buffer grows from the model text (
kValuesBufferHeadroompast its end) instead of using a fixed 256 bytes, which had let a long sweep's seeded text be truncated on the next keystroke and the truncation stored back, silently dropping values from a list the harness would then run as if it were complete. - Discarding unsaved edits is stated, not silent — a load (and
reloadFlow(), which goes through it) reports in the status that the in-tool edits were discarded, andNew from Circuitreports when it replaced a draft.
Testing
- Add four
[issue155]cases totests/test_issue87_flow.cpp: the offered paths are exactly the sweepable leaves ofserialize()(discovery and resolver agree, including that resolving touches nothing), a built document is the flowLoadFlowFile()reads back, a mistyped value list is refused rather than partially accepted, and a failed write leaves the previous file byte-identical. - Extend
tests/test_test_flow_widget.cppwith the authoring contract over real headless frames and clicks: a flow scaffolded from the circuit saves, loads back and runs (with registry-id pinning), an authored condition is refused exactly where the harness would refuse it, the form seeds the current value and the draft is what runs, reload re-reads the selected file and drops the draft, a circuit reload keeps the draft and revalidates it, a scaffold needs something to measure, a refused edit never creates a draft, a 200-value list survives the form round trip, and the authoring buttons drive their own actions.
v0.24.4
[0.24.4] - 2026-09-25
Added
- Test Flow runner panel —
View > Test Flowopens an app-owned panel that runs the JSON test flows of the headless harness (test_flow/) against the live circuit: pick a flow file, see its conditions and measurements resolved against the loaded components, run it, read the per-row results, and export them as JSON. The panel never forms its own opinion of a flow — its pre-flight is the harness's ownValidateFlow()pass, exhaustive over every condition value and reporting every issue inRunFlow()'s check order and wording, with Run disabled until that list is empty — so a flow the panel offers is a flow the harness accepts, and a mistypedpath(aserialize()key such asatten_dB, not the inspector'sattenuation_dBlabel) or a value the resolved slot cannot take is named before the click instead of failing the run. A run is net-neutral: every engine is serialized before execution and restored afterwards, each independently so one failure cannot strand the rest, the graph's input pointers are rewired, and component state, links and the project's dirty flag are exactly what they were. A restoration failure is the exception — it is latched and reported, the run's result is suppressed, and further runs are refused until the circuit is reloaded (newProject(), a successful load, or a failed load that reset the circuit, all clear it). An unmeasurable sample renders asN/Aand exports as JSONnullrather than failing the run, and the panel's selection, load state and last result are session-only while the panel toggle persists with the other window states. - Node hover SNR — the node-body hover tooltip now carries the strongest-tone SNR of the hovered component's first output, measured through the new
SpectrumAnalyzerEngine::computeStrongestToneSNRdB()with the analyzer's current RBW, so changing the spectrum analyzer's resolution bandwidth changes the hover row. The tooltip printsSNR: <value> dB, orSNR: --when the measurement is unavailable (no finite tone, no strictly ascending two-bin grid, a tone outside the swept span, a non-positive RBW); the call is read-only and never touches the analyzer's render caches, VBW or trace history, and a multi-output part still exposes its other outputs through the existing Ctrl+click probe hint. Pin, link and collapsed-subcircuit tooltips are unchanged and carry no SNR row.
Changed
- The flow pre-flight is shared, not reimplemented —
ValidateFlow(spec, components)is the harness's condition and measurement resolution pass exposed as a reusable call, andRunFlow()is now a caller of it that reports the first issue verbatim; an attached UI therefore cannot drift from the harness's verdicts in either order or wording. - Value checking is split from writing —
resolveConditionSlot()resolves a condition's path once per condition and reports the slot kind it found, andconditionSlotAccepts()applies the value rules to that kind with no JSON involved, which are exactly the rulesapplyConditionValue()enforces. Checking a whole sweep therefore costs one path parse plus arithmetic instead of a serialize, clone and patch per value (a 10000-value sweep measures ~0.1 ms, affordable on every frame), andRunFlow()'s own pre-flight drops from ~14.7 µs per value to that same check. - One click cannot hang the window — the panel refuses a sweep above
kMaxRunRows(10000) before it snapshots anything, because the cartesian product executes synchronously on the UI thread (10000 rows measured ~4 s on a two-node circuit in an unoptimized build), and the results table is height-bounded and row-clipped (34 ms per frame at 10000 rows against 0.27 ms bounded). The row ceiling and the current issue are shown in the panel, so a disabled Run always has a visible reason. - Flow addressing is documented, not implicit — flow
conditions[].componentis anIComponentEngine::id(), assigned from a base of 100 in saved order, so a clean save/load reproduces the ids while any deletion or re-order shifts every later one; because ids stay dense, a stale reference can silently bind to a different component of the same type. Bothtest_flow/AGENTS.mdandapp/AGENTS.mdnow state this, along with the pre-flight, row-ceiling and table contracts.
Testing
- Add three standalone test targets (the established pattern for keeping coverage out of the MinGW
TEST_CASEregistration ceiling):tests/test_spectrum_analyzer_snr.cpp(RBW sensitivity, full stored tone power on real-domain and complex-baseband spectra, extreme finite tone levels whose watt conversion underflows or overflows, unavailable-input guards, nonuniform-grid and tie-bin lookup, and the read-only contract against render caches and trace history),tests/test_node_hover_snr.cpp(the app's hover adapter: registry summary passthrough, the analyzer SNR of the node's first output only — an output-1 read would be ~20 dB off and is asserted against — and the empty-optional fallback), andtests/test_test_flow_widget.cpp(the panel contract in full, below). - Add
tests/test_test_flow_widget.cppcoverage for the snapshot/restore boundary aroundRunFlow()(a clean sweep and an ordinary failure both leave every component'sserialize()state, every graph link and the dirty flag untouched; a restoration failure is a distinct latched failure with no result that may leave the throwing component at its swept state, cleared only byresetAfterCircuitReload()), the app's failed-load switch, preview id/port resolution, the saturating expected row count,N/Arendering, export success/failure, reload revalidation of the retained selection, and draw callback dispatch. The pre-flight is pinned against a mistyped or type-inapplicablepath— including a fractional value sitting between two acceptable ones, the case a sampled pre-flight would miss — thekMaxRunRowsrefusal is asserted through an engine that countsdeserialize()calls (nothing may be executed), and frame-cost guards cover both the exhaustive pre-flight over 10000 values and the bounded table. - Extend
tests/test_issue87_flow.cppwith theValidateFlow()contract (no issues for a runnable flow, oneFlowErrorper failing reference inRunFlow()'s order and wording withRunFlow()reporting the first verbatim, and a value rejected mid-list rejected the same way) and theresolveConditionSlot()/conditionSlotAccepts()split it rests on, including a value-by-value equivalence loop asserting the check agrees with the write on outcome, message and the exact JSON type written, and that resolving touches nothing. - Extend
test_engine/ui_tests.cppwith the tooltip-window visibility case's node-body data assertions (non-empty summary plus a populated analyzer SNR from the app callback the widget renders) and theView > Test Flowmenu/window integration; the panel's own model is covered by the headless standalone target.
v0.24.3
[0.24.3] - 2026-09-22
Fixed
- Extension menu label ambiguity — a duplicate (location, label) pair across two extension manifests is now rejected at parse time instead of keeping an entry that cannot be addressed: the menu label is the action's only run-time identifier (
ExternalToolRequest::action_label) and the ImGui item is keyed on it, so two entries sharing a label could launch the wrong tool. The Tools menu and Extensions panel now also suffix the item id with the manifest id and action index (the pattern the Extensions panel already used) so distinct entries never share one ImGui id, while still handing the raw label torunExternalTool()as the dispatch key. - S-parameter copy path traversal — the component-authoring save path joined the raw part number onto the library destination directory, so a part number containing separators or
..wrote the picked S-param file outside the chosen root. The data-file name now comes fromsanitizePathSegment()(moved beside the library code soapp.cppandComponentFormModelshare it) and the copy destination is built bydataFileCopyDestination(), which refuses any name that is not a bare file name with a visible form error.
Documentation
- Release CRLF-trap recipe — the release-pipeline guidance in the root
AGENTS.mdis now a runnable recipe: read the committed bytes withgit cat-file blob HEAD~1:CMakeLists.txt, requiregit diff HEAD~1 --stat -- CMakeLists.txtto print2 +-, and restore + byte-replace +--amendif the MSYSsedhad rewritten the file to LF-only.
Testing
- Add
tests/test_issue130_extension_menu_labels.cppas its own standalone executable (matching the existing pattern that keeps extension coverage out oftests.exeand the MinGWTEST_CASEregistration ceiling): duplicate labels are rejected and unique ids are assigned. - Extend
tests/test_path_containment.cppwith authoring save-path coverage driven through newtestOpenNewComponentForm/testOpenEditComponentForm/testSetComponentFormDestinationRoot/testComponentFormModel/testSaveComponentFormaccessors: the new-entry case asserts the saved JSON path, persisted data-file name, copied bytes, refused traversal target, and an instantiate-with-S-params round trip of the saved entry; the edit case asserts the copy replaces the previous revision in the entry's own directory and that a chosen destination root stays uncreated.