Add laser_echo_analysis/src/laser_echo_analysis/core.py
This commit is contained in:
parent
081b87b8d9
commit
afe54c3d96
1 changed files with 103 additions and 0 deletions
103
laser_echo_analysis/src/laser_echo_analysis/core.py
Normal file
103
laser_echo_analysis/src/laser_echo_analysis/core.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DataValidationError(Exception):
|
||||||
|
"""Custom exception for data validation errors."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LaserMeasurement:
|
||||||
|
timestamp: pd.Timestamp
|
||||||
|
pixel_value: int
|
||||||
|
delta_t: float
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AnalysisResult:
|
||||||
|
peak: int
|
||||||
|
average_noise: float
|
||||||
|
signal_to_noise_ratio: float
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_input_data(data: List[Dict[str, Any]]) -> pd.DataFrame:
|
||||||
|
"""Validate and convert input data into pandas DataFrame."""
|
||||||
|
if not isinstance(data, list):
|
||||||
|
raise DataValidationError("Input data must be a list of dictionaries.")
|
||||||
|
if not data:
|
||||||
|
raise DataValidationError("Input data list is empty.")
|
||||||
|
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
required_columns = {"timestamp", "pixel_value", "delta_t"}
|
||||||
|
missing = required_columns - set(df.columns)
|
||||||
|
if missing:
|
||||||
|
raise DataValidationError(f"Missing required fields: {', '.join(missing)}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
df["timestamp"] = pd.to_datetime(df["timestamp"], errors="raise")
|
||||||
|
df["pixel_value"] = df["pixel_value"].astype(int)
|
||||||
|
df["delta_t"] = df["delta_t"].astype(float)
|
||||||
|
except Exception as e:
|
||||||
|
raise DataValidationError(f"Data type conversion failed: {e}")
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_data(data: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Analysiert Rohmessdaten aus einer CSV-Datei und berechnet Kennzahlen wie Spitzenwert,
|
||||||
|
durchschnittliches Rauschen und Signal-zu-Rausch-Verhältnis.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (list[dict]): Liste der Messdatensätze, eingelesen aus der CSV-Datei.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Analyseergebnisse inklusive peak, average_noise und signal_to_noise_ratio.
|
||||||
|
"""
|
||||||
|
logger.info("Starting laser data analysis on %d records", len(data))
|
||||||
|
|
||||||
|
df = _validate_input_data(data)
|
||||||
|
|
||||||
|
if df.empty:
|
||||||
|
raise DataValidationError("DataFrame is empty after validation.")
|
||||||
|
|
||||||
|
peak = int(df["pixel_value"].max())
|
||||||
|
|
||||||
|
noise_values = df.loc[
|
||||||
|
(df["pixel_value"] < peak * 0.1) & (df["pixel_value"] > 0), "pixel_value"
|
||||||
|
]
|
||||||
|
average_noise = float(noise_values.mean()) if not noise_values.empty else 0.0
|
||||||
|
|
||||||
|
if average_noise <= 0:
|
||||||
|
signal_to_noise_ratio = math.inf
|
||||||
|
else:
|
||||||
|
signal_to_noise_ratio = float(peak / average_noise)
|
||||||
|
|
||||||
|
result = AnalysisResult(
|
||||||
|
peak=peak,
|
||||||
|
average_noise=average_noise,
|
||||||
|
signal_to_noise_ratio=signal_to_noise_ratio,
|
||||||
|
)
|
||||||
|
|
||||||
|
result_dict = {
|
||||||
|
"peak": result.peak,
|
||||||
|
"average_noise": result.average_noise,
|
||||||
|
"signal_to_noise_ratio": result.signal_to_noise_ratio,
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Analysis completed: %s", result_dict)
|
||||||
|
assert all(k in result_dict for k in ("peak", "average_noise", "signal_to_noise_ratio"))
|
||||||
|
|
||||||
|
return result_dict
|
||||||
Loading…
Reference in a new issue