Dispatch Tone Out Decoder Part 4: Display the Unit or Department that was Toned Out to the Output
To display the actual unit or department that was toned out we need to first make a CSV with the department/unit and the tones, to start I used what was available here: https://wiki.radioreference.com/index.php/Boulder_County_(CO)
This is what the departments.csv file should resemble:

Next to add code to read in and compare the contents to the tone we need to add pandas to the code:
import pandas as pd
Then we add a line of code that reads in the CSV file as a dataframe:
df = pd.read_csv("departments.csv")
Next we will compare the two detected tones, first_tone and second_tone to the dataframe Tone1 and Tone2 values with +/-15Hz of tolerance around the tones, and put a check in for no matching tone out pair in the CSV by indexing through the dataframe values with iloc and adding or subtracting 15Hz from that tone to compare to what was detected:
tone_found = 0
if first_tone_detect_count >=5 and second_tone_detect_count > 25:
print("")
for i in range(len(df)):
if first_tone > df.iloc[i]['Tone1']-15 and first_tone < df.iloc[i]['Tone1']+15 and second_tone > df.iloc[i]['Tone2']-15 and second_tone < df.iloc[i]['Tone2']+15:
print(df.iloc[i]['Department'],": Tone out on", time.strftime("%m-%d-%y at %H:%M:%S"))
tone_found = 1
if tone_found == 0:
print ("****** Tone out on", time.strftime("%m-%d-%y at %H:%M:%S"))
print (" 1: {:0.1f}Hz 2:{:1.1f}Hz C1:{:2.0f} C2{:3.0f}".format (first_tone,second_tone,first_tone_detect_count,second_tone_detect_count))
When running the code against this test case recorded audio dispatch:
We get this ouput:
****** Tone out on 12-27-22 at 15:40:28
1: 1675.2Hz 2:1338.1Hz C1:23 C2 26
Lafayette : Tone out on 12-27-22 at 15:40:33
2602 : Tone out on 12-27-22 at 15:40:37
Lafayette Battalion Chief : Tone out on 12-27-22 at 15:40:41
When we get a tone out without a CSV reference as shown at 15:40:28 we can listen to the recorded audio then add that unit or department to the CSV , so the next time the tone pair is encountered we will get a display of who was actually toned out:
2718, 1675, 1340 is added to the CSV
Run once again with the test case audio now the display shows:
2718 : Tone out on 12-27-22 at 15:50:26
Lafayette : Tone out on 12-27-22 at 15:50:31
2602 : Tone out on 12-27-22 at 15:50:36
Lafayette Battalion Chief : Tone out on 12-27-22 at 15:50:39
As we learn more about the dispatches we may change the description for each tone pair to suit what we want to see displayed for each dispatch.
I have both of the Python routines running concurrently on the same laptop , one recording the dispatch audio into mp3 files with timestamps and the other decoding tones to display who was toned and when. It will be easy to cross reference time stamps from the displayed dispatched unit/departments with the associated and all following mp3 files.
Update 12/30/22: – I spent a little time making a few changes here and there after leaving the Python code running listening for tone outs on the Broadcastify channel ( I am still waiting for the USB sound card from Amazon so that I can attach either my scanner or a cheap Baofeng UV-5R radio) I changed the script to do 4x the samples size while searching for valid tones, this minimizes the detection of invalid tones by taking an FFT on ~88 milliseconds of audio sample vs ~22 milliseconds so the dominate tone can be pulled out with more distinction:
CHUNK_SIZE = 1024*4
The CSV was changed to include the unit number groups in the department description:

The display was changed to show a little more info, some for debugging the code:
2200: Mountain View Fire St.-6 : Tone out on 12-29-22 at 22:56:00
1: 1130.4Hz 2:871.9Hz C1: 8 C2: 8
2700: Louisville Fire Department : Tone out on 12-30-22 at 01:30:51
1: 1669.3Hz 2:948.4Hz C1: 5 C2: 8
2700: Louisville Ambulance : Tone out on 12-30-22 at 01:30:56
1: 1669.2Hz 2:1130.4Hz C1: 9 C2: 8
2300: Boulder Rural : Tone out on 12-30-22 at 01:48:00
1: 948.7Hz 2:1529.2Hz C1: 9 C2: 8
2300: Boulder Rural Fire : Tone out on 12-30-22 at 01:48:05
1: 948.7Hz 2:1983.7Hz C1: 9 C2: 8
2200: Mountain View Fire St.-4 : Tone out on 12-30-22 at 01:56:07
1: 1669.0Hz 2:871.9Hz C1: 8 C2: 8
2200: Mountain View Fire : Tone out on 12-30-22 at 01:56:12
1: 1498.5Hz 2:1087.5Hz C1: 7 C2: 8
2600: Lafayette Fire Department : Tone out on 12-30-22 at 01:56:16
1: 948.3Hz 2:1345.4Hz C1: 9 C2: 8
2700: Louisville Fire Department : Tone out on 12-30-22 at 03:14:37
1: 1669.3Hz 2:1345.7Hz C1: 9 C2: 8
2600: Lafayette Fire Bat Chief : Tone out on 12-30-22 at 03:14:45
1: 1304.5Hz 2:2074.4Hz C1: 7 C2: 8
5200: Allenspark Fire Department : Tone out on 12-30-22 at 04:08:11
1: 948.8Hz 2:1231.0Hz C1: 8 C2: 8
5200: Allenspark Fire Department : Tone out on 12-30-22 at 04:08:15
1: 948.5Hz 2:1230.9Hz C1: 8 C2: 8
Also I have noticed that when the units transmitting have a faulty radio transmitting a lot of noise the code picks out frequencies less than 600Hz so I put a note that the tone out is not valid, I will reject this low confidence tone out print later on but just want to see how often this occurs so leaving it in for now:
****** Tone out on 12-29-22 at 23:39:00
1: 539.3Hz 2:538.2Hz C1: 6 C2: 8
Actual tone out confidence low
For the next project I want to try and tackle: It is time to take the audio recorded from dispatch and try converting speech to text…hmmm it will be interesting. If successful I can add a snippet of the dispatch info to the display too. For now I hope you enjoyed this Mad Scientist Hut Python series and thanks for visiting, I hope to see you soon!
A future version for this dispatch decoder project will be to put this on a Raspberry Pi (someday they will be back in stock…🙄 ) and couple it with a cheap Baofeng UV-5R radio tuned to dispatch.