JTDX_CONTEST · CE3TSKSuperFox decoder · experimental

derivative work of the SuperFox decoder of WSJT-X 3.0.2 by K1JT, K9AN and IV3NWV

Inside the
SuperFox Decoder

What WSJT-X's SuperFox decoder does. What JTDX_contest does differently.

A DXpedition in SuperFox mode answers up to nine Hounds with one signal. Until now JTDX could not read it: the Fox's period simply stayed empty. JTDX_contest receives it. Its decoder starts from WSJT-X's, ported line by line, and then goes further: it looks for the Fox under QRM, remembers what it has heard, and decodes messages it can partly foresee. This page first explains the decoder as WSJT-X wrote it, and then lists everything JTDX_contest does differently.

Experimental — and why this page shows no figures

SuperFox support in JTDX_contest is experimental. Every measurement made so far comes from simulated transmissions and a handful of short recordings, not from DXpeditions on the air. Simulations are good for comparing two versions of a decoder, but not for promising what it will do on a real band. So this page describes what each step does and why, and deliberately gives no sensitivity, decode count or timing figures.

Once a reasonable number of real SuperFox recordings exist, they will be used to measure every step described here, and the figures will be added. Recordings made with Save all, both periods, while a SuperFox DXpedition is working are the most useful thing a reader can contribute. Ideally about an hour's worth of WAV files - but less is welcome too, even a few minutes: jtdx_contest@ce3tsk.com.

Support this work on Ko-fi

WSJT-X  marks WSJT-X's decoder  ·  JTDX_contest  marks what this program does differently

Part IWSJT-X 3.0.2

The SuperFox decoder as WSJT-X wrote it

SuperFox was designed by the WSJT-X team; its code, a q-ary polar code, is the work of Nico Palermo IV3NWV. Everything in Part I is how WSJT-X 3.0.2 receives it, and it is also the starting point of JTDX_contest's decoder.

00

One signal, nine Hounds

WSJT-X

Why SuperFox exists, and how a SuperFox QSO runs.

In the old Fox-and-Hound mode the DXpedition answered up to five Hounds by transmitting five ordinary FT8 signals at once. Five signals share the transmitter's power, and their sum no longer has a constant envelope, so the amplifier has to be backed off as well. SuperFox replaces them with one constant-envelope signal whose single codeword carries up to nine Hound messages. Every Hound hears the Fox at full power.

The timing is FT8's. The Fox transmits in the even 15-second period, starting half a second in; the Hounds call and answer in the odd period with plain FT8, anywhere in the audio passband. The QSO is the usual exchange (call, report, R+report, RR73) and is logged as FT8. What the Hound decodes in the even period must be on the screen before the odd period begins, or the reply has to wait a whole cycle.

A SuperFox cycle

The Fox's transmission fills the even period; the Hound's decode begins after it ends and has until the start of the odd period to put the Fox's lines on the screen.

EVEN PERIOD · the Fox ODD PERIOD · the Hounds one SuperFox transmission · 151 symbols Hound: R+report · plain FT8 other Hounds call · plain FT8 decode starts → the reply is due 0 s 5 s 10 s 15 s 20 s 25 s 30 s
01

What is on the air

WSJT-X

329 bits, a 21-bit CRC, a polar code over 128 symbols, 128 tones.

A SuperFox transmission carries 329 bits: the Fox's call, up to nine Hound calls with an RR73 or a report each, a flag for "CQ", and a 20-bit one-time code that lets a Hound check that the transmission really comes from the DXpedition. The last three bits say which of four layouts is used:

typecontent
standardthe Fox's call, nine Hounds: five RR73s and four reports
compound Fox callthe Fox's full compound call, eight Hounds
free textthe Fox's call, four Hounds with reports, 26 characters of text
CQ"CQ" with the Fox's call and grid, and text

The 329 bits are read as 47 symbols of seven bits, and a 21-bit CRC fills three more. These 50 symbols are encoded with a polar code (127,50) over GF(128): 127 symbols, each one of 128 values, of which only 50 carry information — the rest is redundancy that lets the decoder correct errors. Each code symbol is sent as one of 128 tones, and 24 more symbols carry a sync tone, the lowest one: 151 symbols in all, 1500 Hz wide, from 750 Hz upwards.

