mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
vox detection
This commit is contained in:
@@ -294,6 +294,10 @@ signalHoldTime = 10
|
||||
# the following are combined to reset the monitor
|
||||
signalCooldown = 5
|
||||
signalCycleLimit = 5
|
||||
# enable VOX detection using default input
|
||||
voxDetectionEnabled = False
|
||||
# description to use in the alert message
|
||||
voxDescription = VOX
|
||||
|
||||
[fileMon]
|
||||
filemon_enabled = False
|
||||
|
||||
@@ -1895,6 +1895,9 @@ async def main():
|
||||
|
||||
if radio_detection_enabled:
|
||||
tasks.append(asyncio.create_task(handleSignalWatcher(), name="hamlib"))
|
||||
|
||||
if voxDetectionEnabled:
|
||||
tasks.append(asyncio.create_task(voxMonitor(), name="vox_detection"))
|
||||
|
||||
logger.debug(f"System: Starting {len(tasks)} async tasks")
|
||||
|
||||
|
||||
@@ -7,6 +7,16 @@ import socket
|
||||
import asyncio
|
||||
from modules.log import *
|
||||
|
||||
voxHoldTime = signalHoldTime
|
||||
previousVoxState = False
|
||||
|
||||
if voxDetectionEnabled:
|
||||
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()
|
||||
|
||||
|
||||
def get_hamlib(msg="f"):
|
||||
try:
|
||||
rigControlSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
@@ -133,6 +143,11 @@ 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
|
||||
@@ -157,4 +172,38 @@ async def signalWatcher():
|
||||
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
|
||||
|
||||
async def voxMonitor():
|
||||
global previousVoxState, voxMsgQueue
|
||||
try:
|
||||
model = Model(lang="en-us")
|
||||
device_info = sd.query_devices(None, '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(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
|
||||
@@ -32,6 +32,7 @@ surveyTracker, tictactoeTracker, hamtestTracker, hangmanTracker, golfTracker, ma
|
||||
cmdHistory = [] # list to hold the command history for lheard and history commands
|
||||
msg_history = [] # list to hold the message history for the messages command
|
||||
max_bytes = 200 # Meshtastic has ~237 byte limit, use conservative 200 bytes for message content
|
||||
voxMsgQueue = [] # queue for VOX detected messages
|
||||
|
||||
# Read the config file, if it does not exist, create basic config file
|
||||
config = configparser.ConfigParser()
|
||||
@@ -360,10 +361,13 @@ try:
|
||||
radio_detection_enabled = config['radioMon'].getboolean('enabled', False)
|
||||
rigControlServerAddress = config['radioMon'].get('rigControlServerAddress', 'localhost:4532') # default localhost:4532
|
||||
sigWatchBroadcastCh = config['radioMon'].get('sigWatchBroadcastCh', '2').split(',') # default Channel 2
|
||||
sigWatchBroadcastInterface = config['radioMon'].getint('sigWatchBroadcastInterface', 1) # default interface 1
|
||||
signalDetectionThreshold = config['radioMon'].getint('signalDetectionThreshold', -10) # default -10 dBm
|
||||
signalHoldTime = config['radioMon'].getint('signalHoldTime', 10) # default 10 seconds
|
||||
signalCooldown = config['radioMon'].getint('signalCooldown', 5) # default 1 second
|
||||
signalCycleLimit = config['radioMon'].getint('signalCycleLimit', 5) # default 5 cycles, used with SIGNAL_COOLDOWN
|
||||
voxDetectionEnabled = config['radioMon'].getboolean('voxDetectionEnabled', False) # default VOX detection disabled
|
||||
voxDescription = config['radioMon'].get('voxDescription', 'VOX') # default VOX detected audio message
|
||||
|
||||
# file monitor
|
||||
file_monitor_enabled = config['fileMon'].getboolean('filemon_enabled', False)
|
||||
|
||||
@@ -285,6 +285,9 @@ if checklist_enabled:
|
||||
if radio_detection_enabled:
|
||||
from modules.radio import * # from the spudgunman/meshing-around repo
|
||||
|
||||
if voxDetectionEnabled:
|
||||
from modules.radio import * # from the spudgunman/meshing-around repo
|
||||
|
||||
# File Monitor Configuration
|
||||
if file_monitor_enabled or read_news_enabled or bee_enabled:
|
||||
from modules.filemon import * # from the spudgunman/meshing-around repo
|
||||
@@ -1640,24 +1643,14 @@ async def handleSignalWatcher():
|
||||
if type(sigWatchBroadcastCh) is list:
|
||||
for ch in sigWatchBroadcastCh:
|
||||
if antiSpam and ch != publicChannel:
|
||||
send_message(msg, int(ch), 0, 1)
|
||||
send_message(msg, int(ch), 0, sigWatchBroadcastInterface)
|
||||
time.sleep(responseDelay)
|
||||
if multiple_interface:
|
||||
for i in range(2, 10):
|
||||
if globals().get(f'interface{i}_enabled'):
|
||||
send_message(msg, int(ch), 0, i)
|
||||
time.sleep(responseDelay)
|
||||
else:
|
||||
logger.warning(f"System: antiSpam prevented Alert from Hamlib {msg}")
|
||||
else:
|
||||
if antiSpam and sigWatchBroadcastCh != publicChannel:
|
||||
send_message(msg, int(sigWatchBroadcastCh), 0, 1)
|
||||
send_message(msg, int(sigWatchBroadcastCh), 0, sigWatchBroadcastInterface)
|
||||
time.sleep(responseDelay)
|
||||
if multiple_interface:
|
||||
for i in range(2, 10):
|
||||
if globals().get(f'interface{i}_enabled'):
|
||||
send_message(msg, int(sigWatchBroadcastCh), 0, i)
|
||||
time.sleep(responseDelay)
|
||||
else:
|
||||
logger.warning(f"System: antiSpam prevented Alert from Hamlib {msg}")
|
||||
|
||||
@@ -1795,11 +1788,30 @@ async def handleSentinel(deviceID):
|
||||
else:
|
||||
handleSentinel_loop += 1
|
||||
|
||||
async def process_vox_queue():
|
||||
# process the voxMsgQueue
|
||||
global voxMsgQueue
|
||||
items_to_process = voxMsgQueue[:]
|
||||
voxMsgQueue.clear()
|
||||
if len(items_to_process) > 0:
|
||||
logger.debug(f"System: Processing {len(items_to_process)} items in voxMsgQueue")
|
||||
for item in items_to_process:
|
||||
message = item
|
||||
for channel in sigWatchBroadcastCh:
|
||||
if antiSpam and int(channel) != publicChannel:
|
||||
send_message(message, int(channel), 0, sigWatchBroadcastInterface)
|
||||
time.sleep(responseDelay)
|
||||
|
||||
async def watchdog():
|
||||
global telemetryData, retry_int1, retry_int2, retry_int3, retry_int4, retry_int5, retry_int6, retry_int7, retry_int8, retry_int9
|
||||
logger.debug("System: Watchdog started")
|
||||
while True:
|
||||
await asyncio.sleep(20)
|
||||
|
||||
# perform memory cleanup every 10 minutes
|
||||
if datetime.now().minute % 10 == 0:
|
||||
cleanup_memory()
|
||||
|
||||
# check all interfaces
|
||||
for i in range(1, 10):
|
||||
interface = globals().get(f'interface{i}')
|
||||
@@ -1834,16 +1846,16 @@ async def watchdog():
|
||||
# check for noisy telemetry
|
||||
if noisyNodeLogging:
|
||||
noisyTelemetryCheck()
|
||||
|
||||
# vox queue processing
|
||||
if voxDetectionEnabled:
|
||||
await process_vox_queue()
|
||||
|
||||
# check the load_bbsdm flag to reload the BBS messages from disk
|
||||
if bbs_enabled and bbsAPI_enabled:
|
||||
load_bbsdm()
|
||||
load_bbsdb()
|
||||
|
||||
# perform memory cleanup every 10 minutes
|
||||
if datetime.now().minute % 10 == 0:
|
||||
cleanup_memory()
|
||||
|
||||
def exit_handler():
|
||||
# Close the interface and save the BBS messages
|
||||
logger.debug(f"System: Closing Autoresponder")
|
||||
|
||||
Reference in New Issue
Block a user