Please do not post questions in the shout-box as it will be lost in a few days. Open your own topic in the appropriate section. Thank you.

Recent posts

#71
UberSDR / Re: Changed the GUI of Twente'...
Last post by Bas ON5HB - Sep 30, 2026, 11:40 AM
I have seen it, Sakis shown it to me yesterday.
Pretty cool.
#72
UberSDR / Re: Changed the GUI of Twente'...
Last post by Emmanuel SV1BTL - Sep 30, 2026, 09:51 AM
In case you want to install this WebSDR emulation, please read this: http://sv1btlham.no-ip.org:8901/sv1btl/readme.html

The Github is here: https://github.com/sv1btl/WebSDR-emulation-for-UberSDR
#73
UberSDR / Changed the GUI of Twente's li...
Last post by Emmanuel SV1BTL - Sep 29, 2026, 07:49 AM
I want to inform you that I have changed the GUI's layout of Twente's like UberSDR
Please check: http://sv1btlham.no-ip.org:8901/

#74
FernSDR / Re: FernSDR: what I learned fr...
Last post by magicint1337 - Sep 28, 2026, 09:21 AM
Thanks Emmanuel :)
#75
FernSDR / Re: FernSDR: what I learned fr...
Last post by Emmanuel SV1BTL - Sep 28, 2026, 09:05 AM
Congratulations Steven. Wish you success with the project.
#76
FernSDR / Re: FernSDR: what I learned fr...
Last post by magicint1337 - Sep 27, 2026, 09:01 PM
Here a few more images
#77
FernSDR / FernSDR: what I learned from N...
Last post by magicint1337 - Sep 27, 2026, 09:00 PM
Hey everyone,

It's been a long time since my last development update here. I took a break and used that time to rethink WebSDR from the ground up.

I looked at the WebSDR software that has come out recently, UberSDR among others, and at the problems people kept running into with the existing projects, especially installation and configuration. NovaSDR and PhantomSDR+ taught me a lot. They're not abandoned, but rebuilding them into the base I wanted would have meant rewriting them anyway.

So I did exactly that and started fresh. The result is FernSDR. (I promise this is the last one, no more new projects :D)

This is a preview, not a release yet. I'd love feedback before 1.0.



What's different

  • Written from scratch in C++, with no backend libraries at all. Everything is written for this project, including the FFT, the audio codec and the waterfall compression, and all of it is tuned for one job: serving a WebSDR with low latency and low bandwidth.
  • Built around modules. The core takes no dependencies. Anything that needs a vendor library, like RTL-SDR and other devices, comes as a module that you install from the admin panel.
  • A rebuilt AGC. It works on the same principle as ka9q-radio's: steady, with no pumping on noise, and a hang that holds the level through pauses in speech. Weak signals stay weak and strong ones don't blast you. It's the best-sounding AGC I've built.
  • An admin panel, finally. Reachable from your local network when you host over HTTP, and remotely when you host over HTTPS with a domain. From there you can:
    • watch every band and every listener live
    • change a dongle's gain on the fly
    • moderate the chat
    • set per-band corrections: crystal error, frequency offset, I/Q balance
    • install and switch modules
    • install updates with one click
  • Customizable without touching code.
    • Light and dark themes with your own accent color, and a background picture.
    • The S-meter as numbers or as an analog needle.
    • Widgets you can add, like band conditions, a lightning map, a UTC clock, pictures, links or an embedded page of your own.
  • Waterfall history. Up to 24 hours.
  • A new Svelte frontend. Small, fast, and optimized for phones as much as desktops.
  • Easy to try. A built-in demo input lets you run it without any receiver hardware.
  • Rewritten documentation. Written so newcomers can follow it too.



Benchmarks

This is the first time I'm publishing proper benchmarks against other software. I compared FernSDR with 10 other WebSDR receivers on identical generated input, one receiver at a time, with real browsers as listeners. A few results:

  • Latency (antenna to speaker): ~230 ms. The next best was 264 ms, most others were 375 ms to over 1 s.
  • Capacity: 1600 simultaneous listeners on two CPU cores, using under 100 MB of RAM. Most others topped out around 200.
  • Page size: 78 kB for the whole page.
  • Waterfall: about a third smaller than before, thanks to a new compression.
  • Weak connections: it keeps playing on a 24 kbit/s connection and recovers about a second after a 15-second outage.

To be fair, it doesn't win every row. PA3FWM's WebSDR still uses less CPU per listener, for example. The full tables, the method and the raw data will be published with the release, including where others do better.



