OP25 OP25 Default Audio Issue

rrogers2035

Newbie
Joined
Dec 6, 2024
Messages
2
I'm attempting to run OP25 headless with my Raspberry Pi and thus have it set up as a service. If I run my startup script directly in the terminal it runs without issue - audio and everything, but when run as a service there is no audio. I have my audio locally via a speaker connected to the headphone jack. So what (probably fairly obvious thing) am I missing here?

Start Up .sh Script:
./rx.py --args 'rtl' --gains 'lna:40' -T /home/rrogers/Repos/op25/op25/gr-op25_repeater/apps/tsv/trunk.tsv -S 2500000 -l http:127.0.0.1:8080 -2 -U -o 0 2> stderr.2

StDerr
Using Python /usr/bin/python3
gr-osmosdr 0.2.0.0 (0.2.0) gnuradio 3.10.5.1
built-in source types: file fcd rtl rtl_tcp uhd hackrf bladerf rfspace airspy airspyhf soapy redpitaya freesrp xtrx
Using device #0 RTLSDRBlog Blog V4 SN: 00000001
Found Rafael Micro R828D tuner
RTL-SDR Blog V4 Detected
gain: name: LNA range: start 0 stop 49 step 0
setting gain lna to 40
supported sample rates 250000-2560000 step 24000
Exact sample rate is: 2500000.107620 Hz
demodulator: xlator if_rate=24000, input_rate=2500000, decim=104, taps=1505, resampled_rate=24038, sps=5
op25_audio::eek:pen_socket(): enabled udp host(127.0.0.1), wireshark(23456), audio(23456)
p25_frame_assembler_impl: do_imbe[1], do_output[0], do_audio_output[1], do_phase2_tdma[1], do_nocrypt[0]
metadata update not enabled
using ALSA sound system
audio device: default
failed to open audio device: default
Listening on 127.0.0.1:23456
audio closing
python version detected: 3.11.2 (main, Sep 14 2024, 03:00:30) [GCC 12.2.0]
12/06/24 20:28:51.248720 Reconfiguring NAC from 0x000 to 0x2a5

service
[Unit]
Description=op25-rx
After=syslog.target network.target nss-lookup.target network-online.target
Requires=network-online.target

[Service]
User=rrogers
Group=rrogers
WorkingDirectory=/home/rrogers/Repos/op25/op25/gr-op25_repeater/apps
ExecStart=/bin/bash -- scanner.sh
RestartSec=5
Restart=on-failure

[Install]
WantedBy=multi-user.target
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,422
Location
Talbot Co, MD
I'll give this a 99.9% chance of being related to audio permissions.
When you run headless the pa daemon isn't running (it typically gets started after you log in to the desktop) so the audio doesn't work for anything started by root as a service. I've jumped through hoops trying to resolve this in the past, but I'm pretty sure @wgbecks can point you in the right direction.
 

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,040
Location
NE Wisconsin
When you run headless the pa daemon isn't running (it typically gets started after you log in to the desktop) so the audio doesn't work for anything started by root as a service.

@boatbod is spot on in stating that the PA daemon doesn't run in a headless configuration, but not to despair, follow the steps below
to reconfigure PulseAudio to run as a system-wide daemon.

sudo systemctl --global disable pulseaudio.service pulseaudio.socket
Create the file: /etc/systemd/system/pulseaudio.service containing the following lines

[Unit]
Description=PulseAudio system server

[Service]
Type=notify
ExecStartPre=/bin/sleep 10
ExecStart=pulseaudio --daemonize=no --system --disallow-module-loading --disallow-exit --log-target=journal

[Install]
WantedBy=multi-user.target

Enable the service

sudo systemctl --system enable pulseaudio.service
sudo systemctl --system start pulseaudio.service

Edit Client conf /etc/pulse/client.conf
and replace as below
default-server = /var/run/pulse/native
autospawn = no

Add root and local user to pulse group
sudo adduser root pulse-access
sudo adduser username pulse-access

sudo reboot
 

DC31

Member
Feed Provider
Joined
Feb 19, 2011
Messages
1,576
Location
Massachusetts
Since the brain trust is active in this thread, I will expand to include my question.

