Add audio_signal_analysis/src/audio_signal_analysis/core.py
This commit is contained in:
parent
895de7ce3e
commit
ed72bf7054
1 changed files with 65 additions and 0 deletions
65
audio_signal_analysis/src/audio_signal_analysis/core.py
Normal file
65
audio_signal_analysis/src/audio_signal_analysis/core.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import logging
|
||||||
|
from typing import Dict, Any
|
||||||
|
import numpy as np
|
||||||
|
from scipy import signal
|
||||||
|
import pandas as pd
|
||||||
|
import math
|
||||||
|
|
||||||
|
def analyze_audio_signal(audio_data: Dict[str, Any]) -> float:
|
||||||
|
"""Analyse der Audiozeitreihe und Berechnung der Korrelation mit Temperaturdaten.
|
||||||
|
|
||||||
|
Erwartet ein Dictionary mit Schlüsseln 'audio' und 'temperature', die jeweils
|
||||||
|
Zeitreihen mit Zeitstempeln und Messwerten enthalten.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
audio_data: Ein Dictionary mit Audiound Temperaturdaten.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: Korrelationskoeffizient zwischen Audio- und Temperaturdaten.
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
if not isinstance(audio_data, dict):
|
||||||
|
raise TypeError("audio_data muss ein Dictionary sein.")
|
||||||
|
required_keys = {"audio", "temperature"}
|
||||||
|
if not required_keys.issubset(audio_data.keys()):
|
||||||
|
raise ValueError(f"audio_data muss die Schlüssel {required_keys} enthalten.")
|
||||||
|
|
||||||
|
audio_df = pd.DataFrame(audio_data["audio"])
|
||||||
|
temp_df = pd.DataFrame(audio_data["temperature"])
|
||||||
|
|
||||||
|
if not {"timestamp", "audio_level"}.issubset(audio_df.columns):
|
||||||
|
raise ValueError("Fehlende Spalten in audio_data['audio'].")
|
||||||
|
if not {"timestamp", "temperature_c"}.issubset(temp_df.columns):
|
||||||
|
raise ValueError("Fehlende Spalten in audio_data['temperature'].")
|
||||||
|
|
||||||
|
# Konvertiere Zeitstempel und sortiere nach Zeit
|
||||||
|
audio_df['timestamp'] = pd.to_datetime(audio_df['timestamp'])
|
||||||
|
temp_df['timestamp'] = pd.to_datetime(temp_df['timestamp'])
|
||||||
|
audio_df.sort_values('timestamp', inplace=True)
|
||||||
|
temp_df.sort_values('timestamp', inplace=True)
|
||||||
|
|
||||||
|
# Interpolation auf gemeinsame Zeitbasis
|
||||||
|
merged = pd.merge_asof(audio_df, temp_df, on='timestamp', direction='nearest')
|
||||||
|
|
||||||
|
audio_series = merged['audio_level'].to_numpy(dtype=float)
|
||||||
|
temp_series = merged['temperature_c'].to_numpy(dtype=float)
|
||||||
|
|
||||||
|
if len(audio_series) < 2 or len(temp_series) < 2:
|
||||||
|
raise ValueError("Zu wenige Datenpunkte für Korrelationsberechnung.")
|
||||||
|
|
||||||
|
# Normalisierung
|
||||||
|
audio_series = (audio_series - np.mean(audio_series)) / np.std(audio_series)
|
||||||
|
temp_series = (temp_series - np.mean(temp_series)) / np.std(temp_series)
|
||||||
|
|
||||||
|
# Korrelation über FFT-Kreuzkorrelation
|
||||||
|
corr = signal.correlate(audio_series, temp_series, mode='valid')
|
||||||
|
corr_norm = corr / np.sqrt(np.sum(audio_series**2) * np.sum(temp_series**2))
|
||||||
|
|
||||||
|
correlation_coefficient = float(np.max(corr_norm))
|
||||||
|
|
||||||
|
# Sicherheit: Grenze auf [-1, 1]
|
||||||
|
correlation_coefficient = max(-1.0, min(1.0, correlation_coefficient))
|
||||||
|
logging.debug("Berechneter Korrelationskoeffizient: %s", correlation_coefficient)
|
||||||
|
|
||||||
|
assert math.isfinite(correlation_coefficient), "Korrelationskoeffizient ist nicht endlich"
|
||||||
|
return correlation_coefficient
|
||||||
Loading…
Reference in a new issue