Coming next

  • Decoders running in the backend: FT8, WSPR and more.
  • Updates through the admin panel, so you always stay current.



One more thing: while testing I found a critical security issue in UberSDR (CVE-2026-97723). I reported it to the developer, and we got it fixed right away. That's how open source should work.

Thanks to everyone for the support over the years. I hope to see many of you on FernSDR once it's released, and I'm looking forward to your feedback on the preview!
#78
PhantomSDR / Re: Upgrade to v. 4.2.0
Last post by Emmanuel SV1BTL - Sep 27, 2026, 06:29 AM
UPDATES TO RECENT VERSION 4.2.0

------------------------------------------------------------------------------
WHAT CHANGED IN update.sh
------------------------------------------------------------------------------

update.sh is the updater: run without arguments it reports what a new release would change, and with --apply it writes the files, leaving your configuration, your markers, your site information and your logs alone. The version you have works, but it had faults that only show at the worst moment, and one habit worth changing.

Backups are now easy to find. Every file an update overwrites is still saved first, but it used to go into a hidden folder called .update-backups, which does not appear in a file manager or in a plain ls — a backup nobody can find is not much of a backup, least of all with the receiver off the air. Backups now go into a plain visible folder instead:

  update-backups/phantomsdr-backup-20260923-164530.zip

One dated archive for each update, holding only the files that run actually replaced, each at the path it has in your tree. You can copy it to another machine as a single file, open it anywhere, or unpack it straight back over the installation. Where zip is not installed the archive is written as .tar.gz instead; nothing else differs. The three most recent are kept. Backups made by the older version are still there and can still be restored, and they are counted in the same three.

To put back what the last update replaced, either of these does it:

  ./update.sh --restore LAST
  ./update.sh --restore phantomsdr-backup-20260923-164530.zip

Each archive also carries a restore.sh that works on its own, if update.sh itself is ever the problem. Unpack the archive and give it your installation directory:

  ./restore.sh /home/$USER/PhantomSDR-Plus

Restoring could damage the updater. --restore puts back every file the update replaced, and update.sh is one of those files, so it was rewriting itself while it was still running — the one thing a self-updating script must never do. It now runs from a copy outside the tree while it restores, as it already did while it applied.

An update could disturb a running diversity relay. If you run the WebSDR diversity relay, its service was left running while its program file was replaced underneath it. It is now stopped with the rest and started again when the update finishes. As before, only the services belonging to the directory being updated are touched, so a second installation on the same machine is left alone.

The sample configuration files can be updated again. config.example.*.toml was protected so heavily that an improved sample could never reach you. Those files are now offered instead: you are asked before one is replaced, and you can keep yours, take the new one, or take the new one alongside yours as config.example.rtlsdr.toml.new and compare them at your leisure. Your real configuration — config.toml and config-<receiver>.toml — remains untouchable and is never even mentioned.

Smaller things: --help was cut short and never printed the environment settings it documents; every --apply left a stray temporary file behind in /tmp; and --prune, used before your first --apply, quietly did nothing instead of explaining that it has no earlier update to compare against.


------------------------------------------------------------------------------
REPLIES IN THE CHAT
------------------------------------------------------------------------------

Every chat message now has a small "↪ Reply" button next to its time. Clicking it shows "Replying to <name>: <first words of the message>" above the input box; type and send as usual, or press ✕ or Esc to cancel. The reply appears indented under the message it answers, with a short quote line naming who and what it answers. If that is higher up the list, the chat scrolls to it rather than to the bottom. A reply to a reply joins the same thread, so threads are one level deep. The same works on /mobile.

Nothing changed in the server for this. A reply is an ordinary chat message that begins with a small marker naming the message it answers, for example "[RE:2026-09-25 12:00:01|SV1BTL]"; the server stores and relays it like any other line, and the web page reads the marker. Some consequences worth knowing:

  - The server still keeps only the last 20 messages. When the original has left that history, a reply stays where it arrived and shows only "↪ <name> <time>".
  - The marker takes about 30 to 40 of the 200 characters a message may have.
  - A browser tab left open from before your rebuild shows the marker as plain text until it is reloaded.
  - Deleting a message from the admin panel works exactly as before. Deleting an original leaves its replies with the quote line only.

On the desktop page the arrow keys now move the cursor inside the chat box in both layouts; in one of them they used to tune the receiver instead.


------------------------------------------------------------------------------
RTTY, PSK31, NAVTEX, CW, FT8 AND FAX DECODERS
------------------------------------------------------------------------------

