Skip to content

Repository files navigation

MicroStrain DAQ

platform Apache 2.0 Latest

C++ C# Python (daq-utils) Python (jupyter)

CI C++ (Windows) CI C++ (Linux) CI C# CD

An opinionated library that enhances working with openDAQ modules from MicroStrain by HBK.

Installation

C++

Add the library to your project using CMake FetchContent, replacing <version> with the desired release tag:

include(FetchContent)
FetchContent_Declare(
    MicroStrainDaqUtils
    GIT_REPOSITORY https://github.com/HBK-MicroStrain/microstrain-daq.git
    GIT_TAG        <version>
    SOURCE_SUBDIR  microstrain-daq-utils/daq_utils/cpp
)
FetchContent_MakeAvailable(MicroStrainDaqUtils)

target_link_libraries(your_target PRIVATE microstrain::daq_utils)

To import the library into your project:

#include <daq_utils/daq_utils.h>
#include <daq_utils/wireless.h>

Python

pip install microstrain-daq-utils

To import the library into your project:

import daq_utils

C#

dotnet add package MicroStrain.DaqUtils

To import the library into your project:

using Daq.Utils;

See Usage for examples of how to use the library.

JupyterLab

The Python library bindings can be used with JupyterLab, and a separate package provides notebook templates pre-configured for various use cases.

This is ideal for exploration, prototyping, and testing.

Installation

pip install microstrain-daq-jupyter

Note: Notebook templates require the Library to be installed.

Running a Session

Navigate to the directory where you would like to save your notebooks and launch JupyterLab:

jupyter lab

Creating New Notebooks

To create a new notebook from a template, click the new launcher button:

New launcher button in JupyterLab

Then, click the Template tile in the Notebook section:

Template button in JupyterLab instance launcher

Then, select the desired template:

Template selector in JupyterLab instance launcher

Available Templates

Template Description
Starter Pre-configured openDAQ and library setup ready to use

Usage

See the openDAQ documentation for a full reference on the openDAQ API. For wireless-specific usage and examples, see the Wireless guide.

Adding modules

By default, openDAQ loads modules from its installation directory. To load modules from a different location, set the OPENDAQ_MODULE_PATH environment variable to the desired directory.

For example, to set it to the Downloads directory:

Windows

setx OPENDAQ_MODULE_PATH C:\Users\username\Downloads

Note: setx takes effect in new terminal sessions, not the current one. Restart your terminal after running this command.

Linux

touch ~/.bashrc && \
sed -i '/^export OPENDAQ_MODULE_PATH=/d' ~/.bashrc && \
echo 'export OPENDAQ_MODULE_PATH=~/Downloads' >> ~/.bashrc && \
source ~/.bashrc

Mac

touch ~/.bashrc && \
sed -i '' '/^export OPENDAQ_MODULE_PATH=/d' ~/.bashrc && \
echo 'export OPENDAQ_MODULE_PATH=~/Downloads' >> ~/.bashrc && \
source ~/.bashrc

Note: If multiple versions of the same module exist in the directory, the behavior is undefined. Remove the old version before adding the new one.

Then pass the path when creating your openDAQ instance:

C++

#include <opendaq/opendaq.h>

daq::InstanceBuilderPtr builder = daq::InstanceBuilder();
if (const char* modulePath = std::getenv("OPENDAQ_MODULE_PATH")) {
    builder.setModulePath(modulePath);
}
daq::InstancePtr instance = builder.build();

Python

import os
import opendaq as daq

builder = daq.InstanceBuilder()
if module_path := os.environ.get('OPENDAQ_MODULE_PATH'):
    builder.module_path = module_path
instance = builder.build()

C#

using Daq.Core.OpenDAQ;

var builder = OpenDAQFactory.InstanceBuilder();
var modulePath = Environment.GetEnvironmentVariable("OPENDAQ_MODULE_PATH");
if (modulePath != null)
    builder.ModulePath = modulePath;
var instance = builder.Build();

