Lesson 06 — Selezione e lettura dei dati sperimentali con GNU Octave e GX-TXT

di Giux GX-TXT, octave-it, scripts-it 74 min di lettura

Dall’identificazione dei Dataset alla lettura dei dati

Nella [Lezione 05 — Dataset e Variable ID] abbiamo imparato a identificare i Dataset presenti in una Session GX-TXT, a riconoscere le variabili scientifiche attraverso i loro identificatori stabili (Variable ID) e a consultarne le proprietà mediante le API pubbliche del framework.

Sappiamo quindi come interrogare GX-TXT per conoscere quali dati sono disponibili, come sono organizzati e quali informazioni scientifiche li descrivono.

Ma conoscere la struttura di un Dataset non significa ancora utilizzarne i valori numerici.

In questa sesta lezione compiremo il passaggio successivo: leggeremo i campioni sperimentali direttamente in GNU Octave, selezioneremo intervalli significativi e confronteremo misure differenti.

Per farlo introdurremo una nuova operazione della Canonical Dataset API v2, read_range, che consente di richiedere soltanto una porzione dei dati, senza dover necessariamente caricare l’intero Dataset.

Utilizzeremo inoltre alcune caratteristiche fondamentali del linguaggio Octave: vettori, celle, strutture, indicizzazione, condizioni logiche e funzioni per l’elaborazione numerica.

Tutte le operazioni di accesso ai dati saranno effettuate esclusivamente attraverso le API pubbliche GX-TXT. Non leggeremo direttamente i file conservati nelle directory interne della Session.

Un esperimento reale: il ProvaTelecomando IR

Per questa lezione abbiamo scelto di utilizzare dati sperimentali reali, acquisiti durante lo sviluppo del progetto ProvaTelecomando IR, già documentato su giux-lab.it.

Il circuito permette di rilevare la presenza di segnali infrarossi emessi dai telecomandi, utilizzando diversi stadi analogici di amplificazione, regolazione della sensibilità e gestione dell’indicazione luminosa.

Le acquisizioni sono state effettuate con un oscilloscopio Rigol e successivamente importate in GX-TXT.

Il primo segnale utilizzato è una registrazione della tensione associata alla ricezione di un comando infrarosso, contenente impulsi modulati con una portante di circa 38 kHz.

Le caratteristiche principali dell’acquisizione sono:

  • Canale acquisito: CH1
  • Numero di campioni: 1.200.000
  • Frequenza di campionamento: 10 MSa/s
  • Intervallo fra campioni: 100 ns
  • Durata complessiva: 120 ms
  • Unità della misura: volt

L’elevato numero di campioni rende questo segnale particolarmente adatto a illustrare il vantaggio delle letture parziali.

Per studiare pochi impulsi non è necessario trasferire in Octave tutti i 1.200.000 valori: possiamo richiedere soltanto quelli compresi nell’intervallo di interesse.

Dalla misura originale al Dataset scientifico

Dopo l’importazione mediante il modulo Rigol CSV, GX-TXT ha generato un Dataset numerico di tipo timeseries.table, identificato da:

vir_10x_dc_10ms_1.2m

Il Dataset contiene due variabili:

  • v0001 — time_s, tempo espresso in secondi.
  • v0002 — CH1, tensione misurata in volt.

L’importatore ha interpretato le informazioni temporali contenute nell’intestazione del file Rigol, costruendo una coordinata temporale utilizzabile per l’analisi scientifica.

Nell’anteprima del Dataset possiamo osservare l’intera acquisizione e riconoscere i diversi gruppi di impulsi.

Anteprima GX-TXT del Dataset numerico del segnale infrarosso acquisito con oscilloscopio Rigol.
Anteprima scientifica del segnale infrarosso importato in GX-TXT, con tempo in secondi e tensione CH1 in volt.

È importante distinguere questa anteprima dal contenuto numerico originale: la rappresentazione grafica permette di osservare il segnale, mentre le API di lettura consentono di recuperare i campioni effettivi per elaborarli in Octave.

Un’altra rappresentazione della stessa misura

Assieme al file CSV abbiamo importato anche lo screenshot originale dell’oscilloscopio, ottenendo un secondo Dataset:

vir_10x_dc_10ms_1.2m_png

In questo caso GX-TXT ha registrato un Dataset di tipo image.raster, conservando l’immagine come risorsa binaria.

Visualizzazione in GX-TXT dello screenshot Rigol importato come Dataset immagine.
Lo screenshot originale dell’oscilloscopio viene gestito da GX-TXT come Dataset image.raster, distinto dai campioni numerici.

I due Dataset documentano la medesima acquisizione sperimentale, ma hanno caratteristiche e utilizzi differenti.

Il Dataset numerico contiene campioni che possiamo selezionare ed elaborare matematicamente. Il Dataset immagine conserva invece una rappresentazione visiva dello strumento al momento della misura.

Questo esempio introduce un concetto importante: un Dataset GX-TXT non deve necessariamente contenere una tabella numerica. Il framework può gestire risorse di natura differente, mantenendone identità, formato e informazioni di provenienza.

Nel corso dell’esercitazione vedremo anche come un documento Markdown possa essere importato e consultato come Dataset testuale.

Versione del framework utilizzata

Gli esempi di questa lezione sono stati sviluppati e verificati con GX-TXT DEV111-P027, utilizzando i sorgenti pubblicati nel repository ufficiale GitHub.

  • Framework: GX-TXT DEV111-P027
  • Repository: https://github.com/giuxlab-hub/GX-TXT
  • Branch: maintenance/dev111
  • Commit di riferimento: a3cf459e2b9909c84c6f200eea32d09dbdc676be
  • Linguaggio: GNU Octave

Gli script utilizzati sono stati eseguiti all’interno della Session GX-TXT dedicata alla Lezione 06.

Nelle sezioni successive introdurremo le API necessarie per identificare le variabili, leggere i valori numerici e selezionare intervalli temporali, spiegando di volta in volta lo scopo delle operazioni e il loro utilizzo nel codice Octave.

Esercitazione 06.01 — Leggere i campioni di un Dataset con GNU Octave

Nella prima esercitazione utilizzeremo il segnale infrarosso appena importato per imparare a recuperare i dati attraverso il framework e manipolarli come normali vettori GNU Octave.

Lo script completo si chiama Lesson_06_01_Dataset_Selection_Read.m ed è stato collaudato nella Session GX-TXT dedicata alla lezione.

L’obiettivo non è ancora interpretare il protocollo del telecomando, ma apprendere come:

  • Individuare i Dataset disponibili.
  • Identificare le variabili scientifiche e le relative unità.
  • Leggere un intervallo limitato di campioni.
  • Selezionare i dati mediante condizioni logiche.
  • Verificare che i risultati siano numericamente coerenti.

Preparazione dello script nell’Octave Script Workspace

Apriamo Octave Script Workspace e registriamo lo script nell’ambito della Session corrente, selezionando session come Scope.

Per questa esercitazione utilizziamo:

  • Script ID: Lesson06_01
  • Name: Lesson 06 01 Dataset Selection Read
  • Version: 1.0.0
  • Description: Lesson 06.01 - Discovering and reading experimental datasets

Il Workspace permette di registrare il codice, assegnargli un’identità stabile e conservarne le revisioni. Ogni esecuzione utilizza uno snapshot del sorgente, rendendo possibile risalire alla versione effettivamente eseguita.

Nel nostro esempio abbiamo anche compilato la sezione Typed Parameters, indicando gli identificatori dei due Dataset:

signal_dataset_id | string | 0 | vir_10x_dc_10ms_1.2m | Signal Dataset ID
image_dataset_id | string | 0 | vir_10x_dc_10ms_1.2m_png | Image Dataset ID

Ogni dichiarazione specifica il nome del parametro, il tipo, l’obbligatorietà, il valore predefinito e una breve descrizione. Il valore 0 indica che il parametro è facoltativo.

Una precisazione importante: questa prima versione dello script utilizza ancora i due Dataset ID come costanti definite nel sorgente. I Typed Parameters risultano registrati e visibili nel Job Launcher, ma lo script non ne recupera ancora i valori durante l’esecuzione. La distinzione fra configurazione del Job e utilizzo effettivo dei parametri deve essere mantenuta.

Editor Octave Script Workspace di GX-TXT con lo script della Lezione 06.01 e i parametri tipizzati dei Dataset.
Registrazione dello script Octave della Lezione 06.01, con i Dataset ID dichiarati nella sezione Typed Parameters.

Il controllo delle API disponibili

Prima di leggere i dati, lo script identifica le due API pubbliche necessarie:

dataset_api = 'CANONICAL_DATASET_API_v2';
schema_api = 'DATASET_VARIABLE_SCHEMA_API_v3';

gxtxt_api_require({dataset_api, schema_api});

La funzione gxtxt_api_require() appartiene allo Script Bridge e verifica che le API richieste siano disponibili nell’ambiente di esecuzione.

L’argomento è un cell array, delimitato dalle parentesi graffe {}, che contiene gli identificatori delle API richieste.

Lo script utilizza anche gxtxt_api_has_operation() per controllare che le singole operazioni list, describe, read_range e quelle dello schema delle variabili siano effettivamente registrate.

In questo modo, se una funzionalità necessaria non è disponibile, lo script segnala un errore esplicito anziché procedere con risultati incompleti.

Individuare i Dataset e le variabili

Per ottenere l’elenco dei Dataset presenti nella Session utilizziamo:

dataset_ids = gxtxt_api_call(dataset_api, 'list', {});

La funzione gxtxt_api_call() rappresenta il punto di accesso generico alle API pubbliche registrate per gli script Octave.

I suoi argomenti sono:

  1. L’identificatore dell’API.
  2. Il nome dell’operazione.
  3. Un cell array contenente gli argomenti dell’operazione.

In questo caso list non richiede argomenti, quindi passiamo il cell array vuoto {}.

Il risultato è un elenco di Dataset ID.

Nell’esecuzione originale erano presenti tre Dataset: il segnale numerico, lo screenshot dell’oscilloscopio e un documento Markdown precedentemente importato.

Per identificare le variabili utilizziamo invece la DATASET_VARIABLE_SCHEMA_API_v3, interrogando prima l’elenco delle variabili e successivamente le proprietà di ciascuna.

variables = gxtxt_api_call(schema_api, 'list', ...
                           {signal_dataset_id});

L’operazione list dello schema restituisce i descrittori delle variabili appartenenti al Dataset selezionato.

L’operazione get, chiamata con {dataset_id, variable_id}, permette invece di leggere il descrittore di una variabile specifica.

Nel nostro caso riconosciamo:

  • v0001: ruolo scientifico time, unità s.
  • v0002: ruolo scientifico signal, unità V.

Questo approccio evita di affidarsi esclusivamente alla posizione di una colonna: lo script seleziona le variabili anche in base al loro significato scientifico.

La nuova operazione: read_range

Arriviamo ora alla novità principale della Lezione 06.

L’operazione read_range della Canonical Dataset API v2 permette di leggere un intervallo di campioni di una o più variabili appartenenti allo stesso Dataset.

La chiamata è:

first_row = 100000;
row_count = 3000;

requested_ids = {time_id, voltage_id};

blocks = gxtxt_api_call(dataset_api, 'read_range', ...
    {signal_dataset_id, requested_ids, first_row, row_count});

I quattro argomenti dell’operazione hanno significati precisi:

  • signal_dataset_id: identifica il Dataset da leggere.
  • requested_ids: contiene gli identificatori delle variabili richieste.
  • first_row: indica la posizione iniziale dei campioni.
  • row_count: specifica quanti campioni leggere.

Il framework restituisce una struttura Octave contenente i blocchi delle variabili richieste, indicizzati mediante i rispettivi Variable ID.

La differenza rispetto alla Lezione 05 è fondamentale: non stiamo più chiedendo soltanto informazioni sulla struttura del Dataset, ma stiamo recuperando i suoi valori sperimentali.

Nel nostro caso richiediamo 3.000 campioni da un’acquisizione che ne contiene 1.200.000.

Attenzione: gli indici GX-TXT e quelli Octave sono differenti

L’argomento first_row dell’API è basato su zero: il primo campione del Dataset ha posizione 0.

GNU Octave utilizza invece normalmente indici basati su uno: il primo elemento di un vettore si richiama con vector(1).

Quindi, nella nostra lettura:

  • Primo indice richiesto all’API: 100000.
  • Ultimo indice richiesto all’API: 102999.
  • Primo elemento del vettore restituito: 1.
  • Ultimo elemento del vettore restituito: 3000.

Confondere queste due convenzioni può provocare errori di selezione, soprattutto quando si confrontano intervalli appartenenti a Dataset differenti.

Dai blocchi GX-TXT ai vettori Octave

Una volta ricevuta la risposta, possiamo estrarre i valori utilizzando l’accesso dinamico ai campi delle strutture:

time_block = blocks.(time_id);
voltage_block = blocks.(voltage_id);

time_s = time_block.values(:);
voltage_v = voltage_block.values(:);

La sintassi blocks.(time_id) permette di accedere al campo il cui nome è contenuto nella variabile time_id.

L’espressione values(:) restituisce i valori sotto forma di vettore colonna.

A questo punto time_s e voltage_v possono essere manipolati con le normali istruzioni di GNU Octave.

Il nostro script verifica inoltre la lunghezza dei vettori, la presenza di valori non finiti, l’eventuale maschera dei dati mancanti (missing_mask) e l’ordinamento crescente della coordinata temporale.

Questi controlli sono importanti: una lettura riuscita dal punto di vista informatico non garantisce, da sola, che i valori siano utilizzabili correttamente in un’elaborazione scientifica.

Selezionare una finestra temporale

Supponiamo di voler esaminare soltanto i campioni compresi tra 0,300 ms e 0,450 ms.

Possiamo costruire direttamente una condizione logica:

window_start_s = 0.00030;
window_end_s = 0.00045;

selected_mask = (time_s >= window_start_s) & ...
                (time_s < window_end_s);

selected_time = time_s(selected_mask);
selected_voltage = voltage_v(selected_mask);

