Ken Andersen's blog about technology related subjects.
Wednesday, October 21, 2026
PostgreSQL for Radio Data System in Low Power FM Radio
This is a continuation from my previous post about PostgreSQL in LPFM.
In December 2023, KVWJ installed a new Nautel VX300 transmitter. You can read more about that at these places if you want:
This new transmitter enabled the station to display the song title and artist information on RDS equipped radios. We were excited to start doing this. I was afraid that this was a very complicated process, but it is quite simple. You send a netcat command to the transmitter with the text information you want. No problem. I originally accomplished this with a simple bash script. Here's a snippet from that:
SONGNAME=$(</mnt/zara/ZaraLogs/CurrentSong.txt)
NAMELENGTH=${#SONGNAME}
if [ "$NAMELENGTH" -gt 64 ]; then
SONGNAMECUT=$(echo $SONGNAME | cut -c 1-64)
echo "TEXT=$(echo $SONGNAMECUT )"|nc -i 2 -w 2 192.168.0.108 7005
echo "TEXT?"|nc -i 2 -w 2 192.168.0.108 7005 &
fi
if [ "$NAMELENGTH" -lt 65 ]; then
echo "TEXT=$(echo $SONGNAME )"|nc -i 2 -w 2 192.168.0.108 7005
echo "TEXT?"|nc -i 2 -w 2 192.168.0.108 7005 &
fi
I was surprised how easy it was to send RDS commands.
In 2026, however, we decided it was time to take RDS to the next level. One minor problem we had was that the CurrentSong.txt from ZaraRadio is reading the filename. If we had something wrong with the filename then the information would be displayed incorrectly on RDS. One common thing we run into is that filenames do not include "The" in them. So a song by "The Turtles" is simply labeled "Turtles" in the filename. This means that you see on RDS "So Happy Together - Turtles." This is not ideal.
I realized we could use the PostgreSQL database for this. I extended the database to include artist_display_name and track_display_name fields. I then added a template view with a form so you could edit the artist_display_name and track_display_name.
With the new artist_display_name and track_display_name fields and the template view in place, we were ready to move on from the bash script to something more sophisticated. My original implementation was a Python script watching for the Zara daily log to change and then looking up the song in the database to see if it had a display_name field populated. This was problematic because the file would change and sometimes the song in the log wouldn't be in the database yet. That's when I realized I could use a PostgreSQL database trigger and a Python listener for this. Here's a snippet from the Python:
# Wait for a Postgres notification, up to one second.
ready, _, _ = select.select([listener_conn], [], [], 1)
if ready:
listener_conn.poll()
while listener_conn.notifies:
current_song_category = None
notification = listener_conn.notifies.pop(0)
database_song_text, current_song_category = process_notification(
listener_conn,
notification,
)
So the Python code is waiting for a trigger to fire which then send a notification with a new playback ID when a new record is inserted into the playback table. More database queries are executed to then get the proper artist and track information:
SELECT CONCAT_WS(
' - ',
COALESCE(NULLIF(t.track_display_name, ''), NULLIF(t.track_name, '')),
COALESCE(NULLIF(a.artist_display_name, ''), NULLIF(a.artist_name, '')),
NULLIF(y.year::text, '')
)
FROM playback p
JOIN tracks t
ON t.id = p.trackid
LEFT JOIN artists a
ON a.id = t.artistid
LEFT JOIN years y
ON y.id = t.yearid
WHERE p.id = %s
LIMIT 1
The COALESCE database command allows the display_name field to be selected if it exists, otherwise it falls back to the regular name field.
With the right information we can build a string that can be sent to the transmitter. Instead of using netcat from the shell, we use a Python socket:
def send_rds_command(command: str) -> None:
with socket.create_connection((RDS_HOST, RDS_PORT), timeout=2) as sock:
sock.sendall((command + "\n").encode("utf-8"))
This architecture blew away my expectations. It's a foolproof way to control what information actually gets sent to the transmitter.
More is yet to come from my PostgreSQL in LPFM series...
© The Ramblings of Ken Powered by Bootstrap , Blogger templates and RWD Testing Tool

No comments :
Post a Comment