Discovering devices

This code snippet will display a list of all currently available devices:

C++

for (daq::DeviceInfoPtr deviceInfo : instance.getAvailableDevices()) {
    std::cout << "Name: " << deviceInfo.getName() << " Connection string: " << deviceInfo.getConnectionString() << "\n";
}

Python

for device_info in instance.available_devices:
    print('Name:', device_info.name, 'Connection string:', device_info.connection_string)

C#

foreach (var deviceInfo in instance.AvailableDevices)
    Console.WriteLine($"Name: {deviceInfo.Name} Connection string: {deviceInfo.ConnectionString}");

Adding devices

Add a device using its connection string:

C++

daq::DevicePtr device = instance.addDevice("microstrain-wireless://COM46:3000000");

Python

device = instance.add_device('microstrain-wireless://COM46:3000000')

C#

var device = instance.AddDevice("microstrain-wireless://COM46:3000000");

Connection strings are in the format: prefix://address.

Removing devices

When you are ready to remove the device:

C++

instance.removeDevice(device);

Python

instance.remove_device(device)

C#

instance.RemoveDevice(device);

This will disconnect the device so you can use it in other applications.

Getting channels

Get a reference to a channel using it's index:

C++

daq::ChannelPtr channel = device.getChannels()[0];

Python

channel = device.get_channels()[0]

C#

var channel = device.GetChannels()[0];

Getting property groups

Properties are organized into groups. To print available property groups for a device, channel, group, or other root:

C++

daq_utils::PrintGroups(channel);

Python

daq_utils.print_groups(channel)

C#

DaqUtils.PrintGroups(channel);

To get the groups as a list instead:

C++

std::vector<std::string> groups = daq_utils::Groups(channel);

Python

daq_utils.groups(channel)

C#

DaqUtils.Groups(channel);

Getting properties

To print all properties across every group:

C++

daq_utils::PrintProperties(channel);

Python

daq_utils.print_properties(channel)

C#

DaqUtils.PrintProperties(channel);

To filter to a specific group:

C++

daq_utils::PrintProperties(channel, "Setup.Configure.Sampling");

Python

daq_utils.print_properties(channel, 'Setup.Configure.Sampling')

C#

DaqUtils.PrintProperties(channel, "Setup.Configure.Sampling");

To get the properties as a list instead:

C++

std::vector<daq_utils::PropertyInfo> props = daq_utils::Properties(channel);
std::vector<daq_utils::PropertyInfo> props = daq_utils::Properties(channel, "Setup.Configure.Sampling");

Python

daq_utils.properties(channel)
daq_utils.properties(channel, 'Setup.Configure.Sampling')

C#

DaqUtils.Properties(channel);
DaqUtils.Properties(channel, "Setup.Configure.Sampling");

Finding a property path

If you know a property name but not its full path, use find to get its full dot-notation path:

C++

std::string path = daq_utils::Find(channel, "LostBeaconTimeout");

Python

daq_utils.find(channel, 'LostBeaconTimeout')

C#

DaqUtils.Find(channel, "LostBeaconTimeout");

This can also be used for finding groups:

C++

std::string path = daq_utils::Find(channel, "Sampling");

Python

daq_utils.find(channel, 'Sampling')

C#

DaqUtils.Find(channel, "Sampling");

Accessing properties

Properties can be accessed using dot-notation paths:

C++

daq::BaseObjectPtr timeout = channel.getPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout");

Python

timeout = channel.get_property_value('Setup.Configure.Sampling.LostBeaconTimeout')

C#

var timeout = channel.GetPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout");

They can also be set:

C++

channel.setPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout", 7);

Python

channel.set_property_value('Setup.Configure.Sampling.LostBeaconTimeout', 7)

C#

channel.SetPropertyValue("Setup.Configure.Sampling.LostBeaconTimeout", (IntegerObject)7);

Inspecting function properties

To view a function property's description, arguments, and return type, read the description field from the property:

