From 443f7bac66da3e891e2a8161fe82a44f8aeed33b Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 29 Oct 2026 08:46:31 +0100 Subject: [PATCH] Aggiunge specifica del generatore, configurazione d'esempio e CLAUDE.md Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 19 +++++++++++++ config.toml | 26 ++++++++++++++++++ docs/SPEC.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 CLAUDE.md create mode 100644 config.toml create mode 100644 docs/SPEC.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1e851b6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,19 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Stato + +`genera.py` non esiste ancora: il repository contiene solo specifica, configurazione e dati reali. Implementalo seguendo [docs/SPEC.md](docs/SPEC.md), unica fonte di verità su output, modello, CLI (comandi `uv run`), errori e controlli di accettazione. Leggila per intero prima di scrivere codice. Documentazione, commenti e messaggi di errore sono in italiano. + +## File + +- [config.toml](config.toml): esempio e unica fonte dei valori numerici. Le unità sono nei suffissi delle chiavi (`_s`, `_ms`, `_c`, `_hz`). +- [dati.csv](dati.csv): registrazione reale del sensore IR (`temp_amb IR [C]`, `temp_obj IR [C]`, più roll/jolt). Riferimento per ricalibrare `sigma_c`, `wander_*`, `amb_*`; non è un output del generatore e ha formato diverso. + +## Vincoli facili da perdere + +- `ΔT` = T − ambiente, calcolato **dopo** rumore e quantizzazione di entrambi i canali, mai dalla T vera. +- Nessun valore numerico nello script: chiave mancante ⇒ exit 1. +- Un solo `numpy.random.Generator` seminato da `--seed`, così stesso seed + stessa config ⇒ file identico. +- Verifica l'implementazione con i controlli di §5 di SPEC.md sulla config d'esempio. diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..cd5ec2a --- /dev/null +++ b/config.toml @@ -0,0 +1,26 @@ +# config.toml — unica fonte dei valori (esempio). Unità nei suffissi: _s secondi, _ms millisecondi, _c gradi C, _hz 1/s. + +durata_s = 300.0 # abbastanza lunga perché T torni a T_amb dopo il raffreddamento +dt_ms = 150.5 # intervallo medio tra campioni +jitter_ms = 0.6 # variazione uniforme ±jitter dell'intervallo + +# Ambiente (baseline di T e riferimento per dT: T parte da qui e torna qui) +amb_inf_c = 23.1 +amb_A_c = -0.45 # amb(0) = amb_inf + amb_A +amb_tau_s = 58.0 +amb_sigma_c = 0.017 # rumore prima della quantizzazione +amb_passo_c = 0.1 # quantizzazione del canale ambiente + +# Rumore di T +sigma_c = 0.11 # rumore bianco gaussiano a freddo +sigma_caldo_c = 0.20 # rumore bianco con T molto sopra la baseline +x_rif_c = 20.0 # sopra questo eccesso termico il rumore vale sigma_caldo_c +wander_sigma_c = 0.04 # componente lenta (deriva casuale) +wander_tau_s = 3.5 +passo_T_c = 0.01 # risoluzione di T e dT nel CSV + +# Riscaldamento e raffreddamento +t_impulsi_s = [110.7, 111.9, 114.1] # istanti dei gradini di riscaldamento ([] = nessun riscaldamento) +dT_impulsi_c = [88.0, 75.0, 65.0] # ampiezza di ogni gradino (stessa lunghezza di t_impulsi_s) +k_raffreddamento_hz = 0.063 # raffreddamento esponenziale: tau = 1/k +tau_sensore_s = 0.07 # ritardo di risposta del sensore diff --git a/docs/SPEC.md b/docs/SPEC.md new file mode 100644 index 0000000..8c778c7 --- /dev/null +++ b/docs/SPEC.md @@ -0,0 +1,78 @@ +# Generatore CSV `T` e `ΔT` con rumore realistico — specifica minima + +**Obiettivo**: un solo script, un solo file di configurazione, un CSV con la temperatura `T` e la differenza `ΔT = T − T_amb`, +con rumore, quantizzazione e jitter dei tempi simili a un sensore IR reale. + +**Assunzione da confermare**: `T` = temperatura oggetto del sensore IR, `T_amb` = temperatura ambiente dello stesso sensore +(non scritta nel CSV, serve solo per `ΔT`). + +## 1. Output + +``` +t_ms,T_C,dT_C +150,24.50,1.80 +301,24.41,1.71 +``` +- `t_ms`: intero; `T_C`, `dT_C`: due decimali (`passo_T_c`). Separatore `,`, fine riga `\n`. +- `ΔT` è calcolato **dopo** rumore e quantizzazione di entrambi i canali, quindi eredita il passo a gradini dell'ambiente. + +## 2. Configurazione + +Tutti i valori sono in [`config.toml`](../config.toml) (unica fonte: questo documento non li ripete). Gruppi: +tempi · ambiente (anche baseline di T) · rumore · gradini di riscaldamento e raffreddamento. +Nessun valore è scritto nello script: chiave mancante ⇒ errore. + +## 3. Modello (5 passi) + +1. **Tempi**: `t_i = t_{i−1} + dt_ms + U(−jitter_ms, +jitter_ms)`, fino a `durata_s`. +2. **T vera** = ambiente vero + eccesso termico (T parte da `T_amb`, sale con i gradini e ridiscende fino a `T_amb`): + - ambiente vero `B(t) = amb_inf + amb_A·exp(−t/τ_amb)` (stessa curva del passo 5, senza rumore né quantizzazione); + - eccesso `x(t) = Σ_j ΔT_j · exp(−k·(t − t_j))` per `t ≥ t_j` (un gradino per ogni `t_impulsi_s`, poi raffreddamento esponenziale); + - `T_vera = B + x`. +3. **Risposta del sensore**: filtro del 1° ordine `y' = (T_vera − y)/τ_sens` (griglia fine, 5 ms, poi interpolazione ai `t_i`). +4. **Rumore di T**: `T_i = y_i + w_i + σ(x_i)·N(0,1)`, con + - `σ(x) = σ + (σ_caldo − σ)·min(1, x/x_rif)`; + - `w` = deriva lenta (Ornstein-Uhlenbeck): `w_{i+1} = φ·w_i + σ_w·√(1−φ²)·N(0,1)`, `φ = exp(−Δt/τ_w)`. + Poi arrotondamento a `passo_T_c`. +5. **Ambiente e ΔT**: `amb_i = round_{amb_passo}( amb_inf + amb_A·exp(−t/τ_amb) + N(0, amb_sigma²) )`; `dT_i = round_{passo}( T_i − amb_i )`. + +## 4. Uso con uv (un solo file, nessun progetto) + +`genera.py` con dipendenza dichiarata in testa (PEP 723): +```python +# /// script +# requires-python = ">=3.11" +# dependencies = ["numpy"] +# /// +``` +```bash +uv run genera.py config.toml -o out.csv --seed 42 # riproducibile +uv run genera.py config.toml > out.csv # seed casuale, stampato su stderr +``` +Opzioni: `config` (posizionale), `-o/--output` (default stdout), `--seed` (default: casuale, stampato su stderr). +Uscita con errore (exit 1) e messaggio in italiano se manca una chiave, se `t_impulsi_s` e `dT_impulsi_c` hanno lunghezze diverse +o se il file non è scrivibile. La lettura TOML usa `tomllib` (standard library): unica dipendenza esterna `numpy`. + +## 5. Controlli di accettazione (con la configurazione d'esempio) + +Verificati su un prototipo (30 seed): + +| Controllo | Atteso | +|---|---| +| `T` a inizio run | ≈ `T_amb` (≈ 22.65 °C) | +| Rumore a freddo: `std(diff(T))/√2` sui primi ~100 s | 0.105 – 0.115 | +| Picco di `T` | ≈ 224 °C (da ricontrollare col prototipo) | +| `T` a 134 s | ≈ 80.5 °C | +| `T` a fine run | ≈ `T_amb` (≈ 23.1 °C, entro ~0.1 °C) | +| `ΔT` a inizio run | ≈ 0 °C (solo rumore) | +| `ΔT` a fine run | ≈ 0 °C (solo rumore) | +| Intervalli `dt` | tutti in `dt_ms ± jitter_ms` | +| Stesso `--seed` e stessa config | file identico | + +## 6. Limiti (accettati per semplicità) + +- Un solo tempo di raffreddamento esponenziale (nessuna distinzione conduzione/convezione). +- Gradini istantanei (nessun transito sotto l'induttore e nessuna dipendenza dalla velocità). +- L'ambiente non sale quando T è alta (nessun riscaldamento dell'ambiente del sensore): `ΔT` è leggermente sovrastimato durante e subito dopo il riscaldamento. +- Nessun glitch di campionamento né disturbi a salto durante il raffreddamento. +- Rumore tarato su un solo sensore: ricalibrare `sigma_c`, `wander_*`, `amb_*` con i propri dati.