Difference between revisions of "NeuroMD SDK Manual"
NateColeman (talk | contribs) (→Quickstart) |
|||
Line 1: | Line 1: | ||
− | This section contains information about software development kit required for using | + | This section contains information about software development kit required for using NeuroMD devices in user applications. |
=Introduction= | =Introduction= |
Revision as of 04:46, 18 June 2018
This section contains information about software development kit required for using NeuroMD devices in user applications.
Introduction
NeuroSDK as a part of Callibri and Brainbit products is a set of libraries for different operating systems and platforms providing full set of tools needed for configuring and receiving data from Callibri and Brainbit devices. This software development kit consists of portable core written on C++11/14 and a set of wrappers for different languages and platforms: Java for Android operating system, Objective-C++ for iOS/MacOS, C# for Windows, Python for different OS'es. Also core could be used for making cross-platform applications in C++ using different compilers and IDEs.
NeuroSDK is intended to be used by developers of different systems which requires biopotential data in conjunction with information about movements and respiratory activity. NeuroSDK is also provides tools for making electrostimulation using Callibri MotionAssistant device.
Quickstart
To get started with software development kit you need to go through several steps which could vary in details for different platforms, but the essential sequence is constant.
Download and link
First of all download actual libraries for your platform and link them to your project. You could find detailed tutorial for each plantform on page Binaries and downloads. SDK limitations and features for BLE device communications on different platforms are also described there. Once you've downloaded and linked binaries to your project you may start using NeuroMD SDK classes to work with Callibri and Brainbit devices.
Scan for devices
To find devices you need to cteate DeviceScanner object, subscribe DeviceFound event and call startScan method. Using Java for Android you should pass application Context object to its constructor (see "Java" tab). If you're making your application using C++ you should call factory method createDeviceScanner to get scanner object.
auto scanner = Neuro::createDeviceScanner(); scanner->subscribeDeviceFound([](auto&& device_ptr){ onDeviceFound(std::forward<decltype(device_ptr)>(device_ptr)); }); scanner->startScan(0);//zero timeout for infinity
mScanner = new DeviceScanner(getApplicationContext()); mScanner.deviceFound.subscribe(new INotificationCallback<Device>() { @Override public void onNotify(Object o, Device device) { //working with found device onDeviceFound(device); } }); mScanner.startScan(0); //zero is for infinity
Read device name and address and connect to it
Basicly you should read device parameters only if device is in "Connected" state, but parameters "Name", "Address" and "State" could be read on "Disconnected" device. To read params call readParam method passing appropriate ParamName parameter to method in case of Java or with template parameter of type Parameter in case of C++.
template <typename T> void onDeviceFound(T&& device_ptr){ auto deviceName = device_ptr->readParam<Neuro::Parameter::Name>(); auto deviceAddress = device_ptr->readParam<Neuro::Parameter::Address>(); auto deviceState = device_ptr->readParam<Neuro::Parameter::State>(); using device_t = typename std::remove_reference_t<decltype(device_ptr)>::element_type; auto sharedDevice = std::shared_ptr<device_t>(std::forward<T>(device_ptr)); if (deviceState != Neuro::DeviceState::Connected) { connectDevice(sharedDevice); } } template <typename T> void connectDevice(T&& device_ptr){ using device_t = typename std::remove_reference_t<decltype(device_ptr)>::element_type; auto weakDevice = std::weak_ptr<device_t>(device_ptr); device_ptr->setParamChangedCallback([weakDevice](auto param){ if (param == Parameter::State){ auto device = weakDevice.lock(); if (device != nullptr) { auto state = device->readParam<Parameter::State>(); if (state == Neuro::DeviceState::Connected) { readDeviceFeatures(device); } } } }); device_ptr->connect(); FoundDevices.push_back(device_ptr); }
private void onDeviceFound(final Device device) { String deviceName = device.readParam(ParameterName.Name); String deviceAddress = device.readParam(ParameterName.Address); device.parameterChanged.subscribe(new INotificationCallback<ParameterName>() { @Override public void onNotify(Object o, ParameterName parameterName) { if (parameterName == ParameterName.State){ DeviceState state = device.readParam(ParameterName.State); if (state == DeviceState.Connected){ runOnUiThread(new Runnable() { @Override public void run() { onDeviceConnected(device); } }); } } } }); device.connect(); }
Explore device parameters, channels and commands
Now you could get information about device features: channels, commands and parameters. All communications with device modules are performed through these three substancies.
template <typename T> void readDeviceFeatures(T&& device_ptr){ auto commands = device_ptr->commands(); for (auto& cmd : commands){ auto cmdName = Neuro::to_string(cmd); } auto params = device_ptr->parameters(); for (auto& paramPair : params){ auto paramName = Neuro::to_string(paramPair.first); auto paramAccessMode = Neuro::to_string(paramPair.second); } auto channels = device_ptr->channels(); for (auto& channel : channels){ auto channelName = channel.getName(); } }
private void onDeviceConnected(Device device) { Parameter[] deviceParams = device.parameters(); for (Parameter param : deviceParams) { String paramName = param.getName().toString(); String accessMode = param.getAccess().toString(); } Command[] deviceCommands = device.commands(); for (Command cmd : deviceCommands) { String cmdName = cmd.toString(); } ChannelInfo[] deviceChannels = device.channels(); for (ChannelInfo channel : deviceChannels) { String channelName = channel.getName(); } }
Device abstraction
Every physical device, Callibri or Brainbit, is represented in NeuroSDK as Device abstraction which has state and set of possible control actions. Device state (should not be confused with connection state - one of device parameters) is characterized by parameters of device , which could be read with special function call readParam. Control actions are presented as set of possible commands and set of writable parameters. Commands could be executed by execute function, parameters could be set with setParam function. Device also has separate functions to control connection state. You can get list of all supported parameters by calling parameters member function. To get list of supported commands call commands member function. More information about these functions available on appropriate Device pages for different languages.
namespace Neuro { class Device final { private: std::unique_ptr<DeviceImpl> mImpl; public: ~Device(); void connect(); void disconnect(); std::vector<ChannelInfo> channels() const; std::vector<Command> commands() const; std::vector<ParamPair> parameters() const; bool execute(Command); void setParamChangedCallback(std::function<void(Parameter)>); template <Parameter P> typename ParamValue<P>::Type readParam() const; template <Parameter P> bool setParam(typename ParamValue<P>::Type value); private: Device(std::unique_ptr<DeviceImpl>); }; bool checkHasChannel(const Device &, const ChannelInfo &); bool checkHasCommand(const Device &, Command); bool checkHasParameter(const Device &, Parameter); std::size_t countChannelsWithType(const Device &, ChannelInfo::Type); ParamAccess getParameterAccess(const Device &, Parameter); }
public class Device { public final SubscribersNotifier<ParameterName> parameterChanged; public native void connect(); public native void disconnect(); public native ChannelInfo[] channels(); public native Command[] commands(); public native Parameter[] parameters(); public native boolean execute(Command cmd); public native <ParamType> ParamType readParam(ParameterName param); public native boolean setParam(ParameterName param, Object value); }
Device commands
Whole list of device commands consists of:
- 1. StartSignal command
- 2. StopSignal command
- 3. StartResist command
- 4. StopResist command
- 5. StartMEMS command
- 6. StopMEMS command
- 7. StartRespiration command
- 8. StopRespiration command
- 9. StartStimulation command
- 10. EnableMotionAssistant command
- 11. FindMe command
Device parameters
Set of command for specific device depends on which modules does device have. This is also true for parameters set. All possible parameters are listed below:
- 1. Name,
- 2. State,
- 3. Address,
- 4. SerialNumber,
- 5. HardwareFilterState,
- 6. FirmwareMode,
- 7. SamplingFrequency,
- 8. Gain,
- 9. Offset,
- 10. ExternalSwitchState,
- 11. ADCInputState,
- 12. AccelerometerSens,
- 13. GyroscopeSens,
- 14. StimulatorState,
- 15. MotionAssistantState,
- 16. StimulatorParamPack,
- 17. MotionAssistantParamPack
Device could be configured by setting these parameters, but whether each parameter could be set or not is represented by parameter access modifier:
- 1. Read,
- 2. ReadWrite,
- 3. ReadNotify
Parameters could be readonly, for some of them device provides notification mechanism. Parameters with ReadWrite notifier could be set from user code.
Device channels
All NeuroMD devices provides biopotential signals and telemetry information through channels. Channel set of device depends on device modules. Information about channels could be obtained by calling channels method of device class. All channel classes which device has are inherited from BaseChannel abstract class/interface. Custom channels (channels which do not present in device, also user-created) could be inherited from BaseChannel, but it is not necessary. Representing device information by channels gives easy way to process heterogeneous information in one manner. For example, it is easy to plot function of battery charge, biosignal, and connection statistics depend on same time scale.
namespace Neuro { template <typename DataType> class BaseChannel { public: using data_container = std::vector<DataType>; using length_callback_t = std::function<void(data_length_t)>; using length_listener_ptr = ListenerPtr<void, data_length_t>; BaseChannel(ChannelInfo &&info) noexcept : mInfo(std::move(info)) {} BaseChannel(const ChannelInfo &info) : mInfo(info) {} virtual ~BaseChannel() = default; ChannelInfo& info() noexcept { return mInfo; } virtual length_listener_ptr subscribeLengthChanged(length_callback_t callback) noexcept = 0; virtual data_container readData(data_offset_t, data_length_t) const = 0; virtual data_length_t totalLength() const noexcept = 0; virtual data_length_t bufferSize() const noexcept = 0; virtual sampling_frequency_t samplingFrequency() const noexcept = 0; virtual void setSamplingFrequency(sampling_frequency_t) = 0; virtual std::weak_ptr<Device> underlyingDevice() const noexcept = 0; protected: ChannelInfo mInfo; }; }
public abstract class BaseChannel<SampleType> { public final SubscribersNotifier<Long> dataLengthChanged = new SubscribersNotifier<>(); public abstract ChannelInfo info(); public abstract SampleType[] readData(long offset, long length); public abstract long totalLength(); public abstract long bufferSize(); public abstract float samplingFrequency(); public abstract void setSamplingFrequency(float frequency); public abstract Device underlyingDevice(); }
Channel library
BatteryChannel
Built-in channel containing battery charge data
ConnectionStatsChannel
ElectrodesStateChannel
MEMSChannel
OrientationChannel
RespirationChannel
SignalChannel
Built-in channel containing signal samples of one physical channel of device
Devices versions
Callibri and Brainbit devices could have different sets of modules and functions depend on purpose of specific device. In this section all possible compositions of modules or functions are listed
Callibri
Since Callibri device is positioned as universal biopotential measuring device it has modules to provide as much information as possible.
All posible modules of Callibri device are:
Base Callibri module
Base module is responsible for common Callibri device configuration and BLE configuration.
Signal module
This module controls wide range of bio signals acquisition and its parameters such as sampling frequency, gain etc.
Respiration module
Respiration module controls special signal acquisition - respiration characteristic.
Micro electro-mechanical system module
This module is responsible for orientation and acceleration tracking of device.
Stimulation module
Special module for Callibri MotionAssistant which provides output with stimulation current pulses for miostimulation.
Most commonly Callibri device are supplied with all of these modules except stimulation module. Stimulation module is included in Callibri MotionAssistant modification. Modules could be configured through device parameters. Its is also possible to execute set of module-specific commands. List of supported commands and parameters could be acquired from device by appropriate calls to device library. All possible commands and parameters are listed in separate section below.
Brainbit
Brainbit is more simplier than Callibri device and has only two modules
Base Brainbit module
Base module is responsible for connection establishing through BLE and for device state and mode control.
Signal module
Signal module is responsible for acquisition of EEG signal from 4 device electrodes and for measuring electrodes resistance.
There is no modifications for Brainbit device yet, so it has fixed set of commands and parameters.