I also run OP25 (multi_rx) on a pi With output to pulse. I am Monitoring a single P25 Ph1/2 system but I want to send the audio from two separate talkgroups to separate sinks in PA. I have two sdr sticks each set to decode the P25 system with the two TG whitelisted separately in the trunking section of the json file. This creates two playback instances in PA but both attach to the default sink. I can go in manually and route to the proper sink. Is there some way to designate the sink that each of the audio feeds should connect to in pulse? The goal here being a successful auto restart after a power failure Or reboot.

secondly, the two playback entries in PA are identical. This leaves me guessing which is which when I assign them manually. This is pulled from the “open” function in sockaudio:

def open(self, hwdevice):
pa_simple_new = self.libpa.pa_simple_new
pa_simple_new.restype = c_void_p
self.out = pa_simple_new(None,
"OP25".encode("ascii"),
PA_STREAM_PLAYBACK,
None,
"OP25 Playback".encode('ascii'),
byref(self.ss),
None,
None,
byref(self.error))

is there some way that the “OP25 Playback” could be changed to pull the instance_name from the json file?

Or, perhaps am I out in left field completely with there being a much better way to accomplish my goal?
 

Attachments

  • IMG_1042.jpeg
    IMG_1042.jpeg
    95.9 KB · Views: 10

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,040
Location
NE Wisconsin
I also run OP25 (multi_rx) on a pi With output to pulse. I am Monitoring a single P25 Ph1/2 system but I want to send the audio from two separate talkgroups to separate sinks in PA. I have two sdr sticks each set to decode the P25 system with the two TG whitelisted separately in the trunking section of the json file. This creates two playback instances in PA but both attach to the default sink. I can go in manually and route to the proper sink. Is there some way to designate the sink that each of the audio feeds should connect to in pulse?

Although one can configure multiple audio instances within the "Audio" section of your JSON file, I don't believe the instance name is
passed to the PA subsystem such that it appears in the PA Volume Control Panel.

Conceivably, you could make multiple copies of sockaudio.py, each having a unique filename, and whereby you'd edit the ASCII string
values to reflect the desired labels as they would appear in the PA Volume Control Panel. However, I am not aware that mulit_rx.py is
structured to parse and accept multiple "Audio" sections populated with said "custom" sockaudio modules.

What you could do is to install Liquidsoap crafted with a script to accept multiple sources from op25 with added benefits such as audio
processing, local speaker audio output (via PA with labels), and the ability to simultaneously process those streams to a local or remote
Icecast server(s).

Below is an example op25.liq script that you're welcome to use as a template. See also the screen capture of my PA Volume Control.

op25.liq

#!/usr/bin/liquidsoap

# Example liquidsoap v2.x streaming from op25 to icecast
# (c) 2019-2024 gnorbury@bondcar.com, wllmbecks@gmail.com
#

settings.log.stdout.set(true)
settings.log.file.set(false)
settings.log.file.path.set("/home/username/liquidsoap.log")
settings.log.level.set(2)
settings.log.file.append.set(false)


# Make the native sample rate compatible with op25
settings.frame.audio.size.set(8000)

input_A = mksafe(input.external.rawaudio(buffer=0.00, channels=2, samplerate=8000, restart_on_error=true, "./audio.py -u 23450 -x 1.25 -s"))
input_B = mksafe(input.external.rawaudio(buffer=0.00, channels=2, samplerate=8000, restart_on_error=true, "./audio.py -u 23460 -x 1.25 -s"))


# Consider increasing the buffer value on slow systems such as RPi3. e.g. buffer=0.25
# Longer buffer results in less choppy audio but at the expense of increased latency.


# OPTIONAL AUDIO SIGNAL PROCESSING BLOCKS
# Uncomment to enable
#
# High pass filter
#input_A = filter.iir.butterworth.high(frequency = 200.0, order = 4, input)
#input_B = filter.iir.butterworth.high(frequency = 200.0, order = 4, input)

# Low pass filter
#input_A = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input)
#input_B = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input)

