Add kiesel_temperature_logging/src/kiesel_temperature_logging/core.py
This commit is contained in:
parent
155cb7ca7e
commit
fe129bcc7b
1 changed files with 102 additions and 0 deletions
|
|
@ -0,0 +1,102 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from datetime import datetime
|
||||||
|
import numpy as np
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TemperatureLog:
|
||||||
|
timestamp: str
|
||||||
|
temperature: float
|
||||||
|
|
||||||
|
def validate(self) -> None:
|
||||||
|
try:
|
||||||
|
datetime.fromisoformat(self.timestamp)
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(f"Invalid timestamp format: {self.timestamp}") from e
|
||||||
|
if not isinstance(self.temperature, (float, int)):
|
||||||
|
raise TypeError(f"Temperature must be float or int, got {type(self.temperature)}")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TemperatureAnalysisResult:
|
||||||
|
mean_temperature: float
|
||||||
|
pulse_interval: float
|
||||||
|
correlation: float
|
||||||
|
|
||||||
|
|
||||||
|
_LOG_FILE = Path('data/temperature_log.json')
|
||||||
|
|
||||||
|
def _ensure_logfile_exists(path: Path) -> None:
|
||||||
|
if not path.exists():
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with path.open('w') as f:
|
||||||
|
json.dump([], f)
|
||||||
|
|
||||||
|
|
||||||
|
def log_temperature_data(temperature: float, timestamp: str) -> None:
|
||||||
|
"""Schreibt einen neuen Temperaturmesspunkt in die JSON-Logdatei."""
|
||||||
|
entry = TemperatureLog(timestamp=timestamp, temperature=float(temperature))
|
||||||
|
entry.validate()
|
||||||
|
_ensure_logfile_exists(_LOG_FILE)
|
||||||
|
|
||||||
|
with _LOG_FILE.open('r+', encoding='utf-8') as f:
|
||||||
|
try:
|
||||||
|
data = json.load(f)
|
||||||
|
if not isinstance(data, list):
|
||||||
|
data = []
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
data = []
|
||||||
|
data.append(asdict(entry))
|
||||||
|
f.seek(0)
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
f.truncate()
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_temperature_pulses(data: List[Dict[str, Any]]) -> Dict[str, float]:
|
||||||
|
"""Analysiert Temperaturdaten zur Erkennung periodischer Pulse im Werteverlauf."""
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
raise ValueError("Data list is empty.")
|
||||||
|
|
||||||
|
# Sort by timestamp
|
||||||
|
try:
|
||||||
|
data_sorted = sorted(data, key=lambda x: datetime.fromisoformat(x['timestamp']))
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError("Invalid timestamp in data.") from e
|
||||||
|
|
||||||
|
temps = np.array([float(item['temperature']) for item in data_sorted])
|
||||||
|
times = np.array([
|
||||||
|
datetime.fromisoformat(item['timestamp']).timestamp() for item in data_sorted
|
||||||
|
])
|
||||||
|
|
||||||
|
if len(temps) < 2:
|
||||||
|
raise ValueError("Not enough data points for analysis.")
|
||||||
|
|
||||||
|
mean_temp = float(np.mean(temps))
|
||||||
|
|
||||||
|
# Frequency analysis via FFT for pulse detection
|
||||||
|
detrended = temps - mean_temp
|
||||||
|
fft_vals = np.fft.rfft(detrended)
|
||||||
|
fft_freq = np.fft.rfftfreq(len(times), d=np.median(np.diff(times)))
|
||||||
|
|
||||||
|
if len(fft_freq) < 2:
|
||||||
|
pulse_interval = 0.0
|
||||||
|
corr = 0.0
|
||||||
|
else:
|
||||||
|
power_spectrum = np.abs(fft_vals)
|
||||||
|
dominant_idx = np.argmax(power_spectrum[1:]) + 1
|
||||||
|
dominant_freq = fft_freq[dominant_idx]
|
||||||
|
pulse_interval = float(1.0 / dominant_freq) if dominant_freq != 0 else 0.0
|
||||||
|
corr = float(np.corrcoef(temps[:-1], temps[1:])[0, 1]) if len(temps) > 1 else 0.0
|
||||||
|
|
||||||
|
result = TemperatureAnalysisResult(
|
||||||
|
mean_temperature=mean_temp,
|
||||||
|
pulse_interval=pulse_interval,
|
||||||
|
correlation=corr
|
||||||
|
)
|
||||||
|
|
||||||
|
return asdict(result)
|
||||||
Loading…
Reference in a new issue