-
Notifications
You must be signed in to change notification settings - Fork 5
Add support for load tester device #484
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
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Linq; | ||
| using System.Reactive.Linq; | ||
| using Bonsai; | ||
|
|
||
| namespace OpenEphys.Onix1 | ||
| { | ||
| /// <summary> | ||
| /// Produces a sequence of LoadTester data frames. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This data IO operator must be linked to an appropriate configuration, such as a <see | ||
| /// cref="ConfigureLoadTester"/>, using a shared <c>DeviceName</c>. | ||
| /// </remarks> | ||
| [Description("Produces a sequence of load tester data frames.")] | ||
| public class LoadTesterData : Source<LoadTesterDataFrame> | ||
| { | ||
| /// <inheritdoc cref = "SingleDeviceFactory.DeviceName"/> | ||
| [TypeConverter(typeof(LoadTester.NameConverter))] | ||
| [Description(SingleDeviceFactory.DeviceNameDescription)] | ||
| [Category(DeviceFactory.ConfigurationCategory)] | ||
| public string DeviceName { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Generates a sequence of <see cref="LoadTesterDataFrame"/> objects, each of which contains period signal from the | ||
| /// acquisition system indicating that it is active. | ||
| /// </summary> | ||
| /// <returns>A sequence of <see cref="LoadTesterDataFrame"/> objects.</returns> | ||
| public override IObservable<LoadTesterDataFrame> Generate() | ||
| { | ||
| return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => | ||
| { | ||
| var info = (LoadTesterDeviceInfo)deviceInfo; | ||
| var device = info.GetDeviceContext(typeof(LoadTester)); | ||
| return deviceInfo.Context | ||
| .GetDeviceFrames(device.Address) | ||
| .Select(frame => new LoadTesterDataFrame(frame, info.ReceivedWords)); | ||
| }); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| using System; | ||
| using System.Runtime.InteropServices; | ||
|
|
||
| namespace OpenEphys.Onix1 | ||
| { | ||
| /// <summary> | ||
| /// A load tester data frame. | ||
| /// </summary> | ||
| public class LoadTesterDataFrame : DataFrame | ||
| { | ||
| /// <summary> | ||
| /// Gets the difference between the hub clock and the loopback value at the moment the loopback | ||
| /// value was received. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This value is the result of subtracting the loopback value written by the <see | ||
| /// cref="LoadTesterLoopback"/> operator from the device's hub clock counter value at the moment that | ||
| /// the loop back value was received. Typically, the <see cref="DataFrame.HubClock"/> member is sent | ||
| /// to <see cref="LoadTesterLoopback"/> operator such that this value is a hardware-timed measurement | ||
| /// value of real-time latency. This value is only updated when a new loopback value is sent to the | ||
| /// device using the <see cref="LoadTesterLoopback"/> operator. | ||
| /// </remarks> | ||
| public ulong HubClockDelta { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the counter array. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This is a <see cref="ConfigureLoadTester.ReceivedWords"/>-long array of incrementing integers that | ||
| /// is used for simulating the bandwidth of physical data sources. | ||
| /// </remarks> | ||
| public ushort[] Counter { get; } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="LoadTesterDataFrame"/> class. | ||
| /// </summary> | ||
| /// <param name="frame">A data frame produced by a load tester device.</param> | ||
| /// <param name="receivedWords">The number of counter words that appear at the end of the load test | ||
| /// data frame. This number is determined by the value of <see | ||
| /// cref="ConfigureLoadTester.ReceivedWords"/>.</param> | ||
| public unsafe LoadTesterDataFrame(oni.Frame frame, uint receivedWords) | ||
| : base(frame.Clock) | ||
| { | ||
| var payload = (LoadTesterPayload*)frame.Data.ToPointer(); | ||
| var counterPtr = (ushort*)((byte*)payload + sizeof(LoadTesterPayload)); | ||
|
|
||
| HubClock = payload->HubClock; | ||
| HubClockDelta = payload->HubClockDelta; | ||
| Counter = new Span<ushort>(counterPtr, (int)receivedWords).ToArray(); | ||
| } | ||
| } | ||
|
|
||
| [StructLayout(LayoutKind.Sequential, Pack = 1)] | ||
| struct LoadTesterPayload | ||
| { | ||
| public ulong HubClock; | ||
| public ulong HubClockDelta; | ||
| // NB: The ushort Counter array may or may not reside here. Its size is determined by ReceivedWords. | ||
| } | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| using System; | ||
|
|
||
| namespace OpenEphys.Onix1 | ||
| { | ||
| class LoadTesterDeviceInfo : DeviceInfo | ||
| { | ||
| public LoadTesterDeviceInfo(ContextTask context, Type deviceType, uint deviceAddress, uint receivedWords, uint transmittedWords) | ||
| : base(context, deviceType, deviceAddress) | ||
| { | ||
| ReceivedWords = receivedWords; | ||
| TransmittedWords = transmittedWords; | ||
| } | ||
|
|
||
| public uint ReceivedWords { get; } | ||
|
|
||
| public uint TransmittedWords { get; } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Linq; | ||
| using System.Reactive.Linq; | ||
| using Bonsai; | ||
|
|
||
| namespace OpenEphys.Onix1 | ||
| { | ||
| /// <summary> | ||
| /// Sends loopback data to the load testing device for closed-loop latency measurement. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This data IO operator must be linked to an appropriate configuration, such as a <see | ||
| /// cref="ConfigureLoadTester"/>, using a shared <c>DeviceName</c>. | ||
| /// </remarks> | ||
| [Description("Sends loopback data to an ONIX breakout board.")] | ||
| public class LoadTesterLoopback : Sink<ulong> | ||
| { | ||
| /// <inheritdoc cref = "SingleDeviceFactory.DeviceName"/> | ||
| [TypeConverter(typeof(LoadTester.NameConverter))] | ||
| [Description(SingleDeviceFactory.DeviceNameDescription)] | ||
| [Category(DeviceFactory.ConfigurationCategory)] | ||
| public string DeviceName { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Creates and sends a loopback frame to the load testing device. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// A loopback frame consists of the <c>ulong</c> loopback value provided by <paramref name="source"/> | ||
| /// that is prepended to a <see cref="ConfigureLoadTester.TransmittedWords"/>-element <c>ushort</c> | ||
| /// array of dummy data. When the frame is received by hardware, the loopback value is subtracted from | ||
| /// the current hub clock count on the load testing device and stored. Therefore, if the loopback | ||
| /// value is that of a previous <see cref="DataFrame.HubClock"/> from the <see | ||
| /// cref="LoadTesterData"/> with the same <see cref="DeviceName"/> as this operator, this difference will provide a | ||
| /// hardware-timed measurement of real-time latency. The variably-sized dummy data in the loopback | ||
| /// frame is used for testing the effect of increasing the frame size, and thus the write | ||
| /// communication bandwidth, on real-time latency. | ||
| /// </remarks> | ||
| /// <param name="source">A sequence of loopback values to send to the device</param> | ||
| /// <returns> A sequence of loopback values to send to the device.</returns> | ||
| public unsafe override IObservable<ulong> Process(IObservable<ulong> source) | ||
| { | ||
| return DeviceManager.GetDevice(DeviceName).SelectMany(deviceInfo => | ||
| { | ||
| var info = (LoadTesterDeviceInfo)deviceInfo; | ||
| var device = info.GetDeviceContext(typeof(LoadTester)); | ||
| var transmittedWords = info.TransmittedWords; | ||
|
|
||
| if (transmittedWords > 0) | ||
| { | ||
| var payload = new uint[transmittedWords + 2]; | ||
|
|
||
| return source.Do(loopbackValue => | ||
| { | ||
| payload[0] = (uint)loopbackValue; | ||
| payload[1] = (uint)(loopbackValue >> 32); | ||
| device.Write(payload); | ||
| }); | ||
| } | ||
| else | ||
| { | ||
| return source.Do(device.Write); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.