Files
cosma-log-analyzer/src/cosma_log_analyzer/rules/usbl_snr_low.py
2026-04-19 15:20:17 +00:00

53 lines
1.5 KiB
Python

from __future__ import annotations
import os
import pandas as pd
from ..ingest import TOPIC_USBL
from ..models import Anomaly
from .base import Rule
class UsblSnrLowRule(Rule):
"""Fire when SNR stays below threshold for `consec` consecutive samples."""
name = "usbl_snr_low"
topic = TOPIC_USBL
severity = "warn"
def __init__(
self,
subject: str = "AUV000",
min_snr_db: float | None = None,
consec: int = 3,
) -> None:
super().__init__(subject)
if min_snr_db is None:
min_snr_db = float(os.environ.get("USBL_SNR_LOW", 5.0))
self.min_snr_db = min_snr_db
self.consec = consec
def detect(self, df: pd.DataFrame) -> list[Anomaly]:
if df.empty or len(df) < self.consec:
return []
low = df["snr_db"] < self.min_snr_db
run = low.astype(int).groupby((~low).cumsum()).cumsum()
anomalies: list[Anomaly] = []
fired_run = -1
runs_id = (~low).cumsum()
for idx in df.index:
if run.iloc[idx] >= self.consec and runs_id.iloc[idx] != fired_run:
fired_run = int(runs_id.iloc[idx])
anomalies.append(
self._make(
ts=float(df.at[idx, "ts"]),
value=float(df.at[idx, "snr_db"]),
context={
"min_snr_db": self.min_snr_db,
"consec": self.consec,
},
)
)
return anomalies