-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #66 from neurogears/issue-14
Add HS64 electrical stim and trigger nodes
- Loading branch information
Showing
3 changed files
with
245 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
57 changes: 57 additions & 0 deletions
57
OpenEphys.Onix/OpenEphys.Onix/ConfigureHeadstage64ElectricalStimulator.cs
This file contains 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,57 @@ | ||
using System; | ||
|
||
namespace OpenEphys.Onix | ||
{ | ||
public class ConfigureHeadstage64ElectricalStimulator : SingleDeviceFactory | ||
{ | ||
public ConfigureHeadstage64ElectricalStimulator() | ||
: base(typeof(Headstage64ElectricalStimulator)) | ||
{ | ||
} | ||
|
||
public override IObservable<ContextTask> Process(IObservable<ContextTask> source) | ||
{ | ||
var deviceName = DeviceName; | ||
var deviceAddress = DeviceAddress; | ||
return source.ConfigureDevice(context => | ||
{ | ||
var device = context.GetDeviceContext(deviceAddress, Headstage64ElectricalStimulator.ID); | ||
device.WriteRegister(Headstage64ElectricalStimulator.ENABLE, 0); | ||
return DeviceManager.RegisterDevice(deviceName, device, DeviceType); | ||
}); | ||
} | ||
} | ||
|
||
static class Headstage64ElectricalStimulator | ||
{ | ||
public const int ID = 4; | ||
|
||
// managed registers | ||
public const uint NULLPARM = 0; // No command | ||
public const uint BIPHASIC = 1; // Biphasic pulse (0 = monophasic, 1 = biphasic; NB: currently ignored) | ||
public const uint CURRENT1 = 2; // Phase 1 current | ||
public const uint CURRENT2 = 3; // Phase 2 current | ||
public const uint PULSEDUR1 = 4; // Phase 1 duration, 1 microsecond steps | ||
public const uint INTERPHASEINTERVAL = 5; // Inter-phase interval, 10 microsecond steps | ||
public const uint PULSEDUR2 = 6; // Phase 2 duration, 1 microsecond steps | ||
public const uint INTERPULSEINTERVAL = 7; // Inter-pulse interval, 10 microsecond steps | ||
public const uint BURSTCOUNT = 8; // Burst duration, number of pulses in burst | ||
public const uint INTERBURSTINTERVAL = 9; // Inter-burst interval, microseconds | ||
public const uint TRAINCOUNT = 10; // Pulse train duration, number of bursts in train | ||
public const uint TRAINDELAY = 11; // Pulse train delay, microseconds | ||
public const uint TRIGGER = 12; // Trigger stimulation (1 = deliver) | ||
public const uint POWERON = 13; // Control estim sub-circuit power (0 = off, 1 = on) | ||
public const uint ENABLE = 14; // If 0 then stimulation triggers will be ignored, otherwise they will be applied | ||
public const uint RESTCURR = 15; // Resting current between pulse phases | ||
public const uint RESET = 16; // Reset all parameters to default | ||
public const uint REZ = 17; // Internal DAC resolution in bits | ||
|
||
internal class NameConverter : DeviceNameConverter | ||
{ | ||
public NameConverter() | ||
: base(typeof(Headstage64ElectricalStimulator)) | ||
{ | ||
} | ||
} | ||
} | ||
} |
182 changes: 182 additions & 0 deletions
182
OpenEphys.Onix/OpenEphys.Onix/Headstage64ElectricalStimulatorTrigger.cs
This file contains 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,182 @@ | ||
using System; | ||
using System.ComponentModel; | ||
using System.Drawing.Design; | ||
using System.Linq; | ||
using System.Reactive; | ||
using System.Reactive.Disposables; | ||
using System.Reactive.Linq; | ||
using System.Reactive.Subjects; | ||
using Bonsai; | ||
|
||
namespace OpenEphys.Onix | ||
{ | ||
public class Headstage64ElectricalStimulatorTrigger: Sink<bool> | ||
{ | ||
readonly BehaviorSubject<bool> enable = new(true); | ||
readonly BehaviorSubject<double> phaseOneCurrent = new(0); | ||
readonly BehaviorSubject<double> interPhaseCurrent = new(0); | ||
readonly BehaviorSubject<double> phaseTwoCurrent = new(0); | ||
readonly BehaviorSubject<uint> phaseOneDuration = new(0); | ||
readonly BehaviorSubject<uint> interPhaseInterval = new(0); | ||
readonly BehaviorSubject<uint> phaseTwoDuration = new(0); | ||
readonly BehaviorSubject<uint> interPulseInterval = new(0); | ||
readonly BehaviorSubject<uint> burstPulseCount = new(0); | ||
readonly BehaviorSubject<uint> interBurstInterval = new(0); | ||
readonly BehaviorSubject<uint> trainBurstCount = new(0); | ||
readonly BehaviorSubject<uint> trainDelay = new(0); | ||
readonly BehaviorSubject<bool> powerEnable = new(false); | ||
|
||
const double DacBitDepth = 16; | ||
const double AbsMaxMicroAmps = 2500; | ||
|
||
[TypeConverter(typeof(Headstage64ElectricalStimulator.NameConverter))] | ||
public string DeviceName { get; set; } | ||
|
||
[Description("Specifies whether the electrical stimulation subcircuit will respect triggers.")] | ||
public bool Enable | ||
{ | ||
get => enable.Value; | ||
set => enable.OnNext(value); | ||
} | ||
|
||
[Description("Phase 1 pulse current (uA).")] | ||
[Range(-AbsMaxMicroAmps, AbsMaxMicroAmps)] | ||
[Editor(DesignTypes.SliderEditor, typeof(UITypeEditor))] | ||
[Precision(3, 1)] | ||
public double PhaseOneCurrent | ||
{ | ||
get => phaseOneCurrent.Value; | ||
set => phaseOneCurrent.OnNext(value); | ||
} | ||
|
||
[Description("Interphase rest current (uA).")] | ||
[Range(-AbsMaxMicroAmps, AbsMaxMicroAmps)] | ||
[Editor(DesignTypes.SliderEditor, typeof(UITypeEditor))] | ||
[Precision(3, 1)] | ||
public double InterPhaseCurrent | ||
{ | ||
get => interPhaseCurrent.Value; | ||
set => interPhaseCurrent.OnNext(value); | ||
} | ||
|
||
[Description("Phase 2 pulse current (uA).")] | ||
[Range(-AbsMaxMicroAmps, AbsMaxMicroAmps)] | ||
[Editor(DesignTypes.SliderEditor, typeof(UITypeEditor))] | ||
[Precision(3, 1)] | ||
public double PhaseTwoCurrent | ||
{ | ||
get => phaseTwoCurrent.Value; | ||
set => phaseTwoCurrent.OnNext(value); | ||
} | ||
|
||
[Description("Pulse train start delay (uSec).")] | ||
[Range(0, uint.MaxValue)] | ||
public uint TrainDelay | ||
{ | ||
get => trainDelay.Value; | ||
set => trainDelay.OnNext(value); | ||
} | ||
|
||
[Description("Phase 1 pulse duration (uSec).")] | ||
[Range(0, uint.MaxValue)] | ||
public uint PhaseOneDuration | ||
{ | ||
get => phaseOneDuration.Value; | ||
set => phaseOneDuration.OnNext(value); | ||
} | ||
|
||
[Description("Inter-phase interval (uSec).")] | ||
[Range(0, uint.MaxValue)] | ||
public uint InterPhaseInterval | ||
{ | ||
get => interPhaseInterval.Value; | ||
set => interPhaseInterval.OnNext(value); | ||
} | ||
|
||
[Description("Phase 2 pulse duration (uSec).")] | ||
[Range(0, uint.MaxValue)] | ||
public uint PhaseTwoDuration | ||
{ | ||
get => phaseTwoDuration.Value; | ||
set => phaseTwoDuration.OnNext(value); | ||
} | ||
|
||
[Description("Inter-pulse interval (uSec).")] | ||
[Range(0, uint.MaxValue)] | ||
public uint InterPulseInterval | ||
{ | ||
get => interPulseInterval.Value; | ||
set => interPulseInterval.OnNext(value); | ||
} | ||
|
||
[Description("Inter-burst interval (uSec).")] | ||
[Range(0, uint.MaxValue)] | ||
public uint InterBurstInterval | ||
{ | ||
get => interBurstInterval.Value; | ||
set => interBurstInterval.OnNext(value); | ||
} | ||
|
||
[Description("Number of pulses in each burst.")] | ||
[Range(0, uint.MaxValue)] | ||
public uint BurstPulseCount | ||
{ | ||
get => burstPulseCount.Value; | ||
set => burstPulseCount.OnNext(value); | ||
} | ||
|
||
[Description("Number of bursts in each train.")] | ||
[Range(0, uint.MaxValue)] | ||
public uint TrainBurstCount | ||
{ | ||
get => trainBurstCount.Value; | ||
set => trainBurstCount.OnNext(value); | ||
} | ||
|
||
[Description("Stimulator power on/off.")] | ||
[Range(0, uint.MaxValue)] | ||
public bool PowerEnable | ||
{ | ||
get => powerEnable.Value; | ||
set => powerEnable.OnNext(value); | ||
} | ||
|
||
public override IObservable<bool> Process(IObservable<bool> source) | ||
{ | ||
return Observable.Using( | ||
() => DeviceManager.ReserveDevice(DeviceName), | ||
disposable => disposable.Subject.SelectMany(deviceInfo => | ||
Observable.Create<bool>(observer => | ||
{ | ||
var device = deviceInfo.GetDeviceContext(typeof(Headstage64ElectricalStimulator)); | ||
var triggerObserver = Observer.Create<bool>( | ||
value => device.WriteRegister(Headstage64ElectricalStimulator.TRIGGER, value ? 1u : 0u), | ||
observer.OnError, | ||
observer.OnCompleted); | ||
|
||
static uint uAToCode(double currentuA) | ||
{ | ||
var k = 1 / (2 * AbsMaxMicroAmps / (Math.Pow(2, DacBitDepth) - 1)); // static | ||
return (uint)(k * (currentuA + AbsMaxMicroAmps)); | ||
} | ||
|
||
return new CompositeDisposable( | ||
enable.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.ENABLE, value ? 1u : 0u)), | ||
phaseOneCurrent.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.CURRENT1, uAToCode(value))), | ||
interPhaseCurrent.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.RESTCURR, uAToCode(value))), | ||
phaseTwoCurrent.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.CURRENT2, uAToCode(value))), | ||
trainDelay.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.TRAINDELAY, value)), | ||
phaseOneDuration.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.PULSEDUR1, value)), | ||
interPhaseInterval.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.INTERPHASEINTERVAL, value)), | ||
phaseTwoDuration.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.PULSEDUR2, value)), | ||
interPulseInterval.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.INTERPULSEINTERVAL, value)), | ||
interBurstInterval.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.INTERBURSTINTERVAL, value)), | ||
burstPulseCount.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.BURSTCOUNT, value)), | ||
trainBurstCount.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.TRAINCOUNT, value)), | ||
powerEnable.Subscribe(value => device.WriteRegister(Headstage64ElectricalStimulator.POWERON, value ? 1u : 0u)), | ||
source.SubscribeSafe(triggerObserver) | ||
); | ||
}))); | ||
} | ||
} | ||
} |