# Compression
input_A = compress(input_A, attack = 15.0, gain = 2.0, knee = 1.0, ratio = 3.0, release = 60.0, threshold = -24.0)
input_B = compress(input_B, attack = 15.0, gain = 2.0, knee = 1.0, ratio = 3.0, release = 60.0, threshold = -24.0)

# Normalization
input_A = normalize(input_A, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -40.0)
input_B = normalize(input_B, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -40.0)



# LOCAL AUDIO OUTPUT
# Uncomment the appropriate line below to enable local sound
#
# Default audio subsystem
#out (input)
#
# PulseAudio
output.pulseaudio(client="Fire", input_A)
output.pulseaudio(client="EMS", input_B)
#
# ALSA
#output.alsa(input)

# ICECAST STREAMING
# Uncomment to enable output to an icecast server
# Change the "host", "password", and "mount" strings appropriately first!
# For metadata to work properly, the host address given here MUST MATCH the address in op25's meta.json file
#
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), description="Fire", genre="Public Safety",
url="", fallible=false, host="localhost", port=8000, mount="Fire", password="hackme", mean(input_A))

output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), description="EMS", genre="Public Safety",
url="", fallible=false, host="localhost", port=8000, mount="EMS", password="hackme", mean(input_B))



PA Volume Control

pavc.png
 
Last edited:

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,040
Location
NE Wisconsin
BTW,

Liquidsoap has the capability of processing audio sources into a stereo (Left/right channel) MP3 stream for use in feeding audio
into single Icecast server and/or to the local speaker.
 

DC31

Member
Feed Provider
Joined
Feb 19, 2011
Messages
1,576
Location
Massachusetts
Very interesting. Thanks for information and script. I will need to do some experimenting now. I have attempted a stereo feed in the past without success. Time to revisit that now.

locally, Fire is dispatched on two talkgroups thus i’d like to feed them into two instances of TwoToneDetrct Which requires pulse. Then streaming the dispatch/Ops channels for the two groups as stereo feeds would be great.
 

DC31

Member
Feed Provider
Joined
Feb 19, 2011
Messages
1,576
Location
Massachusetts
Another question.

is it possible to separate the audio output from two talkgroups on the same system using only one sdr stick? I can cover my whole p25 system with one stick but it seems that the only way to separate talkgroups is in the “channels” section where the udp ports are assigned. Assigning the same sdr to two channels returns an error. I have been using two sticks and two channels to monitor the one system.

