Skip to content

Commit

Permalink
Merge pull request #335 from open-ephys/issue-330
Browse files Browse the repository at this point in the history
Add the ability to select fields that are polled by PolledBno055Data
  • Loading branch information
bparks13 authored Oct 17, 2024
2 parents d65244b + 24ec343 commit aed9bdc
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 18 deletions.
10 changes: 5 additions & 5 deletions OpenEphys.Onix1/ConfigurePolledBno055.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
namespace OpenEphys.Onix1
{
/// <summary>
/// Configures a Bosch Bno055 9-axis inertial measurement unit (IMU) that is polled by the
/// computer.
/// Configures a Bosch Bno055 9-axis inertial measurement unit (IMU) that is polled by the host computer.
/// </summary>
/// <remarks>
/// This configuration operator can be linked to a data IO operator, such as <see
Expand All @@ -25,7 +24,8 @@ public ConfigurePolledBno055()
}

/// <summary>
/// Copy constructor for the <see cref="ConfigurePolledBno055"/> class.
/// Initializes a new instance of <see cref="ConfigurePolledBno055"/> with its properties copied from an
/// existing instance.
/// </summary>
/// <param name="configurePolledBno055">A pre-existing <see cref="ConfigurePolledBno055"/> object.</param>
public ConfigurePolledBno055(ConfigurePolledBno055 configurePolledBno055)
Expand Down Expand Up @@ -126,7 +126,7 @@ void ConfigureBno055(DeviceContext device)
static class PolledBno055
{
public const int BNO055Address = 0x28;
public const int DataAddress = 0x1A;
public const int EulerHeadingLsbAddress = 0x1A;

internal class NameConverter : DeviceNameConverter
{
Expand Down Expand Up @@ -216,7 +216,7 @@ public enum Bno055AxisSign : uint
MirrorX = 0b00000_100,
}

// NB: Can be used to remove axis map and sign properties from MutliDeviceFactories that include a
// NB: Can be used to remove axis map and sign properties from MultiDeviceFactories that include a
// ConfigurePolledBno055 when having those options would cause confusion and potential
// commutator malfunction
internal class PolledBno055SingleDeviceFactoryConverter : SingleDeviceFactoryConverter
Expand Down
145 changes: 132 additions & 13 deletions OpenEphys.Onix1/PolledBno055Data.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Bonsai;

namespace OpenEphys.Onix1
{
/// <summary>
/// Produces a sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see> from Bosch Bno055
/// 9-axis inertial measurement unit (IMU)
/// 9-axis inertial measurement unit (IMU) by polling it from the host computer.
/// </summary>
/// <remarks>
/// This data IO operator must be linked to an appropriate configuration, such as a <see
Expand All @@ -24,18 +25,36 @@ public class PolledBno055Data : Source<Bno055DataFrame>
public string DeviceName { get; set; }

/// <summary>
/// Generates a sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see> at approximately 100
/// Hz.
/// Gets or sets which data registers should be collected from the Bno055.
/// </summary>
/// <remarks>
/// This will generate a sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see> at approximately 100 Hz.
/// This rate may be limited by the hardware.
/// The rate that data is sampled is limited by communication with the Bno055 by the host PC (rather
/// than a dedicated controller on the headstage). If the user wishes to maximize the sampling rate of
/// particular measurements, they can select which should be sampled using this property. For
/// instance, specifying "Quaternion, Calibration" means that only the quaternion and sensor
/// calibration status registers will be polled and the rest of the members of <see
/// cref="Bno055DataFrame"/>, with the exception of <c>Clock</c> and <c>HubClock</c>, will be set to
/// 0.
/// </remarks>
[Category(DeviceFactory.ConfigurationCategory)]
[Description("Specifies which data registers should be read from the chip.")]
[TypeConverter(typeof(FlagsConverter))]
public PolledBno055Registers PolledRegisters { get; set; } = PolledBno055Registers.All;

/// <summary>
/// Use a ~100 Hz internal timer to generate a sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see>.
/// </summary>
/// <remarks>
/// An internal timer will be used to poll the Bno055 in order to generate a sequence of <see
/// cref="Bno055DataFrame">Bno055DataFrames</see> at approximately 100 Hz. This rate may be limited by
/// hardware communication speeds (see <see cref="PolledRegisters"/>).
/// </remarks>
/// <returns>A sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see>.</returns>
public override IObservable<Bno055DataFrame> Generate()
{
// Max of 100 Hz, but limited by I2C bus
var source = Observable.Interval(TimeSpan.FromSeconds(0.01));
// NB: NewThreadScheduler runs polling on dedicated thread and uses StopWatch for high resolution
// periodic measurements. This results in much better performance than the default scheduler.
var source = Observable.Interval(TimeSpan.FromMilliseconds(10), new NewThreadScheduler());
return Generate(source);
}

Expand All @@ -44,14 +63,17 @@ public override IObservable<Bno055DataFrame> Generate()
/// input sequence.
/// </summary>
/// <remarks>
/// This will attempt to produce a sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see> that is updated whenever
/// an item in the <paramref name="source"/> sequence is received. This rate is be limited by the
/// hardware and has a maximum meaningful rate of 100 Hz.
/// This will attempt to produce a sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see> that
/// is updated whenever an item in the <paramref name="source"/> sequence is received. This rate may
/// be limited by the hardware communication speeds (see <see cref="PolledRegisters"/>) and has a
/// maximum meaningful rate of 100 Hz, which is the update rate of the sensor fusion algorithm on the
/// Bno055 itself.
/// </remarks>
/// <param name="source">A sequence to drive sampling.</param>
/// <returns>A sequence of <see cref="Bno055DataFrame"/> objects.</returns>
/// <returns>A sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see>.</returns>
public unsafe IObservable<Bno055DataFrame> Generate<TSource>(IObservable<TSource> source)
{
var polled = PolledRegisters;
return DeviceManager.GetDevice(DeviceName).SelectMany(
deviceInfo =>
{
Expand All @@ -68,7 +90,42 @@ public unsafe IObservable<Bno055DataFrame> Generate<TSource>(IObservable<TSource
Bno055DataFrame frame = default;
device.Context.EnsureContext(() =>
{
var data = i2c.ReadBytes(PolledBno055.DataAddress, sizeof(Bno055DataPayload));
byte[] data = {
polled.HasFlag(PolledBno055Registers.EulerAngle) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 0) : (byte)0,
polled.HasFlag(PolledBno055Registers.EulerAngle) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 1) : (byte)0,
polled.HasFlag(PolledBno055Registers.EulerAngle) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 2) : (byte)0,
polled.HasFlag(PolledBno055Registers.EulerAngle) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 3) : (byte)0,
polled.HasFlag(PolledBno055Registers.EulerAngle) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 4) : (byte)0,
polled.HasFlag(PolledBno055Registers.EulerAngle) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 5) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 6) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 7) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 8) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 9) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 10) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 11) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 12) : (byte)0,
polled.HasFlag(PolledBno055Registers.Quaternion) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 13) : (byte)0,
polled.HasFlag(PolledBno055Registers.Acceleration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 14) : (byte)0,
polled.HasFlag(PolledBno055Registers.Acceleration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 15) : (byte)0,
polled.HasFlag(PolledBno055Registers.Acceleration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 16) : (byte)0,
polled.HasFlag(PolledBno055Registers.Acceleration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 17) : (byte)0,
polled.HasFlag(PolledBno055Registers.Acceleration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 18) : (byte)0,
polled.HasFlag(PolledBno055Registers.Acceleration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 19) : (byte)0,
polled.HasFlag(PolledBno055Registers.Gravity) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 20) : (byte)0,
polled.HasFlag(PolledBno055Registers.Gravity) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 21) : (byte)0,
polled.HasFlag(PolledBno055Registers.Gravity) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 22) : (byte)0,
polled.HasFlag(PolledBno055Registers.Gravity) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 23) : (byte)0,
polled.HasFlag(PolledBno055Registers.Gravity) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 24) : (byte)0,
polled.HasFlag(PolledBno055Registers.Gravity) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 25) : (byte)0,
polled.HasFlag(PolledBno055Registers.Temperature) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 26) : (byte)0,
polled.HasFlag(PolledBno055Registers.Calibration) ? i2c.ReadByte(PolledBno055.EulerHeadingLsbAddress + 27) : (byte)0,
};
ulong clock = passthrough.ReadRegister(DS90UB9x.LASTI2CL);
clock += (ulong)passthrough.ReadRegister(DS90UB9x.LASTI2CH) << 32;
fixed (byte* dataPtr = data)
Expand All @@ -85,6 +142,69 @@ public unsafe IObservable<Bno055DataFrame> Generate<TSource>(IObservable<TSource
});
});
}

class FlagsConverter : EnumConverter
{
public FlagsConverter(Type type)
: base(type)
{
}

public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return new StandardValuesCollection(new[] {
PolledBno055Registers.Quaternion,
PolledBno055Registers.EulerAngle,
PolledBno055Registers.Acceleration,
PolledBno055Registers.Gravity,
PolledBno055Registers.Temperature,
PolledBno055Registers.Calibration,
PolledBno055Registers.All
});
}
}
}

/// <summary>
/// Specifies which data registers will be read from the Bno055 during each polling cycle.
/// </summary>
[Flags]
public enum PolledBno055Registers
{
/// <summary>
/// Specifies that the Euler angles will be polled.
/// </summary>
EulerAngle = 0x1,

/// <summary>
/// Specifies that the quaternion will be polled.
/// </summary>
Quaternion = 0x2,

/// <summary>
/// Specifies that the linear acceleration will be polled.
/// </summary>
Acceleration = 0x4,

/// <summary>
/// Specifies that the gravity vector will be polled.
/// </summary>
Gravity = 0x8,

/// <summary>
/// Specifies that the temperature measurement will be polled.
/// </summary>
Temperature = 0x10,

/// <summary>
/// Specifies that the sensor calibration status will be polled.
/// </summary>
Calibration = 0x20,

/// <summary>
/// Specifies that all sensor measurements and calibration status will be polled.
/// </summary>
All = EulerAngle | Quaternion | Acceleration | Gravity | Temperature | Calibration,
}

/// <inheritdoc cref = "PolledBno055Data"/>v
Expand All @@ -98,5 +218,4 @@ public class NeuropixelsV2eBno055Data : PolledBno055Data { }
/// <inheritdoc cref = "PolledBno055Data"/>v
[Obsolete("This operator is obsolete. Use PolledBno055Data instead. Will be removed in version 1.0.0.")]
public class NeuropixelsV2eBetaBno055Data : PolledBno055Data { }

}

0 comments on commit aed9bdc

Please sign in to comment.