Had 3 Pis with 3 SDRs running OP25. Moved to 1 desktop and thought I should switch to multi_rx.py, but I am losing significant amounts of audio now.

nvycat14

Member
Joined
Dec 5, 2006
Messages
36
Hello,

So, I will make this long story as short as possible while including as much detail as I think may be significant. I'm a super noob on some things, yet experienced in others, so thank you to anyone who can work with me, as I can't find any other posts that seem to explain the predicament I'm in.

My old setup was 3 Raspberry Pis, each running either Raspberry Pi OS or DietPi, each with its own SDR (2 RTL-SDRs and 1 Airspy), each using liquidsoap and icecast2 to 1) dump audio for later review to a file of my choice with the date and time in the file name, and 2) stream out for network listening, either on the LAN or using a VPN into my LAN from the internet. This worked well enough, but the Pis are very limited - one is a 3B+ and the other 2 are just 3Bs - and problems I ran into included random and unpredictable delays in restarting processes which led to oddball filenames (with the times varying wildly sometimes). I wanted something more powerful.

My new setup is a desktop PC that came with Windows which I then wiped and installed Dragon OS. So far, I have connected only the 2 RTL-SDR dongles to this PC, but I seem to have them up and running as I intended them - sort of. I struggled for a bit to transition from rx.py to multi_rx.py, then wasn't even sure if multi_rx.py is the route I should go for my use-case. So if this is clunky and not the best route, by all means, please tell me!!

But, as it stands, I am running 4 total system processes: op25-rx_main.service which corresponds with op25-liq1.service and op25-rx_backup.service which corresponds with op25-liq2.service. Each of the rx services calls its own instance of multi_rx.py. For op25-rx_main.service, it uses "./multi_rx.py -c local_main.json > stderr.log". And finally, local_main.json looks like this:

Code:
{
    "channels": [
        {
            "name": "Voice_ch1",
            "device": "sdr0",
            "trunking_sysname": "P25 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": ""
        }
    ],
    "devices": [
        {
            "args": "rtl=0",
            "gains": "LNA:39",
            "gain_mode": false,
            "name": "sdr0",
            "offset": 0,
            "ppm": 0.0,
            "rate": 1000000,
            "usable_bw_pct": 0.85,
            "tunable": true
        }
    ],
    "trunking": {
        "module": "tk_p25.py",
        "chans": [
            {
                "nac": "0x159",
                "sysname": "P25 System",
                "control_channel_list": "858.9425",
                "whitelist": "",
                "blacklist": "blacklist.tsv",
                "tgid_tags_file": "local_main_usual.tsv",
                "rid_tags_file": "",
                "tdma_cc": false,
                "crypt_behavior": 2
            }
        ]
    },
    "audio": {
        "module": "sockaudio.py",
        "instances": [
            {
                "instance_name": "",
                "device_name": "default",
                "udp_port": 23456,
                "audio_gain": 1.0,
                "number_channels": 1
            },
            {
                "instance_name": "",
                "device_name": "default",
                "udp_port": 23466,
                "audio_gain": 1.0,
                "number_channels": 1
            }
        ]
    },
    "terminal": {
        "module": "terminal.py",
        "#terminal_type": "curses",
        "terminal_type": "http:0.0.0.0:8080",
        "curses_plot_interval": 0.1,
        "http_plot_interval": 1.0,
        "http_plot_directory": "../www/images",
        "tuning_step_large": 1200,
        "tuning_step_small": 100
    }
}

The backup json is essentially the same, mostly with label tweaks (like the backup uses the same blacklist.tsv but uses "local_backup_usual.tsv" for the differing priorities) and then the destination being udp://127.0.0.1:23466 instead of udp://127.0.0.1:23456.

Part of the reason I went this route is that I want each of the feeds to have different priorities lists, but still mostly have the same white/blacklists. That is, they can both pick up all the same stuff until there is overlap on the system, in which case I want 1 receiver to prioritize different TGIDs than the other. Running only 1 instance of multi_rx.py didn't seem to give me that ability, that I could find - and perhaps I've even misunderstood the whole purpose of multi_rx.py. I'm not sure.

Anyway, the current configuration seems to work well enough, until I'm listening for awhile and notice several issues have arisen that I didn't have on the 3xPi setup.

The problems with the new setup: First and foremost, I am missing entire dispatches sometimes. For some reason, sometimes the backup feed will pickup the dispatch when the main feed doesn't, but sometimes, they both miss the same audio. I thought perhaps I had the rx services resetting, thereby cutting out the audio, but I checked the logs and the services aren't restarting at the times I'm losing audio. Both rx services are offset so they don't restart at the same time. I thought perhaps this was a problem with the audio filters setup in my liquidsoap configuration files, but I commented all of the filters out, and the "normalization" line, and that didn't improve it - I was still losing some dispatches - so I un-commented them back to where they were.