All of this is in the decoder windows of the web page; nothing changed in the server program. Everything below except FT8 was measured on synthetic signals with calibrated noise, SNR given in a 3 kHz bandwidth (2500 Hz for FT8 and fax); FT8 was measured on real recordings, as its paragraph says.

RTTY copies much better. The decoder looked for the start of each character only once per bit, so it read every character up to half a bit late or early, and once it lost step it tended to stay out of step for whole lines. It now finds the start of each character to the exact sample. On a clean signal that used to garble a quarter of the text now and then; for amateur RTTY (45.45 baud, 170 Hz) it now copies without errors down to 0 dB, with about 1.5% errors at −3 dB (was about 10%) and 20% at −6 dB (was 47%).

Three more faults are fixed. On weak signals a single noise click could stop decoding for the rest of the transmission, with no text and no error; it no longer can. With audio at 24 kHz or more the decoder printed nothing at all; it now works at any audio rate. And the ASCII setting in the Encoding list never decoded anything; it does now.

The SNR reading is real. It used to be a fixed proportion of the signal level and showed about 26 dB whatever was on the frequency. It is now measured on the two tones themselves and reads within about 1 dB of the true SNR from +20 dB down to −5 dB. An empty frequency reads about −12 to −15 dB.

There is a squelch. A new "Squelch (SNR)" slider, shown for Amateur RTTY and Weather RTTY, stops noise printing as random letters. Amateur RTTY starts at −5 dB and Weather RTTY at −8 dB; the far left turns it off. While it is closed the SNR figure is grey. Lower it if you want to try for very weak signals.

The receiver filter is narrower. Amateur RTTY had a 410 Hz filter, wide enough that a stronger station 250 Hz away wiped out the copy completely. It now uses 300 Hz, which removes that neighbour and still copies a signal tuned 40 Hz off. Weather RTTY goes from 690 to 594 Hz. The filter now also follows the decoder when Auto-tune or Auto-shift moves the tones, and when you change the Baud setting; before, it stayed where it was and could cut a tone off. NAVTEX/SITOR keeps its filter as it was.

Auto-shift now only changes the shift when a real two-tone signal is present.

PSK31 reports SNR on the same scale. Its reading was a genuine measurement, but about 17 dB higher than the RTTY figure and never below 0. It now shows SNR in 3 kHz like RTTY, within about 1 dB from +20 down to −5 dB, so 0 dB on the PSK31 panel is now a strong signal. The label reads "SNR" for PSK31; Olivia keeps "S/N", because its figure is the FEC quality. PSK31 decoding itself did not change, and neither did Olivia.

NAVTEX headers are read. Every message banner in the NAVTEX window used to read "ZCZC ????", with the real header (for example JA01: station, subject, serial number) printed as text on the line below it. The decoder tried to read the header the moment "ZCZC" arrived, before the rest of it had. It now waits for the whole header, so the banner reads "ZCZC JA01". The stray "NNN" that ended every message, just before the NNNN banner, is gone too; "N" in the text itself, as in a wind from NNW, still prints.

NAVTEX shows messages you tuned into late. Text received outside a ZCZC ... NNNN frame used to be thrown away, so if you tuned in after a message had started, or its header was lost to noise, you saw nothing until the next message. Such a message is now shown under a "(header missed)" banner. It only opens once several characters in a row arrive with both of their NAVTEX transmissions agreeing, which noise practically never produces: half an hour of pure noise opened nothing. It closes at NNNN as usual, with "(signal lost)" if the signal goes first, or when a new message begins. The decoder also works out the right order of the two copies of each character by itself now; before, someone joining in the middle got that right only half the time, and then lost NAVTEX's error protection without any sign of it. Messages that start normally decode at least as well as before.

CW stays quiet on an empty channel. The CW decoder locked onto plain noise and printed a stream of junk, a couple of hundred characters every five minutes, mostly E, I and bracketed dot-dash patterns. It now prints only when there is a clear difference between key-down and key-up, which two hours of pure noise never reached, while a signal still readable at −10 dB has plenty. The speed and tone shown in the window are held back the same way. Weak-signal copy did not change.

CW copies slow and Farnsworth sending. With Farnsworth spacing (characters sent fast, with longer gaps between them) the decoder could lose its speed lock in the middle of a QSO and print every element as a dash: "TTTT TTT". Slow CW, 8 to 12 wpm, started the same way. With wide Farnsworth spacing every letter also came out as a word of its own ("C Q C Q"). Both are fixed; from 8 to 40 wpm, and Farnsworth at every stretch tried, it now copies with a few percent of errors or fewer at 0 dB.

