Skip to content

Instantly share code, notes, and snippets.

@oyeb
Last active January 2, 2022 11:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oyeb/08910c0da6d3eddaf2ad581c4a72d786 to your computer and use it in GitHub Desktop.
Save oyeb/08910c0da6d3eddaf2ad581c4a72d786 to your computer and use it in GitHub Desktop.
GSoC 2016 Integration of Cyclops in Open-Ephys [Final Work Product Report]

Module to Control Cyclops LED driver from Open-Ephys GUI

GSoC 2016 Final Work Product Report

Name Role Contact
Ananya Bahadur GSoC Intern ananya95@gmail.com
Jonathan P. Newman Mentor jpnewman@mit.edu

Skip to Work Product

About Open-Ephys

The Open-Ephys project provides powerful extensible platforms to neuroscientists who have diverse requirements for conducting their research. One of the most popular tools, the Open Ephys (Plugin) GUI is the central product which connects and controls various hardware and software solutions.
The primary purpose of the GUI is to allow the user to design a data flow for neural activity signals (acquired with the Open Ephys Aquisition Board). Recently, the GUI was redesigned to use a plugin architecture. Since then, a lot of community developed plugins have been made that allow for event detection, visualisation to be baked right into the data flow.

The Cyclops LED Driver is a "Precision, wide-bandwidth current source with optional optical feedback mode for driving high-power LEDs" and is designed for the Optogenetic Stimulation use case. It can be controlled digitally, using the Arduino IDE and (now, even the) Open-Ephys GUI, or be used as a fast analog device to amplify an input signal and deliver an optical signal.
The optical signal is used to stimulate neural circuits, and with response analysis and feedback loops, scientists gain insight on their function and characteristics.

For more general information on the Open Ephys project, visit the Open-Ephys Wiki, project on GitHub, and the Public Mailing List. There's also a slack channel for devs.

Aim

Recently, more scientists are exploring the idea of closed-loop feedback neurons and neural circuits. In this scenario, neurons are not only be monitored but also stimulated in a way that is contingent on their past activity.
This is analogous to the feedback loops used in control systems. Fairly recently, opengenetic techniques have allowed scientists to stimulate genetically defined sets of cell using different colors of light.

This project focused on creation of a plugin for the OE GUI that can talk to Cyclops during an experiment, and forward "actions" based on events detected in the GUI.

See my abstract for more details.

Goals and Deliverables

This project has two major goals:

  1. Port the Cyclops project to the Teensy Family of micro-controllers which are based on ARM Cortex M4 architecture. (Jump)
  2. Implement a plugin and RPC to program and communicate with the Cyclops (Teensy) (Jump)

Hardware Programming on Teensy 3.2

Cyclops Wiki for users. The project is well documented for developers using Doxygen.

This work will be merged into jonnew/cyclops::master when Cyclpos Rev3.6 [which uses Teensy 3.2] is into beta (it is currently in alpha).
A chronological account of the work done follows.

CL.1 Port Cyclops project to Teensy 3.2 💯

Cyclops boards can be stacked (up to 4), and only a single Arduino / Teensy must be able to control up to 4 boards. See a very detailed issue in jonnew/cyclops.

CL.2 Implement an efficient waveform update scheduler. 💯

The scheduler uses the look-ahead strategy to schedule timer interrupts for future, but performs the scheduling when not servicing interrupt.

  • See related bug which is simple to resolve.
  • Full design docs (architecture) (will move this to wiki)
  • State: working, tested @ c8351d1

CL.3 Three kinds of Source Objects 💯

Source objects provide the "signal" data which is driven on the LED as an optical signal.

A potential "source" of confusion The equivalent of Source objects on the OE GUI is called "Signal object". This differentiation is intentional.

  • STORED 💯% support
    • The signal points are stored completely in memory.
    • Also suited for representing PWM signals.
  • SQUARE 💯% support
    • Compact representation for a Square wave, with supporting convenience methods.
  • GENERATED limited support
    • Most compact representation. Instead of saving Signal points, the generating function is stored.
    • Signal is evaluated at runtime. Caution

CL.4 Provide support for Cyclops 3.5 boards

The Arduino Leonardo (or equivalent) powered boards can be used to control up to 4 boards, but at a severe blow to the system's resolution, when the device is controlled digitally. They can deliver a max resolution of just 200 μsec (5kHz), and with each additional connected board the resolution will drop further.

Note By resolution we mean the update-rate of the LED voltage WHEN Cyclops is being driven by a digital device, like an Arduino.
There is virtually no loss (see this) when an input signal is to be amplified and drive the LED.

  • See docs for full explanation.