The 151 symbols of one transmission

The sync symbols (the positions are fixed by the protocol) are dense at the start and thin out, so the decoder can use them for both frequency and time.

SYMBOL 1 SYMBOL 151 sync: the lowest tone, dense at the start, thinning out data: one of 128 tones - one polar codeword symbol
02

The decoder, stage by stage

WSJT-X

From 15 seconds of audio to the Fox's lines.

  1. FT8 signals out firstOrdinary FT8 signals in the Fox's band (Hounds calling in the wrong period, other stations) are found with the FT8 decoder, rebuilt and subtracted from the audio.
  2. Analytic signalThe audio becomes a complex signal, ready to be shifted in frequency.
  3. Carrier notchA steady carrier in the band, narrower than a data tone, is found and removed.
  4. Sync searchA long FFT finds the sync tone's frequency near the receive frequency; the energy of the 24 sync symbols, read at every lag, gives the start time. A sync that does not stand out is marked unreliable.
  5. Shift and demodulateThe signal is shifted so that the sync tone lands on the first bin; one FFT per symbol gives the power of all 128 tones. A tone that is the loudest in too many symbols is a birdie and is flattened.
  6. LikelihoodsEach symbol's 128 powers become 128 probabilities, optionally smoothed across neighbouring tones to allow for frequency spread.
  7. The searchThe polar decoder is run, and the CRC is checked; if it fails, the attempt is repeated with other smoothings, randomly perturbed probabilities and slightly different frequencies and times (section 03).
  8. The floorA word whose CRC checks is accepted only if its estimated signal-to-noise ratio reaches a fixed floor: an empty period makes hundreds of attempts, and a chance CRC pass must not reach the screen.
  9. Unpack and printOne decode line per Hound, a CQ line if flagged, the free text, and a hidden line with the one-time code for the verification.
03

The search: many readings of one signal

WSJT-X

Try again, slightly differently, until the CRC agrees.

The polar decoder always returns a word; only the CRC says whether it is the right one. So a failed attempt costs nothing but time, and the decoder makes many. At the sync estimate it tries four smoothings of the spectra, each with a series of randomly perturbed copies of the probabilities (the random stream is seeded, so the decoder is deterministic). Then it spirals outwards over a grid of small frequency and time offsets, trying the four smoothings at each. The first attempt whose CRC checks is taken. A period with a sync peak but no Fox in it runs every attempt.

Where the search looks, and in which order

The hundred time and frequency offsets of WSJT-X's search, drawn in the order they are tried.

frequency offset → (steps of half a hertz) time offset → (steps of 8/1024 s) the sync estimate: 4 smoothings, each with many perturbed copies then the other offsets, strong colour = tried early, faint = tried late: one attempt per smoothing each the FIRST attempt whose CRC checks wins; a period with no Fox runs them all
04

Successive cancellation

WSJT-X

One decision per symbol, the least reliable first, and no way back.

WSJT-X's polar decoder is a successive-cancellation decoder. It decides the message symbols one after another, each decision building on the ones before, and it happens to settle its least reliable symbols first. A wrong early decision cannot be undone: the rest of the word is decoded on top of it, and the CRC fails. This matters for Part II, where JTDX_contest adds a decoder that keeps more than one path open.

05

Is it really them?

WSJT-X

The one-time code, checked on a server.

A DXpedition can sign its transmissions: the Fox computes a six-digit one-time code from a secret key issued by the NCDXF, and every SuperFox transmission carries it. A Hound needs no key: WSJT-X sends the Fox's call, the time and the code to a verification server, which answers verified or invalid, and shows the answer. An unsigned transmission carries an all-zero code.

Part IIJTDX_contest

What JTDX_contest does differently

Everything below is this program's own. The rule throughout: whatever WSJT-X's decoder decodes is decoded here too, and in the same way — the additions come after WSJT-X's search, or leave its result untouched. Every addition can be switched off (the settings are listed at the end of this part).