"channels": [
{
"name": "Channel 1",
"device": "sdr0",
"trunking_sysname": "TS System",
"meta_stream_name": "stream_0",
"demod_type": "cqpsk",
"cqpsk_tracking": true,
"tracking_threshold": 120,
"tracking_feedback": 0.75,
"destination": "udp://127.0.0.1:23456",
"excess_bw": 0.2,
"filter_type": "rc",
"if_rate": 24000,
"plot": "",
"symbol_rate": 4800,
"enable_analog": "off",
"blacklist": "",
"whitelist": "",
"crypt_keys": "example_keys.json"
},
{
"name": "Channel 2”,
"device": "sdr1",
"trunking_sysname": "GFD System",
"meta_stream_name": "stream_1",
"demod_type": "cqpsk",
"cqpsk_tracking": true,
"tracking_threshold": 120,
"tracking_feedback": 0.75,
"destination": "udp://127.0.0.1:23466",
"excess_bw": 0.2,
"filter_type": "rc",
"if_rate": 24000,
"plot": "",
"symbol_rate": 4800,
"enable_analog": "off",
"blacklist": "",
"whitelist": "",
"crypt_keys": "example_keys.json"
}
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,422
Location
Talbot Co, MD
I can certainly experiment to see if passing the instance name into pa_simple_new() changes what you see in the audio controls. As Bill noted, liquidsoap is generally better for streaming, so I hadn't given it much thought before now.
 

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,040
Location
NE Wisconsin
Below are the key operators and constructs required to produce a stereo local speaker and Icecast streaming feed. Please keep in mind
that each major version of Liquidsoap often has differences in the API from other versions that impact exact form and syntax. I 'll refer you
to obtain and make reference to the Liquidsoap Scripting Language Reference for your particular installed version.


Example Stereo Operators

# Make the native sample rate compatible with op25
set("frame.audio.samplerate", 8000)


# Left channel input source
#left = input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23450 -x 2 -s")
#left = audio_to_stereo(left)
#left = stereo.pan(pan=1., left)

# Right channel input source
#right = input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23460 -x 2 -s")
#right = audio_to_stereo(right)
#right = stereo.pan(pan=-1., right)

# OPTIONAL AUDIO SIGNAL PROCESSING BLOCKS
# Uncomment to enable

# High pass filter
#left = filter.iir.butterworth.high(frequency = 200.0, order = 4, left)
#right = filter.iir.butterworth.high(frequency = 200.0, order = 4, right)

# Low pass filter
#left = filter.iir.butterworth.low(frequency = 3250.0, order = 4, left)
#right = filter.iir.butterworth.low(frequency = 3250.0, order = 4, right)

# Compression
left = compress(left, attack = 15.0, gain = 2.0, knee = 1.0, ratio = 3.0, release = 60.0, threshold = -24.0)
right = compress(right, attack = 15.0, gain = 2.0, knee = 1.0, ratio = 3.0, release = 60.0, threshold = -24.0)

# Normalization
left = normalize(left, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -40.0)
right = normalize(right, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -40.0)

# Create local stereo speaker and streaming channel
input = mksafe(add(normalize=false, [left,right]))

# LOCAL AUDIO OUTPUT
# Uncomment the line below to enable local sound
#
# Default audio subsystem
#out (input)
#
# PulseAudio
output.pulseaudio(input)
#
# ALSA
#output.alsa(input)

# ICECAST STREAMING
# Uncomment to enable output to an icecast server
# Change the "host", "password", and "mount" strings appropriately first!
# For metadata to work properly, the host address given here MUST MATCH the address in op25's meta.json file
#
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=true), description="op25", genre="Public Safety", url="", fallible=false,
host="localhost", port=8000, mount="op25", password="hackme", input)
 
Last edited:

wgbecks

Active Member
Joined
Jan 17, 2005
Messages
1,040
Location
NE Wisconsin
Another question.

is it possible to separate the audio output from two talkgroups on the same system using only one sdr stick? I can cover my whole p25 system with one stick but it seems that the only way to separate talkgroups is in the “channels” section where the udp ports are assigned.

Yes, you can use a single SDR, provided it has the necessary bandwidth. Review "p25_airspy_example.json" found in the /apps folder.
Remember to remove the "sockaudio.py" module from the "Audio" section when running Liquidsoap in conjunction with op25.

Example

