Accorcia la corsa a 120 s, aggiunge grafico.py e istruzioni per installare uv

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-29 08:56:08 +01:00
1 parent c66049575b
commit 90f8eb5893
5 files changed
+97 -8

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Stato
[genera.py](genera.py) è lo script unico (PEP 723, `uv run genera.py config.toml --seed 42`, scrive in `output/`); implementa [docs/SPEC.md](docs/SPEC.md), unica fonte di verità su output, modello, CLI, errori e controlli di accettazione. Leggila per intero prima di scrivere codice. Documentazione, commenti e messaggi di errore sono in italiano.
[genera.py](genera.py) è lo script unico (PEP 723, `uv run genera.py config.toml --seed 42`, scrive in `output/`). [grafico.py](grafico.py) disegna un CSV di output (script separato, dipende da matplotlib; `genera.py` resta con solo numpy); `genera.py` implementa [docs/SPEC.md](docs/SPEC.md), unica fonte di verità su output, modello, CLI, errori e controlli di accettazione. Leggila per intero prima di scrivere codice. Documentazione, commenti e messaggi di errore sono in italiano.
## File
+21 -1
View File
@@ -4,7 +4,17 @@ Genera un CSV con la temperatura `T` di un sensore IR e la differenza `ΔT = T
## Requisiti
[uv](https://docs.astral.sh/uv/). Lo script dichiara le sue dipendenze (Python ≥ 3.11, `numpy`): al primo avvio uv crea da solo l'ambiente virtuale, non serve installare altro.
[uv](https://docs.astral.sh/uv/). Gli script dichiarano le loro dipendenze (Python ≥ 3.11, `numpy`, `matplotlib` per il grafico): al primo avvio uv crea da solo l'ambiente virtuale e, se serve, scarica Python. Non serve installare altro.
### Installare uv
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh # Linux e macOS
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" # Windows
pip install uv # alternativa, se hai già Python
```
Apri un nuovo terminale e verifica con `uv --version`. Altre opzioni (Homebrew, WinGet, ecc.) sono nella [guida ufficiale](https://docs.astral.sh/uv/getting-started/installation/).
## Uso
@@ -23,6 +33,16 @@ uv run genera.py config.toml -o - > out.csv # su stdout
Stesso seed e stessa configurazione danno sempre lo stesso file. La cartella `output/` è ignorata da git.
## Grafico
```bash
uv run grafico.py # apre il grafico dell'ultimo CSV in output/
uv run grafico.py output/gen_42.csv # un CSV preciso
uv run grafico.py -o grafico.png # salva l'immagine (png, pdf, svg) invece di aprire la finestra
```
Mostra `T` e `ΔT` in funzione del tempo. `grafico.py` ha le sue dipendenze (`numpy`, `matplotlib`), gestite da uv come per `genera.py`; senza schermo usa `-o`.
## Output
```
+2 -2
View File
@@ -1,6 +1,6 @@
# 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
durata_s = 120.0 # basta a far tornare T vicino a T_amb dopo il raffreddamento
dt_ms = 150.5 # intervallo medio tra campioni
jitter_ms = 0.6 # variazione uniforme ±jitter dell'intervallo
@@ -20,7 +20,7 @@ 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)
t_impulsi_s = [15.7, 16.9, 19.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
+3 -4
View File
@@ -61,12 +61,11 @@ 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 |
| Rumore a freddo: `std(diff(T))/√2` prima del primo gradino (~15 s) | 0.09 – 0.13 |
| Picco di `T` | ≈ 220 – 222 °C |
| `T` a 134 s | ≈ 80.5 °C |
| `T` a fine run | ≈ `T_amb` (≈ 23.1 °C, entro ~0.1 °C) |
| `T` a fine run | ≈ `T_amb` (≈ 23.4 °C, entro ~0.5 °C) |
| `ΔT` a inizio run | ≈ 0 °C (solo rumore) |
| `ΔT` a fine run | ≈ 0 °C (solo rumore) |
| `ΔT` a fine run | ≈ 0.4 °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 |
+70
View File
@@ -0,0 +1,70 @@
# /// script
# requires-python = ">=3.11"
# dependencies = ["numpy", "matplotlib"]
# ///
"""Mostra o salva il grafico di T e ΔT di un CSV prodotto da genera.py."""
import argparse
import sys
from pathlib import Path
import matplotlib
import numpy as np
CARTELLA_OUTPUT = Path("output")
def errore(msg):
print(f"Errore: {msg}", file=sys.stderr)
sys.exit(1)
def ultimo_csv():
csv = sorted(CARTELLA_OUTPUT.glob("*.csv"), key=lambda p: p.stat().st_mtime)
if not csv:
errore(f"nessun CSV in '{CARTELLA_OUTPUT}': genera prima i dati con genera.py")
return csv[-1]
def main():
ap = argparse.ArgumentParser(description="Grafico di T e ΔT da un CSV di genera.py.")
ap.add_argument("csv", nargs="?", type=Path, help="CSV da disegnare (default: l'ultimo in output/)")
ap.add_argument("-o", "--output", type=Path, help="salva l'immagine (png, pdf, svg) invece di aprire la finestra")
args = ap.parse_args()
percorso = args.csv or ultimo_csv()
try:
t_ms, T, dT = np.loadtxt(percorso, delimiter=",", skiprows=1, unpack=True)
except OSError as e:
errore(f"impossibile leggere '{percorso}': {e.strerror}")
except ValueError:
errore(f"'{percorso}' non ha il formato t_ms,T_C,dT_C")
if args.output:
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, (ax_T, ax_dT) = plt.subplots(2, 1, sharex=True, figsize=(10, 6))
t_s = t_ms / 1000
ax_T.plot(t_s, T, lw=0.8)
ax_T.set_ylabel("T [°C]")
ax_dT.plot(t_s, dT, lw=0.8, color="tab:orange")
ax_dT.set_ylabel("ΔT = T − T_amb [°C]")
ax_dT.set_xlabel("tempo [s]")
for ax in (ax_T, ax_dT):
ax.grid(alpha=0.3)
fig.suptitle(percorso.name)
fig.tight_layout()
if args.output:
try:
fig.savefig(args.output, dpi=150)
except OSError as e:
errore(f"impossibile scrivere '{args.output}': {e.strerror}")
print(f"scritto: {args.output}", file=sys.stderr)
else:
plt.show()
if __name__ == "__main__":
main()