L’operatore & effettua la congiunzione logica elemento per elemento.

selected_mask è un vettore logico nel quale ciascun elemento vale true se il tempo corrispondente soddisfa entrambe le condizioni.

Applicando questa maschera a time_s e voltage_v, estraiamo soltanto i campioni della finestra desiderata, mantenendo l’associazione fra tempo e tensione.

Introduciamo così uno dei meccanismi più utili di Octave: l’indicizzazione logica dei vettori, che consente di selezionare dati senza scrivere cicli espliciti per controllare ogni singolo campione.

Possiamo applicare anche una soglia alla tensione:

threshold_v = 7.3;

low_mask = selected_voltage < threshold_v;
n_low = nnz(low_mask);

La funzione nnz() conta il numero di elementi non nulli della maschera, che in questo caso corrispondono ai campioni con tensione inferiore a 7,3 V.

Questa soglia viene utilizzata soltanto come esempio di selezione numerica: non costituisce una decodifica del protocollo infrarosso.

Esecuzione dello script e verifica dei risultati

Terminata la registrazione, apriamo il comando Run dello Script Workspace.

GX-TXT presenta il Job Launcher, nel quale possiamo verificare gli eventuali parametri di invocazione, i Dataset di ingresso e le opzioni del Job.

Nel nostro esempio i due Typed Parameters precedentemente registrati compaiono con i rispettivi valori predefiniti.

Finestra Run Job di GX-TXT con i parametri signal_dataset_id e image_dataset_id configurati per la Lezione 06.01.
Job Launcher dello script Octave 06.01 con i due identificatori Dataset e i comandi di validazione ed esecuzione.

Avviamo quindi l’esecuzione e consultiamo il risultato mediante il sistema Script Jobs di GX-TXT.

La prima esercitazione, eseguita con lo script versione 1.0.0, revisione 2, è terminata correttamente con stato COMPLETED e codice di uscita 0.

Il risultato numerico ottenuto è il seguente:

ParametroRisultato
Campioni richiesti3.000
Primo tempo letto0,000199998 s
Ultimo tempo letto0,000499898 s
Intervallo fra campioni100 ns
Finestra selezionata0,300–0,450 ms
Campioni selezionati1.500
Tensione minima nella finestra6,04 V
Tensione massima nella finestra8,80 V
Campioni inferiori a 7,3 V801

Questi valori coincidono con quelli verificati indipendentemente sul Dataset importato.

GX-TXT Script Jobs con la prima esercitazione Octave della Lezione 06 completata correttamente.
Lo Script Jobs di GX-TXT conferma l’esecuzione completata dello script 06.01 in 4,2 secondi.

Il risultato conferma che abbiamo letto una porzione limitata del segnale originale, utilizzato le variabili scientifiche corrette e applicato una selezione temporale direttamente in Octave.

Per questa operazione non abbiamo dovuto accedere al file CSV originale né alle directory interne della Session: GX-TXT ha gestito l’accesso ai dati attraverso il contratto pubblico dell’API.

È esattamente il principio sul quale costruiremo la seconda esercitazione, dedicata al confronto fra più acquisizioni sperimentali.

Esercitazione 06.02 — Leggere e confrontare più Dataset sperimentali

Nella prima esercitazione abbiamo imparato a leggere un intervallo numerico di un singolo Dataset e a selezionare i campioni attraverso condizioni logiche.

Adesso affrontiamo un problema più vicino alle normali attività di laboratorio: confrontare acquisizioni effettuate in condizioni sperimentali differenti.

Utilizzeremo altre quattro misure del progetto ProvaTelecomando IR, ottenute durante le prove del circuito di rivelazione e mantenimento dell’indicazione luminosa.

Questa seconda esercitazione ci permetterà di applicare le API già introdotte a più Dataset, utilizzando cicli, vettori e controlli numerici per verificare la coerenza dei risultati.

Le acquisizioni del circuito ProvaTelecomando IR

Le misure sono state realizzate utilizzando un generatore che simula il segnale del fotodiodo, con una portante di circa 38 kHz e un burst di 100 impulsi.

Le condizioni di prova prevedevano:

  • RGAIN: 500 Ω
  • Regolazione della sensibilità P_SENS: 9.459 Ω
  • Ampiezze nominali Vgen: 1, 2, 3 e 4 V
  • Canale CH2: tensione del nodo HOLD
  • Canale CH3: tensione del nodo N_LED_ARANCIO

Il nodo HOLD conserva temporaneamente l’informazione relativa al segnale ricevuto. Dopo l’eccitazione, la sua tensione diminuisce progressivamente, mentre la tensione del nodo associato al LED permette di osservare la risposta dello stadio di indicazione.

Ogni acquisizione contiene 300.000 campioni per canale, registrati a 50 kSa/s, per una durata complessiva di 6 secondi.

La regolazione della sensibilità indicata come P_SENS non deve essere confusa con la resistenza utilizzata nel circuito del fotodiodo virtuale: si tratta di due elementi distinti.

L’importazione dei canali in GX-TXT

Abbiamo importato i quattro file CSV utilizzando il modulo Rigol CSV di GX-TXT, lasciando inalterate le impostazioni predefinite.

In questa configurazione, il modulo ha generato un Dataset numerico distinto per ciascun canale abilitato.

Per esempio, dall’acquisizione con Vgen nominale di 2 V sono stati ottenuti:

  • vgen_2v_hold_led — contiene time_s e CH2.
  • vgen_2v_hold_led.CH3 — contiene time_s e CH3.

Questo comportamento è documentato nell’implementazione del modulo Rigol CSV della versione utilizzata: ogni segnale abilitato viene convertito in un Dataset canonico.

Non si tratta di una limitazione generale dei Dataset GX-TXT, né di una regola che imponga di separare sempre i canali. È la struttura prodotta dal modulo e dal procedimento di importazione utilizzati in questa esercitazione.

Anteprima GX-TXT del segnale CH3 del ProvaTelecomando IR, acquisito con Vgen nominale di 2 V.
Andamento temporale della tensione CH3 con Vgen nominale di 2 V, visualizzato nella scheda Dataset di GX-TXT.

Il Dataset selezionato mostra la tensione del nodo associato al LED nel corso dell’acquisizione. Possiamo osservare il cambiamento di livello e il successivo ritorno verso la condizione iniziale.

Aprendo il visualizzatore scientifico del Dataset possiamo esaminare direttamente i campioni nella tabella canonica.

Tabella canonica GX-TXT con i campioni temporali e la tensione CH3 del ProvaTelecomando IR.
Il Scientific Dataset Viewer mostra i valori di time_s e CH3 associati ai campioni dell’acquisizione da 2 V nominali.

La tabella associa a ciascuna riga il tempo, espresso in secondi, e la tensione del canale CH3, espressa in volt.

La distinzione è importante: la preview mostra l’andamento complessivo del segnale, mentre la tabella e le API consentono di accedere ai valori numerici effettivamente registrati.

Perché dobbiamo considerare il Dataset ID insieme al Variable ID

Tutti gli otto Dataset numerici ottenuti dalle quattro acquisizioni possiedono due variabili, con identificatori:

  • v0001 — coordinata temporale time_s.
  • v0002 — tensione del canale acquisito, CH2 oppure CH3.

Il Variable ID v0002 compare quindi in Dataset differenti, ma non identifica globalmente la medesima grandezza.

La sua identità scientifica completa è costituita dalla coppia Dataset ID + Variable ID.

Questo principio, introdotto nella Lezione 05, diventa essenziale quando elaboriamo simultaneamente dati provenienti da sorgenti differenti.

Nel nostro caso dovremo prima identificare il Dataset relativo al canale desiderato, poi individuare la corrispondente variabile numerica.

Preparazione del secondo script Octave

Lo script utilizzato in questa esercitazione si chiama:

Lesson_06_02_HOLD_LED_Comparison.m

Lo abbiamo registrato nell’Octave Script Workspace con Scope session e identificatore Lesson_06_02.

Nella sezione Additional required APIs abbiamo dichiarato:

CANONICAL_DATASET_API_v2;DATASET_VARIABLE_SCHEMA_API_v3

A differenza della prima esercitazione, non abbiamo utilizzato Typed Parameters: gli identificatori dei Dataset necessari sono definiti direttamente nel sorgente, per rendere riproducibile il confronto delle quattro misure.

Octave Script Workspace di GX-TXT con lo script 06.02 e le API pubbliche richieste per confrontare più Dataset.
Script della seconda esercitazione registrato nella Session GX-TXT, con gli identificatori dei Dataset e le API richieste.

Organizzare le acquisizioni con vettori e cell array

Iniziamo associando i valori nominali di Vgen agli identificatori dei Dataset corrispondenti.

stimulus_v = [1, 2, 3, 4];

hold_ids = {'vgen_1v_hold_led', 'vgen_2v_hold_led', ...
            'vgen_3v_hold_led', 'vgen_4v_hold_led'};

led_ids = {'vgen_1v_hold_led.CH3', 'vgen_2v_hold_led.CH3', ...
           'vgen_3v_hold_led.CH3', 'vgen_4v_hold_led.CH3'};

La variabile stimulus_v è un vettore numerico, mentre hold_ids e led_ids sono cell array di stringhe.

Questa organizzazione consente di mantenere la corrispondenza fra una condizione sperimentale e i due Dataset che la descrivono.

Per esempio, utilizzando l’indice k, possiamo recuperare il valore nominale dello stimolo e gli identificatori dei due canali:

vgen = stimulus_v(k);

hold_dataset = hold_ids{k};
led_dataset = led_ids{k};

Ricordiamo che in Octave le parentesi graffe {} permettono di estrarre il contenuto di una cella, mentre le parentesi tonde () vengono utilizzate per indicizzare normalmente gli elementi di un array.

Leggere più Dataset mediante un ciclo

Nello script utilizziamo un ciclo for per ripetere il procedimento sulle quattro acquisizioni.

for k = 1:numel(stimulus_v)

  source_ids = {hold_ids{k}, led_ids{k}};

  % Process both channels for this generator setting.
  for channel = 1:2
    ds = source_ids{channel};

    % The Dataset is inspected and read through public APIs.

  endfor

endfor

Si tratta di due cicli annidati.

Il ciclo esterno seleziona una delle quattro condizioni Vgen, mentre quello interno considera i due canali della stessa acquisizione.

Il codice completo contiene anche le chiamate alla Variable Schema API v3, utilizzate per identificare le variabili in base a nome canonico, ruolo scientifico e unità di misura.

Una volta riconosciute le variabili, utilizziamo nuovamente read_range.

In questa esercitazione non introduciamo un’altra API di lettura: approfondiamo l’utilizzo di quella già studiata, applicandola ripetutamente a Dataset differenti.

Selezionare soltanto gli intervalli necessari

Le acquisizioni HOLD/LED durano 6 secondi, ma per l’elaborazione proposta non occorre leggere l’intero segnale.

Lo script definisce due intervalli:

early_start = 104400;
early_count = 4000;

late_start = 205350;
late_count = 101;

La prima lettura recupera 4.000 campioni, corrispondenti a una finestra temporale compresa fra circa −20 ms e +59,98 ms rispetto al riferimento temporale dell’acquisizione.

La seconda recupera soltanto 101 campioni, compresi fra 1,999 s e 2,001 s.

Queste posizioni sono state determinate sulla base della coordinata temporale delle acquisizioni, che inizia a −2,108 s e presenta un incremento di 20 µs.

L’offset utilizzato da read_range continua a essere basato su zero, indipendentemente dalla normale indicizzazione degli array Octave.

La lettura di due Dataset diversi richiede due chiamate distinte all’API; non è possibile passare due Dataset ID come se fossero variabili appartenenti a un’unica sorgente.

Verificare la sincronizzazione dei canali

Abbiamo acquisito CH2 e CH3 contemporaneamente, ma il framework li ha registrati come Dataset distinti.

Prima di confrontare i valori, dobbiamo verificare che le coordinate temporali siano effettivamente compatibili.

Nel codice utilizziamo:

t_hold = channel_time{1};
t_led = channel_time{2};

if (max(abs(t_hold - t_led)) > time_alignment_tolerance_s)
  error('CH2 and CH3 timestamps are not aligned');
endif

L’espressione t_hold - t_led calcola la differenza fra gli elementi corrispondenti dei due vettori temporali.

La funzione abs() restituisce i valori assoluti delle differenze, mentre max() individua quella più grande.

Il risultato viene confrontato con una tolleranza di 1 ns, scelta per verificare l’allineamento numerico delle coordinate importate.

Questo controllo non sostituisce una calibrazione temporale dello strumento: verifica che i due Dataset condividano la stessa griglia temporale entro la tolleranza definita.

È una precauzione essenziale. Il fatto che due vettori contengano lo stesso numero di elementi non garantisce che rappresentino misure temporalmente sincronizzate.

Calcolare il massimo della tensione HOLD

Per ogni acquisizione selezioniamo i campioni appartenenti all’intervallo temporale da 0 a 20 ms.

event_mask = (t_hold >= 0) & (t_hold < 0.020);

event_voltage = channel_values{1}(event_mask);
event_time = t_hold(event_mask);

[peak_voltage, peak_index] = max(event_voltage);

peak_time = event_time(peak_index);

La funzione max(), utilizzata con due variabili di uscita, restituisce contemporaneamente il valore massimo e la posizione alla quale è stato individuato.

Possiamo quindi utilizzare peak_index per recuperare il tempo corrispondente.

Questo esempio mostra una caratteristica importante di GNU Octave: una funzione può restituire più risultati senza dover creare strutture aggiuntive.

Lo script calcola anche la tensione media di CH3 nella piccola finestra centrata intorno a 2 secondi, usando la funzione mean().

L’obiettivo è ottenere, per ciascuna condizione sperimentale, pochi valori numerici facilmente confrontabili.

Risultati sperimentali

Il Job registrato in GX-TXT è stato eseguito il 28 ottobre 2026 con lo script versione 1.0.0, revisione 2.

L’esecuzione è terminata con stato COMPLETED, codice di uscita 0 e durata di circa 5,1 secondi.

I risultati sono:

Vgen nominaleHOLD massimoTempo del massimoCH3 intorno a 2 s
1 V2,78 V2,60 ms8,96 V
2 V4,26 V2,60 ms2,08 V
3 V4,62 V2,60 ms2,00 V
4 V4,58 V2,58 ms2,00 V

I valori indicati come Vgen sono le impostazioni nominali del generatore, mentre le tensioni HOLD e CH3 derivano dalle misure effettivamente acquisite.

Le quattro acquisizioni mostrano che il massimo del nodo HOLD aumenta inizialmente al crescere dello stimolo, raggiungendo valori vicini a 4,6 V per le condizioni da 3 e 4 V.

Questo andamento suggerisce un limite nella risposta dello stadio, ma per attribuirne il comportamento a uno specifico meccanismo circuitale occorrerebbe un’analisi elettronica più approfondita.

Anche la tensione CH3 cambia sensibilmente al variare dello stimolo. Non dobbiamo però interpretare automaticamente questa tensione come corrente del LED né come misura diretta del suo stato luminoso.

Consultare il risultato completo in GX-TXT

Dopo l’esecuzione, possiamo consultare il Job attraverso il sistema Script Jobs.

La visualizzazione sintetica dell’elenco può mostrare soltanto una parte del log. Aprendo il dettaglio del Job, invece, possiamo consultare l’output completo, compresa la tabella riassuntiva generata dallo script.

Dettaglio del Job GX-TXT completato con i risultati numerici delle quattro acquisizioni HOLD e LED.
Esecuzione completata dello script 06.02 con i massimi HOLD, i tempi di picco e le tensioni CH3 per quattro condizioni Vgen.

Il risultato termina con il messaggio:

PASS: Four paired acquisitions, eight Dataset IDs, aligned time coordinates.

Il messaggio conferma che lo script ha completato il confronto delle quattro condizioni, individuato gli otto Dataset richiesti e superato le verifiche di allineamento temporale previste.

La verifica è stata effettuata senza modificare i Dataset sorgente o i loro metadata scientifici.

Le guide Markdown come Dataset documentali

Durante la preparazione del corso abbiamo anche importato in GX-TXT la guida operativa di questa esercitazione, scritta in formato Markdown.

Il framework l’ha registrata come Dataset documentale con identificatore:

lesson_06_02_execution_guide

Anteprima della guida Markdown della Lezione 06.02 importata e consultabile come Dataset testuale in GX-TXT.
La guida operativa della seconda esercitazione, importata come Dataset documentale e visualizzata direttamente in GX-TXT.

Questo esempio mostra un’ulteriore possibilità del framework: conservare nella stessa Session dati numerici, immagini e documenti descrittivi.

La documentazione dell’esperimento può quindi essere consultata direttamente nell’ambiente in cui sono presenti le acquisizioni e i risultati delle elaborazioni.

È necessario ricordare che modificare successivamente il file Markdown esterno non comporta automaticamente l’aggiornamento del Dataset già importato: ciascuna acquisizione documentale conserva il contenuto registrato al momento dell’importazione.

Che cosa abbiamo imparato nella seconda esercitazione

Con questo esempio abbiamo esteso il lavoro della prima esercitazione a più Dataset sperimentali.

Abbiamo utilizzato vettori e cell array per organizzare le sorgenti, cicli annidati per elaborare acquisizioni differenti, letture parziali per limitare i campioni trasferiti in Octave e controlli numerici per verificare che i segnali fossero correttamente allineati.

Abbiamo inoltre estratto grandezze significative, come il massimo della tensione HOLD, la posizione temporale del picco e la tensione media di CH3 in un intervallo definito.

I risultati sono ora disponibili come valori numerici, ma non abbiamo ancora costruito una rappresentazione grafica del confronto.

Nella prossima esercitazione utilizzeremo proprio questi risultati per produrre un piccolo Dataset riassuntivo e visualizzarlo attraverso Plot Dataset API v4, senza utilizzare le funzioni grafiche native di Octave.

Esercitazione 06.03 — Visualizzare i risultati con Plot Dataset API v4

Nelle due esercitazioni precedenti abbiamo imparato a leggere i valori numerici dei Dataset, selezionare intervalli temporali e confrontare acquisizioni sperimentali differenti.

Abbiamo così ottenuto quattro valori di tensione massima del nodo HOLD del ProvaTelecomando IR, corrispondenti alle quattro impostazioni nominali del generatore.

I risultati sono già disponibili in forma numerica, ma una rappresentazione grafica permette di interpretarli più rapidamente.

In questa terza esercitazione faremo quindi un ulteriore passo: genereremo un Dataset riassuntivo contenente i risultati dell’analisi e lo utilizzeremo per costruire un grafico scientifico direttamente in GX-TXT.

Non utilizzeremo le istruzioni grafiche native plot() o bar() di GNU Octave. Il grafico sarà creato attraverso le API pubbliche del framework, utilizzando il sistema Plot Dataset v4.

Questo esempio anticipa alcune funzionalità che approfondiremo nelle lezioni dedicate alla pubblicazione e alla visualizzazione scientifica dei dati.

Un grafico diverso da quelli già utilizzati

Nei precedenti esempi del corso abbiamo incontrato principalmente rappresentazioni di grandezze misurate in funzione del tempo.

Questa volta realizzeremo invece un grafico a barre verticali, nel quale ogni barra rappresenta il massimo della tensione HOLD ottenuto con una diversa impostazione del generatore.

I valori da rappresentare sono:

Vgen nominaleTensione HOLD massima
1 V2,78 V
2 V4,26 V
3 V4,62 V
4 V4,58 V

Sull’asse orizzontale rappresenteremo la tensione nominale del generatore, mentre sull’asse verticale riporteremo il massimo della tensione HOLD misurata.

Non si tratta di una curva continua: disponiamo di quattro condizioni sperimentali distinte. La rappresentazione a barre permette di confrontare immediatamente i valori senza suggerire un’interpolazione fra le misure.

Preparazione dello script Octave

Lo script utilizzato si chiama:

Lesson_06_03_HOLD_Peak_Bar_Plot.m

Lo abbiamo registrato nell’Octave Script Workspace della Session lesson_06, utilizzando l’identificatore Lesson_06_03.

Rispetto alle esercitazioni precedenti, lo script richiede quattro API pubbliche:

CANONICAL_DATASET_API_v2;DATASET_VARIABLE_SCHEMA_API_v3;DATASET_PUBLICATION_API_v2;PLOT_DATASET_API_v4

Le prime due sono già note: ci permettono di individuare i Dataset, riconoscere le variabili e leggerne i valori numerici.

Le altre due introducono funzionalità differenti:

  • DATASET_PUBLICATION_API_v2 permette di pubblicare nuovi Dataset scientifici attraverso il framework.
  • PLOT_DATASET_API_v4 permette di definire, registrare e generare grafici scientifici associati ai Dataset.

Anche in questo caso lo script utilizza gli identificatori delle acquisizioni già verificate nella Session, senza richiedere Typed Parameters.

Octave Script Workspace di GX-TXT con lo script 06.03 per il calcolo dei massimi HOLD e la creazione del grafico a barre.
Script Octave della Lezione 06.03 registrato in GX-TXT per elaborare le acquisizioni HOLD e generare un grafico scientifico nativo.

Ricalcolare i valori dai Dataset originali

Un aspetto importante della procedura è che non utilizziamo direttamente i quattro massimi riportati nella tabella precedente per costruire il grafico.

Lo script li ricalcola dai campioni sperimentali, mediante CANONICAL_DATASET_API_v2/read_range.

Per ciascuna acquisizione selezioniamo una finestra temporale di 1.000 campioni, corrispondente all’intervallo da 0 a circa 20 ms.

first_sample = 105400;
sample_count = 1000;

La lettura utilizza gli stessi principi illustrati nella prima esercitazione: Dataset ID, Variable ID, offset iniziale e numero di campioni richiesti.

Una volta recuperati tempo e tensione, individuiamo il massimo nell’intervallo selezionato:

event_mask = (times >= 0) & (times < 0.020);

hold_peaks(k) = max(voltages(event_mask));

Il risultato è un vettore Octave di quattro elementi:

hold_peaks = [2.78; 4.26; 4.62; 4.58];

Questo vettore rappresenta il risultato dell’elaborazione delle misure, non una nuova acquisizione dello strumento.

Lo script confronta inoltre i massimi calcolati con valori di riferimento ricavati da una verifica indipendente dei dati originali. Una differenza superiore alla tolleranza prevista provoca un errore, impedendo di pubblicare risultati non coerenti.

Dall’elaborazione Octave a un nuovo Dataset GX-TXT

Fino a questo punto abbiamo utilizzato GX-TXT principalmente per leggere dati già presenti nella Session.

Adesso vogliamo conservarne anche il risultato.

Per questo introduciamo l’operazione pubblica publish_series di DATASET_PUBLICATION_API_v2.

La funzione riceve una struttura che descrive il nuovo Dataset, le variabili da pubblicare, le unità di misura e le informazioni di provenienza.

Nel nostro esempio prepariamo due vettori numerici:

vgen_nominal = [1; 2; 3; 4];

hold_peaks = [2.78; 4.26; 4.62; 4.58];

Il secondo vettore, nel programma reale, è quello calcolato dalle letture dei Dataset sorgente. I valori riportati qui servono soltanto a mostrarne il contenuto finale.

Definiamo quindi la struttura della richiesta:

request = struct();

request.dataset_id = 'lesson06_hold_peak_summary';
request.contract = 'table.generic';
request.storage = 'csv';

request.x_values = vgen_nominal;
request.y_values = hold_peaks;

Attraverso ulteriori campi della stessa struttura, lo script dichiara gli identificatori delle due variabili, le unità fisiche, il tipo numerico float64 e le informazioni di provenienza.

In particolare, i due Variable ID scelti sono:

  • vgen_nominal — impostazione nominale del generatore, in volt.
  • hold_peak — massimo misurato della tensione HOLD, in volt.

La richiesta di pubblicazione viene quindi inviata al framework:

publication = gxtxt_api_call( ...
    publication_api, 'publish_series', {request});

La pubblicazione non equivale a salvare arbitrariamente un CSV in una directory.

È GX-TXT a gestire l’operazione mediante il contratto pubblico, registrando il nuovo Dataset nella Session e associandogli le informazioni scientifiche previste.

Il risultato è il Dataset:

lesson06_hold_peak_summary

Il suo formato di memorizzazione è CSV, mentre il contratto scientifico è table.generic.

La struttura del Dataset contiene quattro righe e due variabili numeriche.

La provenienza registra anche i quattro Dataset sperimentali dai quali sono stati ottenuti i risultati, permettendo di distinguere la misura originale dall’elaborazione successiva.

Evitare di sovrascrivere involontariamente i risultati

Lo script contiene un’ulteriore verifica.

Prima di pubblicare il Dataset riassuntivo, controlla se nella Session esiste già un Dataset con lo stesso identificatore.

Se è presente, ne legge i valori e li confronta con quelli appena calcolati.

Quando i risultati coincidono, il Dataset viene riutilizzato. Se differiscono, lo script segnala un errore invece di sostituire automaticamente i dati esistenti.

Questo controllo rende più sicura la ripetizione dell’esperimento e introduce il concetto di riproducibilità delle elaborazioni scientifiche.

La pubblicazione completa dei Dataset, le transazioni e la gestione delle diverse modalità di memorizzazione saranno affrontate nella Lezione 07.

Introduzione a Plot Dataset API v4

Ora che disponiamo di un Dataset riassuntivo, possiamo utilizzarlo per generare il grafico.

La API pubblica PLOT_DATASET_API_v4 adotta un modello dichiarativo: lo script descrive il grafico che vuole ottenere e il framework si occupa di interpretare la specifica e produrre la rappresentazione.

Questa descrizione è chiamata PlotSpec.

Nel nostro caso richiediamo inizialmente una nuova specifica:

spec = gxtxt_api_call(plot_api, 'new_spec', ...
    {'plot.dataset.xy', ...
     'ProvaTelecomando IR - maximum HOLD voltage'});

L’operazione new_spec fornisce una struttura di partenza per un grafico cartesiano.

Successivamente lo script definisce le proprietà degli assi e associa al grafico il Dataset scientifico pubblicato.

La parte essenziale della serie è:

series = struct( ...
    'id', 'hold_peak_by_vgen', ...
    'label', 'HOLD peak', ...
    'dataset_id', summary_dataset_id, ...
    'x_variable_id', summary_x_id, ...
    'y_variable_id', summary_y_id, ...
    'axis', 'left', ...
    'kind', 'bar', ...
    'bar_width', 0.60, ...
    'style', struct('opacity', 0.85));

Le proprietà più importanti sono:

  • dataset_id: identifica il Dataset contenente i valori da rappresentare.
  • x_variable_id e y_variable_id: individuano le due variabili scientifiche.
  • kind = 'bar': seleziona la rappresentazione a barre.
  • bar_width = 0.60: imposta la larghezza delle barre, nelle unità numeriche dell’asse X.
  • style: descrive alcune caratteristiche di presentazione della serie.

L’aspetto significativo è che la serie non contiene una nuova copia dei valori numerici: fa riferimento alle variabili del Dataset pubblicato.

In questo modo il grafico rimane collegato alla propria sorgente scientifica.

Validazione e generazione del grafico

Completata la PlotSpec, lo script la sottopone alla validazione prevista dall’API:

is_valid = gxtxt_api_call(plot_api, ...
    'validate_spec', {spec});

Se la specifica viene accettata, possiamo registrare il grafico e richiederne la generazione.

plot_ref = gxtxt_api_call(plot_api, ...
    'upsert_plot', {'lesson06_hold_peak_bar', spec});

render_result = gxtxt_api_call(plot_api, ...
    'render', {plot_ref});

L’operazione upsert_plot crea o aggiorna una rappresentazione identificata da una chiave stabile, mentre render richiede al framework la produzione del grafico.

