Aggiunge --help completo a genera.py, grafico.py e pulisci.sh; riscrive CLAUDE.md
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
33c99afe6a
commit
c0054c49a5
6 files changed
+207
-15
No files matched your search
@@ -2,17 +2,34 @@
|
|||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
## Stato
|
Generatore di CSV `t_ms,T_C,dT_C` (temperatura di un sensore IR che si scalda e si raffredda, con rumore realistico). [docs/SPEC.md](docs/SPEC.md) è l'unica fonte di verità su output, modello, CLI, errori e controlli di accettazione: leggila per intero prima di cambiare il comportamento. Documentazione, commenti e messaggi di errore sono in italiano.
|
||||||
|
|
||||||
[genera.py](genera.py) è lo script unico (`python genera.py config.toml --seed 42` nell'ambiente `.venv`, creato con `uv venv`; 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.
|
## Comandi
|
||||||
|
|
||||||
## File
|
Ambiente `.venv` nel progetto (numpy per `genera.py`, in più matplotlib per `grafico.py`):
|
||||||
|
|
||||||
- [config.toml](config.toml): esempio e unica fonte dei valori numerici. Le unità sono nei suffissi delle chiavi (`_s`, `_ms`, `_c`, `_hz`).
|
```bash
|
||||||
|
uv venv .venv && uv pip install numpy matplotlib
|
||||||
|
.venv/bin/python genera.py config.toml --seed 42 # scrive output/gen_42.csv
|
||||||
|
.venv/bin/python grafico.py -o g.png # grafico dell'ultimo CSV in output/
|
||||||
|
./pulisci.sh # svuota output/
|
||||||
|
```
|
||||||
|
|
||||||
## Vincoli facili da perdere
|
`--help` di ogni script (testo in `DESCRIZIONE`/`EPILOGO` per `genera.py`) elenca le chiavi di `config.toml` per gruppo: va aggiornato insieme a `CHIAVI`.
|
||||||
|
|
||||||
|
Non ci sono test automatici: la verifica è sui controlli di §5 di SPEC.md, su più seed (vedi gotcha).
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
- [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`.
|
||||||
|
|
||||||
|
## 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.
|
||||||
- Nessun valore numerico nello script: chiave mancante ⇒ exit 1.
|
- 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`, così stesso seed + stessa config ⇒ file identico.
|
- 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.
|
||||||
- Verifica l'implementazione con i controlli di §5 di SPEC.md sulla config d'esempio.
|
- 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.
|
||||||
|
- `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`.
|
||||||
@@ -59,6 +59,16 @@ Mostra `T` e `ΔT` in funzione del tempo. `grafico.py` ha le sue dipendenze (`nu
|
|||||||
./pulisci.sh # svuota la cartella output/
|
./pulisci.sh # svuota la cartella output/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Aiuto
|
||||||
|
|
||||||
|
Ogni comando ha un aiuto completo (opzioni, esempi, formato, chiavi della configurazione, codici di uscita):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python genera.py --help # oppure -h
|
||||||
|
python grafico.py --help
|
||||||
|
./pulisci.sh --help
|
||||||
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ Formato dell'uscita: `t_ms,T_C,dT_C` (`t_ms` intero, gli altri a due decimali).
|
|||||||
- Svuota la cartella `output/` (la cartella resta). Non tocca altro.
|
- Svuota la cartella `output/` (la cartella resta). Non tocca altro.
|
||||||
- Se `output/` non esiste lo segnala senza errore.
|
- Se `output/` non esiste lo segnala senza errore.
|
||||||
|
|
||||||
|
## Aiuto da riga di comando
|
||||||
|
|
||||||
|
- `-h` / `--help` su `genera.py`, `grafico.py` e `pulisci.sh`: opzioni, esempi, formato dell'output, gruppi di chiavi di `config.toml` e codici di uscita, in italiano.
|
||||||
|
- Argomenti non validi: messaggio in italiano e exit 2.
|
||||||
|
|
||||||
## Ambiente
|
## Ambiente
|
||||||
|
|
||||||
- Gli script girano in un ambiente `.venv` nel progetto (creato con [uv](https://docs.astral.sh/uv/): `numpy`, più `matplotlib` per il grafico). Le dipendenze sono dichiarate anche nello script (PEP 723), quindi `uv run` funziona comunque.
|
- Gli script girano in un ambiente `.venv` nel progetto (creato con [uv](https://docs.astral.sh/uv/): `numpy`, più `matplotlib` per il grafico). Le dipendenze sono dichiarate anche nello script (PEP 723), quindi `uv run` funziona comunque.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"""Genera un CSV con T e ΔT = T − T_amb con rumore realistico (vedi docs/SPEC.md)."""
|
"""Genera un CSV con T e ΔT = T − T_amb con rumore realistico (vedi docs/SPEC.md)."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
import secrets
|
import secrets
|
||||||
import sys
|
import sys
|
||||||
import tomllib
|
import tomllib
|
||||||
@@ -15,6 +16,56 @@ import numpy as np
|
|||||||
CARTELLA_OUTPUT = Path("output")
|
CARTELLA_OUTPUT = Path("output")
|
||||||
GRIGLIA_MS = 5.0 # passo della griglia fine per il filtro del sensore
|
GRIGLIA_MS = 5.0 # passo della griglia fine per il filtro del sensore
|
||||||
|
|
||||||
|
DESCRIZIONE = """\
|
||||||
|
Genera un CSV con la temperatura T di un sensore IR e la differenza dT = T - T_amb,
|
||||||
|
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
|
||||||
|
ridiscende con raffreddamento esponenziale fino a T_amb. Ogni corsa (seed) è diversa."""
|
||||||
|
|
||||||
|
EPILOGO = """\
|
||||||
|
esempi:
|
||||||
|
genera.py config.toml scrive output/gen_<seed>.csv, seed casuale
|
||||||
|
genera.py config.toml --seed 42 riproducibile: output/gen_42.csv
|
||||||
|
genera.py config.toml -o dati/prova.csv percorso a scelta
|
||||||
|
genera.py config.toml -o - > out.csv su stdout
|
||||||
|
|
||||||
|
formato dell'output (separatore ',', fine riga '\\n'):
|
||||||
|
t_ms,T_C,dT_C
|
||||||
|
151,22.90,0.20
|
||||||
|
t_ms è intero; T_C e dT_C hanno due decimali. dT è calcolato dopo rumore e
|
||||||
|
quantizzazione di entrambi i canali (T e ambiente).
|
||||||
|
|
||||||
|
cosa fa (in ordine):
|
||||||
|
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
|
||||||
|
esponenziale, perturbati a caso a ogni corsa + disturbi a salto
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
_s secondi, _ms millisecondi, _c gradi C, _hz 1/s; ogni chiave è obbligatoria):
|
||||||
|
tempi durata_s, dt_ms, jitter_ms
|
||||||
|
ambiente amb_inf_c, amb_A_c, amb_tau_s, amb_sigma_c, amb_passo_c
|
||||||
|
rumore di T sigma_c, sigma_caldo_c, x_rif_c, wander_sigma_c, wander_tau_s, passo_T_c
|
||||||
|
gradini t_impulsi_s, dT_impulsi_c ([] = nessun riscaldamento; stessa lunghezza),
|
||||||
|
k_raffreddamento_hz, tau_sensore_s
|
||||||
|
variabilità impulsi_jitter_t_s, impulsi_jitter_amp_rel, k_jitter_rel (0 = disattivata)
|
||||||
|
salti salti_rate_hz, salti_media_c, salti_sigma_c, salti_tau_s, salti_x_min_c,
|
||||||
|
salti_max_c (raffreddamento); salti_risc_rate_hz, salti_risc_media_c,
|
||||||
|
salti_risc_sigma_c, salti_risc_tau_s (riscaldamento)
|
||||||
|
|
||||||
|
codici di uscita:
|
||||||
|
0 ok
|
||||||
|
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
|
||||||
|
diversa, file di uscita non scrivibile
|
||||||
|
2 argomenti della riga di comando non validi
|
||||||
|
|
||||||
|
altri strumenti: grafico.py (disegna un CSV), pulisci.sh (svuota output/).
|
||||||
|
documentazione: docs/SPEC.md (modello e controlli), docs/FUNZIONALITA.md (funzionalità)."""
|
||||||
|
|
||||||
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",
|
||||||
@@ -26,6 +77,24 @@ CHIAVI = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ParserIt(argparse.ArgumentParser):
|
||||||
|
"""ArgumentParser con i messaggi d'errore più comuni in italiano (exit 2)."""
|
||||||
|
|
||||||
|
TRADUZIONI = [
|
||||||
|
(r"the following arguments are required: (.*)", r"manca l'argomento obbligatorio: \1"),
|
||||||
|
(r"unrecognized arguments: (.*)", r"argomenti non riconosciuti: \1"),
|
||||||
|
(r"argument (.*): invalid int value: (.*)", r"argomento \1: \2 non è un numero intero"),
|
||||||
|
(r"argument (.*): expected one argument", r"argomento \1: manca il valore"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
for pattern, sostituzione in self.TRADUZIONI:
|
||||||
|
message = re.sub(pattern, sostituzione, message)
|
||||||
|
self.print_usage(sys.stderr)
|
||||||
|
print(f"{self.prog}: errore: {message}", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
def errore(msg):
|
def errore(msg):
|
||||||
print(f"Errore: {msg}", file=sys.stderr)
|
print(f"Errore: {msg}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -149,10 +218,28 @@ def scrivi(f, t_ms, T, dT):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ap = argparse.ArgumentParser(description="Genera un CSV con T e ΔT con rumore realistico.")
|
ap = ParserIt(
|
||||||
ap.add_argument("config", help="file di configurazione TOML")
|
prog="genera.py",
|
||||||
ap.add_argument("-o", "--output", help="file CSV di uscita ('-' = stdout; default: output/gen_<seed>.csv)")
|
usage="genera.py [-h] [-o FILE] [--seed N] config",
|
||||||
ap.add_argument("--seed", type=int, help="seme del generatore (default: casuale, stampato su stderr)")
|
description=DESCRIZIONE,
|
||||||
|
epilog=EPILOGO,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
argomenti = ap.add_argument_group("argomenti")
|
||||||
|
opzioni = ap.add_argument_group("opzioni")
|
||||||
|
opzioni.add_argument("-h", "--help", action="help", help="mostra questo aiuto ed esce")
|
||||||
|
argomenti.add_argument("config", help="file di configurazione TOML (es. config.toml); tutte le chiavi sono obbligatorie")
|
||||||
|
opzioni.add_argument(
|
||||||
|
"-o", "--output", metavar="FILE",
|
||||||
|
help="file CSV di uscita; le cartelle mancanti vengono create. '-' scrive su stdout. "
|
||||||
|
"Default: output/gen_<seed>.csv",
|
||||||
|
)
|
||||||
|
opzioni.add_argument(
|
||||||
|
"--seed", type=int, metavar="N",
|
||||||
|
help="seme del generatore casuale: stesso seed e stessa configurazione danno lo stesso file. "
|
||||||
|
"Default: casuale, stampato su stderr",
|
||||||
|
)
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
cfg = leggi_config(args.config)
|
cfg = leggi_config(args.config)
|
||||||
|
|||||||
+51
-3
@@ -5,6 +5,7 @@
|
|||||||
"""Mostra o salva il grafico di T e ΔT di un CSV prodotto da genera.py."""
|
"""Mostra o salva il grafico di T e ΔT di un CSV prodotto da genera.py."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -14,6 +15,24 @@ import numpy as np
|
|||||||
CARTELLA_OUTPUT = Path("output")
|
CARTELLA_OUTPUT = Path("output")
|
||||||
|
|
||||||
|
|
||||||
|
class ParserIt(argparse.ArgumentParser):
|
||||||
|
"""ArgumentParser con i messaggi d'errore più comuni in italiano (exit 2)."""
|
||||||
|
|
||||||
|
TRADUZIONI = [
|
||||||
|
(r"the following arguments are required: (.*)", r"manca l'argomento obbligatorio: \1"),
|
||||||
|
(r"unrecognized arguments: (.*)", r"argomenti non riconosciuti: \1"),
|
||||||
|
(r"argument (.*): invalid int value: (.*)", r"argomento \1: \2 non è un numero intero"),
|
||||||
|
(r"argument (.*): expected one argument", r"argomento \1: manca il valore"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
for pattern, sostituzione in self.TRADUZIONI:
|
||||||
|
message = re.sub(pattern, sostituzione, message)
|
||||||
|
self.print_usage(sys.stderr)
|
||||||
|
print(f"{self.prog}: errore: {message}", file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
def errore(msg):
|
def errore(msg):
|
||||||
print(f"Errore: {msg}", file=sys.stderr)
|
print(f"Errore: {msg}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -27,9 +46,38 @@ def ultimo_csv():
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ap = argparse.ArgumentParser(description="Grafico di T e ΔT da un CSV di genera.py.")
|
ap = ParserIt(
|
||||||
ap.add_argument("csv", nargs="?", type=Path, help="CSV da disegnare (default: l'ultimo in output/)")
|
prog="grafico.py",
|
||||||
ap.add_argument("-o", "--output", type=Path, help="salva l'immagine (png, pdf, svg) invece di aprire la finestra")
|
usage="grafico.py [-h] [-o FILE] [csv]",
|
||||||
|
description="Disegna T e dT = T - T_amb in funzione del tempo (due pannelli con asse comune)\n"
|
||||||
|
"da un CSV prodotto da genera.py.",
|
||||||
|
epilog="""\
|
||||||
|
esempi:
|
||||||
|
grafico.py finestra con l'ultimo CSV modificato in output/
|
||||||
|
grafico.py output/gen_42.csv un CSV preciso
|
||||||
|
grafico.py -o grafico.png salva l'immagine invece di aprire la finestra
|
||||||
|
(utile senza schermo, es. via SSH)
|
||||||
|
|
||||||
|
formato atteso del CSV: t_ms,T_C,dT_C (come scritto da genera.py).
|
||||||
|
|
||||||
|
codici di uscita:
|
||||||
|
0 ok
|
||||||
|
1 errore, con messaggio in italiano su stderr: nessun CSV in output/, file
|
||||||
|
illeggibile o con formato diverso, immagine non scrivibile
|
||||||
|
2 argomenti della riga di comando non validi
|
||||||
|
|
||||||
|
dipendenze: numpy, matplotlib.""",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
add_help=False,
|
||||||
|
)
|
||||||
|
argomenti = ap.add_argument_group("argomenti")
|
||||||
|
opzioni = ap.add_argument_group("opzioni")
|
||||||
|
opzioni.add_argument("-h", "--help", action="help", help="mostra questo aiuto ed esce")
|
||||||
|
argomenti.add_argument("csv", nargs="?", type=Path, help="CSV da disegnare (default: l'ultimo modificato in output/)")
|
||||||
|
opzioni.add_argument(
|
||||||
|
"-o", "--output", type=Path, metavar="FILE",
|
||||||
|
help="salva l'immagine in FILE invece di aprire la finestra; il formato segue l'estensione (png, pdf, svg)",
|
||||||
|
)
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
percorso = args.csv or ultimo_csv()
|
percorso = args.csv or ultimo_csv()
|
||||||
|
|||||||
+25
@@ -2,6 +2,31 @@
|
|||||||
# Elimina i dati generati: svuota la cartella output/ (la cartella resta).
|
# Elimina i dati generati: svuota la cartella output/ (la cartella resta).
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
uso() {
|
||||||
|
cat <<'USO'
|
||||||
|
uso: pulisci.sh [-h]
|
||||||
|
|
||||||
|
Elimina i dati generati: svuota la cartella output/ (la cartella resta).
|
||||||
|
Non tocca nient'altro. Se output/ non esiste lo segnala ed esce senza errore.
|
||||||
|
|
||||||
|
opzioni:
|
||||||
|
-h, --help mostra questo aiuto ed esce
|
||||||
|
|
||||||
|
codici di uscita:
|
||||||
|
0 ok (anche se output/ non esiste)
|
||||||
|
2 argomenti non validi
|
||||||
|
|
||||||
|
esempio:
|
||||||
|
./pulisci.sh
|
||||||
|
USO
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
-h|--help) uso; exit 0 ;;
|
||||||
|
"") ;;
|
||||||
|
*) echo "pulisci.sh: errore: argomento non riconosciuto: $1" >&2; uso >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
if [ ! -d output ]; then
|
if [ ! -d output ]; then
|
||||||
|
|||||||
Reference in new issue
Block a user