In questo quaderno viene fornito un semplice esempio per convertire in formato ASCII i files binari salvati dall'acquisizione. In particolare i files hanno suffisso _events.ade
import struct
import os
import sys
import numpy as np
from matplotlib import pyplot as plt
Definisco i files di input e output. Chiaramente se si hanno più files da convertire si può automatizzare ciclando
inputFile = ("241Am_scan_000mbar_events.ade")
outputFile = ("241Am_scan_000mbar.txt")
Per leggere dati binari, utilizziamo una struttura. Un evento è infatti una struttura da 16 bytes formattata nel modo seguente:
64 bits - timestamp (format string: Q)
16 bits - qshort (format string: H)
16 bits - qlong (format string: H)
16 bits - baseline (format string: H)
8 bits - channel (format string: B)
8 bits - PUR flag (format string: B)
event_PSD_struct = struct.Struct("<QHHHBB")
event_PSD_size = event_PSD_struct.size
In questo caso apro in scrittura "normale" w
il file di output ed in lettura binaria rb
il file di input. Dopo aver aggiunto un header (che può essere rimosso) vado a leggere un evento per volta (ovvero un subset di lunghezza pari alla mia struttura) e lo scrivo in un file di testo.
A noi interessa solamente qlong: uno volendo poterbbe salvarsi solo quello. Oppure anziché convertire in un file ASCII si può anche memorizzarli direttamente in un vettore. Questo è un esempio base
%%timeit
# Apro il file in cui scrivere l'output
with open(outputFile, "w") as output_file:
# Apro il file da leggere
with open(inputFile, "rb") as input_file:
# Scrivo una intestazione. Può essere tolta
output_file.write("#N\ttimestamp\tqshort\tqlong\tchannel\n")
counter = 0
try:
while True:
# Leggo un evento binario
binary_data = input_file.read(event_PSD_size)
# Lo decodifico
timestamp, qshort, qlong, baseline, channel, pur = event_PSD_struct.unpack(binary_data)
# Lo scrivo nel file ascii
# A noi interessa solo qlong: uno potrebbe scriversi anche solo quella
output_file.write(f"{counter:d}\t{timestamp:d}\t{qshort:d}\t{qlong:d}\t{channel:d}\n")
counter += 1
except struct.error:
pass
29 ms ± 5.29 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
L'esempio sopra può essere utile nella realizzazione di uno script che converta i files binari in files ASCII. Per i nostri scopi tale passaggio non è veramente necessario. A patto di spiegargli come sono codificati, numpy è perfettamente in grado di leggere files binari. Un esempio è riportato in questo esempio del prof. Mascagna.
Definiamo come prima cosa il nostro tipo di dato (u sta per intero senza segno, mentre il numero decimale indica il numero di bytes)
myEvent = np.dtype([
('timestamp', np.dtype('u8')),
('qshort', np.dtype('u2')),
('qlong', np.dtype('u2')),
('baseline', np.dtype('u2')),
('channel', np.dtype('u1')),
('PUR', np.dtype('u1')),
])
Per caricare i dati si utilizza il metodo np.fromfile
che permette di leggere i dati a partire da un file aperto, in questo caso in lettura binaria "rb"
, specificando il tipo di dato appena costruito
%%timeit
with open("241Am_scan_000mbar_events.ade", "rb") as f:
myData = np.fromfile(f, dtype = myEvent)
116 µs ± 7.14 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
h, bins = np.histogram(myData["qlong"], bins = 200)
binc = bins[:-1] + (bins[1] - bins[0])/2
fig, ax = plt.subplots()
ax.plot(binc, h, ds = "steps-mid", c = "darkgreen", lw = 2, label = "Spettro")
ax.fill_between(binc, h, step = "mid", color = "lime", alpha = 1)
ax.grid()
ax.set_xlabel("Energia [ADC]", fontsize = 14)
ax.set_ylabel("Entries", fontsize = 14)
ax.set_title(f"Pressione: {0} mbar", fontsize = 16)
ax.legend()
plt.show()
Il primo approccio è utile nel caso in cui si voglia creare un file ASCII per motivi ben precisi. Tuttavia per poter analizzare i dati con Python tale step non è assolutamente necessario, ed anzi personalmente trovo pure più naturale l'approccio con numpy.
Nonostante le due operazioni non siano proprio analoghe, volevo evidenziare come il primo caso risulti 200 volte più lento del secondo