Quick Start¶
Prerequisites¶
Thalamus requires Python and supports Windows, Mac, and Linux. Drivers and runtimes for integration with third party devices such as GenTL cameras and National Instruments DAQs will need to be installed separately.
Installation¶
First, create a python virtual environment and activate it. This is commonly needed to make sure the version of grpc that pip installs is compatible with Thalamus. https://docs.python.org/3/library/venv.html
python -m venv venv-thalamus
#Mac and Linux
source venv-thalamus/bin/activate
#Windows
call venv-thalamus/scripts/activate
From the releases page download the whl file for your platform and install it. https://github.com/cajigaslab/Thalamus/releases
python -m pip install thalamus-0.3.37-py3-none-manylinux_2_27_x86_64.whl
You should now be able to run the pipeline program and see a window appear with an empty node list.
python -m thalamus.pipeline
Adding a Node¶
Creating Nodes¶
Click the Add button to add a node with the default type, NONE. A NONE node is the simplest type of node and does nothing. When you click on the node type a dropdown list of other node types appear that you can select from, each implementing a different functionality. In this demo we’ll select the WAVE node which is used to generate periodic signals.
Node Properties¶
Once the WAVE type is selected the node becomes expandable because it now has a series of properties you can set.
Running: Whether or not the node is currently generating data
Sample Rate: The frequency of the data.
Poll Interval: How often to generate data. With a sample rate of 1000 Hz and poll interval of 16 ms the node generates 16 samples every 16 ms.
View: Whether or not the legacy data viewer is visible.
Node Widgets¶
Most nodes will have some configuration options in this list and some may even have all of their configuration options here. However, many nodes require a more complicated UI and will often times have a Node Widget that will appear to the right when they are selected. This is a custom UI specific to each type of node. In the case of the WAVE node the Node Widget allows you to add and remove channels and adjust the parameters of each channel including shape, frequency, amplitude, etc.
Data View¶
You can use View > Add Data View to add a data view. In this view you can select a node and any channel on that node. The channels on a node are only known while it’s generating data so you first need to start the WAVE node to see a list of channels. You can also add rows and columns to view multiple channels of data.
Once running is clicked data will appear in the data view and the data stream will pause and restart as Running is toggled.
Recording¶
Adding nodes is how the functionality of Thalamus is extended and data is recorded by adding a STORAGE2 node.
Note
Sometimes expanding a node’s functionality requires backwards incompatible changes to it’s config fields. When this happens a new node is created with a number so that we don’t disrupt users of the previous version, for example, STORAGE, STORAGE2, STORAGE3, etc.
Nodes can be divided into 4 categories:
Generators: These generate data for other nodes to consume:
WAVE: Generate periodic signals
NIDAQ: Record from a National Instruments DAQ and provide that data to Thalamus.
Consumers: These consume data:
STORAGE2: Saves data to disk
NIDAQ_OUT: Takes data from Thalamus and outputs it on a National Instruments DAQ.
Transformers: These consume and generate data:
OCULOMATIC: Takes in image data and generates eye coordinates.
TOUCH_SCREEN: Takes in a stream of data points generated by a touch screen and generates pixel coordinates
Controllers:
RUNNER2: When it’s running status changes it propogates this to other nodes. This allows you to start and stop multiple nodes with a single click.
TASK_CONTROLLER: Sends a message to start and stop a task controller if it’s running.
To demonstrate saving data we are going to have a STORAGE2 node consume data from our WAVE node.
In the video above we do the following
In addition to changing node types we can change node names by clicking on the name. We demonstrate this by changing the names to something more informative: wave and storage.
We set the new node’s type to STORAGE2 and use the Node Widget to have it subscribe to wave’s data.
Once storage starts running it will save any data that wave generates.
Storage also generates a series of diagnostic signals such as how many bytes per second it’s writing to disk.
The data is stored in the file test.tha.YYYYMMDD.N, where YYYY is the year, MM the month, DD the day of the month, and N is a number that increments with each recording.
File Format¶
The Thalamus file format is a series of records. Every time a node that STORAGE2 is subscribed to generates data a record is appended to the file containing that data. The simplest way to view the data is with the thalamus.record_reader2 module.
(venv) jarl@jarl:~/Thalamus$ python -m thalamus.record_reader2 test.tha.20260218.2
filename test.tha.20260218.2
time: 680587776636175
node: "storage"
metadata {
keyvalues {
key: "Rec"
integral: 2
}
}
analog {
data: 0.989576118602651
data: 0.98865174473791417
data: 0.98768834059513766
data: 0.98668594420786837
data: 0.98564459514899816
data: 0.98456433452920522
data: 0.98344520499533
data: 0.98228725072868861
data: 0.98109051744333409
data: 0.979855052384247
data: 0.97858090432547229
data: 0.97726812356819337
data: 0.97591676193874743
data: 0.97452687278657724
data: 0.9730985109821263
data: 0.9716317329146742
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
spans {
end: 16
name: "0"
}
spans {
begin: 16
end: 32
name: "1"
}
sample_intervals: 1000000
sample_intervals: 1000000
time: 680587780991040
}
time: 680587780991040
node: "wave"
analog {
data: 0.9701265964901058
data: 0.96858316112863163
data: 0.96700148776243522
data: 0.96538163883327366
data: 0.96372367829001
data: 0.96202767158608593
data: 0.96029368567694318
data: 0.95852178901737606
data: 0.95671205155883088
data: 0.95486454474664284
data: 0.95297934151721886
data: 0.95105651629515375
data: 0.94909614499029438
data: 0.94709830499474468
data: 0.94506307517980481
data: 0.94299053589286519
data: 0.94088076895422568
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
data: -1
spans {
end: 17
name: "0"
}
spans {
begin: 17
end: 34
name: "1"
}
sample_intervals: 1000000
sample_intervals: 1000000
time: 680587797299457
}
time: 680587797299457
node: "wave"
Each record contains a time in nanoseconds, the name of the node that generated the data and a “body” which depends on the data type generated. The time is not a Unix Epoch, it is a steady clock relative to an arbitrary point in time.
The first record in the thalamus file contains a metadata body that contains a single keyvalue indicating the recording number (the .N suffix on the thalamus file), and the following messages contain analog bodies generated by the wave node.
Hydration¶
To generate a file amenable to analysis the thalamus.hydrate module can be used to generate an HDF5 file.
(venv) 130 jarl@jarl:~/Thalamus$ python -m thalamus.hydrate test.tha.20260218.2
Measuring Capture File
analog/wave/0/data 292715
analog/wave/1/data 292715
Writing H5 file: test.tha.20260218.2.h5
Duration: 867 ms
The generated .h5 file will contain 2 datasets for each channel:
analog/<node>/<channel>/data: The data samples
analog/<node>/<channel>/received: The timing data. Each row corresponds to a record in the file and contains:
The record’s timestamp
The number of samples read so far.
The remote time (in this example it’s just 0)
This dataset can be used to reconstruct the exact time that each data point was generated.
Data Frame¶
You can also extract data from Thalamus files using the thalamus.dataframe module. At minimum this module accepts a node name and input file and will output that node’s data as a .parquet file. It also accepts further parameters to select a subset of channels, different types of data, output file name, and formats.
(venv) jarl@jarl:~/Thalamus$ python -m thalamus.dataframe --help
usage: DataFrame generator [-h] -n NODE [-c CHANNELS] [-t {text,analog}] -i INPUT [-o OUTPUT]
[-f {csv,excel,feather,html,json,latex,orc,parquet,pickle,sql,stata,xml}]
options:
-h, --help show this help message and exit
-n NODE, --node NODE
-c CHANNELS, --channels CHANNELS
-t {text,analog}, --type {text,analog}
-i INPUT, --input INPUT
-o OUTPUT, --output OUTPUT
-f {csv,excel,feather,html,json,latex,orc,parquet,pickle,sql,stata,xml}, --format {csv,excel,feather,html,json,latex, orc,parquet,pickle,sql,stata,xml}
Starting and Stopping Multiple Nodes¶
For any non trivial experiment you will likely want to start and stop multiple nodes at the same time which can be done using the RUNNER2 node. Simply configure it with a list of nodes to control and whenever the RUNNER2 node’s Running state changes it will update all nodes on the list.
Saving Experiments¶
Node configurations and window layout’s can be saved and reloaded