C++

std::cout << channel.getProperty("Capabilities.MaxSweeps").getDescription() << "\n";

Python

print(channel.get_property('Capabilities.MaxSweeps').description)

C#

Console.WriteLine(channel.GetProperty("Capabilities.MaxSweeps").Description);

Calling function properties

Function properties can be called directly through the openDAQ API, but the wrapper simplifies the syntax. To call a function with no arguments using the wrapper:

C++

daq::BaseObjectPtr result = daq_utils::Call(channel, "Setup.Configure.Apply");

Python

result = daq_utils.call(channel, "Setup.Configure.Apply")

C#

var result = DaqUtils.Call(channel, "Setup.Configure.Apply");

To call a function with arguments:

C++

daq::BaseObjectPtr result = daq_utils::Call(channel, "Capabilities.InputRangesWithVoltage", {0xFF, 5000});

Python

result = daq_utils.call(channel, "Capabilities.InputRangesWithVoltage", 0xFF, 5000)

C#

var result = DaqUtils.Call(channel, "Capabilities.InputRangesWithVoltage", (IntegerObject)0xFF, (IntegerObject)5000);

The result object can then be queried for any returned properties. For example:

C++

daq::BaseObjectPtr success = result.asPtr<daq::IPropertyObject>().getPropertyValue("Success");

Python

success = result.get_property_value('Success')

C#

var success = result?.Cast<PropertyObject>()?.GetPropertyValue("Success");

Inspecting Properties

To view what properties are available for a device, channel, or group, create a DaqPropertyInspector:

C++

daq_utils::DaqPropertyInspector propInspector(instance);

Python

prop_inspector = daq_utils.DaqPropertyInspector(instance)

C#

var propInspector = new DaqPropertyInspector(instance);

To inspect a property:

C++

propInspector.Describe(node, "Setup.Configure.CommunicationProtocol");

Python

prop_inspector.describe(node, 'Setup.Configure.CommunicationProtocol')

C#

propInspector.Describe(node, "Setup.Configure.CommunicationProtocol");

Inspecting types

To view what fields/values are available for openDAQ Enumeration and Struct types, create a DaqTypeInspector:

C++

daq_utils::DaqTypeInspector typeInspector(instance);

Python

type_inspector = daq_utils.DaqTypeInspector(instance)

C#

var typeInspector = new DaqTypeInspector(instance);

To inspect a type:

C++

typeInspector.Describe("AutoCalCompletionFlag");

Python

type_inspector.describe('AutoCalCompletionFlag')

C#

typeInspector.Describe("AutoCalCompletionFlag");

Creating typed values

To create openDAQ typed values such as Enumerations and Structs, use DaqTypeFactory. It handles the type manager and string conversion automatically:

C++

daq_utils::DaqTypeFactory daqTypes(instance);

Python

daq_types = daq_utils.DaqTypeFactory(instance)

C#

var daqTypes = new DaqTypeFactory(instance);

Creating an enum value

C++

daq::EnumerationPtr voltage = daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_3000mV");

Python

voltage = daq_types.enum("mscl_WirelessTypes_Voltage", "voltage_3000mV")

C#

var voltage = daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_3000mV");

Creating a Struct value

C++

