IPSC3 separates live radio routing, dashboard display, API clients, and slower background work. The design goal is to keep the live voice path clean and avoid blocking it on database, HTTP, APRS-IS, AI, or dashboard work.
| Component | Role |
|---|---|
| IPSC3 radio daemon | Live radio, data, routing, roaming, prompt, and signalling logic. |
| IPSC3-POC daemon | Separate cellular push-to-talk control, RTP audio, app fanout, and disabled-by-default DMR edge worker. |
| IPSC3 vocoder | Separate loopback-only process providing stateful PCM/AMBE conversion through a backend-neutral protocol. |
| MySQL | Configuration store, runtime projection store, RadioID data, CMS content, and historical event storage. |
| Next.js dashboard | Operational dashboard, CMS, downloads page, and public HTTP API. |
| Apache reverse proxy | Public HTTPS frontend for the dashboard and downloads. |
| IPSC3view | Terminal dashboard client using the public API. |
| IPSC3qt | Qt desktop dashboard client using the public API and map API. |
Process bootstrap settings are kept separate from network policy. The daemon configuration covers process-level concerns such as bind addresses, log paths, capture paths, and database defaults. MySQL stores the operational network model.
A completed Parrot recording is copied to a bounded single-worker analysis queue. The worker decodes no more than 120 seconds of AMBE through the local loopback vocoder, calculates peak and RMS measurements, queues the database record through the existing runtime writer, and assembles the spoken report. Queue submission is nonblocking; if the worker or vocoder is unavailable, normal Parrot echo and caller identification continue without a level report.
No vocoder request, database write, or report playback pacing runs inside a radio packet listener. The stored result can be inspected from the matching Parrot entry in Last Heard call history.
MySQL-backed operational configuration includes:
| Role | Typical Access |
|---|---|
| guest | Public dashboard pages. |
| hotspot token | Scoped access to one token-configured hotspot detail page, including scan start/cancel and current dynamic camp cancel for that hotspot only. |
| viewer | Read-only dashboard use where enabled. |
| operator | Operational controls and selected routing settings. |
| admin | User administration, configuration, and protected system settings. |
Hotspot users can optionally be given a per-hotspot dashboard token through the MMDVM options string. This is not a dashboard role and does not grant operator access. It unlocks only the matching hotspot detail page in that browser and allows limited controls for that hotspot: start scan, cancel scan, and cancel the current dynamic camp.
Private-call routing resolves the destination roaming record, endpoint, timeslot, and any required Motorola bridge template on the first packet of a stream. The resulting route plan is held in memory for the stream instead of repeating synchronous database and configuration reads for every voice packet. A terminator removes the plan immediately; inactive plans expire after 30 seconds, and the cache is bounded to 4096 entries so a missing terminator cannot grow memory without limit.
When an MMDVM private-call destination is unavailable, the packet listener submits the busy announcement to a bounded 64-entry queue using a nonblocking send. A dedicated FIFO worker performs callsign lookup, AMBE prompt assembly, packet transmission, and real-time frame pacing. Requests delayed in the queue for more than 15 seconds are discarded rather than playing stale feedback. This prevents a several-second announcement from pausing every hotspot handled by the same listener. Group-call routing is not part of this cache or prompt path and remains unchanged.
Historical airtime statistics are built from completed calls only. Live calls are projected to dashboard state, while completed call events are aggregated by the web dashboard and cached for display.
Current normal airtime stats:
VK400 and 4000IPSC3 also records lightweight runtime pressure samples for operational tuning. These are shown on the admin-only Configuration > Detail Stats page.
The daemon samples these values every 10 seconds. The voice path only updates relaxed in-memory counters; the database write is performed by a background sampler thread. This keeps the measurement system useful without adding per-packet database writes or noisy packet logging to live audio handling.
Current runtime pressure samples include:
The graph normalises fanout duration, packet-loop duration, UDP receive queue depth, and fanout target count so operators can spot pressure trends quickly. A healthy idle system should normally show a near-zero UDP receive queue and zero worker drops. Kernel UDP error counters are stored as raw since-boot values, while the dashboard highlights new errors since the previous sample to separate historic boot-time loss from active receive pressure. During busy talkgroups such as TG505, the fanout target lines help estimate how much additional repeater and hotspot growth the current host can absorb before buffering or transport refactoring becomes necessary.
Runtime pressure samples are stored in the
runtime_performance_stats table and are pruned after seven days.
A longer passive benchmark from a live TG505 net is recorded at Runtime Benchmark - 2026-07-07 TG505 Net. That test showed no UDP receive loss or daemon CPU pressure during sustained fanout, while identifying MySQL runtime/dashboard projection writes as the main optimisation target.
15.5.20 on Node.js 18.18.0 or newer, with PostCSS 8.5.17 enforced through the npm dependency override.The longer-term refactor direction is to move toward a canonical in-memory stream/session model, bounded queues, downstream dashboard projection, and small protocol-aware jitter/reorder buffers where they help without adding excessive latency.
See also: Project Roadmap and Transport Refactor Roadmap.