Alpha Tags with Uniden BCD996P2

Status
Not open for further replies.

mlapaglia

Member
Joined
Feb 3, 2017
Messages
24
This should work with any linux box that has pyhon installed, but it worked for me on a raspberry pi. It took me a while to figure this out so I thought I'd help someone else out in the future.

I followed this guide to get streaming set up on my raspberry pi: Raspberry Pi Broadcastify Build - The RadioReference Wiki
I used the "recommended" setup options using darkice, but that shouldn't matter since the alpha tags are sent to a different URL than the regular stream.

In order to get alpha tags to show up a separate script needs to be run. I used a simple usb connection between the front unit of the scanner and the raspberry pi. I used the guide found here: Using Python to Update Icecast Scanner Audio Feeds with a Raspberry Pi

Unfortunately the link to the script is dead, but he has it up on his github here: k7bbr/metaPy

Plug the scanner into the raspberry pi, open a terminal and run
Code:
dmesg
and find the name of the scanner, mine looked like
Code:
[    4.363136] cdc_acm 1-1.5:1.0: ttyACM0: USB ACM device

open the file and modify the configuration:
for
Code:
port
enter the device address, mine was
Code:
/dev/ttyACM0
for
Code:
icecastUser
enter
Code:
source
NOT your username or anything else, this is what caught me up for an hour. Without this value you will get 401 errors.

for the remaining values
Code:
icecastPass
icecastServerAddress
icecastMountpoint
use the values found on the technical section of your stream configuration page.

close and run the script and you will start seeing the entries roll in:
Code:
HC 29003 Nbls/Wstfld Disp
Tue Dec 31 05:38:29 2019
Icecast Update OK
HC 29002 Fsh Plc Dispatch
Tue Dec 31 05:38:50 2019
Icecast Update OK
HC 29001 Sherrif Dispatch
Tue Dec 31 05:45:05 2019
Icecast Update OK
HC 29004 Carmel Dispatch
Tue Dec 31 05:45:33 2019
Icecast Update OK
HC 29002 Fsh Plc Dispatch
Tue Dec 31 05:49:09 2019
Icecast Update OK

And tags start showing up in supported players:
 
Last edited:

mikewren

Member
Feed Provider
Joined
Jul 12, 2007
Messages
30
Location
Albany, NY
It would be very beneficial for the community if you could keep the conversation public on this thread - This weekend, my feed will be migrating from standard analog to a P25 phase II trunked system using BCD996P2 and transferring alpha tags is near the top of my wish list with this migration. Thanks for the consideration!
 

mikewren

Member
Feed Provider
Joined
Jul 12, 2007
Messages
30
Location
Albany, NY
Just got alpha tags sending to Broadcastify working on RaspberryPi via BCD996P2’s front panel USB port.

There are some reports from folks that the front panel USB isn’t quite as rock solid stable as the back serial port with a USB dongle, my setup has only been running for a few minutes, so the jury is still out. The serial port requires a null adapter, which I don’t have with me (scanner and RaspberryPi is at my parents house).

This is the python script I'm using, slightly modified for better commenting and tag parsing:

Python:
import serial
import time
import requests
from datetime import datetime,date

'''-----------------USER CONFIGURATION-----------------'''
# Tweaked slightly by mikewren@gmail.com, with better commenting and tag parsing and output
# See the “technicals” tab of your Broadcastify feed configuration page for the correct information to use below
port = "/dev/ttyACM0" # Your scanner USB/serial port in quotes here - your RaspberryPi may be different
baudrate = 115200 # Your scanners baudrate here
icecastUser = "source" # DO NOT enter your username, it must stay as source
icecastPass = “0000000” # Your Broadcastify password in quotes here
icecastServerAddress = "audio9.broadcastify.com:80" # Your Broadcastify server IP Address (and port if necessary) here
icecastMountpoint = "234933386" # Your Broadcastify mountpoint in quotes here - don't add leading '/'
idleTimeLength = 15 # Number of seconds to wait to update an idle message
idleMessage = "Scanning..." # The message to send to Broadcastify server when scanner is idle
'''-----------------END USER CONFIGURATION---------------'''

'''----------UNNECESSARY TO MODIFY SCRIPT BELOW----------'''

urlBase = "http://" + icecastServerAddress + "/admin/metadata?mount=/" + icecastMountpoint + "&mode=updinfo&song="
serTimeout = float(.005) # serial timeout here (.005 is probably sufficient)
test = "GLG" #'''test string to send to Uniden Scanner to get current status
             #for BCT8 will be RF to get frequency, or LCD FRQ to read icon status
             #for BC125AT use CIN'''
TGIDold = 0 #initialize TGID old test variable
metadata = ''
updateTime = datetime.now()

serialFromScanner = serial.Serial(port, baudrate, timeout=serTimeout) #initialize serial connection
serialFromScanner.flushInput() #flush serial input

def getData():
    global serBuffer, nextChar
    serBuffer = '' #clear the serBuffer
    nextChar = '' #reset the nextChar marker
    serialFromScanner.write(test +'\r\n') #send initial request to scanner

def receiveData():
    if (serialFromScanner.inWaiting() > 0): #check to see if there's serial data waiting
        global nextChar, serBuffer
        while nextChar != '\r': #continue filling serBuffer until carriage return
            nextChar = serialFromScanner.read(1) #read one character
            serBuffer += nextChar

def parseData(pserBuffer):
    parsed = pserBuffer.split(",")
    stringtest = parsed[0]
    global TGIDold, TGID, metadata
    if stringtest == "GLG":
        length = len(parsed)
        if (length >= 10): #check list length so we don't get exception 10 for BCT15, 13 for BC886XT
            TGID = parsed[1]
            SYSNAME = parsed[5]
            GROUP = parsed[6]
            TG = parsed[7]
            FREQ = TGID.lstrip('0') #remove leading 0 if present
            try:
                if (FREQ[-1] == '0'):#remove trailing 0 if present
                    FREQ = FREQ[:-1]
            except Exception:
                pass

        if (TGID != TGIDold) and (TGID != ''): #check if group change or scanner not receiving
            #metadata = ((FREQ) + " " + (SYSNAME) + " " + (GROUP) + " " + (TG))
            metadata = ((GROUP) + ": " + (TG) + " (" + (FREQ) + ")")
        else:
            metadata = ''

def updateData(pMetadata):
    global TGID, TGIDold, updateTime
    if pMetadata != '':
        print pMetadata
        TGIDold = TGID
        metadataFormatted = metadata.replace(" ","+") #add "+" instead of " " for icecast2
        requestToSend = (urlBase) +(metadataFormatted)
        r = requests.get((requestToSend), auth=(icecastUser,icecastPass))
        status = r.status_code
        updateTimeGMT = time.gmtime()
        timestamp = time.asctime(updateTimeGMT)
        print (timestamp)
        updateTime = datetime.now()
 
     if status == 200:
            print "Icecast Update OK"
        else:
            print "Icecast Update Error", status
            
    else:# Count the time since the last update and if more than idle time listed in config, update with default message
        idleTime = datetime.now()
        timeCheck = (idleTime - updateTime).seconds

        if (timeCheck) >= idleTimeLength:
            requestToSendScanning = (urlBase) + (idleMessage)
            r2 = requests.get((requestToSendScanning), auth=(icecastUser,icecastPass))
            status = r2.status_code
            updateTimeGMT = time.gmtime()# get update time GMT for printing
            timestamp = time.asctime(updateTimeGMT)
            updateTime = datetime.now()# reset update time for counter
            print (idleMessage)
            print (timestamp)
            
            if status == 200:
                print"Icecast Update OK"
            else:
                print "Icecast Update Error", status

while True: #infinite loop
    getData()

    receiveData()
              
    parseData(serBuffer)

    updateData(metadata)
                
    time.sleep(.1) #pause
 

mlapaglia

Member
Joined
Feb 3, 2017
Messages
24
Can you try and paste that again mike, there are some syntax errors
 
Last edited:

mlapaglia

Member
Joined
Feb 3, 2017
Messages
24
Also, I can't seem to get the delay to work, broadcastify delays the audio by 30 seconds, but the alpha tags get sent instantly, making the tags not match the audio for listeners.
 

mikewren

Member
Feed Provider
Joined
Jul 12, 2007
Messages
30
Location
Albany, NY
What syntax errors are you getting?

Alpha tags are working for me; they’re correctly timed with the feed audio +/- 3 seconds depending on specific audio player.
 

mlapaglia

Member
Joined
Feb 3, 2017
Messages
24
The if / else statement doesn't line up
79744

Also, I have about a 30 seconds delay in the audio through broadcastify, how are you getting streams without the delay. The alpha tags are sent instantly when the radio picks them up, but the audio plays 30 seconds later, so they aren't lined up.
 
Last edited:

mikewren

Member
Feed Provider
Joined
Jul 12, 2007
Messages
30
Location
Albany, NY
Can't seem to edit my earlier post, here it is again with formatting cleaned up.

Python:
#!/usr/bin/env python2

import serial
import time
import requests
from datetime import datetime,date

'''-----------------USER CONFIGURATION-----------------'''
# Tweaked slightly by mikewren@gmail.com, with better commenting and tag parsing and output
# See the “technicals” tab of your Broadcastify feed configuration page for the correct information to use below
port = "/dev/ttyACM0" # Your scanner USB/serial port in quotes here - your RaspberryPi may be different
baudrate = 115200 # Your scanners baudrate here
icecastUser = "source" # DO NOT enter your Broadcastify username, it must stay as source
icecastPass = "00000" # Your Broadcastify password in quotes here
icecastServerAddress = "audio9.broadcastify.com:80" # Your Broadcastify server IP Address (and port if necessary) here
icecastMountpoint = "00000" # Your Broadcastify mountpoint in quotes here - don't add leading '/'
idleTimeLength = 30 # Number of seconds to wait to update an idle message
idleMessage = "Scanning..."# The message to send to Broadcastify server when scanner is idle
'''-----------------END USER CONFIGURATION---------------'''
'''----------UNNECESSARY TO MODIFY SCRIPT BELOW----------'''

