OP25 OP25 Boatbod multi_rx question

ntrlsur

Newbie
Premium Subscriber
Joined
Jan 14, 2018
Messages
13
Location
Glen Ellyn,IL
Good evening all. I am trying to condense my SDR machines and I figured I would give the multi_rx a go. Currently I am using op25 on 2 machines to send Fire Dispatch for 1 stream and Police Dispatch to a 2nd stream on broadcastify. Looking at some of the previous posts I have put together what I think should be a working json file. This is all 1 site using the same P25 system. Can an expert or 2 look it over for me?
anything between ^ ^ is me noting for you what the files are. Its not actually in the configuration. Also when starting since I called out the CC in the json I should be able to omit the trunk.tsv in the start up correct? I should be able to using something like ./multi_rx.py -c multitrunk.json -U -l http:0.0.0.0:8080 2> stderr.2 -X


Code:
{

    "channels": [

        {

            "name": "DUCOMM_Fire",

            "device": "sdr0",

            "trunking_sysname": "STARCOM21",

            "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": "trunkFIRE.whitelist"

        },

        {

            "name": "DUCOMM_PD",

            "device": "sdr1",

            "trunking_sysname": "STARCOM21",

            "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": "trunkPD.whitelist"

        }

    ],

    "devices": [

        {

            "args": "rtl=0", ^ It shouldn't matter what the name of the RTL device is as they are both monitoring the same system correct?^

            "gains": "LNA:39",

            "gain_mode": false,

            "name": "sdr0",

            "offset": 0,

            "ppm": 0.0,

            "rate": 1000000,

            "usable_bw_pct": 0.85,

            "tunable": true

        },

        {

            "args": "rtl=1", ^ Not sure if I need to call out the serial of the RTL device is as they are both monitoring the same system^

            "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": "0x145",

                "sysname": "STARCOM21",

                "control_channel_list": "770.95625,773.84375,774.18125,774.43125",

                "whitelist": "", ^ White lists are noted in the channel area. Do I need to provide a combined white list?^

                "blacklist": "",

                "tgid_tags_file": "tgidall.tags", ^ TG's of both Fire and PD Dispatch^

                "rid_tags_file": "",

                "tdma_cc": false,

                "crypt_behavior": 2

            }

        ]

    },

    "metadata": {

        "module": "icemeta.py",

        "streams": [

            {

                "stream_name": "stream_0",

                "meta_format_idle": "Scanning...",

                "meta_format_tgid": "[%TGID%]",

                "meta_format_tag":  "[%TGID%] %TAG%",

                "icecastServerAddress": "audiox.broadcastify.com",

                "icecastMountpoint": "XXXXXXXX",

                "icecastMountExt": ".m3u",

                "icecastPass": "XXXXX",

                "delay": 0.0

            },

            {

                "stream_name": "stream_1",

                "meta_format_idle": "Scanning...",

                "meta_format_tgid": "[%TGID%]",

                "meta_format_tag":  "[%TGID%] %TAG%",

                "icecastServerAddress": "audiox.broadcastify.com",

                "icecastMountpoint": "XXXXXXXXXX",

                "icecastMountExt": ".m3u",

                "icecastPass": "XXXXXX",

                "delay": 0.0

            }

        ]

    },

    "audio": {  ^Since I am sending this to broadcastify can I remove the audio section completely?  I don't use it in my icecast meta json now^

        "module": "sockaudio.py",

        "instances": [

            {

                "instance_name": "audio0",

                "device_name": "default",

                "udp_port": 23456,

                "audio_gain": 1.0,

                "number_channels": 1

            },

            {

                "instance_name": "audio1",

                "device_name": "default",

                "udp_port": 23466,

                "audio_gain": 1.0,

                "number_channels": 1

            }

        ]

    },

    "terminal": {

        "module": "terminal.py",

        "terminal_type": "curses",

        "#terminal_type": "http:127.0.0.1:8080",

        "curses_plot_interval": 0.1,

        "http_plot_interval": 1.0,

        "http_plot_directory": "../www/images",

        "tuning_step_large": 1200,

        "tuning_step_small": 100

    }

}
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,466
Location
Talbot Co, MD
You will need to individually name your sdr devices (e.g. sdr0, sdr1, ...) and set the args "rtl=0", "rtl=1" otherwise the app is not going to be able to open the two devices. You do not have to set serial numbers since you don't actually care which device is used by which stream.

The audio section need to be completely disabled; this is easy to accomplish by setting the instance_name to "".

Whitelists go in the channels section so that they only apply to the stream for which they are intended.

Lastly, don't worry about trunk.tsv - that file is only used by rx.py
 

ntrlsur

Newbie
Premium Subscriber
Joined
Jan 14, 2018
Messages
13
Location
Glen Ellyn,IL
You will need to individually name your sdr devices (e.g. sdr0, sdr1, ...) and set the args "rtl=0", "rtl=1" otherwise the app is not going to be able to open the two devices. You do not have to set serial numbers since you don't actually care which device is used by which stream.

The audio section need to be completely disabled; this is easy to accomplish by setting the instance_name to "".

Whitelists go in the channels section so that they only apply to the stream for which they are intended.

Lastly, don't worry about trunk.tsv - that file is only used by rx.py
Those were my assumptions but we all know what happens when you assume. Gonna take this out or a spin later this evening when my listener count is low. Thanks again for the information.
 

ntrlsur

Newbie
Premium Subscriber
Joined
Jan 14, 2018
Messages
13
Location
Glen Ellyn,IL
Another question for you if you don't mind. I got everything running but it looks like I am sending the same thing to both streams. Do I need to run 2 liquid soap instances one for each stream?
 

boatbod

Member
Joined
Mar 3, 2007
Messages
3,466
Location
Talbot Co, MD
Another question for you if you don't mind. I got everything running but it looks like I am sending the same thing to both streams. Do I need to run 2 liquid soap instances one for each stream?

You can ignore the previous question. I figured out the input issue..
Sounds like you already figured it out, but yes, each stream needs it's own entry in the .liq file. Here's mine for a 3-stream setup. Note the port numbers in the "audio.py" portion must match what op25 has defined for each stream.
Code:
#!/usr/bin/liquidsoap

# Example liquidsoap streaming from op25 to icecast
# (c) 2019, gnorbury@bondcar.com
#

set("log.stdout", false)
set("log.file", false)
set("log.syslog", true)
set("log.level", 3)

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

input_0 = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23456 -x 1.5 -s"))
input_1 = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23466 -x 1.5 -s"))
input_2 = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23476 -x 1.5 -s"))

# Low pass filter
input_0 = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input_0)
input_1 = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input_1)
input_2 = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input_2)

# Compression
input_0 = compress(input_0, attack = 2.0, gain = 0.0, knee = 13.0, ratio = 2.0, release = 12.3, threshold = -18.0)
input_1 = compress(input_1, attack = 2.0, gain = 0.0, knee = 13.0, ratio = 2.0, release = 12.3, threshold = -18.0)
input_2 = compress(input_2, attack = 2.0, gain = 0.0, knee = 13.0, ratio = 2.0, release = 12.3, threshold = -18.0)

# Normalization
input_0 = normalize(input_0, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -65.0)
input_1 = normalize(input_1, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -65.0)
input_2 = normalize(input_2, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -65.0)


# 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="Talbot Fire & EMS", genre="Public Safety", url="", fallible=false, host="audio3.broadcastify.com", port=80, mount="AAAAAAAA", password="xxxxxxxxx", mean(input_0))
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), description="Talbot LEO Dispatch", genre="Public Safety", url="", fallible=false, host="audio9.broadcastify.com", port=80, mount="BBBBBBBB", password="yyyyyyyy", mean(input_1))
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), description="Dorchester Fire & EMS", genre="Public Safety", url="", fallible=false, host="audio1.broadcastify.com", port=80, mount="CCCCCCCC", password="zzzzzzzz", mean(input_2))
 

ntrlsur

Newbie
Premium Subscriber
Joined
Jan 14, 2018
Messages
13
Location
Glen Ellyn,IL
Sounds like you already figured it out, but yes, each stream needs it's own entry in the .liq file. Here's mine for a 3-stream setup. Note the port numbers in the "audio.py" portion must match what op25 has defined for each stream.
Code:
#!/usr/bin/liquidsoap

# Example liquidsoap streaming from op25 to icecast
# (c) 2019, gnorbury@bondcar.com
#

set("log.stdout", false)
set("log.file", false)
set("log.syslog", true)
set("log.level", 3)

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

input_0 = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23456 -x 1.5 -s"))
input_1 = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23466 -x 1.5 -s"))
input_2 = mksafe(input.external(buffer=0.25, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -u 23476 -x 1.5 -s"))

# Low pass filter
input_0 = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input_0)
input_1 = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input_1)
input_2 = filter.iir.butterworth.low(frequency = 3250.0, order = 4, input_2)

# Compression
input_0 = compress(input_0, attack = 2.0, gain = 0.0, knee = 13.0, ratio = 2.0, release = 12.3, threshold = -18.0)
input_1 = compress(input_1, attack = 2.0, gain = 0.0, knee = 13.0, ratio = 2.0, release = 12.3, threshold = -18.0)
input_2 = compress(input_2, attack = 2.0, gain = 0.0, knee = 13.0, ratio = 2.0, release = 12.3, threshold = -18.0)

# Normalization
input_0 = normalize(input_0, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -65.0)
input_1 = normalize(input_1, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -65.0)
input_2 = normalize(input_2, gain_max = 6.0, gain_min = -6.0, target = -16.0, threshold = -65.0)


# 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="Talbot Fire & EMS", genre="Public Safety", url="", fallible=false, host="audio3.broadcastify.com", port=80, mount="AAAAAAAA", password="xxxxxxxxx", mean(input_0))
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), description="Talbot LEO Dispatch", genre="Public Safety", url="", fallible=false, host="audio9.broadcastify.com", port=80, mount="BBBBBBBB", password="yyyyyyyy", mean(input_1))
output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), description="Dorchester Fire & EMS", genre="Public Safety", url="", fallible=false, host="audio1.broadcastify.com", port=80, mount="CCCCCCCC", password="zzzzzzzz", mean(input_2))

Yeah it was my inputs. I thought I had updated them but it would seem I hadn't. Its what I get for not following my normal practice. Thanks again for pointing me in the right direction and also thanks for all the work you do for this community.
 
Top