Agent skill

Freedv Test

by adecarolis in adecarolis/wfweb

Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual wfweb rigs.

GPL-3.0Auto-check passedProductivity & Automation

Install Freedv Test

skills CLI
$ npx skills add adecarolis/wfweb --skill freedv-test -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install adecarolis/wfweb freedv-test --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/adecarolis/wfweb.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/freedv-test .claude/skills/freedv-test && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
freedv-test
GitHub stars
108
Token cost
~2.8k tokens
SKILL.md length
1,332 words
Files
1
Skills in repo
5
Repo updated
First seen
Licence
GPL-3.0

At a glance

Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual wfweb rigs.

  • Works in 3 steps: No browser mic. The headless Playwright… → The assert is the server log, not the… → Sync is unreliable with synthetic audio.…
  • The user asks to test FreeDV
  • SKILL.md covers Prerequisites (verify before…, Boot the bench, Per-tab UI bring-up (do for… and TX: inject synthesized audio…, plus 4 more sections
  • Calls make

What it does

Freedv Test is an agent skill from adecarolis/wfweb. Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual wfweb rigs. Boots the testrig bench, puts both rigs in RADE mode, transmits from one by injecting synthesized audio into the server's RADE encoder (the headless browser has no mic), and verifies the other decodes the EOO callsign from the server log. Use when the user asks to test FreeDV, test RADE, or verify the RADE EOO callsign TX/RX path. (RADE only — classic 700D/700E/1600 are out of scope.)

Its SKILL.md is about 2.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Productivity & Automation, covering Browser automation. The repository describes itself as: Turn any Icom transceiver into a web-accessible station. Headless wfview fork with browser-based control, audio streaming, SSB, FreeDV, AX.25, CW, JS8 and FT8. The licence is GPL-3.0.

When your agent uses it

  • The user asks to test FreeDV
  • Verify the RADE EOO callsign TX/RX path

Example prompts

  • “/freedv-test”

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. No browser mic. The headless Playwright chromium denies getUserMedia, so you can't drive a real voice TX. Instead you inject synthesized…
  2. The assert is the server log, not the DOM. The decoded callsign appears in the browser for only 5 seconds (the server clears it via a…
  3. Sync is unreliable with synthetic audio. This is the honest limitation: feeding the neural RADE codec synthesized (non-speech) audio…

What it can do on your machine

Read from SKILL.md and the folder at commit 7cd5d2f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • make

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Freedv Test loads about 2.8k tokens when it runs. Until then it costs about 121 tokens; SKILL.md has 1,332 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~121
When it runs · the whole SKILL.md, loaded when a task matches
~2.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from adecarolis/wfweb at commit 7cd5d2f, republished under its GPL-3.0 licence (© adecarolis). 1,332 words, ~2,845 tokens.

Download SKILL.mdSave it as .claude/skills/freedv-test/SKILL.md (or your agent's skills folder).
name
freedv-test
description
Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual wfweb rigs. Boots the testrig bench, puts both rigs in RADE mode, transmits from one by injecting synthesized audio into the server's RADE encoder (the headless browser has no mic), and verifies the other decodes the EOO callsign from the server log. Use when the user asks to test FreeDV, test RADE, or verify the RADE EOO callsign TX/RX path. (RADE only — classic 700D/700E/1600 are out of scope.)

Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual rigs: one station transmits a RADE signal and the other must decode the transmitting station's callsign from the EOO (End-Of-Over) frame. Bench-only (virtual rigs, no hardware, no real RF). RADE only — the classic FreeDV modes (700D/700E/1600) are out of scope.

This is the outlier among the mode-test skills. Unlike CW/FT8/JS8 (browser-side codecs), RADE runs server-side (C++), and it breaks the pure-browser pattern in three ways you must handle:

  1. No browser mic. The headless Playwright chromium denies getUserMedia, so you can't drive a real voice TX. Instead you inject synthesized audio straight into the server's RADE encoder by sending 0x03 TX-audio WebSocket frames (the same frame type the mic worklet uses) via window.sendTxAudio().
  2. The assert is the server log, not the DOM. The decoded callsign appears in the browser for only 5 seconds (the server clears it via a timer), so reading window.radeRxCallsign is racy. The authoritative, race-free signal is the RX rig's log line RADE: decoded callsign from EOO: "<CALL>".
  3. Sync is unreliable with synthetic audio. This is the honest limitation: feeding the neural RADE codec synthesized (non-speech) audio produces a modem signal that arrives fine — the receiver sees a healthy SNR (measured up to +15 dB) — but RADE's modem sync still fails most of the time, so the EOO doesn't decode. It works (proven both directions), just not dependably: expect to retry several times, and a clean-room run can miss 5×+ in a row. The robust fix is to inject real speech instead of tones/noise (see "Improving reliability"). Retry on miss and transmit a long burst (~12–15 s).

Prerequisites (verify before starting; fix and stop if missing)

  1. ./wfweb and ./tools/virtualrig/virtualrig are built (qmake wfweb.pro && make -j$(nproc); cd tools/virtualrig && qmake && make -j$(nproc)). The server build is required — RADE is server-side and disabled in the Standalone bundle.
  2. Repo .mcp.json includes --ignore-https-errors for the playwright MCP. Verify: pgrep -af @playwright/mcp must show --ignore-https-errors. If .mcp.json was just edited, the user must restart Claude Code. Stop and tell them.
  3. Load the playwright tools via ToolSearch: select:mcp__playwright__browser_navigate,mcp__playwright__browser_snapshot,mcp__playwright__browser_click,mcp__playwright__browser_type,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_wait_for,mcp__playwright__browser_evaluate,mcp__playwright__browser_close,mcp__playwright__browser_tabs,mcp__playwright__browser_select_option.

Boot the bench

./scripts/testrig.sh up 2

Both rigs come up on 14.074.000 USB (FreeDV is a USB data mode — same freq + mode means audio gates and A's RADE signal reaches B). Prefer a freshly-booted bench: RADE sync gets flakier after many TX cycles on a long-lived bench.

Per-tab UI bring-up (do for both A and B)

  1. browser_navigate to the rig URL.
  2. Wipe stale browser state: browser_evaluate () => { localStorage.clear(); sessionStorage.clear(); }, then browser_navigate to the same URL again.
  3. Click the CLICK TO START splash; poll until window.audioEnabled === true (RX audio enables ~1 s later — B's RADE decoder is fed from this RX-audio path).
  4. Set the station callsign (this becomes the EOO callsign — it's encoded on the first TX frame, so set it before transmitting): window.App.callsign.set('W1AAA') then window.sendReporterConfig() (pushes it to the server's reporterCallsign, which sets the RADE TX callsign). A: W1AAA, B: W1BBB.
  5. Enter RADE: window.toggleFreeDV() (RADE is freedvModes[0], advertised first by the server; opening forces USB). Confirm window.freedvEnabled === true and window.freedvModeName === 'RADE'.

Stable handles / state:

  • Indicators: #freedvSyncEl (SYNC/NO SYNC), #freedvSnrEl, #freedvCallsign (only present while a decoded callsign is showing — clears after 5 s)
  • Functions (global): toggleFreeDV(), cycleFreeDVMode(), sendReporterConfig(), send(obj) (raw WS command), sendTxAudio(arrayBuffer) (raw 0x03 TX-audio frame)
  • State (globals on window): freedvEnabled, freedvModeName, freedvSync, freedvSNR, radeRxCallsign (the 5 s flash)

TX: inject synthesized audio (the receiver-less mic substitute)

The headless browser can't capture a mic, so transmit by streaming synthesized 48 kHz Int16LE PCM as 0x03 frames. Frame layout: [0x03][0x00][seq_u16LE][reserved_u16LE][PCM_Int16LE…]. Run this in browser_evaluate on the transmitting tab, keeping that tab foreground for the whole burst (background tabs throttle setTimeout, stalling the 20 ms pacing and breaking the stream):

js
async () => {
  const RATE = 48000;
  function frame(seq, tSec) {
    const N = 960; const buf = new ArrayBuffer(6 + N*2); const dv = new DataView(buf);
    dv.setUint8(0,0x03); dv.setUint8(1,0x00); dv.setUint16(2,seq&0xFFFF,true); dv.setUint16(4,0,true);
    const f0 = 140 + 30*Math.sin(tSec*5.0);          // pitch with vibrato
    for (let i=0;i<N;i++){ const t=tSec+i/RATE;
      let v = 0.6*Math.sin(2*Math.PI*f0*t) + 0.3*Math.sin(2*Math.PI*(f0*2)*t)
            + 0.25*Math.sin(2*Math.PI*(f0*3.5)*t) + 0.15*Math.sin(2*Math.PI*(f0*6)*t);
      let s = 0.95*Math.max(-1,Math.min(1,v));        // NEAR FULL-SCALE — quiet audio won't sync
      dv.setInt16(6+i*2, s<0 ? s*32768 : s*32767, true);
    }
    return buf;
  }
  window.send({cmd:'enableMic', value:true});
  await new Promise(r=>setTimeout(r,300));
  window.send({cmd:'setPTT', value:true});
  for (let k=0;k<600;k++){ window.sendTxAudio(frame(k, k*0.020)); await new Promise(r=>setTimeout(r,20)); } // ~12 s
  window.send({cmd:'setPTT', value:false});           // PTT-off → server generates the EOO frame w/ callsign
  await new Promise(r=>setTimeout(r,300));
  window.send({cmd:'enableMic', value:false});
  return 'TX done';
}

Two things make or break this: the audio must be loud (~0.95 full-scale — a quiet signal lands at ~−5 dB SNR and never syncs), and the burst must be long (~12 s, raise to 15 s on retry) so the receiver acquires sync before the trailing EOO. A lone EOO with no preceding signal will not decode.

Round-trip sequence (with retry)

For each direction (A→B, then optionally B→A):

  1. On the TX tab (foreground), run the injection block above.
  2. Assert via the RX rig's log — grep for the decode:
    grep 'RADE: decoded callsign from EOO' .testrig/wfweb_1.log   # B is the RX side for A→B
    PASS when it shows decoded callsign from EOO: "W1AAA". (For B→A, grep .testrig/wfweb_0.log for "W1BBB".)
  3. If no decode, retry the injection (sync is probabilistic). Bump the burst to ~15 s (k<750). Give up after 4 attempts and report the SNR you saw (window.freedvSNR on the RX tab) — a floor value (−5) means the signal isn't arriving; a higher value that still won't sync means marginal modem lock.
  4. Optionally, to screenshot the 5 s browser flash: right after a successful TX, switch to the RX tab and poll window.radeRxCallsign tightly (every ~120 ms); the instant it's truthy, browser_take_screenshot. This is best-effort — the log is the real evidence.

Save screenshots as .playwright-mcp/wfweb-rade-A.png / ...-B.png (project-dir/.playwright-mcp/ only; /tmp is rejected).

Show full SKILL.md (496 more words)Show less

The tone/noise synthetic audio above is the weak link — it gives good SNR but marginal sync. Two more reliable approaches, in order of preference:

  • Inject real speech. Fetch a short speech WAV (mono, any rate — resample to 48 kHz Int16 in JS), and stream its samples as the 0x03 payload instead of the synthesized tones. Real speech drives the RADE encoder to produce a properly-syncable modem signal, which is what RADE was trained on. This is the right long-term fix.
  • Enable a fake mic device in the MCP (--use-fake-device-for-media-stream --use-fake-ui-for-media-stream on the chromium launch) so getUserMedia returns Chromium's fake audio and the real mic→RADE path works. This needs a .mcp.json change + Claude Code restart, so confirm with the user first.

Until one of those is in place, treat a PASS as "decoded on retry" and a no-decode-after-N as inconclusive (capability is proven; the synthetic-audio test is just flaky), not a regression.

Teardown and report

  1. browser_close (both tabs), then ./scripts/testrig.sh down.
  2. Report PASS/FAIL per direction with the decode log line(s) as evidence. PASS = the RX rig's log shows RADE: decoded callsign from EOO: "<the TX station's call>".

Pitfalls — read before debugging

  • getUserMedia is denied headless (NotAllowedError) — there is no browser mic. You MUST inject 0x03 audio via sendTxAudio(); the normal mic/PTT UI path produces no TX audio. (Server confirms the route: a 0x03 frame in RADE mode is fed to the RADE encoder.)
  • Audio level matters. ~0.95 full-scale syncs; quiet audio sits at ~−5 dB SNR on the RX side and never locks. If B shows freedvSNR === -5 throughout, your TX is too quiet (or not arriving).
  • Burst length matters. RADE RX needs sustained signal to acquire sync before the EOO. ~12 s works ~half the time; 15 s is safer. A lone EOO (e.g. PTT-cycling with no injected audio) transmits the callsign frame but B never syncs to decode it — confirmed.
  • Sync is probabilistic — retry. Even done right, expect ~40–60% success per attempt with synthetic audio. Loop up to ~4 times. A fresh bench is more reliable than one that's run many TX cycles.
  • Assert on the server log, not the DOM. window.radeRxCallsign (and #freedvCallsign) is cleared after 5 s by radeCallsignClearTimer. The log line is permanent and race-free. Don't gate on freedvSync either — the callsign arrives in the EOO after sync is already lost (this is by design; see CLAUDE.md).
  • Keep the TX tab foreground during the ~12–15 s injection. Switching away throttles setTimeout and stalls the 20 ms frame pacing.
  • Callsign must be set before TX — it's baked into the EOO on the first TX frame (setTxCallsign ← reporterCallsign ← App.callsign).
  • Both rigs USB + same freq. toggleFreeDV() sets USB; the bench defaults both to 14.074. If they diverge, B won't gate A's audio in.
  • Don't run two RADE TXs at once. One TX at a time across the bench; let one direction's EOO decode before starting the other.
  • Never transmit on a physically-connected rig — virtual-bench only.

© adecarolis, GPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/freedv-test of adecarolis/wfweb.

Open the folder on GitHubat commit 7cd5d2f

Compare with similar skills

Freedv Test next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Freedv Test compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Freedv Test this skilladecarolis/wfweb108—~2.8kAutomated safety check: PassGPL-3.0
Agent Browserquran/quran.com-frontend-next1.9k40 repos~3.3kAutomated safety check: PassNone
Dev-Browser CLI AutomationSawyerHood/dev-browser6.7k1 repos~455Automated safety check: PassMIT
Browser Automationopenclaw/openclaw392k—~2.9kAutomated safety check: PassMIT
Camoufox CLIBin-Huang/camoufox-cli3501 repos~4.5kAutomated safety check: PassMIT
BrowserVibiumDev/vibium2.9k—~4.8kAutomated safety check: PassApache-2.0

Similar skills

  • Agent Browser

    quran/quran.com-frontend-next

    Automates browser interactions for web testing, form filling, screenshots, and data extraction.

    1.9k GitHub starsUsed in 40 repos~3.3k tokens
    Productivity & AutomationAuto-check passed
  • Dev-Browser CLI Automation

    SawyerHood/dev-browser

    Browser automation with persistent named pages via the dev-browser CLI. Use when users ask to navigate websites, fill forms, take screenshots, extract web…

    6.7k GitHub starsUsed in 1 repo~455 tokens
    Productivity & AutomationAuto-check passed
  • Browser Automation

    openclaw/openclaw

    A skill your agent uses when controlling web pages with the OpenClaw browser tool, especially multi-step flows, login checks, tab management, or recovery from stale refs/timeouts.

    392k GitHub stars~2.9k tokensUpdated today
    Productivity & AutomationAuto-check passed
  • Camoufox CLI

    Bin-Huang/camoufox-cli

    Anti-detect browser automation CLI & Skills for AI agents. An agent skill from Bin-Huang/camoufox-cli.

    350 GitHub starsUsed in 1 repo~4.5k tokens
    Productivity & AutomationAuto-check passed
  • Browser

    VibiumDev/vibium

    Automate browsers with the Vibium CLI. An agent skill from VibiumDev/vibium.

    2.9k GitHub stars~4.8k tokensUpdated today
    Productivity & AutomationAuto-check passed
  • Test AIRI display-model imports with agent-browser across stage-tamagotchi Electron, stage-web, and stage-pocket mobile web layouts.

    50k GitHub stars~1.1k tokensUpdated today
    Productivity & AutomationAuto-check passed

More from adecarolis/wfweb

  • Cw Test

    adecarolis/wfweb

    Run an end-to-end CW (morse) decode round-trip between two virtual wfweb rigs.

    108 GitHub stars~1.9k tokensUpdated 2 days ago
    Auto-check passed
  • Ft8 Test

    adecarolis/wfweb

    Run an end-to-end FT8 decode round-trip between two virtual wfweb rigs.

    108 GitHub stars~2k tokensUpdated 2 days ago
    Auto-check passed
  • Js8 Test

    adecarolis/wfweb

    Run an end-to-end JS8 message round-trip between two virtual wfweb rigs.

    108 GitHub stars~2.2k tokensUpdated 2 days ago
    Auto-check passed
  • Packet Test

    adecarolis/wfweb

    Run an end-to-end AX.25 packet QSO test between two virtual wfweb rigs at 300 bd and 1200 bd.

    108 GitHub stars~1.7k tokensUpdated 2 days ago
    Auto-check passed

Questions about Freedv Test

What does Freedv Test do?

Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual wfweb rigs. Freedv Test is an agent skill from adecarolis/wfweb. Run an end-to-end FreeDV RADE EOO-callsign round-trip between two virtual wfweb rigs.

When should I use Freedv Test?

Freedv Test fits situations like: the user asks to test FreeDV; verify the RADE EOO callsign TX/RX path.

How do I install Freedv Test in Claude Code?

Run `npx skills add adecarolis/wfweb --skill freedv-test -a claude-code`. Or copy the skill folder (.claude/skills/freedv-test in adecarolis/wfweb) into .claude/skills/freedv-test in your project. Claude Code loads it when a task matches its description.

How do I install Freedv Test in Codex?

Run `npx skills add adecarolis/wfweb --skill freedv-test -a codex`. Or copy the skill folder (.claude/skills/freedv-test in adecarolis/wfweb) into .agents/skills/freedv-test in your project. Codex loads it when a task matches its description.

Can I use Freedv Test in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add adecarolis/wfweb --skill freedv-test -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/freedv-test, .gemini/skills/freedv-test, .github/skills/freedv-test and .opencode/skills/freedv-test in your project.

What does Freedv Test need to run?

Going by SKILL.md and its folder, Freedv Test needs the command-line tools its instructions call (make).

Does Freedv Test access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Freedv Test safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Freedv Test use?

Freedv Test is published under the GPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Freedv Test use?

About 2.8k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Freedv Test?

Skills that share tags, products or a category with Freedv Test: Agent Browser (quran/quran.com-frontend-next, 1.9k stars), Dev-Browser CLI Automation (SawyerHood/dev-browser, 6.7k stars), Browser Automation (openclaw/openclaw, 392k stars) and Camoufox CLI (Bin-Huang/camoufox-cli, 350 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Freedv Test?

adecarolis (a GitHub user) maintains it in adecarolis/wfweb, which has 108 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on October 7, 2026.

Source: adecarolis/wfweb on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.