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:
1 parent
c66049575b
commit
90f8eb5893
5 files changed
+97
-8
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
Reference in new issue
Block a user