Skip to content

Commit 0d14908

Browse files
authored
Merge pull request #2 from open-ephys/modular-commutator
Add commutator control based on swing-twist decomposition
2 parents 0bbd480 + aeadf1b commit 0d14908

File tree

4 files changed

+233
-0
lines changed

4 files changed

+233
-0
lines changed

.bonsai/Bonsai.config

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,103 @@
22
<PackageConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
33
<Packages>
44
<Package id="Bonsai" version="2.8.5" />
5+
<Package id="Bonsai.Arduino" version="2.8.1" />
6+
<Package id="Bonsai.Audio" version="2.8.0" />
57
<Package id="Bonsai.Core" version="2.8.5" />
68
<Package id="Bonsai.Design" version="2.8.5" />
9+
<Package id="Bonsai.Design.Visualizers" version="2.8.0" />
10+
<Package id="Bonsai.Dsp" version="2.8.0" />
11+
<Package id="Bonsai.Dsp.Design" version="2.8.0" />
712
<Package id="Bonsai.Editor" version="2.8.5" />
13+
<Package id="Bonsai.Osc" version="2.7.0" />
14+
<Package id="Bonsai.Scripting.Expressions" version="2.8.0" />
15+
<Package id="Bonsai.Scripting.Expressions.Design" version="2.8.0" />
16+
<Package id="Bonsai.Shaders" version="0.27.0" />
17+
<Package id="Bonsai.Shaders.Design" version="0.27.0" />
18+
<Package id="Bonsai.StarterPack" version="2.8.1" />
819
<Package id="Bonsai.System" version="2.8.1" />
920
<Package id="Bonsai.System.Design" version="2.8.0" />
21+
<Package id="Bonsai.Vision" version="2.8.0" />
22+
<Package id="Bonsai.Vision.Design" version="2.8.1" />
23+
<Package id="Bonsai.Windows.Input" version="2.7.0" />
1024
<Package id="clroni" version="6.1.2" />
1125
<Package id="jacobslusser.ScintillaNET" version="3.6.3" />
1226
<Package id="Markdig" version="0.18.1" />
1327
<Package id="Microsoft.Web.WebView2" version="1.0.1823.32" />
28+
<Package id="openal.redist" version="2.0.7" />
1429
<Package id="OpenCV.Net" version="3.4.2" />
1530
<Package id="OpenEphys.Onix1" version="0.1.0" />
31+
<Package id="OpenTK" version="3.1.0" />
32+
<Package id="OpenTK.GLControl" version="3.1.0" />
1633
<Package id="Rx-Core" version="2.2.5" />
1734
<Package id="Rx-Interfaces" version="2.2.5" />
1835
<Package id="Rx-Linq" version="2.2.5" />
1936
<Package id="Rx-PlatformServices" version="2.2.5" />
2037
<Package id="SvgNet" version="3.3.3" />
2138
<Package id="System.Buffers" version="4.5.1" />
39+
<Package id="System.Linq.Dynamic" version="1.0.7" />
2240
<Package id="System.Memory" version="4.5.5" />
2341
<Package id="System.Numerics.Vectors" version="4.5.0" />
2442
<Package id="System.Resources.Extensions" version="8.0.0" />
2543
<Package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" />
2644
<Package id="YamlDotNet" version="13.1.1" />
45+
<Package id="ZedGraph" version="5.1.7" />
2746
</Packages>
2847
<AssemblyReferences>
2948
<AssemblyReference assemblyName="Bonsai" />
49+
<AssemblyReference assemblyName="Bonsai.Arduino" />
50+
<AssemblyReference assemblyName="Bonsai.Audio" />
3051
<AssemblyReference assemblyName="Bonsai.Core" />
3152
<AssemblyReference assemblyName="Bonsai.Design" />
53+
<AssemblyReference assemblyName="Bonsai.Design.Visualizers" />
54+
<AssemblyReference assemblyName="Bonsai.Dsp" />
55+
<AssemblyReference assemblyName="Bonsai.Dsp.Design" />
3256
<AssemblyReference assemblyName="Bonsai.Editor" />
57+
<AssemblyReference assemblyName="Bonsai.Osc" />
58+
<AssemblyReference assemblyName="Bonsai.Scripting.Expressions" />
59+
<AssemblyReference assemblyName="Bonsai.Scripting.Expressions.Design" />
60+
<AssemblyReference assemblyName="Bonsai.Shaders" />
61+
<AssemblyReference assemblyName="Bonsai.Shaders.Design" />
3362
<AssemblyReference assemblyName="Bonsai.System" />
3463
<AssemblyReference assemblyName="Bonsai.System.Design" />
64+
<AssemblyReference assemblyName="Bonsai.Vision" />
65+
<AssemblyReference assemblyName="Bonsai.Vision.Design" />
66+
<AssemblyReference assemblyName="Bonsai.Windows.Input" />
3567
<AssemblyReference assemblyName="OpenEphys.Onix1" />
3668
</AssemblyReferences>
3769
<AssemblyLocations>
3870
<AssemblyLocation assemblyName="Bonsai" processorArchitecture="MSIL" location="Packages/Bonsai.2.8.5/lib/net48/Bonsai.exe" />
71+
<AssemblyLocation assemblyName="Bonsai.Arduino" processorArchitecture="MSIL" location="Packages/Bonsai.Arduino.2.8.1/lib/net462/Bonsai.Arduino.dll" />
72+
<AssemblyLocation assemblyName="Bonsai.Audio" processorArchitecture="MSIL" location="Packages/Bonsai.Audio.2.8.0/lib/net462/Bonsai.Audio.dll" />
3973
<AssemblyLocation assemblyName="Bonsai.Core" processorArchitecture="MSIL" location="Packages/Bonsai.Core.2.8.5/lib/net462/Bonsai.Core.dll" />
4074
<AssemblyLocation assemblyName="Bonsai.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Design.2.8.5/lib/net462/Bonsai.Design.dll" />
75+
<AssemblyLocation assemblyName="Bonsai.Design.Visualizers" processorArchitecture="MSIL" location="Packages/Bonsai.Design.Visualizers.2.8.0/lib/net462/Bonsai.Design.Visualizers.dll" />
76+
<AssemblyLocation assemblyName="Bonsai.Dsp" processorArchitecture="MSIL" location="Packages/Bonsai.Dsp.2.8.0/lib/net462/Bonsai.Dsp.dll" />
77+
<AssemblyLocation assemblyName="Bonsai.Dsp.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Dsp.Design.2.8.0/lib/net462/Bonsai.Dsp.Design.dll" />
4178
<AssemblyLocation assemblyName="Bonsai.Editor" processorArchitecture="MSIL" location="Packages/Bonsai.Editor.2.8.5/lib/net472/Bonsai.Editor.dll" />
79+
<AssemblyLocation assemblyName="Bonsai.Osc" processorArchitecture="MSIL" location="Packages/Bonsai.Osc.2.7.0/lib/net462/Bonsai.Osc.dll" />
80+
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions" processorArchitecture="MSIL" location="Packages/Bonsai.Scripting.Expressions.2.8.0/lib/net462/Bonsai.Scripting.Expressions.dll" />
81+
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Scripting.Expressions.Design.2.8.0/lib/net462/Bonsai.Scripting.Expressions.Design.dll" />
82+
<AssemblyLocation assemblyName="Bonsai.Shaders" processorArchitecture="MSIL" location="Packages/Bonsai.Shaders.0.27.0/lib/net462/Bonsai.Shaders.dll" />
83+
<AssemblyLocation assemblyName="Bonsai.Shaders.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Shaders.Design.0.27.0/lib/net462/Bonsai.Shaders.Design.dll" />
4284
<AssemblyLocation assemblyName="Bonsai.System" processorArchitecture="MSIL" location="Packages/Bonsai.System.2.8.1/lib/net462/Bonsai.System.dll" />
4385
<AssemblyLocation assemblyName="Bonsai.System.Design" processorArchitecture="MSIL" location="Packages/Bonsai.System.Design.2.8.0/lib/net462/Bonsai.System.Design.dll" />
86+
<AssemblyLocation assemblyName="Bonsai.Vision" processorArchitecture="MSIL" location="Packages/Bonsai.Vision.2.8.0/lib/net462/Bonsai.Vision.dll" />
87+
<AssemblyLocation assemblyName="Bonsai.Vision.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Vision.Design.2.8.1/lib/net462/Bonsai.Vision.Design.dll" />
88+
<AssemblyLocation assemblyName="Bonsai.Windows.Input" processorArchitecture="MSIL" location="Packages/Bonsai.Windows.Input.2.7.0/lib/net462/Bonsai.Windows.Input.dll" />
4489
<AssemblyLocation assemblyName="clroni" processorArchitecture="Amd64" location="Packages/clroni.6.1.2/lib/net472/clroni.dll" />
4590
<AssemblyLocation assemblyName="Markdig" processorArchitecture="MSIL" location="Packages/Markdig.0.18.1/lib/net40/Markdig.dll" />
4691
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Core" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.Core.dll" />
4792
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.WinForms" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.WinForms.dll" />
4893
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Wpf" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.Wpf.dll" />
4994
<AssemblyLocation assemblyName="OpenCV.Net" processorArchitecture="MSIL" location="Packages/OpenCV.Net.3.4.2/lib/net462/OpenCV.Net.dll" />
5095
<AssemblyLocation assemblyName="OpenEphys.Onix1" processorArchitecture="Amd64" location="Packages/OpenEphys.Onix1.0.1.0/lib/net472/OpenEphys.Onix1.dll" />
96+
<AssemblyLocation assemblyName="OpenTK" processorArchitecture="MSIL" location="Packages/OpenTK.3.1.0/lib/net20/OpenTK.dll" />
97+
<AssemblyLocation assemblyName="OpenTK.GLControl" processorArchitecture="MSIL" location="Packages/OpenTK.GLControl.3.1.0/lib/net20/OpenTK.GLControl.dll" />
5198
<AssemblyLocation assemblyName="ScintillaNET" processorArchitecture="MSIL" location="Packages/jacobslusser.ScintillaNET.3.6.3/lib/net40/ScintillaNET.dll" />
5299
<AssemblyLocation assemblyName="SVG" processorArchitecture="MSIL" location="Packages/SvgNet.3.3.3/lib/net462/SVG.dll" />
53100
<AssemblyLocation assemblyName="System.Buffers" processorArchitecture="MSIL" location="Packages/System.Buffers.4.5.1/lib/net461/System.Buffers.dll" />
101+
<AssemblyLocation assemblyName="System.Linq.Dynamic" processorArchitecture="MSIL" location="Packages/System.Linq.Dynamic.1.0.7/lib/net40/System.Linq.Dynamic.dll" />
54102
<AssemblyLocation assemblyName="System.Memory" processorArchitecture="MSIL" location="Packages/System.Memory.4.5.5/lib/net461/System.Memory.dll" />
55103
<AssemblyLocation assemblyName="System.Numerics.Vectors" processorArchitecture="MSIL" location="Packages/System.Numerics.Vectors.4.5.0/lib/net46/System.Numerics.Vectors.dll" />
56104
<AssemblyLocation assemblyName="System.Reactive.Core" processorArchitecture="MSIL" location="Packages/Rx-Core.2.2.5/lib/net45/System.Reactive.Core.dll" />
@@ -60,6 +108,7 @@
60108
<AssemblyLocation assemblyName="System.Resources.Extensions" processorArchitecture="MSIL" location="Packages/System.Resources.Extensions.8.0.0/lib/net462/System.Resources.Extensions.dll" />
61109
<AssemblyLocation assemblyName="System.Runtime.CompilerServices.Unsafe" processorArchitecture="MSIL" location="Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/net461/System.Runtime.CompilerServices.Unsafe.dll" />
62110
<AssemblyLocation assemblyName="YamlDotNet" processorArchitecture="MSIL" location="Packages/YamlDotNet.13.1.1/lib/net47/YamlDotNet.dll" />
111+
<AssemblyLocation assemblyName="ZedGraph" processorArchitecture="MSIL" location="Packages/ZedGraph.5.1.7/lib/net35-Client/ZedGraph.dll" />
63112
</AssemblyLocations>
64113
<LibraryFolders>
65114
<LibraryFolder path="Packages/clroni.6.1.2/build/native/bin/x64" platform="x64" />
@@ -70,6 +119,8 @@
70119
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x64/native_uap" platform="x64" />
71120
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x86/native" platform="x86" />
72121
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x86/native_uap" platform="x86" />
122+
<LibraryFolder path="Packages/openal.redist.2.0.7.0/build/native/bin/x64" platform="x64" />
123+
<LibraryFolder path="Packages/openal.redist.2.0.7.0/build/native/bin/x86" platform="x86" />
73124
<LibraryFolder path="Packages/OpenCV.Net.3.4.2/runtimes/win-x64/native/vc14/bin" platform="x64" />
74125
<LibraryFolder path="Packages/OpenCV.Net.3.4.2/runtimes/win-x86/native/vc14/bin" platform="x86" />
75126
</LibraryFolders>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<WorkflowBuilder Version="2.8.5"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core"
5+
xmlns:commutator="clr-namespace:OpenEphys.Commutator;assembly=OpenEphys.Commutator"
6+
xmlns:port="clr-namespace:Bonsai.IO.Ports;assembly=Bonsai.System"
7+
xmlns="https://bonsai-rx.org/2018/workflow">
8+
<Description>Control an Open Ephys commutator by using rotation angle measurements.</Description>
9+
<Workflow>
10+
<Nodes>
11+
<Expression xsi:type="WorkflowInput">
12+
<Name>Source1</Name>
13+
</Expression>
14+
<Expression xsi:type="Combinator">
15+
<Combinator xsi:type="rx:SampleInterval">
16+
<rx:Interval>PT0.1S</rx:Interval>
17+
</Combinator>
18+
</Expression>
19+
<Expression xsi:type="ExternalizedMapping">
20+
<Property Name="RotationAxis" />
21+
</Expression>
22+
<Expression xsi:type="Combinator">
23+
<Combinator xsi:type="commutator:QuaternionToTwist">
24+
<commutator:RotationAxis>
25+
<commutator:X>0</commutator:X>
26+
<commutator:Y>0</commutator:Y>
27+
<commutator:Z>1</commutator:Z>
28+
</commutator:RotationAxis>
29+
</Combinator>
30+
</Expression>
31+
<Expression xsi:type="Format">
32+
<Format>{{turn : {0}}}</Format>
33+
<Selector>it</Selector>
34+
</Expression>
35+
<Expression xsi:type="ExternalizedMapping">
36+
<Property Name="Value" DisplayName="LedEnable" Description="If true, the commutator's LED will show status information. Otherwise, it will turn off." />
37+
</Expression>
38+
<Expression xsi:type="Combinator">
39+
<Combinator xsi:type="BooleanProperty">
40+
<Value>false</Value>
41+
</Combinator>
42+
</Expression>
43+
<Expression xsi:type="rx:Condition">
44+
<Workflow>
45+
<Nodes>
46+
<Expression xsi:type="WorkflowInput">
47+
<Name>Source1</Name>
48+
</Expression>
49+
<Expression xsi:type="WorkflowOutput" />
50+
</Nodes>
51+
<Edges>
52+
<Edge From="0" To="1" Label="Source1" />
53+
</Edges>
54+
</Workflow>
55+
</Expression>
56+
<Expression xsi:type="Combinator">
57+
<Combinator xsi:type="StringProperty">
58+
<Value>{led: true}</Value>
59+
</Combinator>
60+
</Expression>
61+
<Expression xsi:type="rx:Condition">
62+
<Workflow>
63+
<Nodes>
64+
<Expression xsi:type="WorkflowInput">
65+
<Name>Source1</Name>
66+
</Expression>
67+
<Expression xsi:type="BitwiseNot" />
68+
<Expression xsi:type="WorkflowOutput" />
69+
</Nodes>
70+
<Edges>
71+
<Edge From="0" To="1" Label="Source1" />
72+
<Edge From="1" To="2" Label="Source1" />
73+
</Edges>
74+
</Workflow>
75+
</Expression>
76+
<Expression xsi:type="Combinator">
77+
<Combinator xsi:type="StringProperty">
78+
<Value>{led: false}</Value>
79+
</Combinator>
80+
</Expression>
81+
<Expression xsi:type="Combinator">
82+
<Combinator xsi:type="rx:Merge" />
83+
</Expression>
84+
<Expression xsi:type="ExternalizedMapping">
85+
<Property Name="PortName" />
86+
</Expression>
87+
<Expression xsi:type="Combinator">
88+
<Combinator xsi:type="port:SerialWriteLine">
89+
<port:PortName />
90+
<port:NewLine>\r\n</port:NewLine>
91+
</Combinator>
92+
</Expression>
93+
<Expression xsi:type="WorkflowOutput" />
94+
</Nodes>
95+
<Edges>
96+
<Edge From="0" To="1" Label="Source1" />
97+
<Edge From="1" To="3" Label="Source1" />
98+
<Edge From="2" To="3" Label="Source2" />
99+
<Edge From="3" To="4" Label="Source1" />
100+
<Edge From="4" To="11" Label="Source1" />
101+
<Edge From="5" To="6" Label="Source1" />
102+
<Edge From="6" To="7" Label="Source1" />
103+
<Edge From="6" To="9" Label="Source1" />
104+
<Edge From="7" To="8" Label="Source1" />
105+
<Edge From="8" To="11" Label="Source2" />
106+
<Edge From="9" To="10" Label="Source1" />
107+
<Edge From="10" To="11" Label="Source3" />
108+
<Edge From="11" To="13" Label="Source1" />
109+
<Edge From="12" To="13" Label="Source2" />
110+
<Edge From="13" To="14" Label="Source1" />
111+
</Edges>
112+
</Workflow>
113+
</WorkflowBuilder>

OpenEphys.Commutator/OpenEphys.Commutator.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<TargetFramework>net472</TargetFramework>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<EmbeddedResource Include="**/*.bonsai" />
12+
</ItemGroup>
13+
1014
<ItemGroup>
1115
<PackageReference Include="Bonsai.Core" Version="2.8.5" />
1216
<PackageReference Include="Bonsai.System" Version="2.8.1" />
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.Linq;
4+
using System.Numerics;
5+
using System.Reactive.Linq;
6+
using Bonsai;
7+
8+
namespace OpenEphys.Commutator
9+
{
10+
/// <summary>
11+
/// Calculates a the rotation about a specified axis (the "twist") that has occurred between successive 3D
12+
/// rotation measurements.
13+
/// </summary>
14+
[Description("Calculates a feedback control signal to compensate for twisting about the specified axis, in units of turns.")]
15+
public class QuaternionToTwist : Combinator<Quaternion, double>
16+
{
17+
/// <summary>
18+
/// Gets or sets the direction vector specifying the axis around which to calculate the twist.
19+
/// </summary>
20+
/// <remarks>
21+
/// This vector should point, using the reference frame of the device producing rotation measurements,
22+
/// in the direction that the tether exits the headstage. Note that negating this vector will result in
23+
/// negating the direction of twisting.
24+
/// </remarks>
25+
[TypeConverter(typeof(NumericRecordConverter))]
26+
[Description("The direction vector specifying the axis around which to calculate the twist.")]
27+
public Vector3 RotationAxis { get; set; } = Vector3.UnitZ;
28+
29+
/// <summary>
30+
/// Calculates a twist about <see cref="RotationAxis"/> that has occurred between successive rotation
31+
/// measurements provided by the input sequence.
32+
/// </summary>
33+
/// <param name="source">The sequence of rotation measurements.</param>
34+
/// <returns>The sequence of twist values, in units of turns.</returns>
35+
public override IObservable<double> Process(IObservable<Quaternion> source)
36+
{
37+
return Observable.Defer(() =>
38+
{
39+
double? previousTwist = default;
40+
return source.Select(rotation =>
41+
{
42+
// project rotation axis onto the direction axis
43+
var direction = RotationAxis;
44+
var rotationAxis = new Vector3(rotation.X, rotation.Y, rotation.Z);
45+
var dotProduct = Vector3.Dot(rotationAxis, direction);
46+
var projection = dotProduct / Vector3.Dot(direction, direction) * direction;
47+
var twist = new Quaternion(projection, rotation.W);
48+
twist = Quaternion.Normalize(twist);
49+
if (dotProduct < 0) // account for angle-axis flipping
50+
{
51+
twist = -twist;
52+
}
53+
54+
// normalize twist feedback in units of turns
55+
var twistAngle = 2 * Math.Acos(twist.W);
56+
var feedback = previousTwist.HasValue
57+
? (twistAngle - previousTwist.GetValueOrDefault() + 3 * Math.PI) % (2 * Math.PI) - Math.PI
58+
: 0;
59+
previousTwist = twistAngle;
60+
return -feedback / (2 * Math.PI);
61+
});
62+
});
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)