Skip to content

Reading Outputs

Michelle Sands edited this page Apr 24, 2026 · 2 revisions

File Structure

StimControl outputs files in the following structure:

Base_Out_Dir
 └─ MouseID
    └─ YYMMDD                                   # experiment date
        └─ YYMMDD_hhmmss_StimFileName           # experiment start time
            └─ StimFileName.stim                # a copy of the stimulus file
            └─ StimFileName_meta.json           # metadata of the stimulus
            └─ DaqName_ChannelNames.csv         # DAQ channel names
                    ## each of the above is saved once per .stim file
                    ## while each of the below is saved once per trial
            └─ xxxxx_stimxxxxx.csv              # execution index / stim number DAQ outputs as a csv, columns mapping to channelnames
            └─ xxxxx_stimxxxxx_ComponentID      # Separate folders for components with other output types (e.g. cameras)
                └─ ...                          # format determined by individual components

Interpreting Outputs

.stim Files

These are direct copies of the stimulus files used during the experiment.

Metadata files

...Are a bit complicated, so we'll build up an understanding. At their simplest level, metadata files are .json files structured as follows:

{
  "trials": [
    {Trial1Data},
    {Trial2Data},
    ...
  ],
  "hardware": [
    {HardwareParams1},
    {HardwareParams2},
    ...
  ]
}

hardware params differ between devices, but in general are the settings of each piece of hardware used in the experiment, and are fairly self-explanatory.

Trial Data

"stimuli": [
    {StimulusNode1Data}, 
    {StimulusNode2Data}, 
    ...
    ],
"trialInfo": {
    "tPre": 1000,
    "tPost": 70000,
    "nRuns": 1,
    "comment": "",
    "params": {
        TargetName1: {TargetName1Params},
        TargetName2: {TargetName2Params},
        ...
    },
    "line": Trial_String,
    "trialIdx": 1,
    "RootNodeIdx": 1,
    "tags": [],
    }

Note that because this file is generated in MATLAB, ALL indices start at 1, not 0. If you're reading the file in another language, I'd recommend going through it in full first and identifying which fields you want to update to an appropriate indexing system. I've tried to be consistent by ending all indexing fields with idx, but I can't guarantee either that or that configurable fieldnames like stimulus labels don't also include idx somewhere in their names.

Extracting Relevant Metadata

The structure of trials(trialNum).stimuli and trials(trialNum).trialInfo.params are outlined in following sections, but for most use cases an in-depth understanding of them is not necessary. To extract the basic metadata for a stimulus:

  • Extract componentInformation = trials{trialNum}.trialInfo.params.(key), where key is the ProtocolID of a targeted HardwareComponent, or the targeted device name in a DAQ channel config (see here for more information)
  • componentInformation has keys sequence, params, delay, and sequenceStack, where:
    • params is an ordered list of length X, containing the parameters for each stimulus sent to the component during the trial.
    • sequence and delay are ordered lists of length N. Each element of sequence satisfies 1<=s<=N and refers to an element of params.
    • sequence defines the execution order of the stimulus, and delay[i] defines the delay between the end of sequence[i-1] and the beginning of sequence[i]
    • sequenceStack is a 2d array of size MxN that preserves the context in which each element of sequence originally appeared (e.g. if a stimulus was called from within an oddball paradigm, that information is preserved). It may be thought of as a stack trace.
  • For each element=[m1, m2, m3, ...mx] in sequenceStack, reference trials.stimuli{mx}.tags and trials.stimuli{mx}.tokenName from mx...m1 to find the tags for each stimulus, its name, and the name and tags of any stimulus groups they were a part of.

Although stimuli may be defined to target multiple devices, this metadata traceback is separated by device (or, in the case of DAQs, channel).

TrialData:stimuli

trials(trialNum).stimuli records all nodes in the tree generated by the trial text, which is then flattened to generate trials(trialNum).trialInfo.params.

The Tree

Stimulus sequences within StimControl are constructed using similar logic to that used in abstract syntax trees. Leaf nodes in the tree record individual stimuli (as defined in the stimulus definitions section of the .stim file). Branch nodes record the execution order of their children (simultaneous, sequential, or oddball), and other non-stim-specific parameters like repeat delays and start delays.

All nodes that represent either a named Stimulus or a StimulusGroup have the tokenName attribute set. Note that some nodes represent unnamed groups (i.e. any bracket pairs within the trial definition) and will not have this attribute set. Any tags will be attached to the node at the level at which the tags were defined (i.e. if tags are provided in-line with a stimulus definition, then the node defining that stimulus will have its tags attribute set.)

Each node has an "idx" attribute, which acts as its key within the tree. Branch nodes will have childIdxes set, and all non-root nodes have parentIdx set.

Some additional information on the tree structure is available in the For Developers Section of this wiki.

A Node
{
    "idx": 1,
    "repeatDelay": 0,       # in ms
    "startDelay": 0,        # in ms
    "nStimRuns": 1,         # including the first
    "stimParams": [],       # parameters of stimulus. Only set for leaf nodes.
    "parentIdx": [],        
    "childIdxes": [],       # idx property of all children. Ordered.
    "oddParams": [],        # oddball-specific parameters
    "childRel": "sim",
    "tokenName": "",        # set if named as either a Stimulus or StimulusGroup
    "tags": [],
    "isLeaf": false,
    "comment": [],          # Any comment given in-line with definition
}

TrialData::TrialInfo::params

"TargetComponentName": {
    "sequence": 1,              # indices begin at 1 and refer to items in params below
    "delay": 0,                 # ms, relative to the end of the previous stimulus
    "params": [
        {
        "type": "digitalPulse",
        "alignRight": false,
        "identifier": "",
        "targetDevices": [
            "TwoPhotonStart",
            "TwoPhotonNext"
        ],
        "isAcquisitionTrigger": true, # whether the stimulus starts before or after the end of tPre
        "duration": 1,                # ms, -1 runs to the end of the time available
        }
    ],
    "sequenceStack": [
        [1,2,5]      # each sub-array in sequenceStack this references a single item in sequence. 
                        # NB: numbers here refer to nodes in trials(trialIdx).stimuli, not items in params.
    ]
    },

Clone this wiki locally