06

The decoder, stage by stage — again

JTDX_contest

The same chain, with the steps that are new or changed marked.

  1. Look first, remove FT8 laterThe Fox is looked for in the band as received. The FT8 step comes only if nothing is found (section 09) — a Fox in the clear is printed without waiting for it.
  2. Analytic signal, carrier notch, sync searchAs in WSJT-X.
  3. Shift and demodulateAs in WSJT-X, plus one step: tone bins that hold QRM are normalised (section 07). One demodulation per time/frequency step instead of one per smoothing.
  4. The search, in threadsThe same attempts in the same order, decoded in parallel, with a result identical to the serial search (section 08).
  5. The floorLower for a Fox the decoder does not know, as MSHV sets it; none for a Fox it knows (section 10).
  6. The a-priori passMessages the decoder can foresee almost whole: the Fox's CQ, its answer to you (section 11).
  7. The pool passA list decoder with every Hound slot held to the Hounds heard in the odd period (section 13).
  8. The list passThe list decoder with only the Fox told, and then with nothing told (section 14).
  9. The FT8 stepThis program's own FT8 decoder goes over the Fox's band, silently; what it decodes is subtracted and the decoder runs again on what is left (section 09).
  10. MSHV's three sync windowsA last step, on the other places MSHV looks for a Fox (section 16).
  11. Unpack and printAs in WSJT-X; the lines of the new passes are marked *, as FT8's a-priori decodes are. A compound Fox call typed into DX Call is resolved (section 17).
07

Tone bins that hold QRM

JTDX_contest

The one step of the demodulator that is not WSJT-X's.

The likelihoods weigh all 128 tone bins with a single noise level. An FT8 caller inside the Fox's band breaks that twice: in its own bins it is far stronger than the Fox, so it wins every symbol it is keyed in; and the symbol FFT leaks it over the whole band, so every other bin's noise level is wrong as well. So a bin whose mean power over the whole transmission clearly exceeds the noise is divided by that mean. A caller's bins come back to the order of the noise; a bin that only carries leakage comes back to exactly the noise. The Fox's own bins may qualify too, but the polar code spreads even the barest message over many tones, so its cells still stand out after the division. On a band without QRM no bin qualifies, and nothing changes.

The SNR estimate is taken from the spectra without the step, so a strong Fox is not reported weaker than it is.

Before and after, schematically

The dashed line is the noise level the likelihoods assume.

AS RECEIVED: A CALLER IN THE FOX'S BAND tone bins → EACH BIN DIVIDED BY ITS OWN MEAN tone bins → the caller wins every symbol in its bins, its leakage lifts its neighbours every bin back at the noise level; the Fox's own cells still stand out
08

The same search, faster, in threads

JTDX_contest

Nothing about what is tried changes — only how long it takes.

WSJT-X demodulates the signal again for each of the four smoothings at every offset; here it is demodulated once per offset, and the median it needs is found by selection instead of a full sort. Then the attempts are decoded in parallel on as many threads as the FT8 decoder uses, under the rules JTDX_contest's FT8, FT4 and FT2 threads already follow:

  • every attempt is prepared in the serial order, and the random perturbations come from the one seeded stream, drawn in order;
  • every thread works on data of its own — buffers made once and never moved, no locks;
  • after a batch, the lowest-numbered attempt whose CRC checks is taken, never the first one to finish.

So the decoder prints the same lines at any thread count, and the same lines as without threads. That is checked, not assumed: the tests compare the threaded decoder line by line with the serial one and with the version before the threads, on simulated, faded, noisy, QRM-laden and recorded periods, repeated to catch any run-to-run difference. Each thread's FFT buffer is also checked once, bit for bit, against the serial demodulation, and should it ever differ the steps fall back to the serial demodulation.

Why the thread count cannot change the result

Threads finish in any order; the rule picks by position in the search, not by time.

