Kernel tasks SDK
  • 08 Apr 2022
  • PDF

Kernel tasks SDK

  • PDF

The Kernel SDK allows you to emit your own task events to be recorded alongside Flow data in the same dataset.

Specification

To send events to our system, send a multicast packet containing JSON to 239.128.35.86:7891.

The fields in the JSON should be:

NAMETYPENOTES
idintegerAn incrementing integer signifying a unique event identifier
timestampintegerAn epoch timestamp in nanoseconds
eventstringThe name of the event
valuestringThe value of the event

If tasks are not running on the provided Kernel Flow PC, ensure that:

  •  The multicast packets can reach the Kernel Flow PC over the network
  •  The two computers' clocks are synchronized via NTP or a similar protocol

Conventions

Context Setting Events

To take advantage of the export and analysis tools available in the Kernel Researcher Portal, structure your custom tasks according to the following conventions:

  • The first event must be start_experiment.
  • The last event must be end_experiment.
  • A task must be structured using the following hierarchy of events: experiment > task > block > trial.
    • Task, block, and trial are optional, but the hierarchy must be respected. (e.g. a block cannot be within a trial.)
  • The value for these context events MUST be an integer that represents the ordinal index (block 1, block 2, ..., block N)
  • Epochs (events with a non-zero duration) must be delineated by 2 events:
    • start_{}
    • end_{}
  • A duration will be calculated from these and will appear in the task data exported in the SNIRF files.

Instantaneous Events

Events (with a zero duration) which you wish to record a timestamp for MUST start with event_.

Metadata Events

All other events (those which are not prefixed with start_, end_, or event_ are considered metadata and their timestamps are not included in the task data in exported SNIRF files.

Additional Notes

The value field of an event can be a dictionary, in which case its keys appear as separate columns (prefixed with the name of the event) in the task data in exported SNIRF files.

BEST PRACTICES REMINDER
Send events whose timestamps matter for your analyses as close in time as possible to when events actually happen in the physical world. E.g., for visual stimuli displayed on a screen, events should be sent right after the screen flip command in environments such as psychopy (Python) or Psychtoolbox (Matlab).

Data Example

A simple finger tapping paradigm may create this sequence of events:

{"id": 1, "timestamp": 1.6416027480329585e+18, "event": "start_experiment", "value": "1"}
{"id": 2, "timestamp": 1.6416027480330596e+18, "event": "experiment_type", "value": "finger_tapping"}
{"id": 3, "timestamp": 1.6416027480496115e+18, "event": "start_rest", "value": "1"}
{"id": 4, "timestamp": 1.641602771773098e+18, "event": "end_rest", "value": "1"}
{"id": 5, "timestamp": 1.6416027717732467e+18, "event": "start_block", "value": "1"}
{"id": 6, "timestamp": 1.6416027717733532e+18, "event": "block_type", "value": "right"}
{"id": 7, "timestamp": 1.6416027768250962e+18, "event": "end_block", "value": "1"}
{"id": 8, "timestamp": 1.6416027768454572e+18, "event": "start_rest", "value": "2"}
{"id": 9, "timestamp": 1.6416027970639432e+18, "event": "end_rest", "value": "2"}
{"id": 10, "timestamp": 1.6416027970640435e+18, "event": "start_block", "value": "2"}
{"id": 11, "timestamp": 1.6416027970641178e+18, "event": "block_type", "value": "left"}
{"id": 12, "timestamp": 1.6416028020961134e+18, "event": "end_block", "value": "2"}
...
{"id": 248, "timestamp": 1.6416038780122324e+18, "event": "end_experiment", "value": "1"}

Which would be transformed by the Kernel Analysis tools to the following flattened version, ready for analysis (and export)

timestamp
event
duration
experiment
experiment_type
rest
block
block_type
0.000287start_experiment1129.9792741.0finger_tappingNaNNaNnan
0.016940start_rest23.7234861.0finger_tapping1NaNnan
23.740575start_block5.0518491.0finger_tappingNaN1.0right
28.812785start_rest20.2184861.0finger_tapping2NaNnan
49.031372start_block5.0320701.0finger_tappingNaN2.0left


Note that the timestamps are "zeroed" to the beginning of the first timestamp of the fNIRS data. 

Code Examples

import socket
from time import time
import json

id=1
timestamp = time()
data_to_send = {
    "id": id,
    "timestamp": int(timestamp * 1e9),
    "event": "start_trial",
    "value": "5",
}
event = json.dumps(data_to_send).encode("utf-8")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(event, ("239.128.35.86", 7891))


using System;
using System.Net;
using System.Net.Sockets;
using Newtonsoft.Json;
using System.Text;

class KernelEvent {
  public int id { get; set; }
  public long timestamp { get; set; }
  public string @event { get; set; }
  public string value { get; set; }
}

class KernelEvents {
  Socket socket;
  int id = 0;
  DateTime epoch = new DateTime(1970, 1, 1);

  public KernelEvents(string ip, int port) {
    this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    IPAddress ipAddress = IPAddress.Parse(ip);
    this.socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress));
    this.socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 2);
    IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);
    this.socket.Connect(ipEndPoint);
  }

  public void send(KernelEvent kernelEvent) {
	this.id++;
	kernelEvent.id = this.id;
	  
	TimeSpan timestamp = DateTime.UtcNow - epoch;
	kernelEvent.timestamp = (long)(timestamp.TotalMilliseconds * 1000);
	  
    string json = JsonConvert.SerializeObject(kernelEvent);
	Console.WriteLine(json);
	byte[] bytes = Encoding.UTF8.GetBytes(json);
    this.socket.Send(bytes, bytes.Length, SocketFlags.None);
  }
}

public class Program {
    public static void Main(string[] args) {
        KernelEvents kernelEvents = new KernelEvents("239.128.35.86", 7891);
		kernelEvents.send(new KernelEvent {
			@event="start_trial",
			value="5",
		});
    }
}

Matlab: https://www.mathworks.com/help/instrument/send-and-receive-multicast-data-packets-using-udp.html