Update DOCS.md

This commit is contained in:
Alexandre
2024-10-28 08:02:44 +01:00
committed by GitHub
parent 6ff3c5bd84
commit 44e8ebe04c

View File

@@ -455,20 +455,25 @@ Changelog:
- 2024-10-27: Enhanced debug output with logging levels. - 2024-10-27: Enhanced debug output with logging levels.
- 2024-10-28: Added summary log mode for simplified output. - 2024-10-28: Added summary log mode for simplified output.
- 2024-10-28: Removed gain stabilization delay for immediate gain adjustments. - 2024-10-28: Removed gain stabilization delay for immediate gain adjustments.
- 2024-10-28: Max gain capped at 40 dB, suppressed `amixer` logging.
- 2024-10-28: Implemented sampling rate reduction, capture duration reduction, scipy.fft usage, lower filter order, and multiprocessing for efficiency.
""" """
import subprocess import subprocess
import numpy as np import numpy as np
from scipy.signal import butter, sosfilt, find_peaks from scipy.signal import butter, sosfilt, find_peaks
from scipy.fft import rfft, rfftfreq
import time import time
import re import re
import multiprocessing as mp
import sys
# ---------------------------- Configuration ---------------------------- # ---------------------------- Configuration ----------------------------
# Microphone Settings # Microphone Settings
MICROPHONE_NAME = "Line In 1 Gain" MICROPHONE_NAME = "Line In 1 Gain"
MIN_GAIN_DB = 20 MIN_GAIN_DB = 20
MAX_GAIN_DB = 45 MAX_GAIN_DB = 40 # Capped at 40 dB
DECREASE_GAIN_STEP_DB = 1 DECREASE_GAIN_STEP_DB = 1
INCREASE_GAIN_STEP_DB = 5 INCREASE_GAIN_STEP_DB = 5
CLIPPING_REDUCTION_DB = 3 CLIPPING_REDUCTION_DB = 3
@@ -481,7 +486,10 @@ NOISE_THRESHOLD_LOW = 0.00035
TREND_COUNT_THRESHOLD = 3 TREND_COUNT_THRESHOLD = 3
# Sampling Rate # Sampling Rate
SAMPLING_RATE = 48000 # Updated from 32000 to 48000 Hz SAMPLING_RATE = 44100 # Reduced from 48000 Hz to 44100 Hz
# Audio Capture Duration
AUDIO_CAPTURE_DURATION = 2 # Reduced from 5 seconds to 2 seconds
# RTSP Stream URL # RTSP Stream URL
RTSP_URL = "rtsp://192.168.178.124:8554/birdmic" RTSP_URL = "rtsp://192.168.178.124:8554/birdmic"
@@ -501,6 +509,11 @@ REFERENCE_PRESSURE = 20e-6
THD_FUNDAMENTAL_THRESHOLD_DB = 60 THD_FUNDAMENTAL_THRESHOLD_DB = 60
MAX_THD_PERCENTAGE = 5.0 MAX_THD_PERCENTAGE = 5.0
# Filter Settings
LOWCUT = 2000
HIGHCUT = 8000
FILTER_ORDER = 3 # Reduced from 5 to 3 for efficiency
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
@@ -539,28 +552,23 @@ def get_gain_db(mic_name):
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode() output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode()
match = re.search(r'\[(-?\d+(\.\d+)?)dB\]', output) match = re.search(r'\[(-?\d+(\.\d+)?)dB\]', output)
if match: if match:
gain_db = float(match.group(1)) return float(match.group(1))
debug_print(f"Retrieved gain: {gain_db} dB", "info")
return gain_db
else: else:
debug_print("No gain information found in amixer output.", "warning")
return None return None
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError:
debug_print(f"amixer sget failed: {e}", "error")
return None return None
def set_gain_db(mic_name, gain_db): def set_gain_db(mic_name, gain_db):
""" """
Sets the gain of the specified microphone using amixer. Sets the gain of the specified microphone using amixer.
Suppresses logging related to this action.
""" """
cmd = ['amixer', 'sset', mic_name, f'{gain_db}dB'] cmd = ['amixer', 'sset', mic_name, f'{gain_db}dB']
try: try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT) subprocess.check_call(cmd, stderr=subprocess.STDOUT)
debug_print(f"Set gain to: {gain_db} dB", "info")
return True return True
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError:
debug_print(f"amixer sset failed: {e}", "error")
return False return False
@@ -591,8 +599,8 @@ def thd_calculation(audio, sampling_rate, num_harmonics=5):
""" """
Calculates Total Harmonic Distortion (THD) for the audio signal. Calculates Total Harmonic Distortion (THD) for the audio signal.
""" """
fft_vals = np.fft.rfft(audio) fft_vals = rfft(audio)
fft_freqs = np.fft.rfftfreq(len(audio), 1 / sampling_rate) fft_freqs = rfftfreq(len(audio), 1 / sampling_rate)
fft_magnitude = np.abs(fft_vals) fft_magnitude = np.abs(fft_vals)
fundamental_freq, fundamental_amplitude = find_fundamental_frequency(fft_freqs, fft_magnitude) fundamental_freq, fundamental_amplitude = find_fundamental_frequency(fft_freqs, fft_magnitude)
@@ -642,13 +650,14 @@ def detect_microphone_overload(spl, mic_clipping_spl):
return False return False
def calculate_noise_rms_and_thd(rtsp_url, bandpass_sos, sampling_rate, num_bins=5): def calculate_noise_rms_and_thd(rtsp_url, bandpass_sos, sampling_rate):
""" """
Captures audio from an RTSP stream, calculates RMS, THD, and SPL, and detects microphone overload. Captures audio from an RTSP stream, calculates RMS, THD, and SPL, and detects microphone overload.
""" """
cmd = [ cmd = [
'ffmpeg', '-loglevel', 'error', '-rtsp_transport', 'tcp', '-i', rtsp_url, 'ffmpeg', '-loglevel', 'error', '-rtsp_transport', 'tcp', '-i', rtsp_url,
'-vn', '-f', 's16le', '-acodec', 'pcm_s16le', '-ar', str(sampling_rate), '-ac', '1', '-t', '5', '-' '-vn', '-f', 's16le', '-acodec', 'pcm_s16le', '-ar', str(sampling_rate),
'-ac', '1', '-t', str(AUDIO_CAPTURE_DURATION), '-'
] ]
retries = 3 retries = 3
@@ -685,18 +694,25 @@ def calculate_noise_rms_and_thd(rtsp_url, bandpass_sos, sampling_rate, num_bins=
return None, None, None, False return None, None, None, False
def audio_capture_process(queue, rtsp_url, sos, sampling_rate):
"""
Separate process for capturing and processing audio.
"""
while True:
rms, thd, spl, overload = calculate_noise_rms_and_thd(rtsp_url, sos, sampling_rate)
queue.put((rms, thd, spl, overload))
time.sleep(60) # Wait for the next capture cycle
def main(): def main():
""" """
Main loop that continuously monitors background noise, detects clipping, calculates THD, Main loop that continuously monitors background noise, detects clipping, calculates THD,
and adjusts microphone gain with retry logic for RTSP stream resilience. and adjusts microphone gain with multiprocessing for audio capture and processing.
""" """
TREND_COUNT = 0 TREND_COUNT = 0
PREVIOUS_TREND = 0 PREVIOUS_TREND = 0
# Precompute bandpass filter coefficients with updated SAMPLING_RATE # Precompute bandpass filter coefficients with updated SAMPLING_RATE and lower FILTER_ORDER
LOWCUT = 2000
HIGHCUT = 8000
FILTER_ORDER = 5
sos = butter(FILTER_ORDER, [LOWCUT, HIGHCUT], btype='band', fs=SAMPLING_RATE, output='sos') sos = butter(FILTER_ORDER, [LOWCUT, HIGHCUT], btype='band', fs=SAMPLING_RATE, output='sos')
# Set the microphone gain to the maximum gain at the start # Set the microphone gain to the maximum gain at the start
@@ -705,14 +721,26 @@ def main():
print(f"Microphone gain set to {MAX_GAIN_DB} dB at start.") print(f"Microphone gain set to {MAX_GAIN_DB} dB at start.")
else: else:
print("Failed to set microphone gain at start. Exiting.") print("Failed to set microphone gain at start. Exiting.")
return sys.exit(1)
# Initialize multiprocessing queue
manager = mp.Manager()
queue = manager.Queue()
# Start audio capture and processing in a separate process
p = mp.Process(target=audio_capture_process, args=(queue, RTSP_URL, sos, SAMPLING_RATE))
p.start()
debug_print("Started audio capture and processing process.", "info")
while True: while True:
rms, thd, spl, overload = calculate_noise_rms_and_thd(RTSP_URL, sos, SAMPLING_RATE) if not queue.empty():
rms, thd, spl, overload = queue.get()
else:
time.sleep(1)
continue
if rms is None: if rms is None:
print("Failed to compute noise RMS. Retrying in 1 minute...") print("Failed to compute noise RMS. Retrying in 1 minute...")
time.sleep(60)
continue continue
# Adjust gain if overload detected # Adjust gain if overload detected
@@ -722,12 +750,10 @@ def main():
NEW_GAIN_DB = max(current_gain_db - CLIPPING_REDUCTION_DB, MIN_GAIN_DB) NEW_GAIN_DB = max(current_gain_db - CLIPPING_REDUCTION_DB, MIN_GAIN_DB)
if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB): if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB):
print(f"Clipping detected. Reduced gain to {NEW_GAIN_DB} dB") print(f"Clipping detected. Reduced gain to {NEW_GAIN_DB} dB")
debug_print(f"Gain reduced to {NEW_GAIN_DB} dB due to clipping.", "warning") current_gain_db = NEW_GAIN_DB # Update current gain
# No stabilization delay; continue to next iteration # Output summary log
# Skip trend adjustment in case of clipping
summary_log(current_gain_db if current_gain_db else MIN_GAIN_DB, True, rms, thd) summary_log(current_gain_db if current_gain_db else MIN_GAIN_DB, True, rms, thd)
time.sleep(60) continue # Skip trend adjustment in case of clipping
continue
# Handle THD if SPL is above threshold # Handle THD if SPL is above threshold
if spl >= THD_FUNDAMENTAL_THRESHOLD_DB: if spl >= THD_FUNDAMENTAL_THRESHOLD_DB:
@@ -738,7 +764,7 @@ def main():
NEW_GAIN_DB = max(current_gain_db - DECREASE_GAIN_STEP_DB, MIN_GAIN_DB) NEW_GAIN_DB = max(current_gain_db - DECREASE_GAIN_STEP_DB, MIN_GAIN_DB)
if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB): if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB):
print(f"High THD detected. Decreased gain to {NEW_GAIN_DB} dB") print(f"High THD detected. Decreased gain to {NEW_GAIN_DB} dB")
debug_print(f"Gain decreased to {NEW_GAIN_DB} dB due to high THD.", "info") current_gain_db = NEW_GAIN_DB # Update current gain
else: else:
debug_print("THD within acceptable limits.", "info") debug_print("THD within acceptable limits.", "info")
else: else:
@@ -769,11 +795,8 @@ def main():
if current_gain_db is None: if current_gain_db is None:
print("Failed to get current gain level. Retrying in 1 minute...") print("Failed to get current gain level. Retrying in 1 minute...")
time.sleep(60)
continue continue
debug_print(f"Current gain: {current_gain_db} dB", "info")
# Output summary log for the current state # Output summary log for the current state
summary_log(current_gain_db, overload, rms, thd) summary_log(current_gain_db, overload, rms, thd)
@@ -784,14 +807,14 @@ def main():
NEW_GAIN_DB = max(current_gain_db - DECREASE_GAIN_STEP_DB, MIN_GAIN_DB) NEW_GAIN_DB = max(current_gain_db - DECREASE_GAIN_STEP_DB, MIN_GAIN_DB)
if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB): if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB):
print(f"Background noise high. Decreased gain to {NEW_GAIN_DB} dB") print(f"Background noise high. Decreased gain to {NEW_GAIN_DB} dB")
debug_print(f"Gain decreased to {NEW_GAIN_DB} dB due to high noise.", "info") current_gain_db = NEW_GAIN_DB # Update current gain
TREND_COUNT = 0 TREND_COUNT = 0
elif CURRENT_TREND == -1: elif CURRENT_TREND == -1:
# Increase gain by INCREASE_GAIN_STEP_DB dB # Increase gain by INCREASE_GAIN_STEP_DB dB
NEW_GAIN_DB = min(current_gain_db + INCREASE_GAIN_STEP_DB, MAX_GAIN_DB) NEW_GAIN_DB = min(current_gain_db + INCREASE_GAIN_STEP_DB, MAX_GAIN_DB)
if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB): if set_gain_db(MICROPHONE_NAME, NEW_GAIN_DB):
print(f"Background noise low. Increased gain to {NEW_GAIN_DB} dB") print(f"Background noise low. Increased gain to {NEW_GAIN_DB} dB")
debug_print(f"Gain increased to {NEW_GAIN_DB} dB due to low noise.", "info") current_gain_db = NEW_GAIN_DB # Update current gain
TREND_COUNT = 0 TREND_COUNT = 0
else: else:
debug_print("No gain adjustment needed based on noise trend.", "info") debug_print("No gain adjustment needed based on noise trend.", "info")