import json from fastapi import APIRouter, HTTPException from app.config import (STATE_PATH, BACKGROUND_PATH, energy_axis, NUM_CHANNELS, clip_to_range, correct_csi_nonlinear) import numpy as np router = APIRouter() @router.get("/current") async def get_current_spectrum(): """Current accumulated spectrum with energy axis (CsI-corrected).""" if not STATE_PATH.exists(): raise HTTPException(status_code=503, detail="Monitor not started yet") try: with open(STATE_PATH) as f: state = json.load(f) except (json.JSONDecodeError, OSError): raise HTTPException(status_code=503, detail="Monitor state file corrupt") raw_counts = state.get("counts", [0] * 1024)[:NUM_CHANNELS] # Apply CsI correction so peaks appear at theoretical energy positions corrected_counts = correct_csi_nonlinear(np.array(raw_counts, dtype=np.float64)) return { "timestamp": state.get("timestamp", ""), "connected": state.get("connected", False), "cumulated_live_time_s": state.get("cumulated_live_time_s", 0), "cumulated_live_time_h": state.get("cumulated_live_time_h", 0), "cps": state.get("cps", 0), "total_counts": state.get("total_counts", 0), "background_subtracted": state.get("background_subtracted", False), "isotopes_detected": state.get("isotopes_detected", []), "channels": clip_to_range(list(range(NUM_CHANNELS))), "energy_kev": energy_axis(), "counts": clip_to_range([round(float(c), 1) for c in corrected_counts]), } @router.get("/difference") async def get_difference_spectrum(): """Background-subtracted spectrum (net signal, CsI-corrected).""" if not STATE_PATH.exists(): raise HTTPException(status_code=503, detail="Monitor not started yet") try: with open(STATE_PATH) as f: state = json.load(f) except (json.JSONDecodeError, OSError): raise HTTPException(status_code=503, detail="Monitor state file corrupt") counts = np.array(state.get("counts", [0] * 1024), dtype=np.float64)[:NUM_CHANNELS] live_time = state.get("cumulated_live_time_s", 0) if live_time <= 0: raise HTTPException(status_code=503, detail="No live time data yet") rate = counts / live_time if BACKGROUND_PATH.exists(): bg_data = np.load(str(BACKGROUND_PATH), allow_pickle=True).item() bg_counts = bg_data["counts"].astype(np.float64)[:NUM_CHANNELS] bg_live_time = float(bg_data["duration"]) bg_rate = bg_counts / bg_live_time net_rate = np.clip(rate - bg_rate, 0, None) # Apply CsI correction to net spectrum corrected_net = correct_csi_nonlinear(net_rate) net_counts = corrected_net * live_time bg_available = True else: net_counts = counts bg_available = False net_counts_list = [round(float(c), 1) for c in net_counts] return { "timestamp": state.get("timestamp", ""), "cumulated_live_time_s": live_time, "background_subtracted": bg_available, "channels": clip_to_range(list(range(NUM_CHANNELS))), "energy_kev": energy_axis(), "counts": clip_to_range(net_counts_list), "raw_counts": clip_to_range(state.get("counts", [])[:NUM_CHANNELS]), }