Introduction
//get the version of MSCL (MSCL_VERSION.majorPart(), .minorPart(), and .patchPart() are also available)
+cout << mscl::MSCL_VERSION.str() << endl;
+
//get the version of MSCL (MSCL_VERSION.majorPart(), .minorPart(), and .patchPart() are also available)
+Console.WriteLine(mscl.MSCL_VERSION.ToString());
+
# get the version of MSCL (MSCL_VERSION.majorPart(), .minorPart(), and .patchPart() are also available)
+print mscl.MSCL_VERSION
+
+Welcome to the MSCL (MicroStrain Communication Library) Quick Start Guide!
+ +You can use MSCL to interact with sensors developed by LORD Sensing.
+ +MSCL has language bindings in C++, .NET (C#, VB, LabView, MATLAB), and Python! You can view code examples in the area to the right, and you can switch the programming language of the examples with the tabs above that.
+ +This guide will help get you started with using MSCL. It does not encompass everything that MSCL can do. Please see the complete Documentation for a list of all functionality.
+ +For simplicity, the examples below do not show the necessary error handling that should be performed when using MSCL. Please see the API Documentation for details on Exceptions that each function can throw.
+Wireless
Creating a BaseStation
#include "mscl/mscl.h"
+
+//create the connection object with port and baud rate
+mscl::Connection connection = mscl::Connection::Serial("COM3", 921600);
+
+//create the BaseStation, passing in the connection
+mscl::BaseStation basestation(connection);
+
//create the connection object with port and baud rate
+mscl.Connection connection = mscl.Connection.Serial("COM3", 921600);
+
+//create the BaseStation, passing in the connection
+mscl.BaseStation basestation = new mscl.BaseStation(connection);
+
import mscl
+
+#create the connection object with port and baud rate
+connection = mscl.Connection.Serial("COM3", 921600)
+
+#create the BaseStation, passing in the connection
+basestation = mscl.BaseStation(connection)
+
+++ +Make sure to replace
+COM3
and921600
with your own port settings.
To interface with a wireless device, you will first need to create a BaseStation
that represents a physical BaseStation or Gateway.
A BaseStation
takes a Connection
as a parameter.
Serial
, TcpIp
, and UnixSocket
are all available as connection types.
Creating a WirelessNode
+//create a WirelessNode with the BaseStation
+mscl::WirelessNode node(31849, baseStation);
+
//create a WirelessNode with the BaseStation we created
+mscl.WirelessNode node = new mscl.WirelessNode(31849, baseStation);
+
# create a WirelessNode with the BaseStation we created
+node = mscl.WirelessNode(31849, baseStation)
+
+++ +Make sure to replace
+31849
with the Node Address of your Wireless Node.
To communicate with any Wireless Node, create a WirelessNode
instance, providing the Node Address of the physical Node, and the BaseStation
that you wish to communicate to the Node with.
Communicating with a Node
//ping the Node
+mscl::PingResponse response = node.ping();
+
+//if the ping response was a success
+if(response.success())
+{
+ response.baseRssi(); //the BaseStation RSSI
+ response.nodeRssi(); //the Node RSSI
+}
+
//ping the Node
+mscl.PingResponse response = node.ping();
+
+//if the ping response was a success
+if (response.success())
+{
+ response.baseRssi(); //the BaseStation RSSI
+ response.nodeRssi(); //the Node RSSI
+}
+
# ping the Node
+response = node.ping()
+
+# if the ping response was a success
+if response.success():
+ response.baseRssi() # the BaseStation RSSI
+ response.nodeRssi() # the Node RSSI
+
+A Wireless Node, when powered on, can be in 1 of 3 states:
+ +Node State | +Description | +
---|---|
Idle | +The Node is awake and waiting for commands. | +
Sleep | +The Node is in a low power sleep state. | +
Sampling | +The Node is actively sampling / sending data. | +
If a Node is in an Idle state, it will respond to pings, can be configured, and can be put into the other states (sleep or sampling). To test communicating with a WirelessNode that is in an idle state, use the ping()
function.
//call the setToIdle function and get the resulting SetToIdleStatus object
+mscl::SetToIdleStatus idleStatus = node.setToIdle();
+
+// checks if the set to idle operation has completed (successfully or with a failure)
+while(!idleStatus.complete())
+{
+ cout << ".";
+
+ if(userCancels)
+ idleStatus.cancel();
+}
+
+//check the result of the Set to Idle operation
+switch(idleStatus.result())
+{
+case mscl::SetToIdleStatus::setToIdleResult_success:
+ cout << "Node is now in idle mode." << endl;
+ break;
+
+case mscl::SetToIdleStatus::setToIdleResult_canceled:
+ cout << "Set to Idle was canceled!" << endl;
+ break;
+
+case mscl::SetToIdleStatus::setToIdleResult_failed:
+ cout << "Set to Idle has failed!" << endl;
+ break;
+}
+
//call the setToIdle function and get the resulting SetToIdleStatus object
+mscl.SetToIdleStatus idleStatus = node.setToIdle();
+
+// checks if the Set to Idle operation has completed (successfully or with a failure)
+while (!idleStatus.complete())
+{
+ Console.Write(".");
+
+ if(userCancels)
+ idleStatus.cancel();
+}
+
+//check the result of the Set to Idle operation
+switch (idleStatus.result())
+{
+ case mscl.SetToIdleStatus.SetToIdleResult.setToIdleResult_success:
+ Console.WriteLine("Node is now in idle mode.");
+ break;
+
+ case mscl.SetToIdleStatus.SetToIdleResult.setToIdleResult_canceled:
+ Console.WriteLine("Set to Idle was canceled!");
+ break;
+
+ case mscl.SetToIdleStatus.SetToIdleResult.setToIdleResult_failed:
+ Console.WriteLine("Set to Idle has failed!");
+ break;
+}
+
# call the setToIdle function and get the resulting SetToIdleStatus object
+idleStatus = node.setToIdle()
+
+# checks if the Set to Idle operation has completed (successfully or with a failure)
+while not idleStatus.complete():
+ print ".",
+
+ if userCancels:
+ idleStatus.cancel()
+
+# check the result of the Set to Idle operation
+result = idleStatus.result()
+if result == mscl.SetToIdleStatus.setToIdleResult_success:
+ print "Node is now in idle mode."
+elif result == mscl.SetToIdleStatus.setToIdleResult_canceled:
+ print "Set to Idle was canceled!"
+else:
+ print "Set to Idle has failed!"
+
+If a Node is in the Sleep state or Sampling state, it cannot be directly communicated with. In order to communicate with the Node, it must be put back into the Idle state. This can be done by using the setToIdle()
function.
The Set to Idle operation uses all of the BaseStation's bandwidth to attempt to stop/wake the Node. Thus, this can only be performed on a single Node at a time. Once started, the operation will continue until canceled by calling the cancel()
function on the SetToIdleStatus
object returned from the setToIdle()
function.
Configuring a Node
Getting Current Config
//get the number of datalogging sessions stored on the node
+node.getNumDatalogSessions();
+
+//get the user inactivity timeout in seconds
+node.getInactivityTimeout();
+
+//get the ActiveChannels
+node.getActiveChannels();
+
+//get the number of sweeps to sample for
+node.getNumSweeps();
+
+//verify Thermocouple Type is supported by this Node
+if(node.features().supportsThermocoupleType())
+{
+ //get the thermocouple type
+ node.getThermocoupleType();
+}
+
//get the number of datalogging sessions stored on the node
+node.getNumDatalogSessions();
+
+//get the user inactivity timeout in seconds
+node.getInactivityTimeout();
+
+//get the ActiveChannels
+node.getActiveChannels();
+
+//get the number of sweeps to sample for
+node.getNumSweeps();
+
+//verify Thermocouple Type is supported by this Node
+if(node.features().supportsThermocoupleType())
+{
+ //get the thermocouple type
+ node.getThermocoupleType();
+}
+
# get the number of datalogging sessions stored on the node
+node.getNumDatalogSessions()
+
+# get the user inactivity timeout in seconds
+node.getInactivityTimeout()
+
+# get the ActiveChannels
+node.getActiveChannels()
+
+# get the number of sweeps to sample for
+node.getNumSweeps()
+
+# verify Thermocouple Type is supported by this Node
+if node.features().supportsThermocoupleType():
+ # get the thermocouple type
+ node.getThermocoupleType()
+
+To get the current configuration settings of a Wireless Node, each setting has an individual function on the WirelessNode
object.
However, not all settings are supported by all Nodes. If a function is called (such as getThermocoupleType()
) on a Node that doesn't support that feature (doesn't support thermocouples), it will throw an Error_NotSupported
exception.
Use the NodeFeatures
class (node.features()
) to check which features are available for the Wireless Node.
+
+
+
Setting the Config
//create a WirelessNodeConfig which is used to set all node configuration options
+mscl::WirelessNodeConfig config;
+
+//set the configuration options that we want to change
+config.bootMode(mscl::WirelessTypes::bootMode_normal);
+config.inactivityTimeout(7200);
+config.samplingMode(mscl::WirelessTypes::samplingMode_sync);
+config.sampleRate(mscl::WirelessTypes::sampleRate_256Hz);
+config.unlimitedDuration(true);
+
+//apply the configuration to the Node
+node.applyConfig(config);
+
//create a WirelessNodeConfig which is used to set all node configuration options
+mscl.WirelessNodeConfig config = new mscl.WirelessNodeConfig();
+
+//set some of the node's configuration options
+config.bootMode(mscl.WirelessTypes.BootMode.bootMode_normal);
+config.inactivityTimeout(7200);
+config.samplingMode(mscl.WirelessTypes.SamplingMode.samplingMode_sync);
+config.sampleRate(mscl.WirelessTypes.WirelessSampleRate.sampleRate_256Hz);
+config.unlimitedDuration(true);
+
+//apply the configuration to the Node
+node.applyConfig(config);
+
# create a WirelessNodeConfig which is used to set all node configuration options
+config = mscl.WirelessNodeConfig()
+
+# set the configuration options that we want to change
+config.bootMode(mscl.WirelessTypes.bootMode_normal)
+config.inactivityTimeout(7200)
+config.samplingMode(mscl.WirelessTypes.samplingMode_sync)
+config.sampleRate(mscl.WirelessTypes.sampleRate_256Hz)
+config.unlimitedDuration(True)
+
+# apply the configuration to the Node
+node.applyConfig(config)
+
+++ +These are just some examples of the Wireless Node config options available. See the complete API Documentation for the full list of available options.
+
To change configuration settings of a Wireless Nodes, use the WirelessNodeConfig
object. This object contains functions for all Wireless Node config options.
When setting an option in the WirelessNodeConfig
, the value is just stored on that object itself. The object must then be given as a parameter in the node.applyConfig()
function to write all of the changed options to the physical Node. In this way, you can apply the same WirelessNodeConfig
object to multiple Nodes if desired.
Some of the configuration options may conflict with other options that are being set, or that are currently set on the Wireless Node. For example, only certain Sample Rates are supported in the Burst Sync Sampling mode. If you only update the WirelessNodeConfig
object's sample rate, this will be checked against the current sampling mode on the Node when applying and will throw an Error_InvalidNodeConfig
exception if it is invalid for that sampling mode. If both the sample rate and sampling mode are set in the WirelessNodeConfig
, they will be checked against each other before applying to the Node.
Not all settings are supported by all Nodes. If the WirelessNodeConfig
has an option set (such as thermocoupleType()
) and that config is applied to a Node that doesn't support that feature (doesn't support thermocouples), it will throw an Error_InvalidNodeConfig
exception.
Use the NodeFeatures
class (node.features()
) to check which features are available for the Wireless Node.
Starting Sampling
+There are 2 main sampling modes: Synchronized Sampling and Non-Synchronized Sampling.
+//create a SyncSamplingNetwork object, giving it the BaseStation that will be the master BaseStation for the network
+mscl::SyncSamplingNetwork network(baseStation);
+
+//add a WirelessNode to the network.
+//Note: The Node must already be configured for Sync Sampling before adding to the network, or else Error_InvalidNodeConfig will be thrown.
+network.addNode(node);
+
+network.ok(); //check if the network status is ok
+network.lossless(true); //enable Lossless for the network
+network.percentBandwidth(); //get the total percent of bandwidth of the network
+
+//apply the network configuration to every node in the network
+network.applyConfiguration();
+
+//start all the nodes in the network sampling.
+network.startSampling();
+
//create a SyncSamplingNetwork object, giving it the BaseStation that will be the master BaseStation for the network
+mscl.SyncSamplingNetwork network = new mscl.SyncSamplingNetwork(baseStation);
+
+//add a WirelessNode to the network.
+//Note: The Node must already be configured for Sync Sampling before adding to the network, or else Error_InvalidNodeConfig will be thrown.
+network.addNode(node);
+
+network.ok(); //check if the network status is ok
+network.lossless(true); //enable Lossless for the network
+network.percentBandwidth(); //get the total percent of bandwidth of the network
+
+//apply the network configuration to every node in the network
+network.applyConfiguration();
+
+//start all the nodes in the network sampling.
+network.startSampling();
+
# create a SyncSamplingNetwork object, giving it the BaseStation that will be the master BaseStation for the network
+network = mscl.SyncSamplingNetwork(baseStation)
+
+# add a WirelessNode to the network.
+# Note: The Node must already be configured for Sync Sampling before adding to the network, or else Error_InvalidNodeConfig will be thrown.
+network.addNode(node)
+
+network.ok() # check if the network status is ok
+network.lossless(true) # enable Lossless for the network
+network.percentBandwidth() # get the total percent of bandwidth of the network
+
+# apply the network configuration to every node in the network
+network.applyConfiguration()
+
+# start all the nodes in the network sampling.
+network.startSampling()
+
Synchronized Sampling
+Synchronized Sampling involves creating a Time Division Multiple Access (TDMA) network with one or more Wireless Nodes tied to a single Base Station. In this mode, all of the Nodes will have a common timestamp that is transmitted with the data, so that data can be accurately aligned. +The BaseStation provides a beacon which keeps the Nodes in the network time-synchronized to +/- 32 microseconds.
+ +To start a Sync Sampling network, create a SyncSamplingNetwork
instance. This takes a BaseStation
as a parameter, which will be the master BaseStation that communicates with all of the Nodes, starts them sampling, and provides the beacon.
After you have configured all of the Nodes, add them to the network using the networks addNode()
function.
Once all of the Nodes have been added to the network, start the network using the network's startSampling()
function.
//start the Non-Sync Sampling session.
+node.startNonSyncSampling();
+
//start the Non-Sync Sampling session.
+node.startNonSyncSampling();
+
# start the Non-Sync Sampling session.
+node.startNonSyncSampling()
+
Non-Synchronized Sampling
+Non-Synchronized Sampling requires no beacon, or network. Instead, each Node is set to sample individually. This mode has much lower latency than Synchronized Sampling.
+ +Since there is no synchronization, there is a high chance of data loss, as Nodes could send their data at the same time as other Nodes, resulting in a loss of packets. This sampling mode also does not transmit timestamps with the data. Instead, timestamps will be assigned to the data when MSCL receives it.
+ +To start a Non-Sync Sampling network, use the startNonSyncSampling()
function on the WirelessNode
object.
Collecting Data
//create the BaseStation, passing in the connection
+mscl::BaseStation basestation(connection);
+
+while(true)
+{
+ //get all the data sweeps that have been collected, with a timeout of 500 milliseconds
+ mscl::DataSweeps sweeps = basestation.getData(500);
+
+ for(mscl::DataSweep sweep : sweeps)
+ {
+ sweep.nodeAddress(); //the node address the sweep is from
+ sweep.timestamp(); //the TimeStamp of the sweep
+ sweep.tick(); //the tick of the sweep (0 - 65535 counter)
+ sweep.sampleRate(); //the sample rate of the sweep
+ sweep.samplingType(); //the SamplingType of the sweep (sync, nonsync, burst, etc.)
+ sweep.nodeRssi(); //the signal strength at the Node
+ sweep.baseRssi(); //the signal strength at the BaseStation
+ sweep.frequency(); //the radio frequency this was collected on
+
+ //get the vector of data in the sweep
+ mscl::ChannelData data = sweep.data();
+
+ //iterate over each point in the sweep (one point per channel)
+ for(mscl::WirelessDataPoint dataPoint : data)
+ {
+ dataPoint.channelName(); //the name of the channel for this point
+ dataPoint.storedAs(); //the ValueType that the data is stored as
+ dataPoint.as_float(); //get the value as a float
+ }
+ }
+}
+
//create the BaseStation, passing in the connection
+mscl.BaseStation basestation = new mscl.BaseStation(connection);
+
+while(true)
+{
+ //get all the data sweeps that have been collected, with a timeout of 500 milliseconds
+ mscl.DataSweeps sweeps = basestation.getData(500);
+
+ foreach(mscl.DataSweep sweep in sweeps)
+ {
+ sweep.nodeAddress(); //the node address the sweep is from
+ sweep.timestamp(); //the TimeStamp of the sweep
+ sweep.tick(); //the tick of the sweep (0 - 65535 counter)
+ sweep.sampleRate(); //the sample rate of the sweep
+ sweep.samplingType(); //the SamplingType of the sweep (sync, nonsync, burst, etc.)
+ sweep.nodeRssi(); //the signal strength at the Node
+ sweep.baseRssi(); //the signal strength at the BaseStation
+ sweep.frequency(); //the radio frequency this was collected on
+
+ //get the vector of data in the sweep
+ mscl.ChannelData data = sweep.data();
+
+ //iterate over each point in the sweep (one point per channel)
+ foreach(mscl.WirelessDataPoint dataPoint in data)
+ {
+ dataPoint.channelName(); //the name of the channel for this point
+ dataPoint.storedAs(); //the ValueType that the data is stored as
+ dataPoint.as_float(); //get the value as a float
+ }
+ }
+}
+
#create the BaseStation, passing in the connection
+basestation = mscl.BaseStation(connection)
+
+while True:
+
+ # get all the data sweeps that have been collected, with a timeout of 500 milliseconds
+ sweeps = basestation.getData(500)
+
+ for sweep in sweeps:
+ sweep.nodeAddress() # the node address the sweep is from
+ sweep.timestamp() # the TimeStamp of the sweep
+ sweep.tick() # the tick of the sweep (0 - 65535 counter)
+ sweep.sampleRate() # the sample rate of the sweep
+ sweep.samplingType() # the SamplingType of the sweep (sync, nonsync, burst, etc.)
+ sweep.nodeRssi() # the signal strength at the Node
+ sweep.baseRssi() # the signal strength at the BaseStation
+ sweep.frequency() # the radio frequency this was collected on
+
+ # get the vector of data in the sweep
+ data = sweep.data()
+
+ # iterate over each point in the sweep (one point per channel)
+ for dataPoint in data:
+ dataPoint.channelName() # the name of the channel for this point
+ dataPoint.storedAs() # the ValueType that the data is stored as
+ dataPoint.as_float() # get the value as a float
+
+++ ++
as_float()
returns the dataPoint value as a 4-byte float. However there are more options, such asas_uint16()
,as_double()
,as_string()
, etc. TheValueType
that is returned from thestoredAs()
function determines how the data is actually stored.
As soon as the BaseStation
object is created, all incoming packets will be parsed and stored in an internal circular buffer.
So if a Wireless Node is already sampling on the same frequency as your BaseStation, you can simply access and consume the data that is being transmitted.
+ +basestation.getData()
will return a vector of DataSweep
objects. This will contain all of the sweeps that have been collected after the BaseStation
object was created.
Each DataSweep
contains various information describing it. The DataSweep's data()
function will return a vector of WirelessDataPoint
objects. This will have one point for each active channel that was enabled on the Node.
Each data point will be stored as a certain type. The datapoint's storedAs()
function will return an enum specifying which type the data is stored in (valueType_float
, valueType_uint16
, etc).
Using this information, you can call one of the datapoint's as_XXXXX()
functions (as_float()
, as_uint16()
, etc) to get the value in the way it was stored.
Inertial
Creating an InertialNode
#include "mscl/mscl.h"
+
+//create the connection object with port and baud rate
+mscl::Connection::Serial connection("COM3", 115200);
+
+//create the InertialNode, passing in the connection
+mscl::InertialNode node(connection);
+
//create the connection object with port and baud rate
+mscl.Connection connection = mscl.Connection.Serial("COM3", 115200);
+
+//create the InertialNode, passing in the connection
+mscl.InertialNode node = new mscl.InertialNode(connection);
+
import mscl
+
+#create the connection object with port and baud rate
+connection = mscl.Connection.Serial("COM3", 115200)
+
+#create the InertialNode, passing in the connection
+node = mscl.InertialNode(connection)
+
+++ +Make sure to replace
+COM3
and115200
with your own port settings.
To interace with an Inertial Node, create an InertialNode
object.
An InertialNode
takes a Connection
as a parameter.
Serial
, TcpIp
, and UnixSocket
are all available as connection types.
Communicating with a Node
//ping the Node
+bool success = node.ping();
+
//ping the Node
+bool success = node.ping();
+
# ping the Node
+success = node.ping()
+
+To test communicating with an Inertial Node, use the ping()
function.
+
//put the Inertial Node into its idle state
+node.setToIdle();
+
//put the Inertial Node into its idle state
+node.setToIdle();
+
# put the Inertial Node into its idle state
+node.setToIdle()
+
+Unlike a Wireless Node, an Inertial Node can be directly communicated with even if it is sampling. However, in most cases, it is still useful to set the Node to an idle state between sampling sessions and when changing configurations. To do this, use the setToIdle()
function.
Configuring a Node
//get all of the active channels for the GPS category on the Node
+mscl::MipChannels activeChs = node.getActiveChannelFields(mscl::MipTypes::CLASS_GNSS);
+
//get all of the active channels for the GPS category on the Node
+mscl.MipChannels activeChs = node.getActiveChannelFields(mscl.MipTypes.DataClass.CLASS_GNSS);
+
# get all of the active channels for the GPS category on the Node
+activeChs = node.getActiveChannelFields(mscl.MipTypes.CLASS_GNSS)
+
+Each Inertial Node has 1 or more Data Classes on the Node:
+ +Category | +Code | +
---|---|
AHRS/IMU | +CLASS_AHRS_IMU |
+
GNSS | +CLASS_GNSS |
+
Estimation Filter | +CLASS_ESTFILTER |
+
When getting and setting configuration options, it is sometimes necessary to choose the class to target for the command. This is the case in getting the active channels that are currently enabled on the Node.
+
+mscl::MipChannels ahrsImuChs;
+ahrsImuChs.push_back(mscl::MipChannel(mscl::MipTypes::CH_FIELD_SENSOR_RAW_ACCEL_VEC, mscl::SampleRate::Hertz(500)));
+ahrsImuChs.push_back(mscl::MipChannel(mscl::MipTypes::CH_FIELD_SENSOR_RAW_GYRO_VEC, mscl::SampleRate::Hertz(100)));
+
+mscl::MipChannels gnssChs;
+gnssChs.push_back(mscl::MipChannel(mscl::MipTypes::CH_FIELD_GNSS_LLH_POSITION, mscl::SampleRate::Hertz(1)));
+
+mscl::MipChannels estFilterChs;
+estFilterChs.push_back(mscl::MipChannel(mscl::MipTypes::CH_FIELD_ESTFILTER_ESTIMATED_GYRO_BIAS, mscl::SampleRate::Hertz(100)));
+
+//set the active channels for the different data classes on the Node
+node.setActiveChannelFields(mscl::MipTypes::CLASS_AHRS_IMU, ahrsImuChs);
+node.setActiveChannelFields(mscl::MipTypes::CLASS_GNSS, gnssChs);
+node.setActiveChannelFields(mscl::MipTypes::CLASS_ESTFILTER, estFilterChs);
+
+
+mscl::MipChannels ahrsImuChs = new mscl.MipChannels();
+ahrsImuChs.Add(new mscl.MipChannel(mscl.MipTypes.ChannelField.CH_FIELD_SENSOR_RAW_ACCEL_VEC, mscl.SampleRate.Hertz(500)));
+ahrsImuChs.Add(new mscl.MipChannel(mscl.MipTypes.ChannelField.CH_FIELD_SENSOR_RAW_GYRO_VEC, mscl.SampleRate.Hertz(100)));
+
+mscl::MipChannels gnssChs = new mscl.MipChannels();
+gnssChs.Add(new mscl.MipChannel(mscl.MipTypes.ChannelField.CH_FIELD_GNSS_LLH_POSITION, mscl.SampleRate.Hertz(1)));
+
+mscl::MipChannels estFilterChs = new mscl.MipChannels();
+estFilterChs.Add(new mscl.MipChannel(mscl.MipTypes.ChannelField.CH_FIELD_ESTFILTER_ESTIMATED_GYRO_BIAS, mscl.SampleRate.Hertz(100)));
+
+//set the active channels for the different data classes on the Node
+node.setActiveChannelFields(mscl.MipTypes.DataClass.CLASS_AHRS_IMU, ahrsImuChs);
+node.setActiveChannelFields(mscl.MipTypes.DataClass.CLASS_GNSS, gnssChs);
+node.setActiveChannelFields(mscl.MipTypes.DataClass.CLASS_ESTFILTER, estFilterChs);
+
+
+ahrsImuChs = mscl.MipChannels()
+ahrsImuChs.append(mscl.MipChannel(mscl.MipTypes.CH_FIELD_SENSOR_RAW_ACCEL_VEC, mscl.SampleRate.Hertz(500)))
+ahrsImuChs.append(mscl.MipChannel(mscl.MipTypes.CH_FIELD_SENSOR_RAW_GYRO_VEC, mscl.SampleRate.Hertz(100)))
+
+gnssChs = mscl.MipChannels()
+gnssChs.append(mscl.MipChannel(mscl.MipTypes.CH_FIELD_GNSS_LLH_POSITION, mscl.SampleRate.Hertz(1)))
+
+estFilterChs = mscl.MipChannels()
+estFilterChs.append(mscl.MipChannel(mscl.MipTypes.CH_FIELD_ESTFILTER_ESTIMATED_GYRO_BIAS, mscl.SampleRate.Hertz(100)))
+
+# set the active channels for the AHRS/IMU class on the Node
+node.setActiveChannelFields(mscl.MipTypes.CLASS_AHRS_IMU, ahrsImuChs)
+node.setActiveChannelFields(mscl.MipTypes.CLASS_GNSS, gnssChs)
+node.setActiveChannelFields(mscl.MipTypes.CLASS_ESTFILTER, estFilterChs)
+
+
+Setting the active channels involves building an MipChannels
container, and then passing that to the setActiveChannelFields
function for a specific category.
Not all categories and channel fields are supported by each Inertial Node. Use the node.features()
function to get this information on the Node.
Starting Sampling
//start sampling the active channels on the AHRS/IMU class of the Node
+node.enableDataStream(mscl::MipTypes::CLASS_AHRS_IMU);
+
+//start sampling the active channels on the GNSS class of the Node
+node.enableDataStream(mscl::MipTypes::CLASS_GNSS);
+
//start sampling on the AHRS/IMU class of the Node
+node.enableDataStream(mscl.MipTypes.DataClass.CLASS_AHRS_IMU);
+
+//start sampling on the GNSS class of the Node
+node.enableDataStream(mscl.MipTypes.DataClass.CLASS_GNSS);
+
# start sampling on the AHRS/IMU class of the Node
+node.enableDataStream(mscl.MipTypes.CLASS_AHRS_IMU)
+
+# start sampling on the GNSS class of the Node
+node.enableDataStream(mscl.MipTypes.CLASS_GNSS)
+
+++ +You can provide an optional 2nd parameter to the
+enableDataStream
function which is a boolean that allows enabling (true, default), or disabling (false) of the data stream.
There are a couple ways to start sampling on an Inertial Node. The first is the enableDataStream
command. This allows you to start sampling on individual categories of the Node.
//use the resume command to return to the mode before setToIdle
+node.resume();
+
//use the resume command to return to the mode before setToIdle
+node.resume();
+
# use the resume command to return to the mode before setToIdle
+node.resume()
+
+Alternatively, you can use the resume()
command. This command places the Node back into the mode it was in before issuing the setToIdle()
command. So if the Node was previously sampling on all of its categories, then setToIdle()
was called, calling resume()
will start the Node sampling again.
Collecting Data
//create the InertialNode, passing in the connection
+mscl::InertialNode node = mscl.InertialNode(connection);
+
+while(true)
+{
+ //get all the packets that have been collected, with a timeout of 500 milliseconds
+ mscl::MipDataPackets packets = node.getDataPackets(500);
+
+ for(mscl::MipDataPacket packet : packets)
+ {
+ packet.descriptorSet(); //the descriptor set of the packet
+ packet.timestamp(); //the PC time when this packet was received
+
+ //get all of the points in the packet
+ mscl::MipDataPoints points = packet.data();
+
+ for(mscl::MipDataPoint dataPoint : points)
+ {
+ dataPoint.channelName(); //the name of the channel for this point
+ dataPoint.storedAs(); //the ValueType that the data is stored as
+ dataPoint.as_float(); //get the value as a float
+ }
+ }
+}
+
//create the InertialNode, passing in the connection
+mscl.InertialNode node = new mscl.InertialNode(connection);
+
+while(true)
+{
+ //get all the packets that have been collected, with a timeout of 500 milliseconds
+ mscl.MipDataPackets packets = node.getDataPackets(500);
+
+ foreach(mscl.MipDataPacket packet in packets)
+ {
+ packet.descriptorSet(); //the descriptor set of the packet
+ packet.timestamp(); //the PC time when this packet was received
+
+ //get all of the points in the packet
+ mscl.MipDataPoints points = packet.data();
+
+ foreach(mscl.MipDataPoint dataPoint in points)
+ {
+ dataPoint.channelName(); //the name of the channel for this point
+ dataPoint.storedAs(); //the ValueType that the data is stored as
+ dataPoint.as_float(); //get the value as a float
+ }
+ }
+}
+
# create the InertialNode, passing in the connection
+node = mscl.InertialNode(connection)
+
+while True:
+
+ # get all the packets that have been collected, with a timeout of 500 milliseconds
+ packets = node.getDataPackets(500)
+
+ for packet in packets:
+ packet.descriptorSet() # the descriptor set of the packet
+ packet.timestamp() # the PC time when this packet was received
+
+ # get all of the points in the packet
+ points = packet.data()
+
+ for dataPoint in points:
+ dataPoint.channelName() # the name of the channel for this point
+ dataPoint.storedAs() # the ValueType that the data is stored as
+ dataPoint.as_float() # get the value as a float
+ }
+
+++ ++
as_float()
returns the dataPoint value as a 4-byte float. However there are more options, such asas_uint16()
,as_double()
,as_string()
, etc. TheValueType
that is returned from thestoredAs()
function determines how the data is actually stored.
As soon as the InertialNode
object is created, all data packets will be parsed and stored in an internal circular buffer.
node.getDataPackets()
will return a vector of MipDataPacket
objects. This will contain all of the packets that have been collected after the InertialNode
object was created.
Each MipDataPacket
contains various information describing it. The MipDataPacket's data()
function will return a vector of MipDataPoint
objects. These data points represent data from the active channels that were sampling on the Node.
Each data point will be stored as a certain type. The datapoint's storedAs()
function will return an enum specifying which type the data is stored in (valueType_float
, valueType_uint16
, etc).
Using this information, you can call one of the data point's as_XXXXX()
functions (as_float()
, as_uint16()
, etc) to get the value in the was it was stored.
Displacement
Creating a DisplacementNode
#include "mscl/mscl.h"
+
+//create the connection object with port and baud rate
+mscl::Connection::Serial connection("COM3", 115200);
+
+//create the DisplacementNode, passing in the connection
+mscl::DisplacementNode node(connection);
+
//create the connection object with port and baud rate
+mscl.Connection connection = mscl.Connection.Serial("COM3", 115200);
+
+//create the InertialNode, passing in the connection
+mscl.DisplacementNode node = new mscl.DisplacementNode(connection);
+
import mscl
+
+#create the connection object with port and baud rate
+connection = mscl.Connection.Serial("COM3", 115200)
+
+#create the DisplacementNode, passing in the connection
+node = mscl.DisplacementNode(connection)
+
+++ +Make sure to replace
+COM3
and115200
with your own port settings.
To interace with a digital Displacement Node, create a DisplacementNode
object.
A DisplacementNode
takes a Connection
as a parameter.
Serial
, TcpIp
, and UnixSocket
are all available as connection types.
Communicating with a Node
//ping the Node
+bool success = node.ping();
+
//ping the Node
+bool success = node.ping();
+
# ping the Node
+success = node.ping()
+
+To test communicating with a Displacement Node, use the ping()
function.
+
//put the Inertial Node into its idle state
+node.setToIdle();
+
//put the Inertial Node into its idle state
+node.setToIdle();
+
# put the Inertial Node into its idle state
+node.setToIdle()
+
+Unlike a Wireless Node, a Displacement Node can be directly communicated with even if it is sampling. However, in most cases, it is still useful to set the Node to an idle state between sampling sessions and when changing configurations. To do this, use the setToIdle()
function.
Configuring a Node
+//seed the device time with the current PC time
+//Note: you can pass your own time to this function as a parameter in nanoseconds since unix epoch
+node.setDeviceTime();
+
+
+//seed the device time with the current PC time
+//Note: you can pass your own time to this function as a parameter in nanoseconds since unix epoch
+node.setDeviceTime();
+
+
+#seed the device time with the current PC time
+#Note: you can pass your own time to this function as a parameter in nanoseconds since unix epoch
+node.setDeviceTime()
+
+
+You can seed the DisplacementNode
time before you start sampling so that the time channel that is output has an accurate timestamp.
Starting Sampling
+//start sampling
+node.resume();
+
+
+//start sampling
+node.resume();
+
+
+# start sampling
+node.resume()
+
+
+Use the resume
function to start sampling on the Displacement Node.
Collecting Data
//create the DisplacementNode, passing in the connection
+mscl::DisplacementNode node = mscl.DisplacementNode(connection);
+
+while(true)
+{
+ //get all the packets that have been collected, with a timeout of 500 milliseconds
+ mscl::MipDataPackets packets = node.getDataPackets(500);
+
+ for(mscl::MipDataPacket packet : packets)
+ {
+ packet.timestamp(); //the PC time when this packet was received
+
+ //get all of the points in the packet
+ mscl::MipDataPoints points = packet.data();
+
+ for(mscl::MipDataPoint dataPoint : points)
+ {
+ dataPoint.channelName(); //the name of the channel for this point
+ dataPoint.storedAs(); //the ValueType that the data is stored as
+ dataPoint.as_float(); //get the value as a float
+ }
+ }
+}
+
//create the DisplacementNode, passing in the connection
+mscl.DisplacementNode node = new mscl.DisplacementNode(connection);
+
+while(true)
+{
+ //get all the packets that have been collected, with a timeout of 500 milliseconds
+ mscl.MipDataPackets packets = node.getDataPackets(500);
+
+ foreach(mscl.MipDataPacket packet in packets)
+ {
+ packet.timestamp(); //the PC time when this packet was received
+
+ //get all of the points in the packet
+ mscl.MipDataPoints points = packet.data();
+
+ foreach(mscl.MipDataPoint dataPoint in points)
+ {
+ dataPoint.channelName(); //the name of the channel for this point
+ dataPoint.storedAs(); //the ValueType that the data is stored as
+ dataPoint.as_float(); //get the value as a float
+ }
+ }
+}
+
# create the DisplacementNode, passing in the connection
+node = mscl.DisplacementNode(connection)
+
+while True:
+
+ # get all the packets that have been collected, with a timeout of 500 milliseconds
+ packets = node.getDataPackets(500)
+
+ for packet in packets:
+ packet.descriptorSet() # the descriptor set of the packet
+ packet.timestamp() # the PC time when this packet was received
+
+ # get all of the points in the packet
+ points = packet.data()
+
+ for dataPoint in points:
+ dataPoint.channelName() # the name of the channel for this point
+ dataPoint.storedAs() # the ValueType that the data is stored as
+ dataPoint.as_float() # get the value as a float
+ }
+
+++ ++
as_float()
returns the dataPoint value as a 4-byte float. However there are more options, such asas_uint16()
,as_double()
,as_string()
, etc. TheValueType
that is returned from thestoredAs()
function determines how the data is actually stored.
As soon as the DisplacementNode
object is created, all data packets will be parsed and stored in an internal circular buffer.
node.getDataPackets()
will return a vector of MipDataPacket
objects. This will contain all of the packets that have been collected after the DisplacementNode
object was created.
Each MipDataPacket
contains various information describing it. The MipDataPacket's data()
function will return a vector of MipDataPoint
objects. These data points represent data from the active channels that were sampling on the Node.
Each data point will be stored as a certain type. The datapoint's storedAs()
function will return an enum specifying which type the data is stored in (valueType_float
, valueType_uint16
, etc).
Using this information, you can call one of the data point's as_XXXXX()
functions (as_float()
, as_uint16()
, etc) to get the value in the was it was stored.
Exceptions
+MSCL uses the following Exceptions. Check the full documentation for details of which exceptions are thrown by each function.
+ +Exception | +Description | +
---|---|
Error | +An error has occurred. | +
Error_BadDataType | +A value was accessed as a type that it cannot be converted to. | +
Error_Communication | +Failed to communicate with a device. | +
Error_Connection | +An error has occurred with the connection. | +
Error_MipCmdFailed | +The MIP Command has failed. | +
Error_InvalidConfig | +The configuration is invalid. | +
Error_InvalidNodeConfig | +The configuration for a WirelessNode is invalid. | +
Error_InvalidSerialPort | +An error has occurred with the COM port. | +
Error_InvalidTcpServer | +An error has occured with the TCP/IP port. | +
Error_InvalidUnixSocket | +An error has occurred with the Unix Socket. | +
Error_NoData | +There is no data available. | +
Error_NodeCommunication | +Failed to communicate with a Wireless Node. | +
Error_NotSupported | +A command or feature was used that is not supported. | +
Error_UnknownSampleRate | +The sample rate is unknown or invalid. | +