Skip to content

Commit

Permalink
docs bump
Browse files Browse the repository at this point in the history
  • Loading branch information
cboulay committed Nov 19, 2019
1 parent d84e1ab commit 3708988
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 25 deletions.
6 changes: 0 additions & 6 deletions .idea/$CACHE_FILE$

This file was deleted.

2 changes: 0 additions & 2 deletions .idea/.gitignore

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ access as well as optionally the centralized collection, viewing and disk record

The most common way to use LSL is to use one or more applications with integrated LSL functionality.

* Take a look at the list of [supported devices](https://github.com/sccn/labstreaminglayer/wiki/SupportedDevices)
* Take a look at the list of [supported devices](https://labstreaminglayer.readthedocs.io/en/latest/info/supported_devices.html)
and follow the instructions to start streaming data from your device.
If your device is not in the list then see the "Getting Help" section below.
* Download [LabRecorder](https://github.com/labstreaminglayer/App-LabRecorder) from its
Expand Down
65 changes: 64 additions & 1 deletion docs/dev/dev_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,67 @@ Add introductory text here.

The distribution includes a range of code examples in C, C++, Python, MATLAB, Java, and C# including some very simple sender and receiver programs, as well as some fairly extensive demo apps.

See `ExampleCode <https://github.com/labstreaminglayer/App-Examples/>`__ for a broader overview of example programs, API documentation link, and general programming tips, tricks, and frequently asked questions.
See `Example Applications <https://github.com/labstreaminglayer/App-Examples/>`__ for a broader overview of example programs, API documentation link, and general programming tips, tricks, and frequently asked questions.

# Coding Guides
The distribution includes a range of code examples in C, C++, Python, MATLAB, Java, and C# including some very simple sender and receiver programs, as well as some fairly extensive demo apps. This page includes just some simple teasers. See [[ExampleCode|ExampleCode]]for a broader overview of example programs, API documentation link, and general programming tips, tricks, and frequently asked questions.

## Sending Random Data in C++
```
#include "lsl_cpp.h"
#include <stdlib.h>
using namespace lsl;

/**
* This is an example of how a simple data stream can be offered on the network.
* Here, the stream is named SimpleStream, has content-type EEG, and 128 channels.
* The transmitted samples contain random numbers (and the sampling rate is irregular
* and effectively bounded by the speed at which the program can push out samples).
*/
int main(int argc, char* argv[]) {

// make a new stream_info (128ch) and open an outlet with it
stream_info info("SimpleStream","EEG",128);
stream_outlet outlet(info);

// send data forever
float sample[128];
while(true) {
// generate random data
for (int c=0;c<128;c++)
sample[c] = (rand()%1500)/500.0-1.5;
// send it
outlet.push_sample(sample);
}

return 0;
}
```

## Receiving Data in C++
```
#include "lsl_cpp.h"

/**
* This is a minimal example that demonstrates how a multi-channel stream (here 128ch)
* of a particular name (here: SimpleStream) can be resolved into an inlet, and how the
* raw sample data & time stamps are pulled from the inlet. This example does not
* display the obtained data.
*/
int main(int argc, char* argv[]) {
using namespace lsl;

// resolve the stream of interest & make an inlet to get data from the first result
std::vector<stream_info> results = resolve_stream("name","SimpleStream");
stream_inlet inlet(results[0]);

// receive data & time stamps forever (not displaying them here)
float sample[128];
while (true)
double ts = inlet.pull_sample(&sample[0],128);

return 0;
}
```
153 changes: 153 additions & 0 deletions docs/dev/examples.rst

Large diffs are not rendered by default.

Binary file removed docs/images/balert.png
Binary file not shown.
5 changes: 4 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ Welcome to Labstreaminglayer's documentation!
:caption: User's Guide

info/intro
info/getting_started
info/user_guide
info/supported_devices.rst
info/supported_devices
info/faqs

.. toctree::
:maxdepth: 1
Expand All @@ -18,6 +20,7 @@ Welcome to Labstreaminglayer's documentation!
dev/build_env
dev/app_dev
dev/repository
dev/examples


Indices and tables
Expand Down
42 changes: 42 additions & 0 deletions docs/info/faqs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Frequently Asked Questions
##########################

**I want to check the most recent sample of a stream every few seconds. How do I do that?**

Because the result of inlet.pull\_sample() is the next sample in the order provided by the sender, you first need to pull out all samples that have been buffered up in the inlet. You can do this by calling pull\_sample() with a timeout of 0.0 -- once it returns zero, there are no more samples. To speed this up, you can set a short buffer size when creating the inlet (e.g., one second).

**What clock does LSL use? / How do I relate LSL's local\_clock() to my wall clock?**

LSL's local\_clock() function measures the time since the local machine was started, in seconds (other system clocks usually do not have sufficient resolution for use with LSL). The correct way to map its output to the time measured by your preferred system clock is to first determine the constant offset between the two clocks, by reading them out at the same time, and then to add that offset to the result of local\_clock() whenever it is needed. Also keep in mind that the time-stamps that are returned by inlet.pull\_sample() will generally be local to the sender's machine -- only after you add the time offset returned by inlet.time\_correction() to them you have them in your local domain.

**What is the latency of LSL? Does the chosen buffer size have anything to do with it?**

The latency of LSL is typically under 0.1 milliseconds on a local machine, unless a very large amount of data is transmitted (megabytes per sample). The buffer size does not affect the latency unless you allow data to queue up by not querying it for an extended period of time (or when the network connection is temporarily interrupted). In such a case, the queued-up data will be quickly transmitted in a burst once network connectivity is restored. If you only need a limited backlog of data, you can set a shorter buffer size when creating the inlet.

**I want to transmit data with multiple data types (e.g., floats, ints) per sample. What is the best way to do that?**

The simplest way is to use a channel format that can hold all numbers that you want to represent and concatenate your data into a vector of that type -- the double64 format can store integers up to 53 bit, so it will hold virtually anything (floats, doubles, ints, etc.) that you want to store. It is also possible to transmit raw structs, but note that this is generally unsafe and non-portable (e.g., different compilers insert varying amount of padding between struct elements; also on platforms with different endianness your data will be garbled). In principle you can also send multiple streams and use the same time stamp when sending the samples, but that will require some extra complexity at the receiving that is rarely worth the effort.

**I want to make an LSL driver for a piece of hardware. What is the fastest way to do that?**

If a quick-and-dirty solution is fine, then the best way is to take one of the example programs for your favorite language and extend it as needed. If you want a graphical user interface and you know C++ and are on Windows, you can copy one of the applications in the LSL distribution and adapt it as needed.

**I am making a driver for a piece of hardware and want to make sure that my time stamps are accurate. How to do that?**

If your data comes from a separate device your samples will generally be a few milliseconds old. If you know or have measured this delay, you can correct for it by submitting the time stamp as local\_clock()-my\_sample\_age when pushing a sample. However, it is strongly recommended to log any constant offset (here: my\_sample\_age) in the meta-data of the stream, otherwise it can be hard to later reconstruct what value was used, especially if it is occasionally revised. Aside from a delay, your time stamps will also have a jitter (due to OS multi-tasking). It is difficult to smooth the jitter in real time correctly without introducing inadvertent clock drift and therefore it is recommended to submit non-smoothed time stamps and leave it to the receiver to smooth them if needed. In particular, when you analyze the data offline (e.g., in MATLAB), you or the XDF importer can do a much better job at linearizing the jitter post-hoc.

**I am transferring data at high sampling rate or bandwidth. What is the most efficient way to do this?**

When sending the data it usually does not matter how you perform the sending (via push\_sample, push\_chunk, or etc.), since the bottleneck at high bandwidth will typically be the operating system's network stack. You can call push\_sample or pull\_sample at least a million times per second without a significant performance hit. For small sample sizes (few channels), consider to send the data in chunks to avoid forcing frequent OS calls and network transmission. You can do this by either setting a chunk size when creating the outlet, or by using push\_chunk() instead of push\_sample(), or by setting the pushthrough flag in push\_sample() to false for every sample except the last one in a batch. Also, if you have a large number of channels (e.g., transferring image data), make sure that the data type that you pass to the push function corresponds to the data type of the stream, otherwise you pay extra for type casting. When receiving data at very high rate (100KHz+) or bandwidth (20MBps+), it is faster to avoid the basic pull\_chunk functions and instead use pull\_chunk\_multiplexed with a pre-allocated buffer. Make sure that you use a recent version of liblsl (1.10 or later offers a faster network protocol) at both the sender and the receiver.

**My hardware can produce time stamps of its own. Should I pass them into LSL?**

Usually the answer is no -- the preferred way is to either leave it to LSL's push\_sample() or push\_chunk() functions to time-stamp the data (easiest), or to call the local\_clock() function to read out the LSL clock, and then pass that in, either unmodified or with a constant delay subtracted (if you know the delay of your hardware). The only exception is if you have multiple pieces of hardware, all of which have access to the same high-precision clock, and you want to use that clock instead of the LSL clock (if the millisecond precision provided by LSL is not enough for your needs, e.g., demanding physics experiments), and you know exactly what you are doing. If you have any doubt on how you would use your own clock to synchronize multiple pieces of hardware after you've recorded the data, don't use them.

**My hardware supports different block/chunk sizes. Which one is best for use with LSL?**

The chunk size trades off latency vs. network overhead, so we suggest to allow the user to override the value if desired. A good range for the default value is between 5-30 milliseconds of data (resulting in an average latency that is between 2.5-15 ms and an update rate between 200-30 Hz). Shorter chunks make sense in very low-latency control settings, though note that chunks that comprise only a few bytes of data waste some network bandwidth due to the fixed Ethernet packet overhead. Longer chunks can also be used (any duration is permitted, e.g. for sporadic data logging activities), although the longer the chunks are the harder it becomes to perform sample-accurate real-time time-synchronization (specifically, removing the jitter in the chunk time stamps): the longest chunks that can be synchronized in real time would be around 100ms in typical settings.

**I am getting more than one matching stream in my resolve query. What is the best way to handle this?**

You either have to rename one of your streams (if the software that provides them allows you to do that), or you can make the query more specific; for instance, instead of "type='EEG'" you could use e.g., "name='Cognionics Quick-20'" (if that's the name of the stream), or specify the hostname of the computer from which you want to read, as in "name='Cognionics Quick-20' and hostname='My-PC001'" (assuming that this is your hostname). You can find out the names of the streams and of the computers that they run on using the Lab Recorder (it will list them in the format "streamname (hostname)" -- keep in mind that the this is just how the recorder prints it, the " (hostname)" part is of course not baked into the stream name). As the developer of the software, a good way is to warn the user that their query was ambiguous (so they can address it), and inform them that you are using the last-created stream that matches the query. This would be the stream with the highest value for the created_at() property (they come back unordered from the resolve function call). You could also point them to this FAQ entry on how they can make their query more specific.
26 changes: 26 additions & 0 deletions docs/info/getting_started.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Quick Start
###########

A more up-to-date version of this document can be found on the main repository README:
https://github.com/sccn/labstreaminglayer/blob/master/README.md

The most common way to use LSL is to use one or more applications with integrated LSL functionality.

* Take a look at the list of `supported devices <https://labstreaminglayer.readthedocs.io/en/latest/info/supported_devices.html>`_ and follow the instructions to start streaming data from your device. If your device is not in the list then see the "Getting Help" section below.
* Download `LabRecorder <https://github.com/labstreaminglayer/App-LabRecorder>`_ from its `release page <https://github.com/labstreaminglayer/App-LabRecorder/releases>`_. (Note that LabRecorder saves data to `Extensible Data Format (xdf) <https://github.com/sccn/xdf>`_ which has its own set of tools for loading data after finishing recording.)
* Use LSL from your scientific computing environment. LSL has many language interfaces, including Python and Matlab.
* Python users need only `pip install pylsl` then try some of the `provided examples <https://github.com/labstreaminglayer/liblsl-Python/tree/master/pylsl/examples>`_.
* The `Matlab interface <https://github.com/labstreaminglayer/liblsl-Matlab/>`_ is also popular but requires a little more work to get started; please see its README for more info.

If you are not sure what you are looking for then try browsing through the code which has submodule links to different repositories for tools and devices (Apps) and language interfaces (LSL). When you land in a new repository then be sure to read its README and look at its Releases page.

Getting Help
############

If you are having trouble with LSL, there are few things you can do to get help.

* `Read the docs <https://labstreaminglayer.readthedocs.io/>`_
* Search GitHub issues in this repository, in the old `archived repository <https://github.com/sccn/lsl_archived>`_, and in the submodule for your App or language interface of interest.
* Create a new GitHub issue. Please use the repository specific to the item you are having difficulty with. e.g. if you are having trouble with LabRecorder then open a new issue in its repository. If you don't know which repository is best then you can use the parent sccn/labstreaminglayer repository.
* Join the LabStreamingLayer `#users` channel on Slack. `Invite Link <https://join.slack.com/t/labstreaminglayer/shared_invite/enQtMzA2NjEwNDk0NjA5LTcyYWI4ZDk5OTY5MGI2YWYxNmViNjhkYWRhZTkwYWM0ODY0Y2M0YzdlZDRkZTg1OTUwZDU2M2UwNDgwYzUzNDg>`_. Someone there may be able to get to the bottom of your problem through conversation.
* You may also wish to subscribe to the `LSL mailing list <https://mailman.ucsd.edu/mailman/listinfo/lsl-l>`_
13 changes: 0 additions & 13 deletions docs/info/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,3 @@ Developer Information
=====================

Please see the separate `build documentation <https://labstreaminglayer.readthedocs.io/en/latest/dev/dev_guide.html>`__.

Clock synchronization
---------------------

Here we talk about important functions like the local clock
(:func:`lsl_local_clock`), a function to query a time offset
(:func:`lsl_time_correction_ex`) and a function to get the library version used
by the loaded library (:func:`lsl_library_version`).

Creating outlets
----------------

The main outlet class is :class:`lsl::stream_outlet`.
28 changes: 28 additions & 0 deletions docs/info/matlab_example_with_muse.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
This is the series of steps to connect with the Muse.

1. Download the Muse SDK from http://developer.choosemuse.com
2. Pair your computer with your Muse headset and connect to your Muse using muse-io (replace Muse-XXXX with the name of your Muse device as it shows in the Bluetooth settings)
``muse-io --device Muse-XXXX --lsl-eeg EEG``

A successful connection should show like

.. image:: ../images/muse1lsl.png

3. Now the stream should be visible on your local network. To check this is the case, the easiest is to download the `LSL LabRecorder <https://github.com/labstreaminglayer/App-LabRecorder/releases>`_ for data acquisition. This will show you which streams are available and save them in XDF format if you wish to.
#. Download `liblsl-Matlab <https://github.com/labstreaminglayer/liblsl-Matlab/releases>`_
#. Start Matlab and add the folder liblsl-Matlab **with subfolder** to the Matlab path.
#. Clone the `Matlab viewer <https://github.com/labstreaminglayer/App-MATLABViewer/>`_.
#. Go to the folder above and execute ``vis_stream`` from the Matlab command prompt

**Alternative solution**

1. and 2. are identical

3. Clone `BCILAB <https://github.com/sccn/BCILAB>`_. BCILAB contains a version of the Matlab viewers as well as liblsl binaries for Matlab.
#. Start matlab and go to the folder above and type ``bcilab``
#. Go to the subfolder code/visualization of BCILAB and run _vis_stream_ (select all defaults and press OK)
#. If you have problem (such as Error using supergui (line 122) supergui error: argument 'fig' must be numeric), it could be due to the fact that you are using a newer Matlab version that is not compatible with some functions of BCILAB. If this is the case, then install the latest version of `EEGLAB <https://github.com/sccn/eeglab>`_ which will overload the functions that cause problem. Go to the newly downloaded EEGLAB folder and type ``eeglab``. Try again to run ``vis_stream`` as in Step 3.

You should now be able to stream Muse data

.. image:: ../images/muse2lsl.png
2 changes: 1 addition & 1 deletion docs/info/ovas.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ To enable LSL output, click on Preferences in the OVAS application, check the bo

.. image:: ../images/OVAS-main.png

.. image:: ../images/OVAS-driverprops.png
.. image:: ../images/ovas-driverprops.png

Minimizing Latency
******************
Expand Down
1 change: 1 addition & 0 deletions docs/info/supported_devices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The following devices support LSL via vendor-provided software:
* `Cognionics (all headsets) <http://www.cognionics.com/>`__
* `EB Neuro BE Plus LTM <http://www.ebneuro.biz/en/neurology/ebneuro/galileo-suite/be-plus-ltm>`__
* `InteraXon Muse <http://www.choosemuse.com/>`__
* `Example use with Matlab <matlab_example_with_muse>`__
* `mBrainTrain SMARTING <http://www.mbraintrain.com/smarting/>`__
* `neuroelectrics Enobio <http://www.neuroelectrics.com/products/enobio/>`__
* `OpenBCI (all headsets) <http://docs.openbci.com/software/06-labstreaminglayer>`__
Expand Down
Loading

0 comments on commit 3708988

Please sign in to comment.