Infine, attraverso l’operazione artifacts, lo script può consultare le informazioni relative agli artefatti generati.

L’intero procedimento utilizza le API grafiche pubbliche di GX-TXT.

Non vengono chiamate le funzioni Octave figure(), bar(), print() o saveas() per generare un’immagine separata.

Il risultato in GX-TXT

Lo script è stato eseguito nella Session della Lezione 06 il 28 ottobre 2026.

Il Job è terminato correttamente con stato COMPLETED e codice di uscita 0.

Il log dell’esecuzione conferma il calcolo dei quattro massimi, la pubblicazione del Dataset riassuntivo e la generazione del grafico tramite PLOT_DATASET_API_v4.

Il grafico è identificato nel framework da:

  • Graph ID: plot.dataset.xy
  • Variant ID: p0001
Dettaglio del Job Octave 06.03 completato in GX-TXT con la pubblicazione del Dataset e la generazione del grafico HOLD.
Lo script Octave 06.03 termina correttamente, pubblicando il Dataset riassuntivo e generando il grafico scientifico a barre.

Aprendo la scheda Plots di GX-TXT possiamo visualizzare il risultato direttamente nell’ambiente scientifico del framework.

Scheda Plots di GX-TXT con il grafico a barre della tensione HOLD massima rispetto alla Vgen nominale.
Visualizzazione del grafico a barre generato tramite Plot Dataset API v4 direttamente nell’interfaccia GX-TXT.

La rappresentazione contiene quattro barre verticali. L’asse orizzontale mostra la Vgen nominale, mentre quello verticale riporta il massimo della tensione HOLD.

Il framework conserva il grafico come risorsa associata alla Session, utilizzando la struttura di riferimento e gli artefatti del sistema Plot Dataset.

Abbiamo successivamente esportato la rappresentazione in formato PNG, ottenendo un’immagine di 1600 × 900 pixel, utilizzabile anche nella documentazione e negli articoli scientifici.

Grafico a quattro barre della tensione massima HOLD del ProvaTelecomando IR per Vgen nominali da 1 a 4 V.
Tensione massima del nodo HOLD misurata con quattro livelli nominali di stimolo: 2,78, 4,26, 4,62 e 4,58 V. Grafico generato con GX-TXT Plot Dataset v4.

Il PNG esportato è stato confrontato con l’artefatto originale della Session e ne è stata verificata l’identità mediante SHA-256.

Questo conferma che l’immagine utilizzata per documentare l’esperimento proviene effettivamente dal grafico generato da GX-TXT, e non da una ricostruzione realizzata con un programma esterno.

Interpretazione del grafico

Il risultato mette in evidenza l’andamento del massimo della tensione HOLD al variare della tensione nominale di stimolo.

Il passaggio da 1 a 2 V produce un aumento marcato del valore massimo, da 2,78 a 4,26 V.

Passando a 3 V, il massimo raggiunge 4,62 V, mentre con Vgen nominale di 4 V si osserva un valore molto simile, pari a 4,58 V.

L’andamento suggerisce che, nelle condizioni sperimentali considerate, la tensione massima del nodo HOLD tende a stabilizzarsi intorno a 4,6 V.

Il grafico non permette, da solo, di stabilire il meccanismo fisico responsabile di questo comportamento. Per identificarlo sarebbe necessario considerare la struttura del circuito e ulteriori misure.

Dal punto di vista didattico, però, abbiamo raggiunto l’obiettivo: partendo da acquisizioni reali, abbiamo letto i campioni con Octave, calcolato una grandezza significativa, pubblicato il risultato in GX-TXT e generato una rappresentazione grafica nativa.

La gestione approfondita di PlotSpec, delle serie, degli assi, dei layout e delle varianti grafiche sarà ripresa nella Lezione 08.

Per il momento questo esempio dimostra come le API pubbliche permettano di integrare elaborazione numerica e visualizzazione scientifica senza uscire dal framework.

Esercitazione 06.04 — Selezionare un Dataset di ingresso direttamente dal Job Launcher

Nelle esercitazioni precedenti abbiamo utilizzato gli identificatori dei Dataset definiti direttamente all’interno degli script Octave.

Questo approccio è utile quando vogliamo eseguire un esperimento riproducibile su sorgenti prestabilite, ma presenta un limite: per utilizzare lo stesso script su un altro Dataset, occorre modificare il codice oppure prevedere un meccanismo di configurazione.

GX-TXT offre una possibilità differente, particolarmente utile nelle normali attività di laboratorio: selezionare il Dataset di ingresso direttamente nell’interfaccia del Job Launcher e renderlo disponibile allo script tramite lo Script Bridge.

Questo meccanismo permette di separare il codice di elaborazione dalla scelta della sorgente sperimentale.

Nella quarta esercitazione vedremo come funziona e perché è diverso dall’utilizzo dei Typed Parameters.

Tre modalità per identificare un Dataset

Nel corso abbiamo incontrato due modalità di configurazione e ne introduciamo ora una terza.

ModalitàCaratteristica
Dataset ID nel codiceL’identificatore della sorgente è definito direttamente nello script
Typed ParametersLo script può ricevere parametri configurabili, se ne implementa esplicitamente la lettura
Input Dataset BindingL’utente seleziona un Dataset della Session tramite il Job Launcher

Nella prima esercitazione avevamo dichiarato due Typed Parameters per gli identificatori dei Dataset, ma il codice utilizzava ancora valori fissati nel sorgente. La loro dichiarazione, da sola, non rendeva quindi configurabile la selezione.

Con l’Input Dataset Binding adottiamo un approccio diverso.

Il Job Launcher permette di selezionare un Dataset esistente attraverso l’interfaccia di GX-TXT. Il framework associa questa scelta a un alias semantico e rende disponibile allo script l’identità della sorgente selezionata.

In questo modo non occorre inserire manualmente il Dataset ID né utilizzare un parametro testuale per comunicarlo al programma.

Preparazione dello script Octave

Il nuovo script si chiama:

Lesson_06_04_Dataset_Input_Binding.m

Lo abbiamo registrato nell’Octave Script Workspace utilizzando:

  • Scope: session
  • Script ID: Lesson_06_04_Dataset_Input_Binding
  • Versione: 1.0.0
  • Revisione collaudata: 1

Lo script richiede le due API già utilizzate per leggere e identificare i Dataset:

CANONICAL_DATASET_API_v2;DATASET_VARIABLE_SCHEMA_API_v3

La sezione Typed Parameters viene lasciata vuota.

Il codice non contiene un identificatore prestabilito del Dataset di ingresso, ma conserva le informazioni necessarie a interpretare correttamente le acquisizioni HOLD utilizzate nell’esperimento.

Octave Script Workspace di GX-TXT con lo script 06.04 che legge un Dataset selezionato tramite il Job Launcher.
Script Octave 06.04 registrato in GX-TXT per recuperare il Dataset di ingresso attraverso gxtxt_context(), senza utilizzare Typed Parameters.

Selezionare il Dataset nel Job Launcher

Una volta registrato lo script, apriamo il comando Run.

Nel Job Launcher espandiamo la sezione Inputs and output e utilizziamo il pulsante Add input Dataset.

Per ciascun input possiamo configurare un alias semantico e selezionare il Dataset che vogliamo utilizzare.

Nella nostra prova abbiamo scelto:

  • Semantic alias: hold_measurement
  • Namespace: module/acq.rigol_csv
  • Dataset ID: vgen_3v_hold_led

Si tratta dell’acquisizione del nodo HOLD effettuata con Vgen nominale di 3 V.

L’alias hold_measurement rappresenta il nome con cui l’input viene identificato nel contesto dell’esecuzione. Il Dataset ID, invece, identifica la sorgente scientifica effettivamente selezionata.

Questi due concetti non sono equivalenti.

Lo stesso alias potrebbe essere utilizzato in un’altra esecuzione per associare un Dataset differente, purché compatibile con il tipo di elaborazione previsto dallo script.

Il framework registra inoltre il riferimento alla revisione della sorgente associata al Job, in modo che l’esecuzione rimanga collegata alla selezione effettivamente effettuata.

La nuova funzione pubblica: gxtxt_context()

Per recuperare l’identità del Dataset selezionato introduciamo la funzione:

gxtxt_context()

Questa funzione appartiene alla Octave Script Bridge API v1 e restituisce una struttura contenente informazioni sull’esecuzione corrente.

Fra le proprietà disponibili troviamo inputs, che contiene le associazioni fra alias e Dataset selezionati.

La chiamata fondamentale è:

context = gxtxt_context();

Il risultato è una struttura GNU Octave.

Possiamo quindi esaminarne gli input:

aliases = fieldnames(context.inputs);

La funzione Octave fieldnames() restituisce i nomi dei campi presenti in una struttura.

Nel nostro caso questi nomi corrispondono agli alias degli input resi disponibili dal framework.

Se conosciamo l’alias utilizzato, possiamo accedere direttamente al relativo campo:

binding = context.inputs.hold_measurement;

dataset_id = binding.dataset_id;

Questo esempio illustra il meccanismo fondamentale: il Dataset ID non viene scritto nel sorgente, ma recuperato dal contesto dell’esecuzione.

Individuare l’input senza conoscere l’alias

Lo script completo utilizza una soluzione più generale.

Non richiede che l’alias si chiami necessariamente hold_measurement.

Esamina invece gli input disponibili, cerca quelli che contengono un Dataset ID valido e verifica che sia stato selezionato un solo Dataset.

Una parte significativa del codice è:

context = gxtxt_context();

aliases = fieldnames(context.inputs);

chosen_alias = '';
selected_dataset_id = '';

for k = 1:numel(aliases)

  alias = aliases{k};
  binding = context.inputs.(alias);

  if (!isstruct(binding) || ...
      !isfield(binding, 'dataset_id'))
    continue;
  endif

  current_id = binding.dataset_id;

  if (!ischar(current_id) || ...
      isempty(strtrim(current_id)))
    continue;
  endif

  if (!isempty(selected_dataset_id))
    error('Select exactly one nonempty input Dataset');
  endif

  chosen_alias = alias;
  selected_dataset_id = strtrim(current_id);

endfor

Questo frammento introduce alcuni aspetti interessanti di GNU Octave.

L’accesso dinamico ai campi con context.inputs.(alias) permette di selezionare una proprietà della struttura utilizzando un nome contenuto in una variabile.

La funzione isfield() verifica l’esistenza di un campo, mentre isstruct() controlla che l’oggetto sia effettivamente una struttura.

La funzione strtrim() elimina gli spazi iniziali e finali da una stringa.

Infine, l’istruzione continue permette di passare direttamente all’iterazione successiva del ciclo quando una condizione rende inutilizzabile l’elemento corrente.

Questi controlli servono anche a ignorare eventuali input opzionali privi di un Dataset ID.

Se non viene individuata alcuna sorgente valida, oppure se ne vengono selezionate più di una, lo script interrompe l’esecuzione con un messaggio diagnostico.

L’Input Binding non sostituisce le API di lettura

Un punto fondamentale è che gxtxt_context() non restituisce direttamente i campioni numerici del Dataset.

La sua funzione è comunicare allo script quali sorgenti sono state associate al Job.

Per leggere i dati continuiamo a utilizzare le API già studiate.

Dopo aver ottenuto selected_dataset_id, lo script verifica la presenza del Dataset nel catalogo della Session e utilizza la Variable Schema API v3 per individuare le variabili corrette.

Nella nostra esercitazione sono richieste:

  • Una variabile temporale con ruolo scientifico time e unità s.
  • Una variabile di segnale denominata CH2, con ruolo signal e unità V.

Questo controllo impedisce, per esempio, di trattare accidentalmente un Dataset immagine o il canale CH3 come se fosse la misura del nodo HOLD.

Una volta identificati i Variable ID, utilizziamo nuovamente read_range:

blocks = gxtxt_api_call(dataset_api, 'read_range', ...
    {selected_dataset_id, {time_id, hold_id}, ...
     source_start, sample_count});

Il meccanismo di lettura non cambia rispetto alle esercitazioni precedenti.

Cambia invece il modo in cui viene determinato il Dataset ID passato all’API.

È questo il vero elemento nuovo dell’esperimento.

Elaborazione dei campioni selezionati

Lo script è stato preparato per le quattro acquisizioni HOLD già presenti nella Session, che condividono le medesime condizioni di campionamento.

Per tutte le misure utilizziamo:

source_start = 105400;
sample_count = 1000;

La richiesta recupera 1.000 campioni a partire dal riferimento temporale di 0 secondi, con un intervallo fra campioni di 20 µs.

Dopo aver verificato la presenza di valori numerici validi e la corretta progressione della coordinata temporale, selezioniamo la finestra dell’evento:

event_mask = (time_s >= 0) & (time_s < 0.020);

event_voltage = hold_voltage_v(event_mask);
event_time = time_s(event_mask);

[peak_voltage, local_peak_index] = max(event_voltage);

peak_time_s = event_time(local_peak_index);

Calcoliamo quindi il valore massimo, il tempo corrispondente e la media della tensione HOLD nella finestra selezionata.

Lo script rimane volutamente semplice: non produce nuovi Dataset e non modifica le informazioni scientifiche delle acquisizioni originali.

Occorre inoltre ricordare che questa versione è progettata per la specifica famiglia di acquisizioni HOLD del corso. Il Dataset ID è configurabile attraverso l’interfaccia, ma la finestra temporale e le caratteristiche attese del segnale sono definite nel codice.

Non si tratta quindi di un programma universale capace di elaborare qualsiasi Dataset GX-TXT senza adattamenti.

Risultato dell’esecuzione

Abbiamo eseguito lo script nella Session della Lezione 06 il 28 ottobre 2026.

Il Job ha utilizzato effettivamente il Dataset:

vgen_3v_hold_led

associato all’alias:

hold_measurement

L’esecuzione è terminata con stato COMPLETED, codice di uscita 0 e durata di 5,1 secondi.

I risultati numerici ottenuti sono:

ParametroRisultato
Dataset selezionatovgen_3v_hold_led
Campioni letti1.000
Campioni nella finestra dell’evento1.000
Tensione HOLD massima4,620 V
Tempo del massimo2,600 ms
Tensione HOLD media4,321 V

Il valore massimo di 4,620 V coincide con quello già ricavato nella seconda esercitazione per la misura con Vgen nominale di 3 V.

Job GX-TXT completato con lettura del Dataset vgen_3v_hold_led selezionato tramite Input Binding e calcolo del massimo HOLD.
Esecuzione completata dello script 06.04: il Dataset HOLD selezionato nel Job Launcher restituisce un massimo di 4,620 V a 2,600 ms.

Il log termina con:

PASS: A Job Launcher-selected Dataset was read by public API.

Questo risultato conferma che lo script ha ottenuto l’identità del Dataset attraverso il contesto pubblico del Job e ha letto correttamente i campioni sperimentali.

Non sono stati utilizzati Typed Parameters, né letture dirette dei CSV originali.

Perché questo meccanismo è importante

L’Input Dataset Binding rende più flessibile la progettazione degli script scientifici.

Lo stesso programma può essere associato a misure differenti senza modificare il suo sorgente, a condizione che i Dataset selezionati rispettino il contratto scientifico previsto dall’elaborazione.

La selezione viene effettuata nel framework, mentre il codice continua a operare attraverso identificatori logici e API pubbliche.

Questo approccio permette di mantenere distinti tre elementi:

  • Il programma Octave, che descrive come elaborare i dati.
  • Il Dataset scientifico, che rappresenta la sorgente sperimentale.
  • Il Job GX-TXT, che registra quale programma è stato eseguito e con quali input.

È una distinzione particolarmente importante per costruire procedure di laboratorio riproducibili.

Con questa quarta esercitazione abbiamo completato il percorso pratico della Lezione 06: dall’identificazione delle variabili alla lettura dei campioni, dal confronto fra acquisizioni alla generazione di un grafico, fino alla selezione dinamica di un Dataset tramite il Job Launcher.

Prima di concludere la lezione, approfondiremo sistematicamente le funzioni GNU Octave e le API pubbliche GX-TXT introdotte o utilizzate, distinguendo quelle già affrontate nelle lezioni precedenti dalle novità di questa esercitazione.

Approfondimento — Le funzioni GNU Octave e le API GX-TXT della Lezione 06

Abbiamo completato quattro esercitazioni, utilizzando acquisizioni reali del ProvaTelecomando IR per leggere, selezionare, confrontare e rappresentare dati sperimentali.

È il momento di esaminare più approfonditamente gli strumenti di programmazione che abbiamo utilizzato.

Questa sezione ha un obiettivo preciso: consolidare le conoscenze GNU Octave e comprendere come utilizzarle correttamente all’interno dell’architettura GX-TXT.

Non ci limiteremo a elencare le funzioni incontrate. Cercheremo di capire perché sono necessarie, quali dati accettano, che cosa restituiscono, quali problemi permettono di risolvere e quali errori è opportuno evitare.

Per costruire una progressione didattica coerente, distingueremo inoltre le novità effettive della Lezione 06 dagli argomenti già affrontati nelle lezioni precedenti.

1. Il collegamento con le Lezioni 01–05

Prima di approfondire il codice, riepiloghiamo il percorso compiuto.

LezioneArgomenti principaliConoscenze riutilizzate nella Lezione 06
01 — Primo script OctaveAmbiente di esecuzione, Script Bridge, primi calcoli, API numericheEsecuzione degli script, gxtxt_api_call(), gxtxt_api_require(), gxtxt_context()
02 — Variabili, tipi e operatoriVariabili, operatori, tipi, vettori, strutture e gestione degli erroriEspressioni numeriche, controlli logici, strutture e cell array
03 — Vettori e matriciIndicizzazione, dimensioni, operazioni matriciali, riduzioni e pubblicazionediff(), mean(), max(), find(), all(), isfinite() e accesso ai vettori
04 — Condizioni, cicli e funzioniif, for, while, funzioni riutilizzabili, simulazione numerica e graficiCicli annidati, continue, break, funzioni, pubblicazione e Plot Dataset v4
05 — Dataset e Variable IDIdentità scientifiche, metadata e schema delle variabiliDataset ID, Variable ID, list, describe, get e validazione degli identificatori

La Lezione 06 non riparte da zero. Utilizza queste conoscenze per affrontare un problema nuovo: l’accesso ai valori numerici di misure sperimentali già registrate in GX-TXT.

Le principali novità sono:

  • L’operazione CANONICAL_DATASET_API_v2/read_range.
  • La lettura limitata di intervalli di campioni.
  • La gestione dei valori mancanti restituiti dal framework.
  • Il confronto fra Dataset numerici distinti e la verifica delle coordinate temporali.
  • L’utilizzo del contesto del Job per ottenere un Dataset selezionato dall’utente.
  • L’applicazione del tipo grafico bar nella Plot Dataset API v4.
  • L’utilizzo delle operazioni validate_spec e artifacts nel flusso grafico.
  • Un impiego più approfondito di alcune funzioni Octave, fra cui median(), nnz(), fieldnames() e strtrim().

Le funzioni mean(), max(), diff(), find(), abs() e isfinite() non sono nuove per il corso: erano già state introdotte, soprattutto nella Lezione 03.

Qui assumono però un significato più concreto perché vengono applicate a dati acquisiti realmente.

2. Tre livelli distinti: Octave, Script Bridge e GX-TXT

Per comprendere correttamente il funzionamento degli script, dobbiamo distinguere tre livelli.

Il primo livello è GNU Octave.

Octave è il linguaggio nel quale scriviamo il programma. Gestisce variabili, array, cicli, condizioni, strutture e operazioni numeriche.

Per esempio:

voltage = [2.78; 4.26; 4.62; 4.58];

maximum = max(voltage);

Questa è una normale elaborazione Octave: non richiede alcuna API GX-TXT.

Il secondo livello è lo Script Bridge.

Il Bridge permette al programma Octave di comunicare con le API pubbliche registrate dal framework.

Per esempio:

dataset_ids = gxtxt_api_call( ...
    'CANONICAL_DATASET_API_v2', 'list', {});

Questa istruzione non esegue un calcolo numerico ordinario. Chiede a GX-TXT di restituire gli identificatori dei Dataset disponibili nella Session.

Il terzo livello è costituito dai servizi pubblici GX-TXT.

Le API canoniche gestiscono le identità scientifiche, le letture, la pubblicazione e le rappresentazioni grafiche.

Quando invochiamo read_range, il framework individua il Dataset richiesto e utilizza il proprio provider di memorizzazione per recuperarne i valori.

Quando invochiamo publish_series, il framework gestisce la creazione del nuovo Dataset.

Quando invochiamo render della Plot Dataset API v4, il framework genera la rappresentazione grafica scientifica.

Il principio da ricordare è:

Octave esprime l’algoritmo; lo Script Bridge permette di invocare i servizi pubblici; GX-TXT gestisce le risorse scientifiche.

Il nostro corso usa esclusivamente le API pubbliche per interagire con il framework. Le normali funzioni native Octave rimangono invece disponibili per esprimere la logica del programma.

3. Dataset ID, Variable ID e identità scientifica

Abbiamo introdotto questi concetti nella Lezione 05, ma ora possiamo comprenderne meglio l’importanza.

Consideriamo due Dataset:

vgen_2v_hold_led
vgen_3v_hold_led

Entrambi contengono una variabile con identificatore:

v0002

Nel primo Dataset, v0002 identifica la tensione CH2 dell’acquisizione con Vgen nominale di 2 V.

Nel secondo identifica la tensione CH2 dell’acquisizione con Vgen nominale di 3 V.

Sono variabili differenti, anche se possiedono lo stesso Variable ID locale.

L’identità scientifica deve quindi essere considerata come una coppia:

(dataset_id, variable_id)

Per esempio:

(vgen_2v_hold_led, v0002)
(vgen_3v_hold_led, v0002)

Questo meccanismo impedisce di confondere la posizione di una colonna con l’identità della misura.

Un Dataset ID non è il percorso del file CSV che contiene i campioni. Allo stesso modo, un Variable ID non deve necessariamente coincidere con il nome della colonna originale.

La distinzione permette al framework di conservare un riferimento logico stabile indipendentemente da dettagli della memorizzazione fisica.

4. Il contratto fondamentale: CANONICAL_DATASET_API_v2/read_range

Questa è la principale nuova operazione pubblica introdotta nella Lezione 06.

Nella Lezione 05 utilizzavamo list e describe per ottenere identificatori e metadata.

Con read_range possiamo invece recuperare valori numerici.

La sintassi della chiamata attraverso lo Script Bridge è:

result = gxtxt_api_call( ...
    'CANONICAL_DATASET_API_v2', 'read_range', ...
    {dataset_id, variable_ids, start, count});

Esaminiamo i quattro argomenti.

dataset_id — Identificatore del Dataset

È una stringa contenente l’identità della sorgente scientifica.

Deve riferirsi a un Dataset accessibile nella Session corrente.

variable_ids — Variabili da leggere

È un cell array contenente i Variable ID richiesti.

Per esempio:

variable_ids = {'v0001', 'v0002'};

La lettura recupererà i valori delle due variabili indicate, appartenenti allo stesso Dataset.

start — Offset iniziale

Indica la posizione del primo campione da leggere.

Il contratto dell’API usa indici basati su zero.

count — Numero di campioni

Indica quanti elementi devono essere recuperati.

Per esempio:

first_row = 100000;
row_count = 3000;

La richiesta comprende le posizioni sorgente dalla 100000 alla 102999.

L’intervallo è espresso mediante una posizione iniziale e un numero di elementi, non mediante due estremi temporali.

La funzione non interpreta automaticamente un valore di start come secondi, millisecondi o altre unità fisiche.

È il programma a dover stabilire quali posizioni numeriche corrispondono all’intervallo temporale desiderato.

Perché leggere soltanto una parte dei dati?

La prima acquisizione contiene 1.200.000 campioni.

Richiederli tutti sarebbe possibile in determinate condizioni, ma spesso non è necessario.

Se vogliamo studiare un singolo gruppo di impulsi, possiamo limitarci a poche migliaia di valori.

Questo offre alcuni vantaggi:

  • Riduce la quantità di dati trasferita attraverso il Bridge.
  • Limita la memoria necessaria ai vettori Octave.
  • Permette di concentrare l’elaborazione sull’intervallo significativo.
  • Rende più esplicita la selezione scientifica effettuata.

È però importante non confondere una lettura limitata con un accesso fisico casuale necessariamente efficiente.

Le caratteristiche prestazionali dipendono dal provider utilizzato. La documentazione della Canonical Dataset API v2 specifica, per esempio, che il provider CSV supporta letture per intervallo ma non dichiara accesso casuale efficiente, mentre il provider HDF5 dispone di capacità differenti.

L’interfaccia pubblica rimane comune; il funzionamento interno del provider può cambiare.

5. Che cosa restituisce read_range?

Il risultato è una struttura Octave i cui campi sono associati ai Variable ID richiesti.

Nel nostro caso:

blocks = gxtxt_api_call( ...
    dataset_api, 'read_range', ...
    {dataset_id, {time_id, voltage_id}, ...
     first_row, row_count});

Possiamo recuperare il blocco temporale e quello della tensione:

time_block = blocks.(time_id);
voltage_block = blocks.(voltage_id);

Questa sintassi merita attenzione.

Quando scriviamo:

data.temperature

accediamo direttamente a un campo chiamato temperature.

Quando invece scriviamo:

data.(field_name)

il nome del campo viene ricavato dal contenuto della variabile field_name.

Per esempio:

field_name = 'temperature';

data = struct();
data.temperature = 25;

value = data.(field_name);

value contiene 25.

L’accesso dinamico è particolarmente utile con GX-TXT perché i Variable ID vengono recuperati dal framework durante l’esecuzione.

Non dobbiamo costruire nomi arbitrari o supporre che la variabile richiesta abbia sempre lo stesso identificatore.

La risposta di read_range comprende i valori e le informazioni previste dal contratto del blocco, come tipo numerico e metadata dell’intervallo.

Per accedere ai campioni utilizziamo:

time_s = time_block.values(:);
voltage_v = voltage_block.values(:);

La proprietà values contiene i valori della variabile.

L’espressione (:) dispone gli elementi in un vettore colonna.

Questa operazione è appropriata per le serie monodimensionali utilizzate nella lezione. Non dobbiamo però applicarla indiscriminatamente a dati multidimensionali, perché linearizzerebbe l’array perdendone la struttura geometrica originale.

La Canonical Dataset API v2 distingue infatti le letture di intervalli monodimensionali da quelle multidimensionali, per le quali sono previste operazioni specifiche come le letture hyperslab, quando supportate dal provider.

6. Una distinzione fondamentale: gli indici partono da zero oppure da uno?

Le API e il linguaggio non utilizzano sempre la stessa convenzione.

La nostra chiamata:

first_row = 100000;
row_count = 3000;

richiede 3.000 elementi a partire dalla posizione sorgente 100000.

Una volta ricevuti, il primo elemento del vettore Octave è:

voltage_v(1)

L’ultimo è:

voltage_v(3000)

Questo non significa che il campione sorgente 100000 sia diventato il primo campione dell’acquisizione originale.

Significa soltanto che è il primo elemento del blocco restituito.

La corrispondenza è:

Indice Octave del bloccoIndice sorgente GX-TXT
1100000
2100001
3100002
3000102999

In generale, per il blocco restituito:

source_index = start + octave_index - 1

Questa relazione è essenziale quando vogliamo associare una posizione trovata in Octave alla posizione originale nell’acquisizione.

Attenzione: l’operatore di indicizzazione Octave e l’offset delle API pubbliche appartengono a due sistemi differenti. Non dobbiamo trasferire direttamente gli indici da uno all’altro senza considerare la convenzione utilizzata.

7. Selezione logica: lavorare sui dati senza cicli espliciti

Abbiamo già imparato a utilizzare i vettori e gli operatori logici nelle lezioni precedenti.