urlBase = "http://" + icecastServerAddress + "/admin/metadata?mount=/" + icecastMountpoint + "&mode=updinfo&song="
serTimeout = float(.005) # serial timeout here (.005 is probably sufficient)
test = "GLG" #'''test string to send to Uniden Scanner to get current status
             #for BCT8 will be RF to get frequency, or LCD FRQ to read icon status
             #for BC125AT use CIN'''
TGIDold = 0 #initialize TGID old test variable
metadata = ''
updateTime = datetime.now()

serialFromScanner = serial.Serial(port, baudrate, timeout=serTimeout) #initialize serial connection
serialFromScanner.flushInput() #flush serial input

def getData():
    global serBuffer, nextChar
    serBuffer = '' #clear the serBuffer
    nextChar = '' #reset the nextChar marker
    serialFromScanner.write(test +'\r\n') #send initial request to scanner

def receiveData():
    if (serialFromScanner.inWaiting() > 0): #check to see if there's serial data waiting
        global nextChar, serBuffer
        while nextChar != '\r': #continue filling serBuffer until carriage return
            nextChar = serialFromScanner.read(1) #read one character
            serBuffer += nextChar

def parseData(pserBuffer):
    parsed = pserBuffer.split(",")
    stringtest = parsed[0]
    global TGIDold, TGID, metadata
    if stringtest == "GLG":
        length = len(parsed)
        if (length >= 10): #check list length so we don't get exception 10 for BCT15, 13 for BC886XT
            TGID = parsed[1]
            SYSNAME = parsed[5]
            GROUP = parsed[6]
            TG = parsed[7]
            FREQ = TGID.lstrip('0') #remove leading 0 if present
            try:
                if (FREQ[-1] == '0'):#remove trailing 0 if present
                    FREQ = FREQ[:-1]
            except Exception:
                pass

        if (TGID != TGIDold) and (TGID != ''): # Check if group change or scanner not receiving
            # This is the alpha tag text that's sent to Broadcastify
            #metadata = ((FREQ) + " " + (SYSNAME) + " " + (GROUP) + " " + (TG))
            metadata = ((GROUP) + ": " + (TG) + " (" + (FREQ) + ")")
        else:
            metadata = ''

def updateData(pMetadata):
    global TGID, TGIDold, updateTime
    if pMetadata != '':
        print (pMetadata)
        TGIDold = TGID
        metadataFormatted = metadata.replace(" ","+") #add "+" instead of " " for icecast2
        requestToSend = (urlBase) +(metadataFormatted)
        r = requests.get((requestToSend), auth=(icecastUser,icecastPass))
        status = r.status_code
        updateTimeGMT = time.gmtime()
        timestamp = time.asctime(updateTimeGMT)
        print (timestamp)
        updateTime = datetime.now()

        if status == 200:
            print ("Icecast Update OK")
        else:
            print ("Icecast Update Error", status)

    else:#Count the time since the last update and if 30 s update with a "Scanning" message
        idleTime = datetime.now()
        timeCheck = (idleTime - updateTime).seconds

        if (timeCheck) >= idleTimeLength:
            requestToSendScanning = (urlBase) + (idleMessage)
            r2 = requests.get((requestToSendScanning), auth=(icecastUser,icecastPass))
            status = r2.status_code
            updateTimeGMT = time.gmtime()#get update time GMT for printing
            timestamp = time.asctime(updateTimeGMT)
            updateTime = datetime.now()#reset update time for counter
            print (idleMessage)
            print (timestamp)

            if status == 200:
                print ("Icecast Update OK")
            else:
                print ("Icecast Update Error", status)

while True: #infinite loop
    getData()

    receiveData()

    parseData(serBuffer)

    updateData(metadata)

    time.sleep(.1) #pause

If you're using darkice to capture and encode audio, make sure you set your output buffer to something reasonable, we're using bufferSecs = 2 and haven't experienced any issues, not sure what the default value is. Having a high buffer time could account for your alphatags not matching the scanner audio feed.

Another trick I'm using to help combat the fickleness of the alphatag python script which likes to crash every 24-36 hours, is to run it through a bash script:

Bash:
#!/bin/sh

while true; do
echo "Restarting alpha tags script at $(date)..." | mail -s "WebsterWxPi" 5185730000@vtext.com
  python ~/alphatags.py > /dev/null 2>&1
  wait $!
  sleep 10
done
exit

I also have a script that runs every 5 minutes that checks to make sure the following processes are running on the RaspberryPi: Alphatags, Darkice, ffmpeg (used for livestreaming video to YouTube from an outdoor webcam), and Weewx (weather station data). Although it's a kudgey solution, it works well enough that I don't have to babysit things... all I get are txt messages when processes relaunch after crashing. If it gets chatty, I'll know what to look into. For instance, when the YouTube stream crashed with more frequency during rain events, it was tracked to waterlogged coax causing high packet loss, which was eventually fixed by Spectrum running new coax to the house.
 
Status
Not open for further replies.
Top