Also, sometimes I catch only a part of a dispatch. Whereas, on my old setup, the Pis would receive and record the tones before the voice dispatch and then the entire voice dispatch, now, a lot of the tones are cut out, so if I catch anything, I often miss the tones and catch the just the voice portion, starting at a random point in the transmission.

I tried changing the sample rates in my liquidsoap, as well as the if_rate in the json file above. These screwed things up in their own ways and each got reset to their respective defaults before moving to the next attempt.

What it seems like to my less-than-experienced-in-these-things brain is like when the old analog scanners wouldn't pause long enough after someone talked and it would roll on to the next frequency without waiting for a reply. Except, it's clipping out the entire transmission, not just the reply. In fact, I often pickup the replies, which is how I know there was a dispatch to begin with, then I go and check the backup feed I wasn't actively listening to, to see if it caught it.

So, is there something else I can tweak? I need some real guidance because my current method of google-implement-monitor-find-out-it-is-still-messed-up-google-again is taking far too long lol Thanks so much in advance.

And where can I donate to keep the op25 project rolling? I am so glad it exists.
 
Last edited:

boatbod

Member
Joined
Mar 3, 2007
Messages
3,357
Location
Talbot Co, MD
Lots to digest there...

If I get the gist of what you're saying, it's that you are running three completely separate instances of multi_rx because you want to separately prioritize the tgids (using different trunk_tags.tsv files) and that parameter is only specified in the trunking section of the config. Under those circumstances there really is no advantage of using multi_rx vs just running rx.py three times (which is also entirely possible). Either way that you run things, you need to make sure that none of the udp ports clash for either audio or http terminal. Preferably separate the audio ports by 10 (so for example 23456, 23466, 23476) because behind the scenes each multi_rx instance uses two ports (e.g. 23456 and 23457).

For liquidsoap you can configure a single instance (op25.liq) by defining input1, input2, input3 all within the same script file. Each input listens on its respective udp port.

As an alternative to running duplicate processes, it would also be possible to create a single instance of multi_rx and in the config file specify 3 different trunking instances in the trunking section, along with 3 voice channels, and 3 sdr devices. You then use the channel to tie a specific sdr device to a specific trunking_sysname and it'll behave much as before, but without as much overhead that individual multi_rx processes incurs. The udp ports still mustn't clash, but you'd only have a single terminal and single logfile.
 

nvycat14

Member
Joined
Dec 5, 2006
Messages
36
boatbod,

Thank you so much for this project and your reply. You did not answer the part where I asked how I can support your continuing efforts. ;)

You absolutely got the gist of what I was saying, so thank you... again. I do, now, believe 3 separate instances of regular rx.py would be my best game plan. I sort of like the idea of combined terminals/logging, but.... I don't think that will outweigh the... convenience(?) of separate rx.py instances.

Can I pick your brain on why you think this may have happened? Does the multi_rx.py script handle trunking differently... e.g. scanning/switching TGIDs faster or staying latched to them less after a transmission or something? It seemed so odd to me that everything functioned so well and so similarly to the previous setup, with the exception of just missing random bits of things here and there.

Thanks again - you and this project rock!
 

nvycat14

Member
Joined
Dec 5, 2006
Messages
36
Edit: I found this helpful answer to my question OP25 - What's The Syntax For RTL-SDR Dongles On OP25?

So, I guess I also need to know how to alter the op25.sh file(s?) to specify which SDR receiver to attach to.... Currently, I only know of the --args "rtl" part, but that doesn't specify which to use. When I eventually put all 3 of my SDRs on this desktop, I'll also need 1 of them to grab the Airspy. I see in the README:

Code:
--antenna=ANTENNA     select antenna

But I'm not sure of the syntax for that setting that option (if, indeed, this is the option I'm looking for).
 
Last edited:

boatbod

Member
Joined
Mar 3, 2007
Messages
3,357
Location
Talbot Co, MD
Can I pick your brain on why you think this may have happened? Does the multi_rx.py script handle trunking differently... e.g. scanning/switching TGIDs faster or staying latched to them less after a transmission or something? It seemed so odd to me that everything functioned so well and so similarly to the previous setup, with the exception of just missing random bits of things here and there.
multi_rx.py has a completely different trunking module (tk_p25.py) vs rx.py (trunking.py) and also a completely different frame assembler down in the c++ library code.

I personally run several instances of multi_rx in a multi-system environment and haven't noticed any strange behavior, so I'm wondering if your experiences with it were more related to configuration than anything else. It's hard to say without capturing level 11 logs and spending time digging through them.
 
Top