Nella Lezione 06 li applichiamo a un problema sperimentale concreto.

Supponiamo di avere:

time_s = [0; 0.1; 0.2; 0.3; 0.4];
voltage_v = [2.0; 2.5; 3.2; 2.8; 2.1];

Vogliamo selezionare i campioni compresi fra 0,1 e 0,3 secondi, includendo il primo estremo ed escludendo il secondo.

Scriviamo:

mask = (time_s >= 0.1) & (time_s < 0.3);

Il risultato è:

false
true
true
false
false

La maschera è un array logico: contiene un elemento per ogni campione temporale.

Applicandola ai vettori:

selected_time = time_s(mask);
selected_voltage = voltage_v(mask);

otteniamo:

selected_time:

0.1
0.2

selected_voltage:

2.5
3.2

Abbiamo selezionato contemporaneamente tempo e tensione, mantenendo la corrispondenza fra i due vettori.

Non abbiamo dovuto scrivere un ciclo for per controllare ogni campione.

Questo stile di programmazione viene spesso chiamato indicizzazione logica ed è una delle caratteristiche più importanti dell’elaborazione numerica in Octave e MATLAB.

L’operatore & e la differenza rispetto a &&

L’operatore & esegue un’operazione logica elemento per elemento.

È quindi appropriato quando lavoriamo con vettori di condizioni.

Gli operatori && e ||, invece, sono destinati a condizioni logiche scalari con valutazione a corto circuito.

Per selezionare elementi di un vettore utilizziamo quindi espressioni come:

mask = (voltage_v >= 2.0) & (voltage_v <= 3.0);

È opportuno mantenere le parentesi intorno alle singole condizioni, per rendere evidente l’ordine delle operazioni e prevenire interpretazioni errate.

8. find() e nnz(): individuare oppure contare i campioni

La funzione find() era già stata introdotta nella Lezione 03.

Restituisce gli indici degli elementi non nulli o logicamente veri.

Per esempio:

voltage = [2.1; 3.4; 4.2; 2.8];

mask = voltage > 3.0;

positions = find(mask);

Il risultato è:

2
3

Possiamo utilizzare questi indici per recuperare gli elementi corrispondenti.

Nella Lezione 06 impieghiamo anche nnz(), una funzione particolarmente comoda quando ci interessa soltanto conoscere il numero di elementi che soddisfano la condizione.

La sintassi è:

count = nnz(mask);

nnz significa number of nonzeros.

Nel nostro esempio il risultato è 2.

Per un vettore logico possiamo ottenere lo stesso conteggio mediante:

sum(mask)

oppure:

numel(find(mask))

Tuttavia, nnz(mask) esprime direttamente l’intenzione di contare gli elementi veri, senza dover prima costruire esplicitamente un vettore di posizioni.

Nel primo esperimento della Lezione 06 abbiamo utilizzato proprio questa funzione per contare i campioni inferiori alla soglia di 7,3 V.

La funzione nnz() non determina la durata fisica dell’impulso: conta campioni. Per trasformare il conteggio in una durata dobbiamo conoscere anche il comportamento temporale del campionamento.

9. max(): non soltanto il valore massimo

La funzione max() era già stata studiata nella Lezione 03.

La novità dell’applicazione attuale è il suo utilizzo con due valori di uscita.

In Octave possiamo scrivere:

[peak_value, peak_index] = max(voltage);

La funzione restituisce:

  • peak_value: il valore massimo del vettore.
  • peak_index: la posizione alla quale il massimo è stato individuato.

Consideriamo:

voltage = [2.1; 3.7; 4.2; 3.8];

L’istruzione:

[peak_value, peak_index] = max(voltage);

produce:

peak_value = 4.2
peak_index = 3

Se possediamo un vettore temporale corrispondente:

time_s = [0; 0.001; 0.002; 0.003];

possiamo recuperare il tempo del massimo:

peak_time_s = time_s(peak_index);

ottenendo:

0.002

Questo procedimento è stato utilizzato nelle acquisizioni HOLD.

Occorre però ricordare che peak_index identifica una posizione nel vettore effettivamente elaborato.

Se abbiamo prima applicato una maschera temporale, l’indice si riferisce al vettore selezionato e non necessariamente all’intera acquisizione originale.

10. mean(): il significato della media dipende dalla selezione

Abbiamo già incontrato mean() nella Lezione 03, dove l’abbiamo utilizzata per calcolare medie lungo le dimensioni di matrici sperimentali.

Qui la utilizziamo per ottenere un valore medio su un intervallo temporale.

La forma elementare è:

average = mean(values);

Consideriamo:

voltage = [2.0; 2.4; 2.8; 3.2];

La media aritmetica è:

2.6 V

Nella Lezione 06.04 il calcolo è applicato soltanto ai campioni selezionati nella finestra 0–20 ms.

Il risultato ottenuto per il nodo HOLD, nell’acquisizione con Vgen nominale di 3 V, è:

4.321 V

Questa media non rappresenta la tensione media dell’intera acquisizione di 6 secondi.

Rappresenta esclusivamente la media dei campioni appartenenti alla finestra selezionata.

È importante specificarlo perché una funzione matematica può essere corretta dal punto di vista numerico ma descrivere una grandezza scientifica differente da quella che intendiamo misurare.

Per campioni uniformemente distanziati nel tempo, la media aritmetica dei campioni può rappresentare una stima appropriata della media temporale sull’intervallo.

In presenza di campionamento non uniforme, invece, una media temporale potrebbe richiedere una ponderazione basata sulla durata degli intervalli.

11. diff() e median(): controllare la coordinata temporale

La funzione diff() era stata introdotta nella Lezione 03.

Calcola la differenza fra elementi consecutivi.

Per esempio:

time_s = [0; 0.001; 0.002; 0.003];

intervals = diff(time_s);

Il risultato è:

0.001
0.001
0.001

La funzione restituisce un vettore con un elemento in meno rispetto a quello iniziale.

Nella Lezione 06 abbiamo utilizzato diff() per verificare che i tempi fossero crescenti e per stimare l’intervallo di campionamento.

Introduciamo inoltre median().

La sintassi è:

value = median(x);

La funzione restituisce la mediana degli elementi di un vettore numerico.

Consideriamo:

x = [1, 2, 3, 4, 100];

La media è:

22

La mediana è:

3

La mediana è meno sensibile ai valori estremi rispetto alla media aritmetica.

Nelle nostre acquisizioni utilizziamo:

dt = median(diff(time_s));

L’espressione esegue due operazioni successive.

Prima diff() calcola gli intervalli fra tempi consecutivi.

Successivamente median() restituisce il valore centrale della loro distribuzione.

Per il primo segnale infrarosso abbiamo ottenuto:

100 ns

Per le acquisizioni HOLD/LED:

20 µs

Questi valori sono coerenti con le rispettive frequenze di campionamento.

Un limite importante della mediana

Il fatto che la mediana degli intervalli sia corretta non garantisce che tutti gli intervalli siano identici.

Per esempio:

dt = [0.001; 0.001; 0.001; 0.050; 0.001];

La mediana è ancora 0,001 secondi, anche se uno degli intervalli è molto più lungo.

Per una verifica più rigorosa possiamo affiancare alla mediana il controllo dello scostamento massimo:

time_steps = diff(time_s);

typical_step = median(time_steps);

maximum_deviation = max(abs(time_steps - typical_step));

Questo esempio costituisce un possibile approfondimento: non sostituisce il codice collaudato, ma mostra come svilupparne ulteriormente i controlli.

12. all(), any() e isfinite(): verificare la qualità dei dati

Abbiamo già incontrato all() e isfinite() nella Lezione 03.

Nella Lezione 06 queste funzioni sono particolarmente importanti perché operiamo su acquisizioni sperimentali.

La funzione:

isfinite(values)

restituisce un array logico che indica quali elementi sono numeri finiti.

Per esempio:

values = [1.2; NaN; 3.5; Inf];

Il risultato di:

isfinite(values)

è equivalente a:

true
false
true
false

NaN rappresenta un valore numerico indefinito (Not a Number), mentre Inf rappresenta infinito.

Con:

all(isfinite(values))

verifichiamo che tutti gli elementi siano finiti.

Con:

any(~isfinite(values))

verifichiamo invece se esiste almeno un elemento non finito.

La funzione all() restituisce vero soltanto se tutti gli elementi della condizione risultano veri.

La funzione any() restituisce vero se almeno un elemento soddisfa la condizione.

Per un vettore di controllo, questa distinzione è immediata.

Per array multidimensionali, occorre prestare attenzione alla dimensione lungo la quale viene effettuata la riduzione logica.

Nel nostro esercizio operiamo su vettori colonna.

13. I campioni mancanti e il campo missing_mask

Questa è una delle novità più importanti dal punto di vista del contratto GX-TXT.

La Canonical Dataset API v2 può restituire, per le variabili numeriche CSV a virgola mobile, un campo aggiuntivo:

missing_mask

Il campo identifica le celle numeriche originariamente vuote nei dati sorgente.

Questo è importante perché una cella vuota non è necessariamente equivalente a un valore numerico NaN scritto esplicitamente nel file.

Secondo il contratto documentato dell’API, le celle vuote nelle variabili float32 e float64 vengono rappresentate nei valori numerici mediante NaN, accompagnato da una maschera che conserva l’informazione sull’assenza originaria.

Un NaN esplicitamente presente nel sorgente resta invece distinto mediante tale maschera.

Lo script può verificare la presenza del campo:

if (isfield(voltage_block, 'missing_mask'))

  missing = logical(voltage_block.missing_mask(:));

  if (any(missing))
    error('Missing experimental samples detected');
  endif

endif

La funzione logical() converte i valori nel tipo logico Octave.

In questo caso viene utilizzata per assicurare che la maschera sia trattata come una sequenza di condizioni vere o false.

Se la maschera non è presente, non dobbiamo inventare valori mancanti.

Per gli script della Lezione 06, inoltre, abbiamo scelto di interrompere l’elaborazione quando i campioni richiesti non sono tutti finiti.

Questa è una decisione scientifica del programma, non un comportamento obbligatorio di tutti gli utilizzatori dell’API.

Un’altra analisi potrebbe, per esempio, prevedere una politica esplicita di esclusione o trattamento dei valori mancanti.

Tale scelta dovrebbe però essere documentata e giustificata.

14. strcmp(), isfield() e isstruct(): usare correttamente le strutture restituite dalle API

Queste funzioni non sono tutte nuove per il corso: alcune erano già presenti negli script precedenti, in particolare nell’Inspector della Lezione 05.

Ora ne comprendiamo meglio il ruolo.

strcmp() — Confrontare stringhe

La sintassi è:

result = strcmp(text1, text2);

La funzione restituisce vero quando le due stringhe sono uguali.

Il confronto distingue le lettere maiuscole dalle minuscole.

Per esempio:

strcmp('CH2', 'CH2')

restituisce vero, mentre:

strcmp('CH2', 'ch2')

restituisce falso.

Nella nostra lezione questa funzione viene utilizzata per riconoscere nomi canonici, ruoli scientifici e unità.

Per esempio:

if (strcmp(details.semantic_role, 'time') && ...
    strcmp(details.unit, 's'))

  time_id = variable.variable_id;

endif

Questa condizione identifica una variabile temporale espressa in secondi.

Non dobbiamo confondere strcmp() con il confronto == fra array numerici.

Per confrontare stringhe di identificatori, strcmp() rende esplicita l’intenzione del programma.

isfield() — Verificare la presenza di un campo

La sintassi è:

present = isfield(data, 'field_name');

Un campo opzionale potrebbe non comparire nella risposta di una determinata API.

Prima di utilizzarlo possiamo quindi verificare che esista.

Per esempio:

if (isfield(details, 'unit'))
  unit = details.unit;
endif

È un controllo particolarmente utile quando il contratto prevede metadata opzionali.

isstruct() — Verificare il tipo della risposta

La funzione:

isstruct(value)

restituisce vero quando il valore è una struttura Octave.

Nel nostro caso viene utilizzata per verificare che le risposte ricevute abbiano il tipo previsto dal contratto pubblico.

Questa precauzione evita di accedere a proprietà inesistenti quando la struttura della risposta non corrisponde alle aspettative del programma.

15. fieldnames() e strtrim(): due strumenti utili per gli Input Binding

Nella quarta esercitazione abbiamo approfondito un nuovo utilizzo delle strutture, introducendo in particolare fieldnames() e strtrim().

fieldnames() — Ottenere i nomi dei campi

La sintassi è:

names = fieldnames(s);

La funzione restituisce un cell array contenente i nomi dei campi della struttura s.

Consideriamo:

measurement = struct();

measurement.time = 0.25;
measurement.voltage = 4.62;

names = fieldnames(measurement);

I nomi restituiti sono:

time
voltage

Nella Lezione 06.04 la funzione viene applicata alla struttura degli input del Job:

context = gxtxt_context();

aliases = fieldnames(context.inputs);

In questo modo il programma può scoprire quali alias sono disponibili senza doverne conoscere anticipatamente i nomi.

Successivamente può accedere ai singoli input attraverso la sintassi dinamica:

alias = aliases{k};

binding = context.inputs.(alias);

È lo stesso principio utilizzato per accedere ai campi della risposta di read_range, ma applicato alla configurazione dell’esecuzione.

strtrim() — Eliminare gli spazi esterni

La sintassi è:

result = strtrim(text);

Per esempio:

name = '  measurement_A  ';

clean_name = strtrim(name);

Il risultato è:

measurement_A

Nel nostro script utilizziamo strtrim() per escludere identificatori vuoti o costituiti soltanto da spazi.

La funzione non verifica, da sola, che un Dataset ID esista né che il relativo Dataset sia accessibile.

Dopo aver ottenuto la stringa, lo script deve ancora verificare l’identificatore mediante le API pubbliche.

16. gxtxt_context(): una funzione già nota, utilizzata in un modo nuovo

La funzione gxtxt_context() era stata introdotta nelle lezioni precedenti come strumento per consultare informazioni sull’esecuzione corrente.