Code:
  },
    "audio": {
        "module": "",
        "instances": [
            {
                "instance_name": "audio0",
                "device_name": "default",
                "udp_port": 23466,
                "audio_gain": 1.0,
                "number_channels": 1
            }
        ]
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,422
Location
Talbot Co, MD
I can certainly experiment to see if passing the instance name into pa_simple_new() changes what you see in the audio controls. As Bill noted, liquidsoap is generally better for streaming, so I hadn't given it much thought before now.
For gr310 branch, the use of instance_name in the pulseaudio stream is done.
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,422
Location
Talbot Co, MD
now I have it broken. Enough for tonight. Back at it tomorrow…
Is your system gnuradio-3.10 or gnuradio-3.8?
It's possible if you may now be on the wrong branch based on recent changes I made to 'master'.
Run these commands and post the output please.
Code:
apt list --installed | grep gnuradio
cd ~/op25
git status
 

DC31

Member
Feed Provider
Joined
Feb 19, 2011
Messages
1,576
Location
Massachusetts
yeah, I sorted that out a little earlier. The pi that I am working on I believe has Buster on it. Gnuradio is 3.7

apt list --installed | grep gnuradio

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

gnuradio-dev/oldoldstable,now 3.7.13.4-4+b1 armhf [installed]
gnuradio/oldoldstable,now 3.7.13.4-4+b1 armhf [installed]
libgnuradio-analog3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-atsc3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-audio3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-blocks3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-channels3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-comedi3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-digital3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-dtv3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-fcd3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-fcdproplus3.7.11/oldoldstable,now 3.7.25.4b6464b-5+b2 armhf [installed,automatic]
libgnuradio-fec3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-fft3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-filter3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-fosphor3.7.12/oldoldstable,now 3.7.0.2.7b6b996-3+b1 armhf [installed,automatic]
libgnuradio-iqbalance3.7.11/oldoldstable,now 0.37.2-11+b2 armhf [installed,automatic]
libgnuradio-noaa3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-osmosdr0.1.4/oldoldstable,now 0.1.4-14+b7 armhf [installed,automatic]
libgnuradio-pager3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-pmt3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-qtgui3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-runtime3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-trellis3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-uhd3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-video-sdl3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-vocoder3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-wavelet3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-wxgui3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
libgnuradio-zeromq3.7.13/oldoldstable,now 3.7.13.4-4+b1 armhf [installed,automatic]
pi@burnham:~ $ cd op25
pi@burnham:~/op25 $ git status
On branch master
Your branch is behind 'origin/master' by 447 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: op25/gr-op25_repeater/apps/multi_rx.py
modified: op25/gr-op25_repeater/apps/op25.sh
modified: op25/gr-op25_repeater/apps/p25_rtl_example.json
modified: op25/gr-op25_repeater/apps/p25_rtl_example.sh
modified: op25/gr-op25_repeater/apps/sockaudio.py
modified: op25/gr-op25_repeater/apps/trunk.tsv

Untracked files:
(use "git add <file>..." to include in what will be committed)

op25/gr-op25_repeater/apps/.op25.sh.swp
op25/gr-op25_repeater/apps/launchop25.log
op25/gr-op25_repeater/apps/trunk.tsv.csv

no changes added to commit (use "git add" and/or "git commit -a")
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,422
Location
Talbot Co, MD
You'd need to be on the "gr38" branch.
Code:
git pull
git checkout gr38
cd ~/op25
./rebuild.sh
Unfortunately I did not propagate the sockaudio change to that branch yet. Not a big deal to do it, but not tonight.
 

DC31

Member
Feed Provider
Joined
Feb 19, 2011
Messages
1,576
Location
Massachusetts
Yes, you can use a single SDR, provided it has the necessary bandwidth. Review "p25_airspy_example.json" found in the /apps folder.
Remember to remove the "sockaudio.py" module from the "Audio" section when running Liquidsoap in conjunction with op25.

Example

Code:
  },
    "audio": {
        "module": "",
        "instances": [
            {
                "instance_name": "audio0",
                "device_name": "default",
                "udp_port": 23466,
                "audio_gain": 1.0,
                "number_channels": 1
            }
        ]
I am still struggling with this on a pi5 running the gr310 branch. If I show the rtl device as tunable, I get an error indicating that a tunable device is not shareable. If I set the device to tunable: false my tightly grouped constellation plot goes completely scattered.

when it is set as tuneable:true, it receives the first channel in the list but not the remaining two.

from the stderr.2 file:

12/20/24 05:12:48.536415 [0] rx_sync::sync_reset:
12/20/24 05:12:48.536515 [0] mute channel(0)
12/20/24 05:12:48.536532 [0] mute channel(1)
12/20/24 05:12:48.537076 [0] frame_assembler_impl::control: cmd(crypt_behavior), args({"tuner": 0, "cmd": "crypt_behavior", "behavior": 2})
12/20/24 05:12:48.537136 crypt behavior: 2
* * * Channel 'GFD' cannot share a tunable device - ignoring!
* * * Channel 'ORG' cannot share a tunable device - ignoring!
12/20/24 05:12:48.538238 [rx_ctl] post initialize receiver[0]
12/20/24 05:12:48.538324 [0] Initializing P25 receiver: CH1
12/20/24 05:12:48.538344 [0] metadata updates not enabled
12/20/24 05:12:48.538365 [0] reading channel whitelist file: whitelist3697.txt
12/20/24 05:12:48.538491 [0] added talkgroup 3697 from whitelist3697.txt
12/20/24 05:12:48.538582 [0] idling receiver

1734691230653.png
 
Top