ONE BATCH OF TRIALS, IN THE SERIAL ORDER 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 thread 1 1 5 9 13 17 21 thread 2 2 6 10 thread 3 3 7 11 14 18 22 thread 4 4 8 12 15 16 19 20 23 24 trials 10 and 18 both pass the CRC; 18 finishes first, but ✓ trial 10 is taken: the lowest number - the serial search's answer, at any thread count
09

FT8 on top of the Fox

JTDX_contest

Taken out by JTDX_contest's own FT8 decoder, and only when it is needed.

A DXpedition works on a frequency of its own, so what lies on top of a SuperFox is mostly Hounds calling in the wrong period. WSJT-X and MSHV remove FT8 signals before they look for the Fox; this decoder looks first, and only when the Fox is not found does JTDX_contest's own FT8 decoder go over the Fox's band — silently, nothing of it is printed:

  • a quick round with the plain recipe, so that a Fox rescued this way is still printed before the reply is due;
  • after each round, every FT8 signal decoded is subtracted from the band as received, its timing refined by what the subtraction leaves, and the SuperFox decoder runs again on the residual;
  • a deeper round (SWL mode, more cycles, the alternate pass) only if the Fox is still not there and the round can end inside the RX budget.

The FT8 decodes of these rounds fill FT8's hint memory like any others, so the next Fox period's quick round already knows the stations that were there. The QRM step and the bin normalisation are complements: the normalisation deals with a few callers at once and with what a subtraction leaves behind, the FT8 step with a band full of them.

10

The floor, and a Fox the decoder knows

JTDX_contest

What keeps chance decodes off the screen, set per case.

WSJT-X's floor rejects a word with a good CRC when its SNR estimate is too low. Two changes:

  • A Fox the decoder does not know gets MSHV's lower floor. The words WSJT-X's floor throws away just under it turned out to be true decodes; chance passes read much lower.
  • A Fox it knows — the call in DX Call, or the Fox it last decoded — has no floor: in a period without that Fox, a chance pass would also have to hit the call's bits.

"The Fox it last decoded" is remembered for a few Fox periods without a decode of it, and forgotten at once on a band or mode change, as the FT8 hint lists are. Its grid, from its last CQ, is remembered with it.

11

Messages that can be foreseen

JTDX_contest

The a-priori pass: tell the decoder what it already knows. Lines marked *.

FT8 decoders state MyCall DxCall in advance and let the decoder find the rest. The same works for SuperFox: a symbol whose value is known is handed to the polar decoder as one more fixed position. When the search has found nothing, the decoder tries — for a Fox it knows — the messages it can state almost whole:

  • the Fox's CQ, with its grid from DX Grid or remembered from its last CQ;
  • its answer to you and nobody else — your report while you call, RR73 once you have sent your R+report;
  • your report or RR73 beside reports to others, and messages of nothing but reports.

As in FT8, what concerns your own call is tried only while a transmission of yours is recent and the QSO expects that answer, and only from the Fox in DX Call. Every stated bit must stand in the decoded word, and its SNR estimate must reach a floor of its own. It has to be whole messages: the Hound slots hold most of the code's least reliable positions (section 04), and with one of them left open the rest of what is known is worth little — which is what the list decoder is for.

12

A list decoder

JTDX_contest

CRC-aided successive-cancellation list decoding, written for this program.

Where WSJT-X's decoder keeps one path, a list decoder keeps the L most probable ones at every decision, and at the end the CRC chooses among them. A wrong early decision is no longer final: the right path survives in the list. With a list of one it takes exactly WSJT-X's decisions, and the tests check that decision for decision. It can also be told candidates for a slot: "this Hound slot holds one of these calls, or nothing". The next two passes use it; the ordinary search does not — it stays WSJT-X's.

CRC-aided successive-cancellation list decoding (CA-SCL) is the decoding technique of 5G mobile networks: state of the art, now in amateur radio.

One path against a list of paths

Schematic. On the left a wrong early decision dooms the word; on the right the list keeps the right path alive and the CRC picks it.

SUCCESSIVE CANCELLATION one decision per symbol; a wrong early one is final wrong turn ✗ CRC fails the right word, never reached LIST DECODING (JTDX_contest) the L most probable paths kept; the CRC picks ✓ CRC ok
13

The Hounds you heard: the pool pass

JTDX_contest

A busy Fox's message is full of calls — often calls you have heard. Lines marked *.

In S-Hound mode the odd periods go to the FT8 decoder, so the program hears who calls and who answers the Fox in DX Call — and those are the stations the Fox's next messages will name. Their calls form a pool. When the search and the a-priori pass have found nothing, the list decoder runs with the Fox told and every Hound slot held to one of the pool, your own call while your QSO runs, or empty. No order, no placement and no guess at the message are needed.

  • Your own call follows the QSO: a report may come once you have called, RR73 only after your R+report.
  • A Hound leaves the pool after several odd periods that were listened to without it — while you transmit, the pool's clock stands.
  • The pool is emptied on a band or mode change, when DX Call changes and when S-Hound mode is left.

From the odd period to the next Fox period

Schematic: the calls heard become the candidates of every Hound slot.

ODD PERIOD · FT8 DECODER VP8PJ W1AW FN31 VP8PJ DL1ABC R-12 VP8PJ JA2XYZ PM95 VP8PJ PY3QRP -10 VP8PJ G5NOP IO91 ... THE POOL W1AW DL1ABC JA2XYZ PY3QRP G5NOP your call* empty * while your own QSO runs NEXT EVEN PERIOD · EACH HOUND SLOT slot 1 = one of the pool slot 2 = one of the pool slot 3 = one of the pool slot 4 = one of the pool slot 5 = one of the pool slot 6 = one of the pool only these are searched
14

When no Hound was heard: the list pass

JTDX_contest

Only the Fox told — and then nothing at all.

Often the Fox's Hounds are not in the pool: you hear the DX and not its pile-up, or you have just arrived on the frequency. One thing is still known — the Fox. So the list decoder runs once more with only the Fox told and the Hound slots free (its lines are marked *), and then a round with nothing told, under MSHV's floor, for a Fox the decoder does not know yet (those lines are ordinary decodes). The pass states nothing about you: if it prints RR73 to your call, the decoder found it in free slots and the 21-bit CRC vouches for it. Against false lines only the best few paths are tried against the CRC, only the message types that carry the Fox's call are accepted, and the SNR estimate must reach a floor of its own.

15

Following the Fox

JTDX_contest

The decoder finds the Fox by itself.

It looks for the Fox's sync tone at 750 Hz, and then follows the frequency where it last decoded it. After a few Fox periods without a decode it tries 750 Hz and the last place in turn, and when you move the dial it starts again at 750 Hz. A Fox that announces another frequency can be set in the ini file. The decoder never reads the RX frequency, but two things in the program do — the Rx Frequency window shows the lines near it, and in the odd period Filter keeps the decode around it — so while the mode is on the RX frequency box follows the Fox: greyed, set where the decoder last decoded it. Your own RX frequency comes back when you leave the mode.

16

MSHV's three sync windows

JTDX_contest

A last step, when nothing else has decoded.

MSHV looks for the Fox in three places — the receive frequency, a window around 750 Hz and the whole band — and searches each one it finds. Here that is the last step of a Fox period: only when everything above has found nothing, the search and the passes run again on each of MSHV's candidates, each one only if it can end inside the RX budget, so that nothing decoded today comes any later: a period that decodes without it costs exactly what it did. Since the search runs in threads the step is cheap enough to be on by default; JTDX_SFOX_SYNC3=0 switches it off. The first real recordings will say what it is worth on the air.

17

Operating in S-Hound mode

JTDX_contest

What the program does around the decoder.

  • DXpedition → SuperFox mode (S-Hound), or a right click on the Hound button: the even period goes to the SuperFox decoder, the odd one to the FT8 decoder, and the Hound button reads S-Hound.
  • You call by double-clicking a Fox line and stay on your own TX frequency for the whole QSO, anywhere in the passband; the program never moves you. Lock Tx=Rx is ignored in this mode, so that a double click cannot put your transmitter on top of the Fox.
  • Your TX period is the odd one and cannot be changed while the mode is on; your earlier choice comes back when you leave it.
  • A Fox that has not been decoded cannot be called — SuperFox's own rule. "Decoded" means recently and on this dial frequency.
  • A Fox with a compound callsign sends only a hash of it, and a busy one may never send its call in full. Type the call into DX Call, and its lines read K1ABC <VP2X/K1JT> RR73 from the next transmission on. A line with two long hashed calls keeps its report whole rather than cutting it.
  • Verification of the one-time code, for SuperFox and also for the old-style Fox's CALL.123456 free text, through the program's own network layer (which works without OpenSSL on Windows): VP8PJ verified on green or VP8PJ invalid on red. What is sent: the Fox's call, the time and the code — switch it off with DXpedition → Verify Fox online (OTP). More than one server can be listed, best first: a later one is asked only when every earlier one said it does not know the call, so no weaker server can overrule a better one.
  • JTDX_contest does not transmit SuperFox.
18

A code definition of its own

JTDX_contest

The same code, defined without the restricted table.

In WSJT-X the polar code's definition — which 50 of the 128 positions carry the message — sits in a file whose licence notice restricts it to WSJT-X. JTDX_contest does not ship that file. Its definition was measured from transmissions of WSJT-X's own transmit tool, and the tests rebuild it from nothing and check it against WSJT-X's decoder. It is the same code: every transmission decodes exactly as before.

19

The settings

JTDX_contest

Every addition is a setting until real recordings decide.

The decoder's recipe was worked out on simulated transmissions, so each of its choices is a setting, read from the environment when the decoder starts; the defaults are provisional and will be revisited with the first real recordings. A value that cannot be read stops the decoder with a message that names it — a setting is never silently ignored.

settingwhat it controlsdefault
JTDX_SFOX_NORMthe normalisation of tone bins that hold QRM (07); JTDX_SFOX_NORMX its thresholdon
JTDX_SFOX_THREADSthreads of the search (08): 0 = as many as the FT8 decoder, 1 = serialas FT8
JTDX_SFQRMthe FT8 step (09): 0 off, 1 the quick round onlyboth rounds
JTDX_SFOX_FLOORthe floor for a Fox that is not known (10)MSHV's
JTDX_SFOX_KNOWNno floor for a known Fox (10)on
JTDX_SFOX_APthe a-priori pass (11); APFAM the message families, APFLOOR1/2 its floors, APLOOKS the forms of the spectra tried, APAGE how long a Fox is rememberedon
JTDX_SFOX_POOLthe pool pass (13); POOLL list size, POOLLOOKS, POOLCRC paths tried against the CRC, POOLFLOOR, POOLAGE how long a Hound stayson
JTDX_SFOX_LISTthe list pass with the Fox told (14); LISTL, LISTLOOKS, LISTCRC, LISTFLOORon
JTDX_SFOX_LISTANYits round with nothing told (14); LISTANYFLOOR, LISTANYLOOKSon
JTDX_SFOX_SYNC3MSHV's three sync windows as the last step (16)on
20

What comes next

JTDX_contest

Real signals.

Everything above has been tested against WSJT-X's own decoder, against MSHV's, and by a regression suite, on simulated transmissions and a few recordings. What it still lacks is the air: a reasonable number of recordings of real SuperFox operations, both periods, with Hounds calling. They will decide the defaults, what each step is worth, and the figures this page will then show.

SuperFox was designed by the WSJT-X team; its polar code is by Nico Palermo IV3NWV. The decoder in JTDX_contest is a derivative of WSJT-X 3.0.2's by K1JT, K9AN and IV3NWV. SuperFox support is experimental: no figures are shown until there are real recordings to measure it on. ce3tsk.com · github.com/ce3tsk/jtdx_contest · How the FT8 decoder works · jtdx_contest@ce3tsk.com JTDX Contest Edition · Tihomir Sokcevic · CE3TSKCE3TSK · Tihomir Sokcevic · September 202673

Support this work on Ko-fi