Nella Lezione 06.04 ne utilizziamo per la prima volta in modo operativo il campo inputs per selezionare una sorgente scientifica.

La chiamata rimane:

context = gxtxt_context();

Il risultato contiene informazioni relative all’esecuzione supervisionata da GX-TXT, tra cui gli identificatori del Job, i riferimenti al programma e gli eventuali input collegati.

La struttura inputs è organizzata per alias semantico.

Nel nostro collaudo era presente:

Alias: hold_measurement
Dataset ID: vgen_3v_hold_led

Il programma ha recuperato l’identificatore mediante:

aliases = fieldnames(context.inputs);

e successivamente attraverso il campo dataset_id del binding selezionato.

È essenziale distinguere tre elementi:

Alias: nome logico dell’input all’interno dell’esecuzione.

Dataset ID: identificatore scientifico del Dataset selezionato.

Variable ID: identificatore della variabile da leggere all’interno di quel Dataset.

Sono tre livelli di identità differenti.

Il contesto non sostituisce le API numeriche: fornisce allo script l’identità della sorgente, mentre i campioni vengono letti tramite CANONICAL_DATASET_API_v2/read_range.

Perché l’Input Binding è diverso da un Typed Parameter?

Un Typed Parameter è un valore dichiarato nello schema dei parametri dello script.

Può rappresentare una soglia, un intervallo numerico, un’opzione oppure un identificatore testuale, secondo il contratto previsto dal programma.

Un Input Dataset Binding, invece, associa uno specifico Dataset GX-TXT al Job attraverso il sistema di selezione delle sorgenti scientifiche.

Il framework registra il binding e ne verifica le condizioni di accesso previste dal sistema di esecuzione.

Nel Job verificato della Lezione 06.04, il Dataset selezionato è stato congelato insieme al riferimento alla sua revisione.

Questo permette di collegare il risultato alla sorgente effettivamente utilizzata.

La differenza non è soltanto una comodità dell’interfaccia: riguarda anche la tracciabilità scientifica dell’esecuzione.

17. DATASET_PUBLICATION_API_v2: un servizio già studiato, utilizzato per risultati derivati

La pubblicazione dei Dataset non costituisce una novità assoluta della Lezione 06.

Nella Lezione 03 avevamo già utilizzato DATASET_PUBLICATION_API_v2 per pubblicare serie scientifiche, anche attraverso l’operazione publish_series_batch.

Nella Lezione 04 avevamo utilizzato la pubblicazione per conservare le soluzioni numerica e analitica del modello di raffreddamento.

Nella Lezione 06.03 abbiamo ripreso la stessa API in un contesto diverso: la pubblicazione di un risultato ottenuto elaborando acquisizioni sperimentali reali.

La chiamata utilizzata è:

publication = gxtxt_api_call( ...
    'DATASET_PUBLICATION_API_v2', ...
    'publish_series', {request});

La struttura request contiene la descrizione scientifica del nuovo Dataset.

Nel nostro esempio:

request.dataset_id = 'lesson06_hold_peak_summary';

request.contract = 'table.generic';

request.storage = 'csv';

request.x_values = vgen_nominal;
request.y_values = hold_peaks;

La richiesta completa contiene anche i descrittori delle due variabili, le unità, i tipi numerici e i riferimenti ai Dataset sorgente.

L’API gestisce la pubblicazione e la registrazione del risultato secondo il proprio contratto.

Non dobbiamo confondere il nuovo Dataset con il vettore Octave temporaneamente conservato in memoria.

Il vettore esiste durante l’esecuzione dello script.

Il Dataset pubblicato è invece una risorsa scientifica registrata nella Session, con identità e metadata.

Che cosa significa pubblicare un risultato derivato?

Nel nostro caso, il Dataset:

lesson06_hold_peak_summary

contiene quattro valori di Vgen nominale e quattro massimi HOLD.

Questi valori sono derivati dalle acquisizioni originali, non rappresentano nuove letture dirette dell’oscilloscopio.

La provenienza scientifica deve quindi permettere di distinguere i Dataset sorgente dal risultato dell’elaborazione.

Lo script della Lezione 06.03 contiene inoltre una verifica contro la sostituzione involontaria di risultati già pubblicati.

Se il Dataset riassuntivo esiste, il programma ne confronta il contenuto con i nuovi valori calcolati.

Se i risultati non coincidono, il programma interrompe l’operazione.

Questo controllo non costituisce una garanzia generale di idempotenza per qualsiasi pubblicazione GX-TXT: è una protezione specificamente implementata nel nostro script.

18. PLOT_DATASET_API_v4: dalle curve temporali al grafico a barre

Anche Plot Dataset API v4 era già stata introdotta nella Lezione 04, dove avevamo confrontato due curve di raffreddamento.

Nella Lezione 06 riprendiamo il medesimo sistema, ma cambiamo la rappresentazione.

Il grafico non contiene una curva temporale continua, bensì quattro barre verticali corrispondenti a condizioni sperimentali distinte.

La procedura rimane dichiarativa: definiamo un PlotSpec, associamo le variabili scientifiche, verifichiamo la specifica e chiediamo al framework di generare il grafico.

new_spec — Preparare la specifica

spec = gxtxt_api_call( ...
    'PLOT_DATASET_API_v4', 'new_spec', ...
    {'plot.dataset.xy', ...
     'ProvaTelecomando IR - maximum HOLD voltage'});

La chiamata produce una specifica iniziale per il grafico cartesiano richiesto.

kind = bar — Definire una rappresentazione a barre

La serie utilizzata contiene:

series = struct( ...
    'id', 'hold_peak_by_vgen', ...
    'dataset_id', summary_dataset_id, ...
    'x_variable_id', summary_x_id, ...
    'y_variable_id', summary_y_id, ...
    'axis', 'left', ...
    'kind', 'bar', ...
    'bar_width', 0.60);

Il campo kind determina il tipo di rappresentazione.

Con bar chiediamo una serie a barre.

bar_width definisce la larghezza delle barre nelle unità dell’asse numerico X.

Questa distinzione è importante: la larghezza non rappresenta automaticamente la risoluzione o l’incertezza sperimentale dello stimolo.

È una proprietà di presentazione grafica.

Le coordinate X e Y continuano a provenire dalle variabili scientifiche del Dataset pubblicato.

validate_spec — Controllare la specifica

Nella Lezione 06 abbiamo utilizzato esplicitamente:

is_valid = gxtxt_api_call( ...
    'PLOT_DATASET_API_v4', ...
    'validate_spec', {spec});

Questa operazione chiede al framework di verificare la specifica secondo il contratto supportato.

Una validazione positiva significa che la specifica è stata accettata per le verifiche previste.

Non significa automaticamente che i dati rappresentati siano scientificamente corretti.

La correttezza delle misure e dell’elaborazione deve essere verificata separatamente.

upsert_plot — Registrare il grafico

plot_ref = gxtxt_api_call( ...
    'PLOT_DATASET_API_v4', 'upsert_plot', ...
    {'lesson06_hold_peak_bar', spec});

La funzione permette di creare o aggiornare un grafico associato alla chiave indicata, nel rispetto delle regole di proprietà previste dal framework.

Nel nostro caso la chiave permette allo script di mantenere un’identità riconoscibile per la propria rappresentazione.

render — Generare la rappresentazione

render_result = gxtxt_api_call( ...
    'PLOT_DATASET_API_v4', 'render', {plot_ref});

Il framework interpreta la specifica e genera il grafico.

artifacts — Consultare gli artefatti prodotti

Infine utilizziamo:

artifacts = gxtxt_api_call( ...
    'PLOT_DATASET_API_v4', 'artifacts', {plot_ref});

Questa operazione permette di ottenere le informazioni pubbliche relative agli artefatti disponibili per il grafico.

Nel nostro collaudo è stato prodotto un PNG di 1600 × 900 pixel.

L’immagine esportata è risultata identica all’artefatto originale della Session, come verificato mediante SHA-256.

La combinazione di Dataset pubblicato, PlotSpec, riferimento persistente e artefatto grafico permette di mantenere separati i dati numerici dalla loro rappresentazione.

Questo è uno dei principi centrali del sistema grafico GX-TXT.

19. GNU Octave e MATLAB: quali parti del codice sono compatibili?

GNU Octave e MATLAB condividono una parte molto ampia del linguaggio.

I concetti studiati in questa lezione sono in gran parte comuni:

  • Vettori e matrici numeriche.
  • Cell array.
  • Strutture con campi nominati.
  • Indicizzazione ordinaria e logica.
  • Cicli e condizioni.
  • Funzioni come mean, max, find, diff, median, fieldnames e isfinite.

Tuttavia, occorre distinguere la compatibilità del linguaggio dalla disponibilità del framework GX-TXT.

Un normale script MATLAB non possiede automaticamente le funzioni gxtxt_api_call() o gxtxt_context().

Queste funzioni appartengono allo Script Bridge predisposto dall’ambiente di esecuzione GX-TXT.

Anche quando il codice numerico è compatibile, le chiamate al framework richiedono un’interfaccia effettivamente disponibile e supportata nell’ambiente utilizzato.

Differenze sintattiche da conoscere

Nei nostri script GNU Octave utilizziamo istruzioni come:

if (condition)
  disp('Condition satisfied');
endif

In MATLAB la terminazione ordinaria è:

if condition
    disp('Condition satisfied');
end

Lo stesso principio riguarda endfor ed endfunction, utilizzati nei nostri script Octave.

GNU Octave permette anche operatori come:

!
!=

per negazione e disuguaglianza.

Per scrivere codice più facilmente trasferibile a MATLAB, preferiamo le forme comuni:

~
~=

Per esempio:

if (~isfinite(value))
  error('Invalid numeric value');
endif

In MATLAB sostituiremmo inoltre endif con end.

Le differenze non riguardano soltanto la sintassi. Possono coinvolgere versioni, toolbox disponibili, tipi numerici, formati di dati e comportamento di specifiche funzioni.

Per questo motivo, la compatibilità deve essere verificata con prove effettive: una somiglianza sintattica non equivale a una garanzia di funzionamento identico.

20. Gestione degli errori e programmazione scientifica difensiva

Negli script scientifici non è sufficiente ottenere un risultato numerico.

Dobbiamo prima verificare che i dati siano compatibili con l’algoritmo.

Nella Lezione 06 abbiamo utilizzato numerosi controlli.

Per esempio:

if (!isnumeric(time_s) || !isnumeric(voltage_v))
  error('Expected numeric Dataset variables');
endif

Questo controllo verifica che le variabili contengano dati numerici.

Un altro esempio:

if (numel(time_s) != numel(voltage_v))
  error('Time and voltage lengths do not match');
endif

Qui verifichiamo che le due serie abbiano lo stesso numero di elementi.

E ancora:

if (!all(diff(time_s) > 0))
  error('Time coordinate is not increasing');
endif

La verifica impedisce di procedere quando il tempo non è strettamente crescente.

Questi controlli non sono formalità.

Un algoritmo può produrre valori apparentemente plausibili anche quando i dati non rappresentano correttamente l’esperimento previsto.

Il rischio è particolarmente elevato quando si confrontano più acquisizioni o si selezionano intervalli temporali.

Le tolleranze numeriche

Un altro concetto importante è la differenza fra confronto esatto e confronto entro tolleranza.

Per numeri in virgola mobile, un confronto esatto può essere inappropriato quando i valori derivano da operazioni numeriche differenti.

Possiamo quindi scrivere:

difference = abs(measured_value - reference_value);

if (difference > tolerance)
  error('Measurement differs from reference');
endif

La tolleranza deve essere scelta in funzione dello scopo del controllo.

Nella nostra lezione abbiamo utilizzato tolleranze numeriche per verificare i risultati attesi e l’allineamento dei tempi.

Non dobbiamo però confondere queste tolleranze con l’incertezza di misura dello strumento.

Una tolleranza software definisce quando due valori vengono considerati compatibili dal programma.

L’incertezza metrologica richiede invece la valutazione delle sorgenti di incertezza e un modello appropriato della misura.

21. Un metodo generale per sviluppare nuovi script GX-TXT

Dopo quattro esercitazioni possiamo proporre una sequenza operativa riutilizzabile per altri esperimenti.

Prima fase — Identificare le sorgenti scientifiche.

Apriamo la Session e individuiamo i Dataset necessari. Possiamo recuperarne gli identificatori dal catalogo pubblico oppure riceverli attraverso il sistema di Input Binding del Job.

Seconda fase — Verificare lo schema delle variabili.

Utilizziamo la Variable Schema API v3 per riconoscere identità, ruoli, tipi, unità e relazioni scientifiche.

Non dobbiamo supporre che una variabile denominata v0002 rappresenti sempre la stessa grandezza.

Terza fase — Verificare le capacità disponibili.

Dichiariamo le API necessarie e controlliamo le operazioni che vogliamo utilizzare.

La disponibilità del Bridge non implica automaticamente la disponibilità di tutte le API possibili.

Quarta fase — Leggere soltanto i dati necessari.

Utilizziamo read_range per recuperare intervalli appropriati all’analisi.

Se il Dataset è molto grande, una selezione limitata può ridurre il trasferimento dei campioni.

Quinta fase — Controllare i risultati della lettura.

Verifichiamo dimensioni, tipi numerici, eventuali campioni mancanti, valori non finiti e coordinate temporali.

Sesta fase — Elaborare i dati in Octave.

Utilizziamo funzioni native, operazioni vettoriali, condizioni e cicli per implementare l’algoritmo scientifico.

Settima fase — Verificare l’elaborazione.

Confrontiamo i risultati con valori noti, controlli indipendenti o modelli di riferimento.

Un Job terminato senza errori non costituisce, da solo, una verifica scientifica sufficiente.

Ottava fase — Pubblicare i risultati quando necessario.

Se il risultato deve diventare una risorsa della Session, utilizziamo le API pubbliche di pubblicazione, specificando identità, unità, schema e provenienza.

Nona fase — Visualizzare i risultati.

Quando serve un grafico persistente, utilizziamo Plot Dataset API v4, mantenendo la rappresentazione collegata alle variabili scientifiche.