CL.5 Teensy RPC for communication with OE-GUI 💯

This step implemented a Remote Procedure Call interface on the Teensy and Arduino serial ports.
Thanks to @PaulStoffregen awesome work on the Teensy USB, it was easy to setup a Task Queue.

  • See the project documentation for all details.
  • Completed @445dc63, Improved @7456353

CL.6 GUI to quickly test/debug Teensy RPC 💯

Built a small python GUI using tkinter to send and receive RPC packets from a connected Teensy.
Completed @fd4b15d

![teensy-gui-screenshot-here][teensy-gui-img]

CL.7 Documentation and Usage Guide

Documentation coverage for Teensy is 💯, but is missing in many places for Arduino. Usage guide is WIP on Open-Ephys wiki.

Integration with Open Ephys GUI

If you are not familiar with the Open Ephys GUI and its workflow, I suggest you read this [excellent article][oe-wiki-guide] to form a better context for the following section.
You can also skip "Introduction" and "Usage".

This was the most challenging part of the project. The Cyclops Plugin is very different from other plugins, and its design pushes the plugin-architecture of the OE-GUI project to its limits. The architecture,

  • does not allow instances of plugins to (easily) hold shared resources/references,
  • does not provide a general interface for inter (and intra) plugin communication, and
  • is very strict about ownership of core objects.

Significant time was devoted on developing workarounds for these during the project. But, I must also note that such features are (and will be) required only by few plugins and that investing time in a general interface for all these would be overkill.

Another cool feature is the concept of Cyclops sub-plugins. So, the dynamically loaded Cyclops Plugin can dynamically load more (user) sub-plugins. I took a lot of inspiration (and code! credit: @aacuevas (Aarón Cuevas López) and @yapatel (Yogi Patel) from the OE GUI's Plugin Manager to make the Cyclops Plugin Manager

Usage

Succinctly, the Cyclops Plugin provides a way to grab events from the Data Graph and transform them into actions for a Cyclops device.

Other plugins process the data channels (aka data streams) during "aquisition", whereas the Cyclops Plugin does not do any kind of processing -- this apparently dumb behaviour is an intended feature! During acquisition (ie, when the experiment is running), the plugin just passes

  • (event) data from GUI -> sub-plugin and
  • actions (RPC) from sub-plugin -> Cyclops device (via USB).

How is being dumb an intended feature?

Implementing the Cyclops Plugin

[Wiki][plug-wiki] for users. The project is well documented for developers using Doxygen.

This work will be merged into development pending review and completion. It will eventually be merged into master when the next version of the plugin-GUI is released.
A chronological account of the work done follows.

OE.1 Share the Visualizer Canvas among Editors

In OE GUI jargon, Editors represent plugin instances and you see them on the bottom (Editor Viewport). If the plugin needs to plot something on the screen, it plots on a Visualizer which appears on the top right.

See motivation for a shared canvas. The shared canvas has better UI interactions, that should be moved to the base Visualizer class.

  • Changes, older and recent, were made to VisualizerEditor and Visualizer to make "canvas sharing" possible and to refactor it (see forum).
  • Also see related PR. My thanks to @sept-en (Kirill Abramov) for suggesting the use of Observer Pattern (Listener interface).

OE.2 Provide a Cyclops API

The API depends on openFrameworks and uses the existing OE code for access to serial ports. The API exposes Cyclops RPC commands.

  • See project documentation for detailed documentation.
  • Completed @b06baba, Tested @68e89f3.

OE.3 Built Cyclops sub-plugin Manager

This was based on code from OE GUI's PluginManager, greatly simplified though.

OE.4 Make CyclopsPlugin Base Class

Users extend this base class to implement their "custom" plugins. This base class clearly defines the capabilities and responsibilities of any Cyclops sub-plugin.
Please see project documentation and the User Guide.

  • The CyclopsPlugin has an built in Timer, which can be used to perform open-loop control using periodic tasks (beta feature: see 660332)
  • There is no need to hardcode the signal data, which is to deployed via the Cyclops device, into the sub-plugin code.
  • The Cyclops sub-plugins are compiled using a separate Makefile.cyclops. They are not built with the OE GUI plugins.

OE.5 Refactor and decouple CyclopsEditor and CyclopsCanvas

As I kept adding more and more functionality, the code for these two classes started to develop a smell and become increasingly coupled. Once again, a solution inspired by Observer Pattern was implemented.
Relevant commit.

OE.6 Allow "migration" of hooks among Canvases

This was needed to support experiments which might require more than 4 Cyclops Boards. The UI interaction helps to:

  • Quickly to migrate a Cyclops Editor (aka hook) from one Canvas to another, with all of its configuration intact. [2 clicks]
  • Quickly Migrate all hooks if canvas is "closed". [between 2 to N clicks]

Even though it is not complete, the current implementation is built with soon-to-be-added features in mind.

OE.7 Added various UI widgets and interactions

This happened throughout the project. Especially interesting are the drag-and-drop SignalViews and the Migration UI.
During acquisition, migration and Cyclops-Testing, all UI widgets become "inactive", this is because these three operations can execute only in mutual exclusion (as they change state/core settings).

#OE.8 A very user friendly SignalEditor

The Cyclops device is used to drive different signals on the LED.

Currently only STORED and SQUARE wave signals can be deployed from the GUI. See why?

Users will spend considerable time to define signals. This nifty package that leverages "ease-of-use" from IPython, allows users to edit, view and save signals quickly using popular scientific python tools like matplotlib, numpy and scipy.

  • Save in YAML for OE GUI 💯
  • Excellent documentation available from inside IPython. 💯
  • Future Work:
    • Save in plain-text for gnuplot, matlab, json
    • Launch from within OE GUI, and update OE GUI's signal database "live". This will also help others to make plugin utilities in python.

Future Work for GUI Integration

These items are required for the completion of the project (as per me). None of these could be delivered by the GSoC 2016 deadline date (Aug 23 UTC) :sad:

  • Canvas Upgrades:
    • Connect the HookView with the LED by drag-n-drop.
  • Core:
    • Automatically generate code for Teensy 3.2, Arduino Due (and equivalent).
    • Flash the generated code to Teensy, without using Arduino IDE or the Teensy Loader (can be done using simple Makefiles that are available in upstream repos).
    • Launch and communicate with any subprocess using redirection.
    • Use the subprocess communications to launch the SignalEditor from the OE GUI.
  • Editor Upgrades:
    • Make channel-mapper like object. Very challenging given the size constraints.

Appendix

Here you'll find long-ish explanations for some design decisions.

Motivation for shared canvases

  • Each canvas represents a single Cyclops device. Almost all configuration for the device and sub-plugin is done here.
  • Each Editor represents a hook in the data flow, that fishes out (grabs) events from that point in the data flow and forwards them to a Cyclops sub-plugin.
  • Each Cyclops sub-plugin can control only 1 Cyclops Board.

Since each Cyclops device can control up to 4 Cyclops Boards, a single canvas must be responsible for more than 1 Editor.

Keeping it... dumb

It is impossible to anticipate how events will or can be transformed into actions -- the answer to that will be found when more research -- which this system (hopefully 😉) enables -- is done!
This is especially true when designing experiments around an object as complex as the mammalian brain. Therefore, it was very important to provide the user with extreme flexibility in determining how stimulus patterns would be generated based upon incoming neural events.
Because this system allows unprecedented performance and customizability in the fairly new domain of Optogenetic Stimulation, all while being fairly easy to use, it may enable research that narrows down exactly what stimulus patterns are most effective at evoking neural activity and, consequently, what features of neural activity are most important for circuit function.

The transformation from neural events to optical output happens inside the cyclops sub-plugins. These are normal C++ programs which implement function(s) that, well... perform the transformation and invoke Remote Procedure Calls on the Cyclops device (via an API over USB).
By passing the buck to the user, to make the sub-plugin program intelligent, the system is flexible enough to suit literally any kind of research in this domain. Not to mention the expressive power, popularity and infinite capabilities of the C++ language, which makes this sub-plugin interface an ideal and elegant solution.

Also see future work.

Why is GENERATED in beta?

Computation of arbitrary expressions is a costly operation. Even seemingly innocent floating point arithmetic can take many CPU cycles on a Teensy 3.2, because it lacks a FPU.

Teensy 3.6 will have an FPU. The CPU clock is also up to 2x faster. Additionally, teensy 3.6 is nearly pin-for-pin compatible with the 3.2, which was targeted by this project. Therefore this upgrade will likely come for free 😄

By beta we mean that it cannot be used from the OE GUI, GENERATED can be used now if you are programming the Cyclops directly (which is entirely possible, btw!).
GENERATED will stay in beta till we can confirm that it does not make a significant affect to the resolution in usual scenarios.

Thank You!

####Google OSPO for this wonderful programme and Open-Ephys and Jonathan for this exciting project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment