mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
228 lines
7.4 KiB
Python
228 lines
7.4 KiB
Python
# meshing around with hamlib as a source for info to send to mesh network
|
||
# detect signal strength and frequency of active channel if appears to be in use send to mesh network
|
||
# depends on rigctld running externally as a network service
|
||
# also can use VOX detection with a microphone and vosk speech to text to send voice messages to mesh network
|
||
# requires vosk and sounddevice python modules
|
||
# 2024 Kelly Keeton K7MHI
|
||
|
||
previousVoxState = False
|
||
from modules.log import *
|
||
import asyncio
|
||
if radio_detection_enabled:
|
||
import socket
|
||
|
||
|
||
if voxDetectionEnabled:
|
||
voxHoldTime = signalHoldTime
|
||
try:
|
||
import sounddevice as sd # pip install sounddevice sudo apt install portaudio19-dev
|
||
from vosk import Model, KaldiRecognizer # pip install vosk
|
||
import json
|
||
q = asyncio.Queue()
|
||
except Exception as e:
|
||
print(f"RadioMon: Error importing VOX dependencies: {e}")
|
||
print(f"To use VOX detection please install the vosk and sounddevice python modules")
|
||
print(f"pip install vosk sounddevice")
|
||
print(f"sounddevice needs pulseaudio, apt-get install portaudio19-dev")
|
||
voxDetectionEnabled = False
|
||
logger.error(f"RadioMon: VOX detection disabled due to import error")
|
||
|
||
|
||
def get_hamlib(msg="f"):
|
||
try:
|
||
rigControlSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
rigControlSocket.settimeout(2)
|
||
rigControlSocket.connect((rigControlServerAddress.split(":")[0],int(rigControlServerAddress.split(":")[1])))
|
||
except Exception as e:
|
||
logger.error(f"RadioMon: Error connecting to rigctld: {e}")
|
||
return ERROR_FETCHING_DATA
|
||
|
||
try:
|
||
build_msg = f"{msg}\n"
|
||
MESSAGE = bytes(build_msg, "utf-8")
|
||
rigControlSocket.sendall(MESSAGE)
|
||
# Look for the response
|
||
data = rigControlSocket.recv(16)
|
||
rigControlSocket.close()
|
||
# strip newline and return
|
||
data = data.replace(b'\n',b'')
|
||
return data.decode("utf-8").rstrip()
|
||
except Exception as e:
|
||
logger.error(f"RadioMon: Error fetching data from rigctld: {e}")
|
||
return ERROR_FETCHING_DATA
|
||
|
||
def get_freq_common_name(freq):
|
||
freq = int(freq)
|
||
if freq == 462562500:
|
||
return "GRMS CH1"
|
||
elif freq == 462587500:
|
||
return "GRMS CH2"
|
||
elif freq == 462612500:
|
||
return "GRMS CH3"
|
||
elif freq == 462637500:
|
||
return "GRMS CH4"
|
||
elif freq == 462662500:
|
||
return "GRMS CH5"
|
||
elif freq == 462687500:
|
||
return "GRMS CH6"
|
||
elif freq == 462712500:
|
||
return "GRMS CH7"
|
||
elif freq == 467562500:
|
||
return "GRMS CH8"
|
||
elif freq == 467587500:
|
||
return "GRMS CH9"
|
||
elif freq == 467612500:
|
||
return "GRMS CH10"
|
||
elif freq == 467637500:
|
||
return "GRMS CH11"
|
||
elif freq == 467662500:
|
||
return "GRMS CH12"
|
||
elif freq == 467687500:
|
||
return "GRMS CH13"
|
||
elif freq == 467712500:
|
||
return "GRMS CH14"
|
||
elif freq == 467737500:
|
||
return "GRMS CH15"
|
||
elif freq == 462550000:
|
||
return "GRMS CH16"
|
||
elif freq == 462575000:
|
||
return "GMRS CH17"
|
||
elif freq == 462600000:
|
||
return "GMRS CH18"
|
||
elif freq == 462625000:
|
||
return "GMRS CH19"
|
||
elif freq == 462675000:
|
||
return "GMRS CH20"
|
||
elif freq == 462670000:
|
||
return "GMRS CH21"
|
||
elif freq == 462725000:
|
||
return "GMRS CH22"
|
||
elif freq == 462725500:
|
||
return "GMRS CH23"
|
||
elif freq == 467575000:
|
||
return "GMRS CH24"
|
||
elif freq == 467600000:
|
||
return "GMRS CH25"
|
||
elif freq == 467625000:
|
||
return "GMRS CH26"
|
||
elif freq == 467650000:
|
||
return "GMRS CH27"
|
||
elif freq == 467675000:
|
||
return "GMRS CH28"
|
||
elif freq == 467700000:
|
||
return "FRS CH1"
|
||
elif freq == 462575000:
|
||
return "FRS CH2"
|
||
elif freq == 462600000:
|
||
return "FRS CH3"
|
||
elif freq == 462650000:
|
||
return "FRS CH5"
|
||
elif freq == 462675000:
|
||
return "FRS CH6"
|
||
elif freq == 462700000:
|
||
return "FRS CH7"
|
||
elif freq == 462725000:
|
||
return "FRS CH8"
|
||
elif freq == 462562500:
|
||
return "FRS CH9"
|
||
elif freq == 462587500:
|
||
return "FRS CH10"
|
||
elif freq == 462612500:
|
||
return "FRS CH11"
|
||
elif freq == 462637500:
|
||
return "FRS CH12"
|
||
elif freq == 462662500:
|
||
return "FRS CH13"
|
||
elif freq == 462687500:
|
||
return "FRS CH14"
|
||
elif freq == 462712500:
|
||
return "FRS CH15"
|
||
elif freq == 462737500:
|
||
return "FRS CH16"
|
||
elif freq == 146520000:
|
||
return "2M Simplex Calling"
|
||
elif freq == 446000000:
|
||
return "70cm Simplex Calling"
|
||
elif freq == 156800000:
|
||
return "Marine CH16"
|
||
else:
|
||
#return Mhz
|
||
freq = freq/1000000
|
||
return f"{freq} Mhz"
|
||
|
||
def get_sig_strength():
|
||
strength = get_hamlib('l STRENGTH')
|
||
return strength
|
||
|
||
def vox_callback(indata, frames, time, status):
|
||
if status:
|
||
logger.warning(f"RadioMon: VOX input status: {status}")
|
||
q.put(bytes(indata))
|
||
|
||
|
||
async def signalWatcher():
|
||
global previousStrength
|
||
global signalCycle
|
||
try:
|
||
signalStrength = int(get_sig_strength())
|
||
if signalStrength >= previousStrength and signalStrength > signalDetectionThreshold:
|
||
message = f"Detected {get_freq_common_name(get_hamlib('f'))} active. S-Meter:{signalStrength}dBm"
|
||
logger.debug(f"RadioMon: {message}. Waiting for {signalHoldTime} seconds")
|
||
previousStrength = signalStrength
|
||
signalCycle = 0
|
||
await asyncio.sleep(signalHoldTime)
|
||
return message
|
||
else:
|
||
signalCycle += 1
|
||
if signalCycle >= signalCycleLimit:
|
||
signalCycle = 0
|
||
previousStrength = -40
|
||
await asyncio.sleep(signalCooldown)
|
||
return None
|
||
except Exception as e:
|
||
signalStrength = -40
|
||
signalCycle = 0
|
||
previousStrength = -40
|
||
|
||
def make_vox_callback(loop, q):
|
||
def vox_callback(indata, frames, time, status):
|
||
if status:
|
||
logger.warning(f"RadioMon: VOX input status: {status}")
|
||
try:
|
||
loop.call_soon_threadsafe(q.put_nowait, bytes(indata))
|
||
except RuntimeError:
|
||
pass
|
||
return vox_callback
|
||
|
||
voxInputDevice = None
|
||
async def voxMonitor():
|
||
global previousVoxState, voxMsgQueue
|
||
try:
|
||
model = Model(lang="en-us")
|
||
device_info = sd.query_devices(voxInputDevice, 'input')
|
||
samplerate = 16000
|
||
logger.debug(f"RadioMon: VOX monitor started on device {device_info['name']} with samplerate {samplerate}")
|
||
rec = KaldiRecognizer(model, samplerate)
|
||
loop = asyncio.get_running_loop()
|
||
callback = make_vox_callback(loop, q)
|
||
with sd.RawInputStream(
|
||
device=voxInputDevice,
|
||
samplerate=samplerate,
|
||
blocksize=8000,
|
||
dtype='int16',
|
||
channels=1,
|
||
callback=callback
|
||
):
|
||
while True:
|
||
data = await q.get()
|
||
if rec.AcceptWaveform(data):
|
||
result = rec.Result()
|
||
text = json.loads(result).get("text", "")
|
||
if text and text != "huh":
|
||
logger.info(f"🎙️Detected {voxDescription}: {text}")
|
||
voxMsgQueue.append(f"🎙️Detected {voxDescription}: {text}")
|
||
await asyncio.sleep(0.5)
|
||
except Exception as e:
|
||
logger.error(f"RadioMon: Error in VOX monitor: {e}")
|
||
|
||
# end of file |