2026-10-29 08:46:31 +01:00
# 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 )` .
2026-10-29 09:08:25 +01:00
## 4. Uso (script unico, ambiente `.venv`)
2026-10-29 08:46:31 +01:00
2026-10-29 09:08:25 +01:00
`genera.py` con dipendenza dichiarata in testa (PEP 723, usata anche da `uv run` ); ambiente `.venv` creato con `uv venv` e `uv pip install numpy` :
2026-10-29 08:46:31 +01:00
```python
# /// script
# requires-python = ">=3.11"
# dependencies = ["numpy"]
# ///
```
```bash
2026-10-29 09:08:25 +01:00
python genera.py config.toml --seed 42 # riproducibile -> output/gen_42.csv
python genera.py config.toml -o out.csv # percorso a scelta, seed casuale (stampato su stderr)
python genera.py config.toml -o - > out.csv # stdout
2026-10-29 08:46:31 +01:00
```
2026-10-29 08:52:32 +01:00
Opzioni: `config` (posizionale), `-o/--output` (default `output/gen_<seed>.csv` , cartella creata se manca; `-` = stdout), `--seed` (default: casuale, stampato su stderr).
2026-10-29 08:46:31 +01:00
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) |
2026-10-29 08:56:08 +01:00
| Rumore a freddo: `std(diff(T))/√2` prima del primo gradino (~15 s) | 0.09 – 0.13 |
2026-10-29 08:52:32 +01:00
| Picco di `T` | ≈ 220 – 222 °C |
2026-10-29 08:56:08 +01:00
| `T` a fine run | ≈ `T_amb` (≈ 23.4 °C, entro ~0.5 °C) |
2026-10-29 08:46:31 +01:00
| `ΔT` a inizio run | ≈ 0 °C (solo rumore) |
2026-10-29 08:56:08 +01:00
| `ΔT` a fine run | ≈ 0.4 °C (residuo del raffreddamento) |
2026-10-29 08:52:32 +01:00
| Intervalli `dt` | tutti in `dt_ms ± jitter_ms` (prima di arrotondare `t_ms` a intero: nel CSV le differenze sono 149– 152) |
2026-10-29 08:46:31 +01:00
| 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.