Commit 2a57e3da authored by Jussi Lindgren's avatar Jussi Lindgren

Contrib: OpenBCI driver stabilization

* Complete rework of the driver's logic
* Added device name in log messages
* Modified default initialization sequence to
    's': stop streaming
    'v': reset board
    custom commands as per configuration in the GUI
    'b': start streaming
* Automatic port now scans available ports and picks first available
* Optimized drift estimation by sending a single block of sample
  per driver mainloop
* Implemented buffering when reading on serial port
* Updated switch/case parsing automaton to use enum values as states
  instead of integers (shuold be easier to read & maintain)
* Optimized the sample buffer array construction by pre-allocating it
  only once and filling the values by copy (no more sample-wise memory
  (de)allocation)
* Added parsing of board replies (when available) to get board
  information and possibly warn if unexpected return content is sent
* Added multiline support in additional commands
* Fixed bad parsing of accelerometers
* GUI: moved hidden widgets in an hidden window instead of hiding the
  widgets in the displayed window
* GUI: now shows feedback about configuration of the device (number
  of EEG channels, number Accelerometer channels, sampling rate)
* GUI: only shows com ports that can be opened, not all of them
  (usually reduces the list to only the OpenBCI board)
* GUI: exposed two different timeouts (see documentation for details)
* DOC: added one page documentation
* Fixed case / namings / indentation / whatever to comply with coding
  rules

Patch contributed by Yann Renard
parent 006b2322
![OpenViBE logo][OpenViBELogo]
![OpenBCI logo][OpenBCILogo]
# OpenBCI driver for OpenViBE documentation #
(documentation stands for [OpenViBE][OpenViBE] **1.2.0-OpenBCI-RC1**)
## Welcome ##
Welcome in the documentation of the [OpenBCI][OpenBCI] driver for [OpenViBE][OpenViBE]. This document gives you step by step details on how to configure your system, hardware and software to get the best possible experience. For further details about OpenBCI and OpenViBE, please refer to their [respective][OpenBCI] [documentation][OpenViBE].
## Getting Started ##
This section gives guidelines on how to set-up your environment configuration to get the best experience using [OpenBCI][OpenBCI] with [OpenViBE][OpenViBE]
### Linux ###
In order to use the [OpenBCI][OpenBCI] driver on Linux, you need to be granted read/write access to the serial port that is used to communicate with the dongle. Unfortunately, this does not come as a standard configuration on many Linux distributions and you will need to configure your setup so that the user is granted these access. On [Ubuntu][UbuntuDotCom] and its derivative, as well as on [Debian][DebianDotOrg], this can simply be achieved by adding the user to the `dialog` group with the following command :
> sudo adduser *username* dialout
Please note that you may need to close and reopen the session for changes to take effect.
On [Fedora][FedoraDotOrg] and its derivative, a similar command should be used to add the user in the `dialout` group :
> sudo usermode -a -G dialout *username*
Again, please note that you may need to close and reopen the session for changes to take effect.
### Windows ###
It is necessary to walk through a litlle bit of configuration to have the best experience with [OpenBCI][OpenBCI] and [OpenViBE][OpenViBE] on Windows. Indeed, when Windows first installs a serial port, its default configuration is not optimal for realtime streaming of the data. It especially configures large buffering and latencies, causing delays in the communication which eventually lead in delays in the acquired signals. While this only delays the detection of Motor Imagery or SSVEP tasks by a few hundreds of milliseconds, this also delays the ERP (eventually leading to a misdetection) and in all cases, alters the neurophysiological validity of the acquired data by a significant fraction. Consequently, it is recommended to configure the serial ports so that they are suitable for realtime use.
Note that this configuration is necessary each time Windows creates a new serial port. This may unfortunately happen **every time** you move the dongle on a new USB port on the computer !
In order to open the configuration dialog of the serial port, please follow these steps:
- Right click on the `Computer` icon and chose `Manage`
- After the `Computer Management` tool is opened, browse the tree in the left pane to the `Device Manager` section
- After the `Device Manager` tree is opened, browse in the right pane to the `Ports (COM & LPT)` section
- Then double click your port (in my case, `USB Serial Port (COM3)`
![The Windows Device Manager][WindowsTweaking1]
- In the `USB Serial Port Properties` dialog, go to the `Port Settings` tab and click `Advanced`
![The Port Settings dialog][WindowsTweaking2]
- This opens the `Advanced Settings for your COM port`
![The Advanced Port Settings dialog][WindowsTweaking3]
From this window, there are three settings we need to change :
- The `Receive` and `Transmit` settings of the `USB Transfer Sizes` section should both be changed from 4096 to **64** as this will reduce the time taken by the OS to deliver the data to either the dongle or the [OpenViBE][OpenViBE] driver
- The `Latency Timer` of the `BM Options` section should be reduced down to **1** as this will dramatically reduce the latency induced by the OS.
After you changed the settings as shown on the following figure, please click `OK`.
![The tweaked configuration of COM ports to have best experienece with OpenViBE and OpenBCI][WindowsTweaking4]
It is *not necessary* to restart the computer for the changes to take effect.
## Configuration ##
The configuration dialog of the [OpenBCI][OpenBCI] driver comes with a number of settings that one can change or pick depending on his preference.
![The OpenBCI configuration dialog][DriverDialog]
The following table documents each option that is specific to the [OpenBCI][OpenBCI] device. Please refer to the [OpenViBE documentation][OpenViBEDoc] for anything generic to the [OpenViBE][OpenViBE] Acquisition Server.
| Option | Default Value | Documentation |
| :-------------------------: | :-------------------------: | :-----------------------------------------------------------------------------------|
| **Device** | *empty* | This allows you to pick a serial port to connect on. The drodown list shows the serial ports that can currently be opened on this computer. If no port is found, the mention *No valid serial port* is shown in this list. If you cannot find your device in this list, please refer . |
| **Use Daisy Module** | *false* | This allows you to configure the daisy module. Four cases should be considered. 1/ if the daisy module is present and this option is set to **true**, then the device will turn to 16 channels samples 125 Hz. 2/ if no daisy module is present and this option is set to **false**, then the device will turn to 8 channels, 250 Hz. 3/ if the daisy module is **not present** on the board and this option is set to **true**, then the initialization of the driver will **fail**. 4/ if the daisy module is **present** on the board and this option is set to **false**, the daisy module will be disabled and the acquisition will be done as if the daisy module was not present on the board, turning the device back to 8 channels sampled at 125 Hz. |
| **Custom Command On Initialization** | *empty* | This option contains additional commands to send to the device at initialization. You must use one line per command, some command may contain multiple characters. For details about the commands, please refer to the [OpenBCI protocol documentation][OpenBCIProto]. Be advised that this will increase the delay of initialization by an order of magnitude that is a direct relation of the number and types of commands you want to add. Finally, not all the commands take the same time to be executed, if you include custom commands, you should consider adjusting the timeout values. |
| **Board Reply Reading Timeout** | 5000 | This allows to define the maximum time until reading a reply from the board after sending a command times out. Many commands end with a **\$\$\$** pattern, which can handily be captured and release the waiting loop when reading the board reply, but not all the commands have this **\$\$\$** pattern. Consequently, it is necessary to have a timeout for the other commands. The default value has been chosen to behave well even with custom commands that need a long time to reply such as **?**. If you don't use such command in your *Custom Command On Initialization*, you may reduce that delay. But be aware that if you reduce it too much, the driver may miss the **\$\$\$** pattern even though the board has sent it, resulting in unexpected behavior. |
| **Board Reply Flushing Timeout** | 500 | This option allows to flush and get rid of the streaming buffer. This is especially used when the driver asks the board to stop streaming and makes the streaming state absolutely clean when the driver needs to send a new command after stopping the streaming. You may reduce this value to make (re)connection faster, but if the buffer came not to be completely flushed, the remaining would be taken as the begining of the next command and this may result in unexpected behavior. |
The Configuration Summary gives information on the current configuration, especially the number of channels and the sampling rate of the device.
## Advanced Configuration ##
In addition to the above settings, another few settings are available to the user as advanced configuration. They are not exposed in the GUI and should be directly set in the [OpenViBEConfig][OpenViBE configuration] file instead. Refere to the [Configuration Manager section][OpenViBEConfig] of the [OpenViBE documentation][OpenViBEDoc] for further details on the configuration file format, location and others.
| Token | Default Value | Documentation |
| :-------------------------: | :-------------------------: | :-----------------------------------------------------------------------------------|
| **AcquisitionDriver OpenBCI MissingSampleDelayBeforeReset** | *1000* | This defines the size of the window to continuously monitor reception of samples from the driver. If no sample is received within that timeframe, the board is requested to stop and restart streaming. While the non-reception of samples from the board may reflect an unexpected state in the board, this strategy seems to sometimes recover and let the streaming go back to normal. The default value allows a good compromise between dealing with buffering and actual transmission delays and recovering fast when something goes wrong. If you experience such unstability in the transmision, we recommend that you first explore anything that may (in)directly affect the quality of the transmission before tweaking this setting. |
| **AcquisitionDriver OpenBCI DroppedSampleCountBeforeReset** | *5* | This defines the number of sample loss events until a recovery is attempted. It happens that the board gets in an unstable state where some sample would be missing in the stream. Stopping and restarting the streaming has proved to recover well. The default setting has been set so that a few occasional sample loss may occur (due to e.g. quality transmission) and be corrected by the drift correction process, while not waiting too long to attempt recovery when too many sample are lost. |
| **AcquisitionDriver OpenBCI DroppedSampleSafetyDelayBeforeReset** | *1000* | This defines a sefety delay where no reset should be attempted because of sample loss (see **AcquisitionDriver OpenBCI DroppedSampleCountBeforeReset**). This prevents a reset on the first sample where the driver synchronises with the streaming protocol and may miss a few samples until it is perfectly synced with the header and tail of the protocol frame. |
[FedoraDotOrg]: http://www.fedora.org
[UbuntuDotCom]: http://www.ubuntu.com
[DebianDotOrg]: http://www.debian.org
[OpenViBE]: http://openvibe.inria.fr
[OpenViBEDoc]: http://openvibe.inria.fr/documentation-index
[OpenViBEConfig]: http://openvibe.inria.fr/the-configuration-manager
[OpenViBELogo]: http://openvibe.inria.fr/openvibe/wp-content/themes/openvibe/images/openvibe-banner.png
[OpenBCI]: http://docs.openbci.com
[OpenBCIProto]: http://docs.openbci.com/software/01-OpenBCI_SDK
[OpenBCILogo]: http://openbci.com/community/wp-content/uploads/2015/09/logo_wide_web_borders_BIG-copy-1024x137.png
[WindowsTweaking1]: ServerDriver_OpenBCI_windows_tweaking_1.png
[WindowsTweaking2]: ServerDriver_OpenBCI_windows_tweaking_2.png
[WindowsTweaking3]: ServerDriver_OpenBCI_windows_tweaking_3.png
[WindowsTweaking4]: ServerDriver_OpenBCI_windows_tweaking_4.png
[DriverDialog]: ServerDriver_OpenBCI_configuration.png
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkAdjustment" id="adjustment1">
<property name="value">8</property>
<property name="lower">1</property>
<property name="upper">256</property>
<property name="value">8</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="value">18</property>
<property name="upper">100</property>
<property name="value">18</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
......@@ -20,6 +20,27 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment_flush_board_reply_timeout">
<property name="lower">1</property>
<property name="upper">30000</property>
<property name="value">500</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment_read_board_reply_timeout">
<property name="lower">1</property>
<property name="upper">30000</property>
<property name="value">5000</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment_sampling_frequency">
<property name="lower">4</property>
<property name="upper">4096</property>
<property name="value">250</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkListStore" id="model1">
<columns>
<!-- column-name gchararray -->
......@@ -40,393 +61,668 @@
</row>
</data>
</object>
<object class="GtkAdjustment" id="adjustment_sampling_frequency">
<property name="value">250</property>
<property name="lower">4</property>
<property name="upper">4096</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment_com_delay">
<property name="value">100</property>
<property name="lower">1</property>
<property name="upper">30000</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<object class="GtkWindow" id="openvibe-acquisition-server-dummy">
<property name="can_focus">False</property>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">10</property>
<property name="n_columns">2</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel" id="label_number_of_channels">
<property name="can_focus">False</property>
<property name="label" translatable="yes">Number of channels :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_sampling_frequency">
<property name="can_focus">False</property>
<property name="label" translatable="yes">Sampling frequency :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="combobox_sampling_frequency">
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment_sampling_frequency</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Device configuration</property>
<property name="window_position">center</property>
<property name="type_hint">dialog</property>
<property name="gravity">center</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
<property name="spacing">8</property>
<child>
<object class="GtkLabel" id="label_title">
<property name="visible">True</property>
<property name="label" translatable="yes">OpenBCI reader
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;big&gt;OpenBCI reader&lt;/big&gt;&lt;/b&gt;
&lt;small&gt;Upon initialization, board responses will appear in the console
and should end by "&lt;b&gt;$$$&lt;/b&gt;" after a few seconds, before stream starts.
at &lt;b&gt;Trace&lt;/b&gt; level and should end by "&lt;b&gt;$$$&lt;/b&gt;".
The last 3 channels represent accelerometer values.
The sampling rate and the total number of channels are automatically
selected depending on the &lt;b&gt;Daisy module&lt;/b&gt; option
selected depending on the &lt;b&gt;Use Daisy Module&lt;/b&gt; option
(8 EEG + 3 acc. at 250Hz without, 16 EEG + 3 acc. at 125Hz with).
Use "&lt;b&gt;Send command on init&lt;/b&gt;" to configure the board between
Use "&lt;b&gt;Custom Commands on Initialisation&lt;/b&gt;" to configure the board between
reset and streaming. Refer to OpenBCI doc for more details.&lt;/small&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="position">1</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button_apply">
<property name="label">gtk-apply</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator">
<object class="GtkHSeparator" id="hseparator5">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">4</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox">
<object class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="border_width">8</property>
<property name="spacing">8</property>
<property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<child>
<object class="GtkLabel" id="label_identifier">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Identifier :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_identifier">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="invisible_char_set">True</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
<property name="adjustment">adjustment3</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox">
<object class="GtkLabel" id="label_gender">
<property name="visible">True</property>
<property name="spacing">8</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Gender :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox_gender">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">model1</property>
<child>
<object class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="n_rows">6</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkLabel" id="label_identifier">
<property name="visible">True</property>
<property name="label" translatable="yes">Identifier :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_identifier">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment3</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_gender">
<property name="visible">True</property>
<property name="label" translatable="yes">Gender :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox_gender">
<property name="visible">True</property>
<property name="model">model1</property>
<child>
<object class="GtkCellRendererText" id="renderer1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_age">
<property name="visible">True</property>
<property name="label" translatable="yes">Age :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_age">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment2</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
<property name="visible">False</property>
<property name="can_focus">False</property>
<property name="adjustment">adjustment1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_number_of_channels">
<property name="visible">False</property>
<property name="label" translatable="yes">Number of channels :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="combobox_sampling_frequency">
<property name="visible">False</property>
<property name="can_focus">False</property>
<property name="adjustment">adjustment_sampling_frequency</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_sampling_frequency">
<property name="visible">False</property>
<property name="label" translatable="yes">Sampling frequency :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_daisy_module">
<property name="visible">True</property>
<property name="label" translatable="yes">Daisy module :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="checkbutton_daisy_module">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>