Radio Channel Logger

From W9CR
Revision as of 00:47, 14 February 2025 by Bryan (talk | contribs) (→‎Software)
Jump to navigation Jump to search

It seems to be a common requirement to make recordings of a radio or scanner output. I've not found much decent Free Software that can do this, and do it reliably in a headless configuration. What I've used is documented here, and will do 4 audio channels reliably.

My concept is based around a RPI 5 with Cmedia USB audio dongles as pictured below. This is compact and low power, while a full Linux environment allows you to do many other things as needed.


Features

  • 4 audio channels
  • Date/time logging down to the seconds
  • Configuration of settings per channel
  • Recordings are 8000 samples per second 16 bit WAV
  • VOX activated recording with VOX level set per channel
  • Low Overhead on as no voice coding is taking place, should run on a pi0 for at least one channel (not tested)
  • 1tb ssd used for recording.

Hardware

RPI 5

  • Case
  • SSD
  • nvme disk
  • 4 usb jumpers
  • PSU
  • Cmedia Dongles

Radio

Per channel

  • CDM radio
  • CDM radio interface cable

Linux

The system is standard raspbian Linux

What is different is that we are booting off the nvme directly.

Disk Setup

This is not a complete howto, but rather enough to figure it out. You may want to do it differently.

GPT table

Part 1 is /boot/firmware

Part 2 is lvm Leave a little at the end free, I picked 11gb

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   0700  Microsoft basic data
   2         1050624      1930430463   920.0 GiB   8E00  Linux LVM

LVM config

From here I make that part 2 a LVM phsyical volume with 'pvcreate', then make the entire thing a volume group with vgcreate

pvcreate /dev/nvme0n1p2
vgcreate vg0 /dev/nvme0n1p2

# pvs
  PV             VG  Fmt  Attr PSize    PFree
  /dev/nvme0n1p2 vg0 lvm2 a--  <920.00g    0

# vgs
  VG  #PV #LV #SN Attr   VSize    VFree
  vg0   1   4   0 wz--n- <920.00g    0

Next we need make the swap thick LV of 8gb

lvcreate --name SWAP --size 8GiB vg0

Now make a the rest into a pool for the thin volumes

lvcreate --type thin-pool --extents +100%FREE --name alberca vg0

Last make a root volume and then one for storing the recordings

lvcreate --type thin --virtualsize 128GiB --name ROOT --thinpool alberca vg0
lvcreate --type thin --virtualsize 128GiB --name recordings --thinpool alberca vg0

# lvs
  LV         VG  Attr       LSize    Pool    Origin Data%  Meta%  Move Log Cpy%Sync Convert
  ROOT       vg0 Vwi-aotz--  128.00g alberca        7.18
  SWAP       vg0 -wi-ao----    8.00g
  alberca    vg0 twi-aotz-- <911.77g                1.14   10.75
  recordings vg0 Vwi-aotz--   50.00g alberca        2.37

Filesystem setup

Make some file systems on the root

mkfs.ext4 /dev/mapper/vg0-ROOT
mkfs.ext4 /dev/mapper/vg0-recordings
mkswap /dev/mapper/SWAP

Copy the sdcard to the filesystems

so you can use cp or rsync to copy the file systems into the new place, but i used dd. dd is not ideal as you need to tune the file systems to change the UUID's.

dd bs=4M conv=sparse if=/dev/mmblockroot of=/dev/mapper/ROOT
dd bs=4M  if=/dev/mmblockboot of=/dev/nvme0n1p1

Change the UUID's of the file systems

tune2fs -U $(uuidgen) /dev/vg0/ROOT
mlabel -n -i /dev/nvme0n1p1
resize2fs /dev/vg0/ROOT

get the new UUID's

lsblk -f

mount these in /mnt and chroot to them

mount /dev/vg0/ROOT /mnt
mount /dev/nvme0n0p1 /mnt/boot/firmware
mount -t proc /proc /mnt/proc/
mount --rbind /sys /mnt/sys/
mount --rbind /dev /mnt/dev/
chroot /mnt

Change fstab to boot off nvme

cat /etc/fstab
proc            /proc           proc    defaults          0       0
UUID=1B01-6969  /boot/firmware  vfat    defaults          0       2
UUID=d185ebc1-68df-4065-afe6-5ed288b28e32  /               ext4    defaults,noatime  0       1
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that
UUID=01b68d2f-9577-4e63-b79f-6eaea8bbd61c       swap    swap    defaults        0       0
UUID=d5d62f0a-054b-4f9b-a615-4c1b02373fdc       /home/svar      ext4    defaults,noatime  0       1

disable swap in the chroot

sudo systemctl disable dphys-swapfile.service

exit the chroot and reboot. remove the sdcard and confirm you're booting off the LVM.

Linux config

I set the timezone to local time and then configure the users/ssh keys. If this will be remote it's a good idea to setup the ssh remote service for remote access. I have a page on this here Secure Tunnel Service

The user I use for this is 'svar' and must be added to the audio group

usermod svar -a -G audio 

# groups svar
svar : svar audio users

udev rules for ports

I identify the ports as upper and lower, left and right on the RPI. It's not possible to rename these, but you can make symlinks. Based on the max of 32 devices and 2 being used for onboard hdmi, this means we could support up to 14 devices. technically we may be able to go to pulse audio and do more, or figure out a way to do this that is not using udev and scale up to 30 since we'd not need to burn 2 devices per real device, but I didn't mess with that. if you do, please let me know.

/etc/udev/rules.d/50-alsa.rules
# snd 31 is bottom right port 3-2:1.0
KERNEL=="controlC[0-9]*", DRIVERS=="snd-usb-audio", KERNELS=="3-2:1.0", ACTION=="add", SYMLINK+="snd/controlC31"
#KERNEL=="hwC[D0-9]*", DRIVERS=="snd-usb-audio", KERNELS=="3-2:1.0", ACTION=="add", SYMLINK+="snd/hwC31"
#KERNEL=="midiC[D0-9]*", DRIVERS=="snd-usb-audio", KERNELS=="3-2:1.0", ACTION=="add", SYMLINK+="snd/midiC31"
KERNEL=="pcmC[0-9]*D0p", DRIVERS=="snd-usb-audio", KERNELS=="3-2:1.0", ACTION=="add", SYMLINK+="snd/pcmC31D0p"
KERNEL=="pcmC[0-9]*D0c", DRIVERS=="snd-usb-audio", KERNELS=="3-2:1.0", ACTION=="add", SYMLINK+="snd/pcmC31D0c"

# snd 30 is top right port 1-2:1.0
KERNEL=="controlC[0-9]*", DRIVERS=="snd-usb-audio", KERNELS=="1-2:1.0", ACTION=="add", SYMLINK+="snd/controlC30"
KERNEL=="pcmC[0-9]*D0p", DRIVERS=="snd-usb-audio", KERNELS=="1-2:1.0", ACTION=="add", SYMLINK+="snd/pcmC30D0p"
KERNEL=="pcmC[0-9]*D0c", DRIVERS=="snd-usb-audio", KERNELS=="1-2:1.0", ACTION=="add", SYMLINK+="snd/pcmC30D0c"

# snd 29 is bottom left port 1-1:1.0
KERNEL=="controlC[0-9]*", DRIVERS=="snd-usb-audio", KERNELS=="1-1:1.0", ACTION=="add", SYMLINK+="snd/controlC29"
KERNEL=="pcmC[0-9]*D0p", DRIVERS=="snd-usb-audio", KERNELS=="1-1:1.0", ACTION=="add", SYMLINK+="snd/pcmC29D0p"
KERNEL=="pcmC[0-9]*D0c", DRIVERS=="snd-usb-audio", KERNELS=="1-1:1.0", ACTION=="add", SYMLINK+="snd/pcmC29D0c"

# snd 28 is top left port 3-1:1.0
KERNEL=="controlC[0-9]*", DRIVERS=="snd-usb-audio", KERNELS=="3-1:1.0", ACTION=="add", SYMLINK+="snd/controlC28"
KERNEL=="pcmC[0-9]*D0p", DRIVERS=="snd-usb-audio", KERNELS=="3-1:1.0", ACTION=="add", SYMLINK+="snd/pcmC28D0p"
KERNEL=="pcmC[0-9]*D0c", DRIVERS=="snd-usb-audio", KERNELS=="3-1:1.0", ACTION=="add", SYMLINK+="snd/pcmC28D0c"


Software

This uses this great software 'svar' https://github.com/arkq/svar

compile and install it as /usr/loca/bin/svar

systemd scripts

This runs as a script under systemd which will set the mic gain and the then invoke svar on each channel per it's config file. if it crashes, it waits 5 seconds and restarts it.

/etc/systemd/system/svar@.service
[Unit]
Description=Start recording from %i
After=network.target

[Service]
User=svar
EnvironmentFile=/etc/default/svar@%i
ExecStartPre=/usr/bin/amixer -D hw:${HW} -n sset Mic capture 0
ExecStart=/usr/local/bin/svar -v --device=plughw:${HW},0 -l${VOXLEVEL} --rate=${RATE} -s ${SEC} --fadeout-lag ${FOLAG} ${DIR}/${FNAME}
# Restart every >2 seconds to avoid StartLimitInterval failure
RestartSec=5
Restart=always

[Install]
WantedBy=multi-user.target

Config