daq::StructPtr cmdInfo = daqTypes.MakeStruct("mscl_ShuntCalCmdInfo",
{
    {"UseInternalShunt",  daq::Boolean(true)},
    {"NumActiveGauges",   daq::Integer(1)},
    {"GaugeResistance",   daq::Integer(350)},
    {"ShuntResistance",   daq::Integer(100000)},
    {"GaugeFactor",       daq::Float(2.0)},
    {"InputRange",        daqTypes.MakeEnum("mscl_WirelessTypes_InputRange", "range_14_545mV")},
    {"HardwareOffset",    daq::Integer(0)},
    {"ExcitationVoltage", daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_1500mV")}
});

Python

cmd_info = daq_types.struct(
    "mscl_ShuntCalCmdInfo",
    {
        "UseInternalShunt": True,
        "NumActiveGauges": 1,
        "GaugeResistance": 350,
        "ShuntResistance": 100000,
        "GaugeFactor": 2.0,
        "InputRange": daq_types.enum("mscl_WirelessTypes_InputRange", "range_14_545mV"),
        "HardwareOffset": 0,
        "ExcitationVoltage": daq_types.enum("mscl_WirelessTypes_Voltage", "voltage_1500mV")
    }
)

C#

var cmdInfo = daqTypes.MakeStruct("mscl_ShuntCalCmdInfo", new Dictionary<string, object>
{
    ["UseInternalShunt"] = true,
    ["NumActiveGauges"] = 1,
    ["GaugeResistance"] = 350,
    ["ShuntResistance"] = 100000,
    ["GaugeFactor"] = 2.0,
    ["InputRange"] = daqTypes.MakeEnum("mscl_WirelessTypes_InputRange", "range_14_545mV"),
    ["HardwareOffset"] = 0,
    ["ExcitationVoltage"] = daqTypes.MakeEnum("mscl_WirelessTypes_Voltage", "voltage_1500mV")
});

Using openDAQ containers

Structs

To read a field from a struct by name:

C++

daq::StructPtr struct_val = result.asPtr<daq::IStruct>();
daq::BaseObjectPtr field = struct_val.get("FieldName");

Python

field = struct_val.FieldName

C#

var field = struct_val.Cast<Struct>()?.Get("FieldName");

To iterate over all fields:

C++

daq::StructPtr struct_val = result.asPtr<daq::IStruct>();
for (const daq::StringPtr& name : struct_val.getFieldNames()) {
    std::cout << name << ": " << struct_val.get(name) << "\n";
}

Python

for name in struct_val.struct_type.field_names:
    print(name, getattr(struct_val, name))

C#

var struct_val = result.Cast<Struct>();
foreach (string name in struct_val.FieldNames)
    Console.WriteLine($"{name}: {struct_val.Get(name)}");

Note: To see what fields a struct type contains, see Inspecting types.

Lists

To access an item by index:

C++

daq::ListPtr<daq::IBaseObject> list = result.asPtr<daq::IList<daq::IBaseObject>>();
daq::BaseObjectPtr first = list[0];

Python

first = result[0]

C#

var list = result.Cast<IListObject<BaseObject>>();
var first = list[0];

To iterate over all items:

C++

daq::ListPtr<daq::IBaseObject> list = result.asPtr<daq::IList<daq::IBaseObject>>();
for (const daq::BaseObjectPtr& item : list) {
    std::cout << item << "\n";
}

Python

for item in result:
    print(item)

C#

var list = result.Cast<IListObject<BaseObject>>();
foreach (var item in list)
    Console.WriteLine(item);

Troubleshooting

Listing detected modules

If a device isn't being detected, run this to check whether the module is loaded:

C++

for (const daq::ModulePtr& module : instance.getModuleManager().getModules()) {
    daq::ModuleInfoPtr info = module.getModuleInfo();
    daq::VersionInfoPtr v = info.getVersionInfo();
    std::cout << info.getName() << " (" << info.getId() << ") v" << v.getMajor() << "." << v.getMinor() << "." << v.getPatch() << "\n";
}

Python

for module in instance.module_manager.modules:
    info = module.module_info
    v = info.version_info
    print(f"{info.name} ({info.id}) v{v.major}.{v.minor}.{v.patch}")

C#

foreach (var module in instance.ModuleManager.Modules)
{
    var info = module.ModuleInfo;
    var v = info.VersionInfo;
    Console.WriteLine($"{info.Name} ({info.Id}) v{v.Major}.{v.Minor}.{v.Patch}");
}
  • Module appears — the module loaded, but something is preventing device detection
  • Module missing — the module itself is not being loaded

About

An opinionated library that enhances working with openDAQ modules from MicroStrain by HBK.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages