Skip to content
Snippets Groups Projects
Commit 2e3b6559 authored by mingf2's avatar mingf2
Browse files

add plot window

parent 4194bade
No related branches found
No related tags found
No related merge requests found
......@@ -25,5 +25,10 @@ python -m unittest tests.utilitiesTest
python example.py
```
## Documentation
Please refer to `documentation/documentation.pdf`.
\ No newline at end of file
## Project Structure
- `example.py`: Main file. Execute this to process the data.
- `config.py`: Initialize gloable variables, including mapping (input) and spectra (output).
- `utilities.py`: Custom data tyeps. For mapping, calibration coefficient of each SiPM channel is required as an input.
- `setupParser.py`: Set up the binaray file parser.
- `coincidenceSelection.py`: Responsible for data processing, including energy calibration and coincidence selection.
- `plotSpectra.py`: Plot the final results.
\ No newline at end of file
File deleted
'''
Description:
Author: Ming Fang
Date: 2022-09-04 03:41:17
Date: 2022-09-20 19:29:43
LastEditors: Ming Fang
LastEditTime: 2022-09-20 17:35:30
LastEditTime: 2022-09-20 22:41:16
'''
from matplotlib import pyplot as plt
import tkinter
# import the parser to use it
from sources.py_BinFileParser import CITIROCEvent, DT5550WBinFile
from sources.utilities import Histogram, ImagerSetup, SiPMEvent, CrystalEvent, CoincidenceEvent
from sources.coincidenceSelection import processPackets
from typing import List
from sources.selectCoincidence import processCITIROCEventChunk
from sources.plotSpectra import plotSpectra
import config
def processChunk(chunk: List[CITIROCEvent]):
timewindow = 50 # ns
packets2Process = []
lastTimeStamp = 0
for newEvent in chunk:
currentTimeStamp = newEvent.RunEventTimecode * 0.5 # ns
if len(packets2Process) > 0 and abs(currentTimeStamp -
lastTimeStamp) >= timewindow:
processPackets(packets2Process)
packets2Process.clear()
lastTimeStamp = currentTimeStamp
packets2Process.append(newEvent)
########### Example 1
########### Data processing
# Read the file in chunks of 100k events until end of file (EOF)
# Depending on available memory size, chunkSize can be made smaller/larger
fileHandle = DT5550WBinFile(b'test_data/test.data', True)
chunkSize = 100000
# Max number of events to process
maxN = 1000000
numberOfPulsesRead = 0
......@@ -39,40 +28,9 @@ while numberOfPulsesRead < maxN and fileHandle.isGood():
newChunk = fileHandle.readNextNEvents(chunkSize)
numberOfPulsesRead += len(newChunk)
print("Read {} events.".format(len(newChunk)))
processChunk(newChunk)
# for i in range(config.imagerSetup.NSiPMs):
# fig, ax = plt.subplots(1, 1, figsize=(6, 6))
# asicID, channelID = config.imagerSetup.getAsicChannelID(i)
# x = config.singleSiPMSpectra[i].binCenters
# y = config.singleSiPMSpectra[i].binCounts
# w = config.singleSiPMSpectra[i].binWidth
# n = config.singleSiPMSpectra[i].getTotalCounts()
# ax.step(x, y, where='mid')
# ax.set_title('ASIC {0}, Channel {1}, Total counts {2}'.format(asicID, channelID, n))
# ax.set_xlabel('Energy (keV)')
# ax.set_ylabel('Counts')
# plt.show()
# ax.clear()
# for i in range(config.imagerSetup.NCrystals):
# fig, ax = plt.subplots(1, 1, figsize=(6, 6))
# x = config.SiPMPairSpectra[i].binCenters
# y = config.SiPMPairSpectra[i].binCounts
# w = config.SiPMPairSpectra[i].binWidth
# n = config.SiPMPairSpectra[i].getTotalCounts()
# ax.step(x, y, where='mid')
# ax.set_title('Crystal {0}, Total counts {1}'.format(i, n))
# ax.set_xlabel('Energy (keV)')
# ax.set_ylabel('Counts')
# plt.show()
# ax.clear()
processCITIROCEventChunk(newChunk)
fig, ax = plt.subplots(1, 1, figsize=(6, 6))
ax.step(config.coincidenceSpectra.binCenters, config.coincidenceSpectra.binCounts, where='mid')
ax.set_title('Coincidence spctrum. Total counts {}'.format(config.coincidenceSpectra.getTotalCounts()))
ax.set_xlabel('Energy (keV)')
ax.set_ylabel('Counts')
plt.show()
########## Plot spectra
root = tkinter.Tk()
plotSpectra(root)
root.mainloop()
'''
Description: Plot spectra
Author: Ming Fang
Date: 2022-09-20 21:06:52
LastEditors: Ming Fang mingf2@illinois.edu
LastEditTime: 2022-09-20 22:25:00
'''
import tkinter
from tkinter import ttk
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import config
def plotSpectra(root):
def SiPMID_changed(event):
AsicID = int(selectedAsicID.get())
channelID = int(selectedChannelID.get())
SiPMID = config.imagerSetup.getChannelID((AsicID, channelID))
# print(AsicID, channelID, SiPMID)
# update datas
line1.set_data(config.singleSiPMSpectra[SiPMID].binCenters, config.singleSiPMSpectra[SiPMID].binCounts)
ax1.set_title('ASIC {0:d}, Channel {1:d}. Total counts {2:d}'.format(AsicID, channelID, config.singleSiPMSpectra[SiPMID].getTotalCounts()))
# rescale
# recompute the ax.dataLim
ax1.relim()
# update ax.viewLim using the new dataLim
ax1.autoscale_view()
# required to update canvas and attached toolbar!
canvas.draw()
def crystalID_changed(event):
crystalID = int(selectedCrystalID.get())
# print(crystalID)
# update datas
line2.set_data(config.SiPMPairSpectra[crystalID].binCenters, config.SiPMPairSpectra[crystalID].binCounts)
ax2.set_title('Crystal {0:d}. Total counts {1:d}'.format(crystalID, config.SiPMPairSpectra[crystalID].getTotalCounts()))
# rescale
# recompute the ax.dataLim
ax2.relim()
# update ax.viewLim using the new dataLim
ax2.autoscale_view()
# required to update canvas and attached toolbar!
canvas.draw()
topFrame = tkinter.Frame(root)
bottomFrame = tkinter.Frame(root)
# create AsicID combobox
AsicIDLabel = ttk.Label(text="ASIC ID: ")
selectedAsicID = tkinter.StringVar()
AsicID_combobox = ttk.Combobox(root, textvariable=selectedAsicID)
AsicID_combobox['values'] = ['0', '1', '2', '3']
AsicID_combobox['state'] = 'readonly'
AsicID_combobox.current(0)
AsicID_combobox.bind('<<ComboboxSelected>>', SiPMID_changed)
# create ChannelID combobox
channelIDLabel = ttk.Label(text="Channel ID: ")
selectedChannelID = tkinter.StringVar()
channelID_combobox = ttk.Combobox(root, textvariable=selectedChannelID)
channelID_combobox['values'] = [str(i) for i in range(2, 16)]
channelID_combobox['state'] = 'readonly'
channelID_combobox.current(0)
channelID_combobox.bind('<<ComboboxSelected>>', SiPMID_changed)
# create crystalID combobox
crystalIDLabel = ttk.Label(text="Crystal ID: ")
selectedCrystalID = tkinter.StringVar()
crystalID_combobox = ttk.Combobox(root, textvariable=selectedCrystalID)
crystalID_combobox['values'] = [str(i) for i in range(28)]
crystalID_combobox['state'] = 'readonly'
crystalID_combobox.current(0)
crystalID_combobox.bind('<<ComboboxSelected>>', crystalID_changed)
fig = Figure(figsize=(8, 8), dpi=100)
ax1 = fig.add_subplot(221)
AsicID = int(selectedAsicID.get())
channelID = int(selectedChannelID.get())
SiPMID = config.imagerSetup.getChannelID((AsicID, channelID))
# print(SiPMID)
line1, = ax1.step(config.singleSiPMSpectra[SiPMID].binCenters, config.singleSiPMSpectra[SiPMID].binCounts, where='mid')
ax1.set_title('ASIC {0:d}, Channel {1:d}. Total counts {2:d}'.format(AsicID, channelID, config.singleSiPMSpectra[SiPMID].getTotalCounts()))
ax1.set_xlabel("Energy (keV)")
ax1.set_ylabel("Counts")
ax2 = fig.add_subplot(222)
crystalID = int(selectedAsicID.get())
# print(crystalID)
line2, = ax2.step(config.SiPMPairSpectra[crystalID].binCenters, config.SiPMPairSpectra[crystalID].binCounts, where='mid')
ax2.set_title('Crystal {0:d}. Total counts {1:d}'.format(crystalID, config.SiPMPairSpectra[crystalID].getTotalCounts()))
ax2.set_xlabel("Energy (keV)")
ax2.set_ylabel("Counts")
ax3 = fig.add_subplot(223)
line3, = ax3.step(config.coincidenceSpectra.binCenters, config.coincidenceSpectra.binCounts, where='mid')
ax3.set_title('Coincidence spctrum. Total counts {0:d}'.format(config.coincidenceSpectra.getTotalCounts()))
ax3.set_xlabel("Energy (keV)")
ax3.set_ylabel("Counts")
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
# pack_toolbar=False will make it easier to use a layout manager later on.
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.update()
canvas.mpl_connect(
"key_press_event", lambda event: print(f"you pressed {event.key}"))
canvas.mpl_connect("key_press_event", key_press_handler)
# Packing order is important. Widgets are processed sequentially and if there
# is no space left, because the window is too small, they are not displayed.
# The canvas is rather flexible in its size, so we pack it last which makes
# sure the UI controls are displayed as long as possible.
AsicIDLabel.pack(in_=topFrame, side=tkinter.LEFT, fill=tkinter.X)
AsicID_combobox.pack(in_=topFrame, side=tkinter.LEFT, fill=tkinter.X)
channelIDLabel.pack(in_=topFrame, side=tkinter.LEFT, fill=tkinter.X)
channelID_combobox.pack(in_=topFrame, side=tkinter.LEFT, fill=tkinter.X)
crystalIDLabel.pack(in_=topFrame, side=tkinter.LEFT, fill=tkinter.X)
crystalID_combobox.pack(in_=topFrame, side=tkinter.LEFT, fill=tkinter.X)
toolbar.pack(in_=bottomFrame, side=tkinter.TOP, fill=tkinter.X)
canvas.get_tk_widget().pack(in_=bottomFrame, side=tkinter.TOP, fill=tkinter.BOTH, expand=True)
topFrame.pack(side=tkinter.TOP)
bottomFrame.pack(side=tkinter.BOTTOM, fill=tkinter.BOTH, expand=True)
'''
Description: Selection coincidences between two SiPM channels on the same crystal, and coincidences between two crystals.
Description: Select coincidences between two SiPM channels on the same crystal, and coincidences between two crystals.
Author: Ming Fang
Date: 2022-09-19 17:50:22
LastEditors: Ming Fang
LastEditTime: 2022-09-20 18:01:07
LastEditors: Ming Fang mingf2@illinois.edu
LastEditTime: 2022-09-20 22:21:22
'''
from sources.utilities import Histogram, ImagerSetup, SiPMEvent, CrystalEvent, CoincidenceEvent
from sources.py_BinFileParser import CITIROCEvent
......@@ -12,11 +12,29 @@ from typing import List
import config
def processPackets(packets: List[CITIROCEvent]) -> None:
"""Process a chunk of CITIROC events. Update the spectra and the list of coincidence events.
def processCITIROCEventChunk(chunk: List[CITIROCEvent]):
"""Process a chunk of events from binary file.
Args:
packets (List[CITIROCEvent]): A chunk of CITIROC events.
chunk (List[CITIROCEvent]): A list of CITIROC events.
"""
packets2Process = []
lastTimeStamp = 0
for newEvent in chunk:
currentTimeStamp = newEvent.RunEventTimecode * 0.5 # ns
if len(packets2Process) > 0 and abs(currentTimeStamp -
lastTimeStamp) >= config.imagerSetup.timeWindow:
processCoincidentCITIROCEvents(packets2Process)
packets2Process.clear()
lastTimeStamp = currentTimeStamp
packets2Process.append(newEvent)
def processCoincidentCITIROCEvents(packets: List[CITIROCEvent]) -> None:
"""Process the CITIROC events that are in coincidence. Update the spectra and the list of coincidence events.
Args:
packets (List[CITIROCEvent]): A list of CITIROC events.
"""
SiPMEvents = []
if extractSiPMEvents(packets, SiPMEvents):
......@@ -138,4 +156,3 @@ def findZPosition(e1: float, z1: float, e2: float, z2: float) -> float:
float: z coordainate of the interaction position.
"""
return (e1 * z1 + e2 * z2) / (e1 + e2)
......@@ -3,7 +3,7 @@ Description:
Author: Ming Fang
Date: 2022-09-19 15:43:38
LastEditors: Ming Fang
LastEditTime: 2022-09-20 18:32:46
LastEditTime: 2022-09-20 19:44:45
'''
import json
import numpy as np
......@@ -23,6 +23,8 @@ class ImagerSetup:
self.NSiPMs = len(self._channelSettings)
# Number of crystal
self.NCrystals = self.NSiPMs // 2
# Coincidence time window, ns
self.timeWindow = 50
def _loadSettingsFromJson(self, fpath) -> None:
"""Load the settings from a json file.
......@@ -248,7 +250,7 @@ class Histogram:
self.binCenters = (self.binEdges[:-1] + self.binEdges[1:]) / 2
self.nBins = nbins
# counts in each bin
self.binCounts = np.zeros(self.binCenters.shape)
self.binCounts = np.zeros(self.binCenters.shape, dtype=int)
def fill(self, x: float) -> None:
"""Find the bin that x falls into and increment the counts in that bin by 1.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment