Radio Channel Logger
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.
Contents
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 ${CAPLEVEL}
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
directory config
as svar you will need to make the following directories
mkdir -p /home/svar/rec/top-right/current mkdir -p /home/svar/rec/top-left/current mkdir -p /home/svar/rec/bottom-right/current mkdir -p /home/svar/rec/bottom-left/current
service config file
for each channel being recorded a seperate service is used with it's own config
/etc/default/svar@top-right #HW is the mixer id that maps to this channel #31 - Bottom Right #30 - Top Right #29 - Bottom Left #28 - Top Left HW="30" #audio level for capture CAPLEVEL="0" #directory to put the files in DIR="/home/svar/rec/top-right/current" #filename BR/TR/TL/BL based on port FNAME="TR-%Y-%m-%d-%H%M.%S" #this sets the VOX level VOXLEVEL="1" RATE="8000" #seconds is the number of seconds to break up #FOLAG is the lag in milliseconds and needs to match the seconds to ensure there is no # clipping of silence in the output SEC="10" FOLAG="10000"