CSVF2 - radio text/file modem      (version 3.5.0, 2026-10-16)
==========================================================

CSVF = client server via frequency.

Sends text or a file over a voice radio as audio tones, and receives it again.
Station ID: press "Send callsign" (bottom of the window). A timer shows how long it has been
since your last callsign and flashes red from 9:30. A transmission gets your callsign by itself
only when it would otherwise go past 9 minutes without one, and closing the window sends it once
the channel has been quiet for 2 seconds. The callsign is spoken (NATO phonetics + numbers), or
tick "Send in Morse code" to send it as Morse (20 words per minute on a 2400 Hz tone, which
receivers ignore). The server scripts still send it at the start and end of every reply.
audio/website.wav plays right after the tones (1.5x faster, same pitch), with the first transmission
and then every 10th one (the server scripts count their replies the same way).
Tick "Auto callsign when the channel is clear" (bar under every tab) and the program sends your
callsign by itself when it is due (9:30 on the timer) or 45 seconds after your last transmission,
but only when nobody, you included, is sending. It needs your callsign and a working input device.

PRIVACY: the first time you run the program it asks you to accept a privacy policy (once). After
that it sends the callsign you enter, once per callsign, to ke9gdp.com, which lists it on the public
CSVF2 users page (https://ke9gdp.com/csvf2users) with the time it was first seen. Nothing else about
you is sent. Full policy: https://ke9gdp.com/csvf2privacy. If you do not accept, the program closes.

UPDATES: the window looks for a newer version when it starts (Settings tab: switch it off, or check
now) and always asks before installing. It keeps a backup of the files it replaces in update_backup/
and never overwrites your settings, document texts or audio clips.

Error correction (Settings tab, and both server scripts): Low, Medium, High or Maximum
adds a Reed-Solomon code (the kind QR codes use, in radio_fec.py) so the receiver can repair damaged
or unheard tones in text and files. The receiver needs no setting, but it must be this version or
newer. A 65 KB file has been received intact over the air at Extra extra fast with Low correction;
apart from that it has been tested in simulation.

Long transmissions are sent in bursts of 2 minutes with a silent gap between them, so a radio
with a time-out timer (e.g. TYT MD-390, 180 s) un-keys and resets. Each burst starts with a
steady 400 Hz keying tone for VOX radios. Both are settings on the Transmit tab.

Quick start
  1. Install Python 3.10 or newer.
  2. pip install -r requirements.txt
  3. python radio_gui.py   (or double-click CSVF2.pyw: the same without a console window)

radio_server_template.py runs a station that answers on its own: it listens all the time, and
when it hears the text "ping" it transmits "pong" (both texts are settings); it also answers
any message sent to path "document" by sending document.txt's text back as a plain message,
not a file. It has the same settings as the window and remembers them.
  python radio_server_template.py

This file is a TEMPLATE: it is meant to be copied and edited, not run as-is forever. To make
your own CSVF2 server -- your own commands, your own files -- copy radio_server_template.py
and edit route_for() and transmit_reply() in it (see the comments at the top of the file).

radio_server_25.py is a worked example of exactly that: it started as a copy of the template
and now has three more paths, alongside "ping" and "document":
  "chatgpt"  sends your message to OpenAI (gpt-4o-mini) and sends the reply back, capped at
             100 calls a day. Needs your own OPENAI_API_KEY environment variable and a working
             internet connection -- see the comments at the top of the file. Nothing here
             stores that key in a file.
  "smpost"   logs your message to a small local file (sm_posts.json, created on first use,
             keeping only the last 5 posts) and replies "200".
  "smget"    replies with the last 5 logged posts, numbered, oldest first.
document_25.txt is what "document" sends back on this server: a short reference for these
paths, meant for a person to read, not a program to parse.
  python radio_server_25.py

The Server tab of the window talks to a station built from either server script: you give an
8-character path (like the part after the / in a web address), send text or a file, and the
tab starts listening the moment the send is finished. After your last callsign it sends an
end tone (450 Hz, 2 s) so the server knows you are done and answers 1 s later, instead of
waiting a fixed 30 s.

Transmit tab : pick Text or File, type your callsign, choose a Speed, Transmit.
Receive tab  : choose the radio's audio input, Start listening. The Sensitivity slider sets how
               weak a signal may be and still start a message (higher = weaker signals, but a
               greater chance of starting on noise; near the far right, down to 1%, it starts
               on plain noise). Messages appear in the log; when a file
               finishes you get a save prompt.

Files
  radio_gui.py               the window
  CSVF2.pyw                  double-click this to start the window with no console window
  csvf2.png                  the logo shown for a few seconds when the window starts
  csvf2.ico, csvf2_icon.png  the window / taskbar icon (a copy of the ke9gdp.com favicon; delete them for the
                              default icon)
  radio_server_template.py   the automatic ping/pong + "document" station -- edit this to
                              build your own server
  radio_server_25.py         the same, plus "chatgpt", "smpost" and "smget" (see above) -- a
                              worked example, also yours to edit
  document.txt               the text radio_server_template.py sends back for path "document"
  document_25.txt            the text radio_server_25.py sends back for path "document"
                              (replace either one with your own, keeping to letters, digits,
                              space and !@#$%^&*() so nothing is dropped)
  radio_modem.py             the modem (encoder + decoder, no GUI, protocol described at the top)
  radio_fec.py               the Reed-Solomon error correction the modem uses (keep it next to
                              radio_modem.py)
  radio_online.py            the privacy-policy question, the one-time callsign registration and the
                              updater (the only code that talks to ke9gdp.com)
  radio_tutorial.py          the guided tour (keep it next to radio_gui.py)
  radio_tcp.py, radio_tcp_tab.py   the Server 2.0 tab: TCP-style connections between two stations
                              (keep them next to radio_gui.py)
  radio_feedback_tab.py      the Feedback tab: a chat with the author about feedback you sent
                              (keep it next to radio_gui.py)
  radio_account.py           the ke9gdp.com account window (log in, sign up, licence upload; keep it next
                              to radio_gui.py)
  radio_compress.py          compression of messages and files (keep it next to radio_modem.py; lossy
                              image steps use the optional Pillow package: pip install pillow)
  radio_map.py               the Map tab's widget (keep it next to radio_gui.py; it downloads map tiles
                              from openstreetmap.org only while the Map tab is open and tiles are ticked)
  audio/                     a-z (phonetic words) and 0-9 clips for the spoken callsign, and
                              website.wav (replace it with your own announcement, or delete it
                              to skip it). Keep this folder next to the .py files.

Full documentation: the CSVF2 page on the KE9GDP site.
You are responsible for operating within your licence and local rules, including for what a
third-party service like OpenAI sends back if you use the "chatgpt" path.
