# Protocol compatibility Compatibility contract: the pinned fldigi implementation (v4.1.23, `61b97f41…`) and the published FSQ documentation by ZL1BPU/ZL2AFP. Key reference facts (from the Gate 0 audit): - Internal sample rate 12000 Hz; symbol length 4096 samples at 3 baud; speeds 1.5/2/3/4.5/6 baud via symlen scaling; 33 tones, 3-bin spacing, ≈290 Hz bandwidth at 3 baud. - Callsign CRC: CRC-8 poly 0x07, init 0x00, two lowercase hex digits. - Callsign validity, as OpenJSQ's transport enforces it (`transports/fsq_legacy/src/fsq_legacy.cpp`, `is_valid_callsign` and `is_valid_station_callsign`): a callsign is 1–15 characters drawn from letters, digits, `-` and `/`, with at least one letter or digit; a STATION callsign (a directed destination) must additionally contain at least one letter and at least one digit. A group destination is `@` followed by 1–30 letters, digits or `/`. The special addresses `allcall` and `cqcqcq` are reserved broadcast destinations. (An earlier revision of this note said "length 3–20"; that was never what the code checked.) - Directed-message triggers and `parse_*` dispatch: see audit §8. The exact on-air frame — BOT, sender, CRC-8, destination, trigger, body, optional CRC-16 suffix, EOT — is specified in `FSQ_FAST_PUBLIC_SPEC.md` Part X–XI, and the bodies OpenJSQ's automatic replies put on the air (`snr=-3 db`, `Heard:` lines, `ack`, and the rest) in `JSQ_PUBLIC_PROTOCOL_SPEC.md` §56.3. ## Directed-command reference (from the pinned fldigi implementation) Semantics verified against the `parse_*` bodies in the vendored `fsq.cxx`. Wire form: `DEST[args]`, sent inside a directed frame. | Trigger | Wire example | Meaning | Remote behavior (fldigi) | |---|---|---|---| | (space) | `w4ijg hello` | Chat text | Displays the message | | `?` | `w4ijg?` | Signal report | Replies `snr=` | | `$` | `w4ijg$` | **Heard-list query** | Replies `Heard:` + its heard list (multi-line) | | `@` | `w4ijg@` | Location query | Replies with its QTH text | | `&` | `w4ijg&` | Station-message query | Replies with its QTC text | | `^` | `w4ijg^` | Version query | Replies `fldigi ` | | `*` | `w4ijg*` | Wake station | Enables its monitor (SELCAL) and acks; works even when "asleep" | | `!` | `w4ijg! text` | Repeat | Echoes the text to everyone immediately, undirected (`call:XX text`, two spaces). **OpenJSQ 1.0.7 matches** byte for byte when text follows ("repeat my message", `JSQ_PUBLIC_PROTOCOL_SPEC.md` §79.7), when the operator allows it; with no text it repeats its own last transmission to the requester (§79). fldigi reads the first word of the echoed text as an address (it strips the spaces), so text such as `allcall#[f]x` would be obeyed by every fldigi station: OpenJSQ refuses to echo it ("not repeating commands"); fldigi echoes it | | `~` | `w4ijg~ text` | Delayed repeat | Echoes the text to everyone after 15 s. **OpenJSQ 1.0.7 matches** when text follows (§79.7); `~30` (a bare number) repeats its last transmission after that delay (§79.4). OpenJSQ's own `~{N}text` delay form is sent only to stations whose capability record says they repeat for others, never to fldigi | | `#` | `w4ijg#[name.txt]text` | **Store message/file** | Writes text to `name.txt` (default `.txt`), acks | | `+` | `w4ijg+[name.txt]` | **Read message/file** | Sends the stored file's contents back | | `\|` | `w4ijg\|text` | Alert | Pops an alert dialog with the text | | `>` | `w4ijg>` | Faster | Steps its FSQ speed up one notch, confirms new baud | | `<` | `w4ijg<` | Slower | Steps its FSQ speed down one notch, confirms | | `;` | `via;dest text` | **Relay** | `via` retransmits to `dest`, inserting `[origin]` | | `[` | `dest[origin] text` | Relayed marker | Marks traffic relayed from `origin`; relayed queries answered via the relay path | | `%` | `w4ijg%S` | Image transfer | Starts image RX (mode letter selects size) — OpenJSQ parses the trigger and answers nothing; no image service is transmitted by this release | | `=` | `w4ijg= hello`, `w4ijg=?` | **Local only, do not relay** (OpenJSQ, `JSQ_PUBLIC_PROTOCOL_SPEC.md` §56.4) | fldigi has no dispatch for `=`: it does not reply and shows the text, including the displaced trigger as the body's first character. The frame stays DIRECTED because `=` is in OpenJSQ's trigger set (`is_trigger`, `transports/fsq_legacy/src/fsq_legacy.cpp`), so the header walk stops on it and the destination and sender CRC are intact | | `-` | — | Unsupported | Nothing: fldigi 4.1.23's `case '-'` is `break;//parse_minus();`, so the frame is dropped without a reply. OpenJSQ never sends `-` as a trigger; since 1.0.7 a `-1`..`-15` after a plain callsign is part of the address (below) | `=` is an **OpenJSQ addition**, not an fldigi mapping: nothing in the pinned implementation emits it and nothing there answers it. It is the one character in OpenJSQ's trigger set that was both free and safe — `{` is taken by the wide-mode structured envelopes, `\` is not in the set at all (it would be absorbed into the destination and turn the frame undirected), and `-` is now part of `call-n` addresses (below). The real trigger is displaced one position into the body, and a receiver un-displaces it before applying any rule in this table. allcall frames only honor space, `*`, `#`, `%`, `[` (no query amplification storms). OpenJSQ mirrors this table in `DirectedCommand` and the session's compose logic; every mapping is unit-tested. ### SSID-style names, `call-n` (OpenJSQ 1.0.7) OpenJSQ reads `w4ijg-7 hi` as chat to the one station W4IJG-7 when the text before the `-` is a plain callsign of 3 to 6 letters and digits, the number is `1` to `15` with no leading zero, and a trigger, a space or the end of the frame follows it. Everything else - a `/` call, `allcall`, `cqcqcq`, a group, `-0`, `-01`, `-16`, `-1x` - splits at the `-` exactly as fldigi splits it. A plain callsign or a `/suffix` behaves exactly as before. The rule and the CRC-16 tie-break are `JSQ_TRANSPORT_SERVICE_SPEC.md` §33.1.1. What the pinned fldigi (4.1.23) does with these names, from `fsq.cxx` and from the round trips in `third_party/fldigi-fsq/tests/test_fsq_roundtrip.cpp`: - **Triggers** (`fsq.cxx:405`) include `-`. - **A `call-n` sender is heard whole.** `valid_callsign` (`:419-434`) is a regex search, not an anchored match, so `w4ijg-1` passes; the sender walk (`:466-483`) lets `-` through and the CRC-8 covers all of `w4ijg-1`. fldigi shows and lists the station as `w4ijg-1`. - **fldigi answers it.** Its automatic replies (`:631-637`) go to `station_calling` verbatim - `w4ijg-1 snr=…` - which OpenJSQ reads as addressed to W4IJG-1. - **fldigi cannot be a `call-n` destination.** The destination word (`:521-559`) ends at the first trigger, so `kk4ib-1 hi` reaches fldigi KK4IB as `kk4ib` plus `-`, and `case '-'` (`:594`) does nothing: the frame is dropped, not shown, not answered. Harmless, and the reason an fldigi operator keeps a plain or `/suffix` callsign. - **fldigi relays it in its own form.** `parse_relay` (`:682-697`) inserts `[origin]` at the first trigger, so `via;w4ijg-7 hi` goes on as `w4ijg[k1a]-7 hi`. OpenJSQ puts the `-7` back on the destination. A plain fldigi W4IJG hearing that form takes it as relayed text for itself (`-7 hi` from k1a), which is fldigi's own behaviour; an OpenJSQ via station writes `w4ijg-7[k1a] hi` instead, which plain fldigi stations drop. Unverified with FSQCall. ### Forms (ICS-213 and similar) Forms are structured text riding the existing transports: transmitted as a directed message or stored at the remote station via `#[formfile]`. The serialized layout (field labels + values, flmsg-compatible where feasible) is a UX/application-layer concern — see `DESKTOP_UX.md`. No modem or protocol changes are required to carry forms. This document grows into the full field-by-field compatibility spec as the fixture corpus expands. Fixture expectations are the ground truth; do not silently update them (see CONTRIBUTING.md).