FT8, FT4 and FT2 show a real SNR. The SNR next to each decode was not a signal-to-noise ratio at all but a rescaled internal match score, and it hardly moved with the signal: a station WSJT-X reads at −20 dB showed about +1 dB, one at +10 dB about +5 dB. It is now measured on the decoded signal itself, against the noise around it, and referred to 2500 Hz as WSJT-X does. Checked on the 31 test recordings that come with the ft8_lib decoder library, 255 of whose decodes also have WSJT-X's readings: the two now agree on average across the whole range from −15 to +14 dB, typically within about 3 dB for a single decode. Decoding itself did not change; every one of the 354 messages in those recordings still decodes. FT4 and FT2 use the same method, but there are no test recordings for them, so compare them with WSJT-X on the air if you can. If you run the automatic spot reporter (autorun), its PSKReporter spots carry this SNR, so they are now correct as well; restart autorun after the update so it loads the new decoder.

The FT8 decoder is a separate file, frontend/public/decoders/ft8_lib.wasm, which the frontend build copies into place. Browsers keep this kind of file for a long time, so after the rebuild everyone should reload the page with a hard refresh (Ctrl+Shift+R, or Cmd+Shift+R on a Mac), or FT8 may keep showing the old numbers. The new file is 48254 bytes; the old one was 37297. jsdsp/ft8_wasm/wasm_wrapper.c is its source, included for anyone who builds the decoder themselves; the page does not need it.

Weather fax keeps the whole chart. The decoder recognises the start and stop signals of a fax by their 300 Hz and 450 Hz rhythm, but it only looked at the first quarter of each line. A hatched area in a chart whose spacing happened to match that rhythm was taken for a start signal, and the decoder then threw away the next 20 seconds of the chart as phasing lines. It now requires the rhythm across the whole line, which the real start and stop signals always fill and chart content practically never does. That margin also let the detection threshold come down, so start and stop are now recognised on much weaker signals (down to about 0 dB SNR, from about +2 dB). Charts received that weak used to come out shifted sideways, because the decoder never saw the start signal and so never lined the picture up. At the very weakest levels the line-up now also averages the phasing lines before it measures them. On test transmissions the chart now lines up to within 1% of the line from strong signals down to about −1 dB.

The fax timetables were checked time by time against the current NOAA worldwide radiofax schedule (edition of 7 March 2025) and all matched. Two things it lists were missing and have been added: the Chilean station CBM Punta Arenas (4322 and 8696 kHz), now in the station list with its own timetable, and four extra Charleville (VMC) broadcasts at 0000, 0345, 0500 and 0515 UTC that go out on 11030 kHz only, beamed towards Tasmania.


------------------------------------------------------------------------------
SHORTER AUDIO DELAY
------------------------------------------------------------------------------

Listeners now hear the receiver noticeably sooner. Nothing changed in the server program, in audio_sps (it stays at 12000, as the decoders need) or in fft_size; all of it is in the web page.

Most of the delay was in the listener's browser, in the part that unpacks the compressed audio. The server sends a 35 ms packet of FLAC audio every 35 ms, but the decoder the page used collected its output into fixed chunks of about 85 ms before letting any of it through: it gave nothing for the first five packets and then released a chunk every two or three packets, holding back about 160 ms on average and up to 200 ms. No setting could avoid it. The page now has its own FLAC decoder that releases each packet the moment it arrives. FLAC is lossless, so this can be checked exactly: on 20 seconds of real receiver audio it produced the same samples as ffmpeg, all 240 240 of them, and the sound and its level are unchanged. The digital decoders still receive the same 12 kHz audio.

The playback buffer no longer grows and stays grown. After any network hiccup the extra audio that arrived in a burst used to stay queued for the rest of the session, so a listener who once had a bad second kept the extra delay until they reloaded. A small difference between the listener's sound card clock and the receiver's clock also slowly shrank the buffer until the audio broke up for a moment, or grew it without limit. The page now watches how much spare audio is actually needed and, when the buffer has more than that for a minute, removes a few milliseconds at a time; when it runs short, it adds a few. The cut or the repeat is made where the sound repeats itself, one cycle of a tone or one voice pitch period, so it cannot be heard. Only what goes to the loudspeaker is touched: the decoders and the recorder still receive every sample. If the listener's connection turns out to need the larger buffer after all, the page waits longer before trimming again.

Phones start with a smaller safety margin: 50 ms instead of 100 ms. That is the second buffer step, which phones use by default; on the desktop page it is the x2 setting. Now that the buffer adjusts itself to the connection, the margin is only extra safety on top of the worst delay the connection showed in the last minute. A listener whose audio still breaks up can move the buffer control one step higher, as before.

Measured on a phone over home Wi-Fi: 140 ms from the packet leaving the server to the loudspeaker (35 ms of packet, 105 ms of buffer), against 231 ms with the old decoder, which also held its roughly 160 ms of audio out of sight of that figure. With the lower margin a phone should settle below 100 ms after its first minute. The network between the receiver and the listener adds to this and is outside anyone's control.

Three page addresses help to look into it. Adding ?diag=1 to the address of /mobile shows the delay and the state of the buffer under the controls; on the desktop page it writes the same line to the browser console (F12) every ten seconds. Adding ?flac=wasm brings back the old decoder and ?drain=0 turns the buffer adjustment off, so either can be compared with the new behaviour. These affect only the browser tab they are typed into.


QuotePlease unzip the file and replace the files in ~/PhantomSDR-Plus folder.
Then recompile both backend AND frontend and restart the server.
#79
UberSDR / UberSDR as NTP-server.
Last post by Bas ON5HB - Sep 25, 2026, 02:29 PM
Hi all,

If you plan on taking ouside NTP-requests (not docker only), then remove the timeserver you use.
Typical ntpsec. And install the ntpdate util.
In Ubuntu:

apt remove ntpsec (or chrony)
apt install ntpdate

Above not needed when planning inside docker only.

You can use UberSDR as NTP stratum 1 sever.
All you need to do is adding the NTP addon and configure it.

See here: https://websdr.heppen.be/addon/ntp/

You cannot view this attachment.

Then you need to configure it, goto the ubersdr directory, then ntp dir.

You see there docker-compose.yml, in the top section you need to modify:

services:
  ubersdr-ntp:
    image: madpsy/ubersdr-ntp:latest
    container_name: ntp
    restart: unless-stopped
    cpu_shares: 512
    ports:
      - "123:123/udp"

    # Every setting is in ./config/config.json — the receivers, frequencies,
    # upstream NTP servers and ports. Edit it there, then ./restart.sh.

Then goto the config directory and modify config.jason, for Europe:

  // The receiver this runs beside, on WWV's three strongest carriers. 5 MHz
  // carries the night, 15 MHz the day, 10 MHz most of both. Addon containers
  // are on the receiver's own Docker network, which its default
  // timeout_bypass_ips exempts from session limits, so no password is needed.
  // A receiver that does not exempt it wants its bypass password in each one.
  "sources": [
    { "name": "DCF77",  "url": "http://ubersdr:8080", "carrier_hz": 77500, "extra_delay_ms": 0.00 },
    { "name": "MSF",    "url": "http://ubersdr:8080", "carrier_hz": 60000, "extra_delay_ms": 0.00 },
    { "name": "ALLOUIS","url": "http://ubersdr:8080", "carrier_hz": 162000, "extra_delay_ms": 0.00}

  //  { "name": "local-5",  "url": "http://ubersdr:8080", "carrier_hz": 5000000 },
  //  { "name": "local-10", "url": "http://ubersdr:8080", "carrier_hz": 10000000 },
  //  { "name": "local-15", "url": "http://ubersdr:8080", "carrier_hz": 15000000 }
  ],

Outside Europe you can use other time-signals.

More info here: https://github.com/madpsy/ubersdr-ntp

To run the NTP-server system-wide and also taking requests you need to instal the systemd-service.

https://github.com/madpsy/ubersdr-ntp/tree/main/systemd

Just copy the file and install it, all info is written in the file itself.

Then reboot, most easy way, and after some 60 minutes it should start taking time-servers and feed your NTP-server.
Do insert a few ntp-servers or pool entries to have fallback time-servers.

To update the systemclock every minute, crontab -e and add:

* * * * * /usr/sbin/ntpdate 127.0.0.1
That's it.

Nathan didn't make it easier just yet....he probably will in the future. ;D



 [/code]
#80
UberSDR / Re: UberSDR now registering on...
Last post by Bas ON5HB - Sep 23, 2026, 03:29 PM
Quote from: Emmanuel SV1BTL on Sep 22, 2026, 06:19 PM
Quote from: Bas ON5HB on Sep 22, 2026, 04:03 PMThen make sure your router opens port 8091 TCP

Done Bas, TNX
Please correct this 8091 to 8901 here and here: https://github.com/madpsy/ka9q_ubersdr/issues/130

Done, was a typo on my part. Tnx.
Powered by EzPortal