Files

85 lines
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 · salti.
Nessun valore è scritto nello script: chiave mancante ⇒ errore.
**Parametri casuali per corsa**: le chiavi scritte come intervallo `[min, max]` (ambiente `amb_inf_c`, `amb_A_c`, `amb_tau_s`; rumore `sigma_c`, `sigma_caldo_c`, `wander_*`; `k_raffreddamento_hz`, `tau_sensore_s`; frequenze e σ dei salti) vengono sorteggiate uniformi a ogni corsa (`[x, x]` = valore fisso). I gradini sono generati da `n_gradini` (intero, estremi inclusi, min ≥ 1), `t_primo_gradino_s`, `distanza_gradini_s` (tra gradini consecutivi) e `dT_gradino_c` (ampiezza di ciascuno). Intervallo non valido (non una coppia di numeri, o min > max) ⇒ 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 istante sorteggiato, poi raffreddamento esponenziale);
- **variabilità**: a ogni corsa numero, istanti e ampiezze dei gradini, `k`, ambiente, livelli di rumore e frequenza dei salti sono sorteggiati dagli intervalli di config, quindi curva, picco e rumore cambiano molto con il seed;
- **salti** (stocastici, scarti casuali con rientro `exp(−t/τ)`, come i gradini irregolari osservati su un sensore reale):
durante il **riscaldamento** (tra il primo e l'ultimo gradino) con `salti_risc_*`, ampiezza `N(media, σ²)` nei due versi;
durante il **raffreddamento** (dopo l'ultimo gradino, mentre `x ≥ salti_x_min_c`) con `salti_*`; istanti di tipo Poisson in entrambi i casi; ampiezza limitata a ±`salti_max_c` (niente scarti estremi);
- `T_vera = B + x + salti`.
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 (script unico, ambiente `.venv`)
`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`:
```python
# /// script
# requires-python = ">=3.11"
# dependencies = ["numpy"]
# ///
```
```bash
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
```
Opzioni: `config` (posizionale), `-o/--output` (default `output/gen_<seed>.csv`, cartella creata se manca; `-` = stdout), `--seed` (default: casuale, stampato su stderr).
Uscita con errore (exit 1) e messaggio in italiano se manca una chiave, se un intervallo `[min, max]` non è valido
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 60 seed (valori min–max; con parametri sorteggiati gli intervalli sono larghi, ma `T` e `ΔT` restano coerenti con ambiente e rumore della corsa):
| Controllo | Atteso |
|---|---|
| `T` a inizio run | ≈ `T_amb` della corsa (20.1 – 26.2 °C) |
| Rumore a freddo: `std(diff(T))/√2` prima del primo gradino (~15 s) | 0.06 – 0.19 (segue `sigma_c` sorteggiato) |
| Picco di `T` | 66 – 227 °C (dipende da numero e ampiezza dei gradini) |
| `T` a fine run | ≈ `T_amb` della corsa: 20.0 – 26.4 °C |
| `ΔT` a inizio run | ≈ 0 °C (solo rumore): −0.18 – 0.15 |
| `ΔT` a fine run | −0.08 – 1.6 °C (residuo del raffreddamento) |
| Intervalli `dt` | tutti in `dt_ms ± jitter_ms` (prima di arrotondare `t_ms` a intero: nel CSV le differenze sono 149–152) |
| 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; i salti durante il raffreddamento sono un modello statistico semplice (rientro esponenziale), non un fenomeno fisico.
- Rumore tarato su un solo sensore: ricalibrare `sigma_c`, `wander_*`, `amb_*` con i propri dati.