Decima fase — Conservare le informazioni per la riproducibilità.

Registriamo versione del framework, commit GitHub di riferimento, identità dello script, revisione, input, parametri, risultati e Job ID.

GX-TXT offre strumenti per gestire questi elementi, ma è responsabilità di chi sviluppa lo script utilizzarli correttamente e descrivere in modo chiaro le condizioni scientifiche dell’esperimento.

22. Verifica delle conoscenze: esercizi proposti

Gli esercizi seguenti non fanno parte dei quattro collaudi originali. Sono proposte per consolidare le conoscenze acquisite.

Esercizio 1 — Modificare la finestra temporale

Prendendo come riferimento lo script 06.01, modificare l’intervallo selezionato in modo da esaminare un diverso gruppo di impulsi.

Verificare come cambiano il numero di campioni e i valori minimo e massimo.

Esercizio 2 — Confrontare media e mediana

Selezionare un intervallo del segnale HOLD e calcolarne sia la media sia la mediana.

Confrontare i risultati e spiegare in quali condizioni potrebbero differire sensibilmente.

Esercizio 3 — Verificare la regolarità del campionamento

Utilizzare diff() per calcolare gli intervalli fra campioni, quindi determinare la mediana e lo scostamento massimo.

Verificare se la griglia temporale è coerente con la frequenza di campionamento dichiarata.

Esercizio 4 — Cambiare il Dataset di ingresso

Utilizzare lo script 06.04 selezionando nel Job Launcher un’altra acquisizione HOLD.

Verificare che il risultato cambi senza modificare il codice Octave.

Esercizio 5 — Gestire un input incompatibile

Provare, in una Session di prova, a selezionare un Dataset che non contiene il segnale CH2 richiesto.

Verificare che lo script interrompa l’elaborazione con un messaggio diagnostico, senza produrre un risultato scientifico ambiguo.

Esercizio 6 — Interpretare il Dataset riassuntivo

Aprire il Dataset lesson06_hold_peak_summary e individuare le due variabili pubblicate, le rispettive unità, i Variable ID e i riferimenti alla provenienza.

Spiegare perché non deve essere considerato un nuovo segnale acquisito dall’oscilloscopio.

Questi esercizi possono essere svolti creando revisioni separate degli script già collaudati, evitando di sostituire gli esempi originali.

Sintesi delle nuove conoscenze

La Lezione 06 ha aggiunto un elemento fondamentale al percorso del corso.

Non ci siamo limitati a identificare i Dataset o a consultare le loro proprietà: abbiamo imparato a utilizzarne direttamente i campioni numerici.

Abbiamo applicato strumenti già studiati, come vettori, strutture, cicli e funzioni di riduzione, a un problema sperimentale reale.

Abbiamo inoltre approfondito il ruolo dello Script Bridge come interfaccia fra l’algoritmo Octave e i servizi scientifici GX-TXT.

La nuova operazione read_range ci ha permesso di accedere a porzioni limitate di grandi acquisizioni.

La gestione dei Variable ID e dei metadata ci ha permesso di selezionare correttamente le grandezze fisiche.

L’Input Dataset Binding ci ha mostrato come separare la scelta della sorgente dal codice di elaborazione.

Infine, pubblicando un Dataset riassuntivo e generando un grafico a barre attraverso Plot Dataset v4, abbiamo completato un flusso scientifico che parte dalle misure originali e arriva a una rappresentazione verificabile.

Il principio più importante da conservare è che un risultato scientifico non è soltanto un numero: deve poter essere associato ai dati che lo hanno prodotto, alle operazioni effettuate e alle condizioni nelle quali è stato ottenuto.

È su questa base che proseguiremo con le successive lezioni del corso.

Riferimenti tecnici

Le informazioni sulle API pubbliche GX-TXT utilizzate in questo approfondimento si riferiscono alla versione DEV111-P027, ramo GitHub maintenance/dev111, commit:

a3cf459e2b9909c84c6f200eea32d09dbdc676be

Documentazione ufficiale:

Le caratteristiche descritte sono quelle verificate nella configurazione utilizzata per gli esperimenti. Versioni successive del framework possono introdurre modifiche, nuove capacità o comportamenti differenti.

Lezione 06 — Materiale didattico, script e Dataset sperimentali

GNU Octave con GX-TXT — Materiali per la lezione dedicata a selezione, lettura, confronto e rappresentazione dei dati sperimentali. Il percorso comprende quattro script .m, documentazione operativa, acquisizioni originali Rigol e un diagramma di flusso delle elaborazioni.

Versione di riferimento: GX-TXT DEV111-P027, ramo maintenance/dev111, commit a3cf459e2b9909c84c6f200eea32d09dbdc676be. I quattro script sono stati eseguiti con successo nella Session GX-TXT della lezione.

Scaricare il materiale

Script Octave e guide (WordPress): Scarica GXTXT_Lesson_06_Scripts_and_Guides_v1.0.zip (circa 58 KB). Contiene i quattro script .m, le quattro guide operative .md, obiettivi e note didattiche, README bilingue, licenza GPL e checksum SHA-256.

Dataset sperimentali (Google Drive): GXTXT_Lesson_06_Datasets_v1.0.zip (circa 6,54 MB). Include i cinque CSV Rigol completi, quattro PNG originali, README italiano/inglese, manifest e checksum.

ArchivioContenutoDestinazione
Scripts and Guides4 script .m + 4 guide .md + obiettivi e noteDownload dal blog
Datasets5 acquisizioni CSV + 4 screenshot PNG + manifestGoogle Drive condiviso

Schema generale di funzionamento

Le acquisizioni vengono importate in una Session GX-TXT come Dataset canonici. Gli script lavorano attraverso lo Script Bridge e le API pubbliche: non aprono direttamente i CSV presenti nella struttura interna della Session. Lo schema rappresenta quattro esercizi distinti, non una catena di elaborazioni obbligatoria.

Lezione 06: flusso dei Dataset e dei quattro script Octave Cinque CSV Rigol vengono importati in GX-TXT e diventano Dataset scientifici nella Session. Quattro script leggono le misure tramite API pubbliche. Lo script 06.03 pubblica un Dataset riassuntivo e un grafico a barre; lo script 06.04 sceglie il Dataset tramite Job Launcher. Lesson 06 — Flusso dei dati e degli script GNU Octave • Script Bridge • API pubbliche GX-TXT • Session scientifica Acquisizioni originali 5 CSV Rigol: IR, HOLD e LED + 4 screenshot PNG Importazione GX-TXT Modulo Rigol CSV / immagini Schema scientifico e unità Session GX-TXT Dataset ID + Variable ID timeseries.table / image.raster Gli script leggono i Dataset tramite CANONICAL_DATASET_API_v2 06.01 — Lettura IR Catalogo + schema variabili read_range: 3.000 campioni Maschera temporale Min, max, conteggio Sorgente: CH1 + PNG 06.02 — Confronto 8 Dataset: 4 CH2 + 4 CH3 Letture indipendenti Allineamento temporale Picchi HOLD e media CH3 Sorgenti: CH2 / CH3 06.03 — Pubblicazione Ricalcolo picchi dai CH2 publish_series PlotSpec: kind = bar render + artifacts Sorgenti: 4 Dataset HOLD 06.04 — Input Binding Job Launcher: Add input gxtxt_context().inputs Schema CH2 + read_range Massimo e media HOLD Dataset scelto nel Job Log del Job Finestra IR e soglia Log del Job Tabella di confronto Vgen Dataset derivato + grafico lesson06_hold_peak_summary Log del Job Risultato del Dataset scelto 06.03 ricalcola i massimi dalle acquisizioni: non legge il log o l’output di 06.02. La sola 06.03 crea risorse scientifiche persistenti; le altre esercitazioni sono di sola lettura.
Diagramma del percorso delle acquisizioni, dei quattro script Octave e dei rispettivi risultati.

I quattro script e il loro flusso di elaborazione

06.01 — Dataset Selection and Read

File: Lesson_06_01_Dataset_Selection_Read.m. Obiettivo: passare dalla descrizione dei Dataset ai campioni numerici effettivi del segnale infrarosso.

  • Ottiene l’elenco degli identificatori con CANONICAL_DATASET_API_v2/list e verifica la presenza del Dataset CH1 e del Dataset immagine.
  • Riconosce tempo (v0001) e tensione (v0002) attraverso DATASET_VARIABLE_SCHEMA_API_v3/list e get.
  • Legge 3.000 campioni dall’offset sorgente zero-based 100000 tramite read_range, verificando valori, dimensioni e dati mancanti.
  • Applica una selezione temporale 0,300–0,450 ms e calcola intervallo di tensione e campioni sotto la soglia dimostrativa di 7,3 V.

Risultato collaudato: 1.500 campioni selezionati, tensione 6,04–8,80 V, 801 campioni inferiori a 7,3 V. Lo script non modifica i Dataset. I due Typed Parameters visualizzati nel Workspace sono registrati, ma non vengono consumati da questa versione del codice.

06.02 — HOLD/LED Comparison

File: Lesson_06_02_HOLD_LED_Comparison.m. Obiettivo: confrontare quattro acquisizioni con Vgen nominale di 1, 2, 3 e 4 V.

  • Associa le quattro condizioni sperimentali a otto Dataset numerici: per ogni CSV Rigol importato, CH2 e CH3 risultano separati.
  • Individua le variabili scientifiche dei due canali e legge finestre numeriche limitate da ciascun Dataset.
  • Controlla che i vettori temporali CH2 e CH3 abbiano coordinate allineate entro la tolleranza stabilita.
  • Calcola il massimo del nodo HOLD e la tensione media del nodo CH3 intorno a 2 s, producendo una tabella nel log.

Massimi HOLD verificati: 2,78 V; 4,26 V; 4,62 V; 4,58 V. CH3 descrive la tensione del nodo associato al LED, non la corrente LED. Questa prova è di sola lettura.

06.03 — HOLD Peak Bar Plot

File: Lesson_06_03_HOLD_Peak_Bar_Plot.m. Obiettivo: trasformare misure sperimentali in un risultato scientifico derivato e in un grafico persistente GX-TXT.

  • Rilegge direttamente i quattro Dataset HOLD (CH2) e ricalcola i massimi nell’intervallo 0–20 ms; non utilizza i numeri stampati nel log dello script 06.02.
  • Pubblica con DATASET_PUBLICATION_API_v2/publish_series il Dataset lesson06_hold_peak_summary, con quattro righe e le variabili vgen_nominal e hold_peak.
  • Costruisce una PlotSpec con una serie kind = bar, la valida, la registra con upsert_plot e richiede il rendering.
  • Ottiene un grafico scientifico nativo mediante PLOT_DATASET_API_v4; non usa figure(), bar() o saveas() di Octave.

Risultato: un Dataset derivato table.generic e un grafico a barre plot.dataset.xy, variante p0001. È l’unica delle quattro esercitazioni che pubblica risorse scientifiche nuove nella Session.

06.04 — Dataset Input Binding

File: Lesson_06_04_Dataset_Input_Binding.m. Obiettivo: separare la selezione del Dataset dal codice Octave.

  • Nel Job Launcher, la sezione Inputs and output permette di usare Add input Dataset e di definire un alias semantico.
  • Lo script chiama gxtxt_context() e recupera context.inputs, senza un Dataset ID d’ingresso fissato nel sorgente.
  • Controlla che sia presente un solo input valido, ne esamina lo schema CH2 e legge 1.000 campioni mediante read_range.
  • Calcola massimo, tempo del massimo e media della tensione HOLD nella finestra selezionata.

Risultato reale: alias hold_measurement, Dataset vgen_3v_hold_led, massimo 4,620 V a 2,600 ms e media 4,321 V. Non sono necessari Typed Parameters. Il programma è riutilizzabile con altri Dataset HOLD compatibili, selezionati dall’interfaccia.

Come riprodurre le esercitazioni

  1. Scaricare entrambi gli archivi, quindi creare una nuova Session in GX-TXT con una versione compatibile (riferimento collaudato DEV111-P027).
  2. Importare i cinque CSV originali con il modulo Rigol CSV. Verificare il riconoscimento dei canali CH1, CH2 e CH3 e i Dataset ID indicati nelle guide. Per 06.01 importare anche il PNG IR come Dataset immagine.
  3. Aprire Octave Script Workspace, registrare gli script .m nell’ambito della Session e dichiarare le API richieste come indicato nelle guide .md.
  4. Eseguire i Job nell’ordine 06.01, 06.02, 06.03 e 06.04, verificando log, metadata delle variabili e risultati numerici.
  5. Per 06.04 scegliere un solo Dataset HOLD/CH2 mediante Add input Dataset; lo script recupererà la sua identità dal contesto del Job.

Integrità, licenza e riproducibilità

I quattro script .m distribuiti coincidono per SHA-256 con i sorgenti delle esecuzioni collaudate il 28 ottobre 2026. Ogni archivio comprende un manifest e un file SHA256SUMS.txt; su sistemi Linux è possibile verificarli dalla cartella estratta con sha256sum -c SHA256SUMS.txt.

Licenza: gli script e la documentazione dell’archivio didattico sono distribuiti con GNU GPL-3.0-or-later.

Limiti scientifici: Vgen indica l’impostazione nominale del generatore. Le acquisizioni HOLD/LED a 50 kSa/s descrivono l’andamento lento dei nodi del circuito, non consentono di ricostruire direttamente una portante IR di circa 38 kHz.

Documentazione del framework

Repository ufficiale GX-TXT · Commit di riferimento DEV111-P027. I contratti pubblici utilizzati comprendono Octave Script Bridge API v1, Canonical Dataset API v2, Dataset Variable Schema API v3, Dataset Publication API v2 e Plot Dataset API v4.

#analisi dati sperimentali #Dataset scientifici #GNU Octave #GX-TXT #Input Dataset Binding #lettura dati #Plot Dataset API v4 #ProvaTelecomando IR #read_range #Rigol #Script Bridge #tutorial Octave

Altre tappe del progetto

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *