Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to select fields that are polled by PolledBno055Data #335

Merged
merged 2 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 0 additions & 2 deletions OpenEphys.Onix1/ConfigureUclaMiniscopeV4Camera.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,6 @@ internal static void ConfigureSerializer(DeviceContext device)

internal static void ConfigureCameraSystem(DeviceContext device, UclaMiniscopeV4FramesPerSecond frameRate, bool interleaveLed)
{
const int WaitUntilPllSettles = 200;

// set up Python480
var atMega = new I2CRegisterContext(device, UclaMiniscopeV4.AtMegaAddress);
WriteCameraRegister(atMega, 16, 3); // Turn on PLL
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 { }

}