NAV Navbar
Logo
C++ C# Python

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 MicroStrain by HBK.

MSCL has language bindings in C++, .NET, 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 and 921600 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 as as_uint16(), as_double(), as_string(), etc. The ValueType that is returned from the storedAs() 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 and 115200 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 as as_uint16(), as_double(), as_string(), etc. The ValueType that is returned from the storedAs() 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 and 115200 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 as as_uint16(), as_double(), as_string(), etc. The ValueType that is returned from the storedAs() 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.