Parametri sorteggiati a ogni corsa: intervalli [min, max] per gradini, ambiente, rumore e salti

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-29 10:49:25 +01:00
1 parent c0054c49a5
commit 797517db33
6 files changed
+97 -70

No files matched your search

+3 -2
View File
@@ -21,15 +21,16 @@ Non ci sono test automatici: la verifica è sui controlli di §5 di SPEC.md, su
## Mappa del codice ## Mappa del codice
- [genera.py](genera.py), script unico: `leggi_config` (valida `CHIAVI`) → `genera` → `scrivi`. Dentro `genera`: `perturba` (variabilità per corsa di istanti, ampiezze e `k` dei gradini), `eccesso` (gradini + raffreddamento), `salti` / `eventi` (disturbi Poisson in riscaldamento e raffreddamento), poi filtro del sensore, rumore, quantizzazione. - [genera.py](genera.py), script unico: `leggi_config` (valida `CHIAVI`) → `genera` → `scrivi`. Dentro `genera`: `sorteggia` (estrae a ogni corsa i valori dagli intervalli `[min, max]` di `config.toml` e genera i gradini), `eccesso` (gradini + raffreddamento), `salti` / `eventi` (disturbi Poisson in riscaldamento e raffreddamento), poi filtro del sensore, rumore, quantizzazione.
- [config.toml](config.toml): unica fonte dei valori numerici; unità nei suffissi (`_s`, `_ms`, `_c`, `_hz`). - [config.toml](config.toml): unica fonte dei valori numerici; unità nei suffissi (`_s`, `_ms`, `_c`, `_hz`).
- [grafico.py](grafico.py): script separato, non importa `genera.py`. - [grafico.py](grafico.py): script separato, non importa `genera.py`.
## Gotcha ## Gotcha
- `ΔT` = T − ambiente, calcolato **dopo** rumore e quantizzazione di entrambi i canali, mai dalla T vera. - `ΔT` = T − ambiente, calcolato **dopo** rumore e quantizzazione di entrambi i canali, mai dalla T vera.
- Le chiavi `[min, max]` stanno in `INTERVALLI` / `INTERVALLI_GRADINI` (validate in `leggi_config`): una nuova chiave-intervallo va aggiunta lì oltre che a `CHIAVI`.
- Nessun valore numerico nello script: ogni chiave nuova di `config.toml` va aggiunta anche a `CHIAVI` in `genera.py` (chiave mancante ⇒ exit 1) e descritta in SPEC.md e nell'epilogo di `--help`. - Nessun valore numerico nello script: ogni chiave nuova di `config.toml` va aggiunta anche a `CHIAVI` in `genera.py` (chiave mancante ⇒ exit 1) e descritta in SPEC.md e nell'epilogo di `--help`.
- Un solo `numpy.random.Generator` seminato da `--seed`: stesso seed + stessa config ⇒ file identico. Cambiare numero o ordine delle estrazioni casuali cambia i file di ogni seed. - Un solo `numpy.random.Generator` seminato da `--seed`: stesso seed + stessa config ⇒ file identico. Cambiare numero o ordine delle estrazioni casuali cambia i file di ogni seed.
- Tutto ciò che sembra "fisso" nelle curve (salti, picco, istanti) è casuale per seed: non esistono istanti programmati. - Tutto ciò che sembra "fisso" nelle curve (salti, picco, istanti) è casuale per seed: non esistono istanti programmati.
- Gli intervalli di §5 di SPEC.md (picco, `T`/`ΔT` finali) sono empirici su ~40 seed: se cambi `config.toml` (gradini, durata, salti, k) ricalcolali e aggiornali. - Gli intervalli di §5 di SPEC.md (picco, `T`/`ΔT` finali) sono empirici su 60 seed: se cambi `config.toml` (intervalli dei gradini, durata, salti, k) ricalcolali e aggiornali.
- `uv run genera.py` usa un ambiente a parte nella cache di uv, non `.venv`; i comandi del progetto sono quelli con `.venv/bin/python`. - `uv run genera.py` usa un ambiente a parte nella cache di uv, non `.venv`; i comandi del progetto sono quelli con `.venv/bin/python`.
+2 -2
View File
@@ -81,9 +81,9 @@ t_ms,T_C,dT_C
## Configurazione ## Configurazione
Tutti i valori numerici sono in [config.toml](config.toml) (nessuno è scritto nello script). Le unità sono nei suffissi delle chiavi: `_s` secondi, `_ms` millisecondi, `_c` gradi C, `_hz` 1/s. Una chiave mancante è un errore (exit 1, messaggio in italiano), così come `t_impulsi_s` e `dT_impulsi_c` di lunghezza diversa. Tutti i valori numerici sono in [config.toml](config.toml) (nessuno è scritto nello script). Le unità sono nei suffissi delle chiavi: `_s` secondi, `_ms` millisecondi, `_c` gradi C, `_hz` 1/s. Una chiave mancante è un errore (exit 1, messaggio in italiano), così come un intervallo `[min, max]` non valido (non una coppia di numeri, o min > max).
Per cambiare il profilo di riscaldamento si modificano `t_impulsi_s` e `dT_impulsi_c` (un gradino per elemento; `[]` = nessun riscaldamento) e `k_raffreddamento_hz`. `durata_s` deve essere abbastanza lunga perché `T` torni a `T_amb`. Le chiavi scritte come intervallo `[min, max]` vengono sorteggiate a ogni corsa (`[x, x]` = valore fisso). Per cambiare il profilo di riscaldamento si modificano `n_gradini`, `t_primo_gradino_s`, `distanza_gradini_s`, `dT_gradino_c` e `k_raffreddamento_hz`. `durata_s` deve essere abbastanza lunga perché `T` torni a `T_amb`.
## Documentazione ## Documentazione
+25 -26
View File
@@ -1,45 +1,44 @@
# config.toml — unica fonte dei valori (esempio). Unità nei suffissi: _s secondi, _ms millisecondi, _c gradi C, _hz 1/s. # config.toml — unica fonte dei valori (esempio). Unità nei suffissi: _s secondi, _ms millisecondi, _c gradi C, _hz 1/s.
# Le chiavi con [min, max] sono intervalli: a ogni corsa (seed) ne viene sorteggiato un valore uniforme.
# [x, x] = valore fisso.
durata_s = 120.0 # basta a far tornare T vicino a T_amb dopo il raffreddamento durata_s = 120.0 # basta a far tornare T vicino a T_amb dopo il raffreddamento
dt_ms = 150.5 # intervallo medio tra campioni dt_ms = 150.5 # intervallo medio tra campioni
jitter_ms = 0.6 # variazione uniforme ±jitter dell'intervallo jitter_ms = 0.6 # variazione uniforme ±jitter dell'intervallo
# Ambiente (baseline di T e riferimento per dT: T parte da qui e torna qui) # Ambiente (baseline di T e riferimento per dT: T parte da qui e torna qui)
amb_inf_c = 23.1 amb_inf_c = [20.0, 26.0]
amb_A_c = -0.45 # amb(0) = amb_inf + amb_A amb_A_c = [-0.8, 0.8] # amb(0) = amb_inf + amb_A
amb_tau_s = 58.0 amb_tau_s = [30.0, 90.0]
amb_sigma_c = 0.017 # rumore prima della quantizzazione amb_sigma_c = 0.017 # rumore prima della quantizzazione
amb_passo_c = 0.1 # quantizzazione del canale ambiente amb_passo_c = 0.1 # quantizzazione del canale ambiente
# Rumore di T # Rumore di T
sigma_c = 0.11 # rumore bianco gaussiano a freddo sigma_c = [0.06, 0.16] # rumore bianco gaussiano a freddo
sigma_caldo_c = 0.20 # rumore bianco con T molto sopra la baseline sigma_caldo_c = [0.15, 0.30] # rumore bianco con T molto sopra la baseline
x_rif_c = 20.0 # sopra questo eccesso termico il rumore vale sigma_caldo_c 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_sigma_c = [0.02, 0.08] # componente lenta (deriva casuale)
wander_tau_s = 3.5 wander_tau_s = [2.0, 6.0]
passo_T_c = 0.01 # risoluzione di T e dT nel CSV passo_T_c = 0.01 # risoluzione di T e dT nel CSV
# Riscaldamento e raffreddamento # Riscaldamento e raffreddamento: numero, istanti e ampiezze dei gradini sono sorteggiati a ogni corsa
t_impulsi_s = [15.7, 17.8, 21.7] # istanti dei gradini di riscaldamento ([] = nessun riscaldamento) n_gradini = [1, 3] # numero di gradini (interi, estremi inclusi)
dT_impulsi_c = [88.0, 75.0, 65.0] # ampiezza di ogni gradino (stessa lunghezza di t_impulsi_s) t_primo_gradino_s = [8.0, 25.0] # istante del primo gradino
k_raffreddamento_hz = 0.063 # raffreddamento esponenziale: tau = 1/k distanza_gradini_s = [1.0, 6.0] # distanza tra due gradini consecutivi
tau_sensore_s = 0.07 # ritardo di risposta del sensore dT_gradino_c = [40.0, 90.0] # ampiezza di ogni gradino
k_raffreddamento_hz = [0.05, 0.09] # raffreddamento esponenziale: tau = 1/k
# Variabilità tra una corsa e l'altra (0 = nessuna) tau_sensore_s = [0.04, 0.12] # ritardo di risposta del sensore
impulsi_jitter_t_s = 0.05 # sigma dello spostamento casuale di ogni gradino
impulsi_jitter_amp_rel = 0.02 # sigma relativa dell'ampiezza di ogni gradino
k_jitter_rel = 0.02 # sigma relativa di k_raffreddamento_hz
# Disturbi a salto durante il raffreddamento, dopo l'ultimo gradino (gradini irregolari come in un sensore reale) # Disturbi a salto durante il raffreddamento, dopo l'ultimo gradino (gradini irregolari come in un sensore reale)
salti_rate_hz = 0.25 # frequenza media (Poisson) salti_rate_hz = [0.1, 0.5] # frequenza media (Poisson)
salti_media_c = -1.0 # ampiezza media (con segno: prevalgono gli scarti verso il basso) salti_media_c = -1.0 # ampiezza media (con segno: prevalgono gli scarti verso il basso)
salti_sigma_c = 2.5 # sigma dell'ampiezza salti_sigma_c = [1.5, 3.5] # sigma dell'ampiezza
salti_tau_s = 0.7 # tempo di rientro del salto salti_tau_s = 0.7 # tempo di rientro del salto
salti_x_min_c = 60.0 # i salti avvengono solo con eccesso termico sopra questo valore salti_x_min_c = 60.0 # i salti avvengono solo con eccesso termico sopra questo valore
salti_max_c = 4.0 # ampiezza massima (in valore assoluto) di ogni salto, anche riscaldamento salti_max_c = 4.0 # ampiezza massima (in valore assoluto) di ogni salto, anche riscaldamento
# Disturbi a salto durante il riscaldamento (tra il primo e l'ultimo gradino), scarti nei due versi # Disturbi a salto durante il riscaldamento (tra il primo e l'ultimo gradino), scarti nei due versi
salti_risc_rate_hz = 0.8 # frequenza media (Poisson) salti_risc_rate_hz = [0.4, 1.2] # frequenza media (Poisson)
salti_risc_media_c = 0.0 # ampiezza media (con segno) salti_risc_media_c = 0.0 # ampiezza media (con segno)
salti_risc_sigma_c = 2.5 # sigma dell'ampiezza salti_risc_sigma_c = [1.5, 3.5] # sigma dell'ampiezza
salti_risc_tau_s = 0.3 # tempo di rientro del salto salti_risc_tau_s = 0.3 # tempo di rientro del salto
+3 -3
View File
@@ -5,8 +5,8 @@ Elenco di ciò che il programma sa fare. Per l'uso passo per passo vedi il [READ
## Generazione dei dati — `genera.py` ## Generazione dei dati — `genera.py`
- **Profilo di temperatura**: `T` parte dalla temperatura ambiente, sale con uno o più gradini di riscaldamento e ridiscende con raffreddamento esponenziale fino a `T_amb`. - **Profilo di temperatura**: `T` parte dalla temperatura ambiente, sale con uno o più gradini di riscaldamento e ridiscende con raffreddamento esponenziale fino a `T_amb`.
- **Gradini configurabili**: numero, istanti e ampiezze (`t_impulsi_s`, `dT_impulsi_c`); lista vuota = nessun riscaldamento. Costante di raffreddamento in `k_raffreddamento_hz`. - **Gradini sorteggiati**: numero, istante del primo, distanza tra gradini e ampiezze sono estratti a caso a ogni seed da intervalli (`n_gradini`, `t_primo_gradino_s`, `distanza_gradini_s`, `dT_gradino_c`). Costante di raffreddamento in `k_raffreddamento_hz`.
- **Variabilità tra corse**: istanti, ampiezze e costante di raffreddamento dei gradini sono perturbati a caso a ogni seed (`impulsi_jitter_*`, `k_jitter_rel`; 0 = disattivata). - **Parametri casuali per corsa**: le chiavi scritte `[min, max]` (ambiente, livelli di rumore, `k`, `tau_sensore_s`, frequenza e σ dei salti) vengono sorteggiate uniformi a ogni seed, così ogni file ha parametri davvero diversi; `[x, x]` = valore fisso.
- **Salti stocastici**: scarti casuali con rientro esponenziale, sia durante il riscaldamento (`salti_risc_*`, nei due versi) sia durante il raffreddamento (`salti_*`, solo con `T` molto sopra l'ambiente), come i gradini irregolari di un sensore reale. - **Salti stocastici**: scarti casuali con rientro esponenziale, sia durante il riscaldamento (`salti_risc_*`, nei due versi) sia durante il raffreddamento (`salti_*`, solo con `T` molto sopra l'ambiente), come i gradini irregolari di un sensore reale.
- **Ambiente realistico**: `T_amb` assestata con andamento esponenziale, con rumore e quantizzazione propri (`amb_*`). Non compare nel CSV, serve per `ΔT` e come baseline di `T`. - **Ambiente realistico**: `T_amb` assestata con andamento esponenziale, con rumore e quantizzazione propri (`amb_*`). Non compare nel CSV, serve per `ΔT` e come baseline di `T`.
- **Tempi con jitter**: intervallo medio `dt_ms` con variazione uniforme ±`jitter_ms`, fino a `durata_s`. - **Tempi con jitter**: intervallo medio `dt_ms` con variazione uniforme ±`jitter_ms`, fino a `durata_s`.
@@ -15,7 +15,7 @@ Elenco di ciò che il programma sa fare. Per l'uso passo per passo vedi il [READ
- **Quantizzazione**: `T` e `ΔT` a `passo_T_c`, ambiente a `amb_passo_c`. `ΔT` è calcolato dopo rumore e quantizzazione di entrambi i canali. - **Quantizzazione**: `T` e `ΔT` a `passo_T_c`, ambiente a `amb_passo_c`. `ΔT` è calcolato dopo rumore e quantizzazione di entrambi i canali.
- **Riproducibilità**: `--seed` fissa il risultato (stesso seed e stessa configurazione ⇒ file identico); senza seed ne viene scelto uno casuale e stampato su stderr. - **Riproducibilità**: `--seed` fissa il risultato (stesso seed e stessa configurazione ⇒ file identico); senza seed ne viene scelto uno casuale e stampato su stderr.
- **Uscita flessibile**: per default `output/gen_<seed>.csv` (cartella creata se manca); `-o percorso` per un file a scelta; `-o -` per stdout. - **Uscita flessibile**: per default `output/gen_<seed>.csv` (cartella creata se manca); `-o percorso` per un file a scelta; `-o -` per stdout.
- **Configurazione senza valori nascosti**: tutti i valori stanno in un TOML; una chiave mancante, un TOML non valido o gradini di lunghezza diversa terminano con exit 1 e un messaggio in italiano. Lo stesso avviene se il file di uscita non è scrivibile. - **Configurazione senza valori nascosti**: tutti i valori stanno in un TOML; una chiave mancante, un TOML non valido o un intervallo `[min, max]` non valido terminano con exit 1 e un messaggio in italiano. Lo stesso avviene se il file di uscita non è scrivibile.
Formato dell'uscita: `t_ms,T_C,dT_C` (`t_ms` intero, gli altri a due decimali). Formato dell'uscita: `t_ms,T_C,dT_C` (`t_ms` intero, gli altri a due decimali).
+13 -11
View File
@@ -19,16 +19,18 @@ t_ms,T_C,dT_C
## 2. Configurazione ## 2. Configurazione
Tutti i valori sono in [`config.toml`](../config.toml) (unica fonte: questo documento non li ripete). Gruppi: 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 · variabilità tra corse · salti. tempi · ambiente (anche baseline di T) · rumore · gradini di riscaldamento e raffreddamento · salti.
Nessun valore è scritto nello script: chiave mancante ⇒ errore. 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) ## 3. Modello (5 passi)
1. **Tempi**: `t_i = t_{i−1} + dt_ms + U(−jitter_ms, +jitter_ms)`, fino a `durata_s`. 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`): 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); - 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); - 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 istanti, ampiezze e `k` dei gradini sono perturbati a caso (σ in `impulsi_jitter_*`, `k_jitter_rel`), quindi la forma cambia con il seed; - **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): - **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 **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); 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);
@@ -55,21 +57,21 @@ python genera.py config.toml -o out.csv # percorso a scelta, seed
python genera.py config.toml -o - > out.csv # stdout 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). 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 `t_impulsi_s` e `dT_impulsi_c` hanno lunghezze diverse 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`. 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) ## 5. Controlli di accettazione (con la configurazione d'esempio)
Verificati su 30 seed (valori min–max; picco e `T`/`ΔT` finali dipendono dalla variabilità dei gradini e dai salti): 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 | | Controllo | Atteso |
|---|---| |---|---|
| `T` a inizio run | ≈ `T_amb` (≈ 22.65 °C) | | `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.09 – 0.13 | | Rumore a freddo: `std(diff(T))/√2` prima del primo gradino (~15 s) | 0.06 – 0.19 (segue `sigma_c` sorteggiato) |
| Picco di `T` | 197 – 209 °C | | Picco di `T` | 66 – 227 °C (dipende da numero e ampiezza dei gradini) |
| `T` a fine run | ≈ `T_amb`: 23.1 – 23.7 °C | | `T` a fine run | ≈ `T_amb` della corsa: 20.0 – 26.4 °C |
| `ΔT` a inizio run | ≈ 0 °C (solo rumore) | | `ΔT` a inizio run | ≈ 0 °C (solo rumore): −0.18 – 0.15 |
| `ΔT` a fine run | 0.3 – 0.65 °C (residuo del raffreddamento) | | `Δ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) | | 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 | | Stesso `--seed` e stessa config | file identico |
+51 -26
View File
@@ -21,7 +21,8 @@ Genera un CSV con la temperatura T di un sensore IR e la differenza dT = T - T_a
con rumore, quantizzazione e jitter dei tempi simili a un sensore reale. con rumore, quantizzazione e jitter dei tempi simili a un sensore reale.
T parte dalla temperatura ambiente, sale con uno o più gradini di riscaldamento e T parte dalla temperatura ambiente, sale con uno o più gradini di riscaldamento e
ridiscende con raffreddamento esponenziale fino a T_amb. Ogni corsa (seed) è diversa.""" ridiscende con raffreddamento esponenziale fino a T_amb. Ogni corsa (seed) è diversa:
i parametri principali sono intervalli [min, max] sorteggiati a ogni corsa."""
EPILOGO = """\ EPILOGO = """\
esempi: esempi:
@@ -38,40 +39,56 @@ formato dell'output (separatore ',', fine riga '\\n'):
cosa fa (in ordine): cosa fa (in ordine):
1. tempi t_i = t_(i-1) + dt_ms +/- jitter_ms, fino a durata_s 1. tempi t_i = t_(i-1) + dt_ms +/- jitter_ms, fino a durata_s
2. T vera ambiente vero + gradini di riscaldamento con raffreddamento 2. T vera ambiente vero + gradini di riscaldamento (numero, istanti, ampiezze
esponenziale, perturbati a caso a ogni corsa + disturbi a salto sorteggiati) con raffreddamento esponenziale + disturbi a salto
3. sensore filtro del 1° ordine (tau_sensore_s) su griglia fine da 5 ms 3. sensore filtro del 1° ordine (tau_sensore_s) su griglia fine da 5 ms
4. rumore di T bianco (più forte a caldo) + deriva lenta, poi quantizzazione 4. rumore di T bianco (più forte a caldo) + deriva lenta, poi quantizzazione
5. ambiente, dT ambiente con rumore e quantizzazione propri; dT = T - ambiente 5. ambiente, dT ambiente con rumore e quantizzazione propri; dT = T - ambiente
gruppi di chiavi di config.toml (unica fonte dei valori, unità nel suffisso: gruppi di chiavi di config.toml (unica fonte dei valori, unità nel suffisso:
_s secondi, _ms millisecondi, _c gradi C, _hz 1/s; ogni chiave è obbligatoria): _s secondi, _ms millisecondi, _c gradi C, _hz 1/s; ogni chiave è obbligatoria).
Le chiavi marcate [min, max] sono intervalli: a ogni corsa ne viene sorteggiato un
valore uniforme (n_gradini: intero, estremi inclusi):
tempi durata_s, dt_ms, jitter_ms tempi durata_s, dt_ms, jitter_ms
ambiente amb_inf_c, amb_A_c, amb_tau_s, amb_sigma_c, amb_passo_c ambiente amb_inf_c [min, max], amb_A_c [min, max], amb_tau_s [min, max],
rumore di T sigma_c, sigma_caldo_c, x_rif_c, wander_sigma_c, wander_tau_s, passo_T_c amb_sigma_c, amb_passo_c
gradini t_impulsi_s, dT_impulsi_c ([] = nessun riscaldamento; stessa lunghezza), rumore di T sigma_c [min, max], sigma_caldo_c [min, max], x_rif_c,
k_raffreddamento_hz, tau_sensore_s wander_sigma_c [min, max], wander_tau_s [min, max], passo_T_c
variabilità impulsi_jitter_t_s, impulsi_jitter_amp_rel, k_jitter_rel (0 = disattivata) gradini n_gradini [min, max], t_primo_gradino_s [min, max],
salti salti_rate_hz, salti_media_c, salti_sigma_c, salti_tau_s, salti_x_min_c, distanza_gradini_s [min, max] (tra gradini consecutivi),
salti_max_c (raffreddamento); salti_risc_rate_hz, salti_risc_media_c, dT_gradino_c [min, max] (ampiezza di ogni gradino),
salti_risc_sigma_c, salti_risc_tau_s (riscaldamento) k_raffreddamento_hz [min, max], tau_sensore_s [min, max]
salti salti_rate_hz [min, max], salti_media_c, salti_sigma_c [min, max],
salti_tau_s, salti_x_min_c, salti_max_c (raffreddamento);
salti_risc_rate_hz [min, max], salti_risc_media_c,
salti_risc_sigma_c [min, max], salti_risc_tau_s (riscaldamento)
codici di uscita: codici di uscita:
0 ok 0 ok
1 errore, con messaggio in italiano su stderr: file di configurazione illeggibile 1 errore, con messaggio in italiano su stderr: file di configurazione illeggibile
o TOML non valido, chiave mancante, t_impulsi_s e dT_impulsi_c di lunghezza o TOML non valido, chiave mancante, intervallo [min, max] non valido,
diversa, file di uscita non scrivibile file di uscita non scrivibile
2 argomenti della riga di comando non validi 2 argomenti della riga di comando non validi
altri strumenti: grafico.py (disegna un CSV), pulisci.sh (svuota output/). altri strumenti: grafico.py (disegna un CSV), pulisci.sh (svuota output/).
documentazione: docs/SPEC.md (modello e controlli), docs/FUNZIONALITA.md (funzionalità).""" documentazione: docs/SPEC.md (modello e controlli), docs/FUNZIONALITA.md (funzionalità)."""
# chiavi che sono intervalli [min, max], sorteggiati uniformi a ogni corsa
INTERVALLI = [
"amb_inf_c", "amb_A_c", "amb_tau_s",
"sigma_c", "sigma_caldo_c", "wander_sigma_c", "wander_tau_s",
"k_raffreddamento_hz", "tau_sensore_s",
"salti_rate_hz", "salti_sigma_c", "salti_risc_rate_hz", "salti_risc_sigma_c",
]
# intervalli che descrivono i gradini (istanti e ampiezze derivati da questi)
INTERVALLI_GRADINI = ["n_gradini", "t_primo_gradino_s", "distanza_gradini_s", "dT_gradino_c"]
CHIAVI = [ CHIAVI = [
"durata_s", "dt_ms", "jitter_ms", "durata_s", "dt_ms", "jitter_ms",
"amb_inf_c", "amb_A_c", "amb_tau_s", "amb_sigma_c", "amb_passo_c", "amb_inf_c", "amb_A_c", "amb_tau_s", "amb_sigma_c", "amb_passo_c",
"sigma_c", "sigma_caldo_c", "x_rif_c", "wander_sigma_c", "wander_tau_s", "passo_T_c", "sigma_c", "sigma_caldo_c", "x_rif_c", "wander_sigma_c", "wander_tau_s", "passo_T_c",
"t_impulsi_s", "dT_impulsi_c", "k_raffreddamento_hz", "tau_sensore_s", "n_gradini", "t_primo_gradino_s", "distanza_gradini_s", "dT_gradino_c",
"impulsi_jitter_t_s", "impulsi_jitter_amp_rel", "k_jitter_rel", "k_raffreddamento_hz", "tau_sensore_s",
"salti_rate_hz", "salti_media_c", "salti_sigma_c", "salti_tau_s", "salti_x_min_c", "salti_rate_hz", "salti_media_c", "salti_sigma_c", "salti_tau_s", "salti_x_min_c",
"salti_max_c", "salti_risc_rate_hz", "salti_risc_media_c", "salti_risc_sigma_c", "salti_risc_tau_s", "salti_max_c", "salti_risc_rate_hz", "salti_risc_media_c", "salti_risc_sigma_c", "salti_risc_tau_s",
] ]
@@ -111,8 +128,15 @@ def leggi_config(percorso):
mancanti = [k for k in CHIAVI if k not in cfg] mancanti = [k for k in CHIAVI if k not in cfg]
if mancanti: if mancanti:
errore(f"chiavi mancanti in '{percorso}': {', '.join(mancanti)}") errore(f"chiavi mancanti in '{percorso}': {', '.join(mancanti)}")
if len(cfg["t_impulsi_s"]) != len(cfg["dT_impulsi_c"]): for k in INTERVALLI + INTERVALLI_GRADINI:
errore("'t_impulsi_s' e 'dT_impulsi_c' hanno lunghezze diverse") v = cfg[k]
if (not isinstance(v, list) or len(v) != 2
or not all(isinstance(x, (int, float)) and not isinstance(x, bool) for x in v)
or v[0] > v[1]):
errore(f"'{k}' deve essere un intervallo [min, max] con min <= max")
n_min, n_max = cfg["n_gradini"]
if not (isinstance(n_min, int) and isinstance(n_max, int) and n_min >= 1):
errore("'n_gradini' deve essere un intervallo [min, max] di interi con min >= 1")
return cfg return cfg
@@ -125,13 +149,14 @@ def eccesso(t_s, cfg):
return x return x
def perturba(cfg, rng): def sorteggia(cfg, rng):
"""Copia di cfg con istanti, ampiezze e k degli impulsi variati a caso (ogni corsa è diversa).""" """Copia di cfg con ogni intervallo sostituito da un valore sorteggiato e i gradini generati (ogni corsa è diversa)."""
n = len(cfg["t_impulsi_s"]) valori = {k: rng.uniform(*cfg[k]) for k in INTERVALLI}
t = np.sort(np.asarray(cfg["t_impulsi_s"], float) + cfg["impulsi_jitter_t_s"] * rng.standard_normal(n)) n = int(rng.integers(cfg["n_gradini"][0], cfg["n_gradini"][1] + 1))
d = np.asarray(cfg["dT_impulsi_c"], float) * (1 + cfg["impulsi_jitter_amp_rel"] * rng.standard_normal(n)) t_primo = rng.uniform(*cfg["t_primo_gradino_s"])
k = cfg["k_raffreddamento_hz"] * (1 + cfg["k_jitter_rel"] * rng.standard_normal()) t = t_primo + np.concatenate(([0.0], np.cumsum(rng.uniform(*cfg["distanza_gradini_s"], n - 1))))
return {**cfg, "t_impulsi_s": t, "dT_impulsi_c": d, "k_raffreddamento_hz": k} d = rng.uniform(*cfg["dT_gradino_c"], n)
return {**cfg, **valori, "t_impulsi_s": t, "dT_impulsi_c": d}
def eventi(g_s, cfg, rng, t_ini, t_fin, rate, media, sigma, tau, x_min): def eventi(g_s, cfg, rng, t_ini, t_fin, rate, media, sigma, tau, x_min):
@@ -169,7 +194,7 @@ def quantizza(v, passo):
def genera(cfg, rng): def genera(cfg, rng):
cfg = perturba(cfg, rng) cfg = sorteggia(cfg, rng)
# 1. tempi # 1. tempi
n_max = int(cfg["durata_s"] * 1000 / (cfg["dt_ms"] - cfg["jitter_ms"])) + 2 n_max = int(cfg["durata_s"] * 1000 / (cfg["dt_ms"] - cfg["jitter_ms"])) + 2
dt = cfg["dt_ms"] + rng.uniform(-cfg["jitter_ms"], cfg["jitter_ms"], n_max) dt = cfg["dt_ms"] + rng.uniform(-cfg["jitter_ms"], cfg["jitter_ms"], n_max)