multichannelsystems / McsUsbNet_Examples

C#, Python and Matlab code examples to interact with the McsUsbNet.dll
BSD 2-Clause "Simplified" License
7 stars 2 forks source link

How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite #13

Closed gchassink closed 1 year ago

gchassink commented 1 year ago

Hi, When I try to stimulate using a MATLAB script based on the example provided by MCS, its works. MEA2100 MCS-IFB-Lite. However if I a want to use automatic stimulation in stead of dedicated electrode stimulation(to reduce artefacts when switching back to reading mode of the electrode, using SetElectrodeMode(electrode, Mcs.Usb.ElectrodeModeEnumNet.emAutomatic) it does not stimulate. (checked with oscilloscope).

I can let it stimulate by: 1) stop my matlab script (see below) 2) switch to MC-Rack program a stimulation on stg1 3) stimulate once 4) stop mc-rack 5) run the matlabscript without rebooting the IFB So Mcs.Usb.ElectrodeModeEnumNet.emAutomatic does work.

After further studying the McsUsbNet. documentation I suspect it has to do with programming the sidebandsignal using something like, SidebandData=CstimulusFunctionNet.CreateSideband(StimulusActive, Syncout, duration, Bit0Time,Bit3Time, Bit4time), however this instruction is not part of CStg200xDownloadNet(). so i don't know how to access it.

Can anybody help me on the right track?

Kind regards,

Gerco

function stimEventTime=stimulate_electrode( devicelist, electrode, channelorder, stimtype, amplitude, duration)

electrode=find(channelorder==electrode)-1;% MCS=0:59, matlab=1:60
if devicelist.Count == 0

    Msgbox('No MEA USB Device connected!', 'Error Connecting To Device', 'error');
end

cStgDevice=Mcs.Usb.CStg200xDownloadNet();

% Connect to the stimulator of the device. The lock mask allows multiple connections to the same device
status = cStgDevice.Connect(devicelist.GetUsbListEntry(0),1); % ,1) is the lockmask
if status == 0

    % Make sure that the stimulation is stopped
    cStgDevice.SendStop(uint32(1));

    % ElectrodeMode: emManual: electrode is permanently selected
    % for stimulation or emAutomatic for automatic
    %                 cStgDevice.SetElectrodeMode(electrode,  Mcs.Usb.ElectrodeModeEnumNet.emManual);
    cStgDevice.SetElectrodeMode(electrode, Mcs.Usb.ElectrodeModeEnumNet.emAutomatic);

    % ElectrodeDacMux: DAC to use for stimulation
    % cStgDevice.SetElectrodeDacMux(electrode,listmodeindex,0);% required to reset the dacmux in between stimulations

    cStgDevice.SetElectrodeDacMux(electrode,0, Mcs.Usb.ElectrodeDacMuxEnumNet.Stg1);% (electrode, index=0, dac)

    % 0 = Ground
    % 1 = Stg1
    % 2 = Stg2
    % 3 = Stg3

    % ElectrodeEnable: enable electrode for stimulation
    % cStgDevice.SetElectrodeEnable(electrode, listmodeindex, enable);
    cStgDevice.SetElectrodeEnable(electrode, 0, true);

    % for i=0:59%find(channelorder==32)-1
    %     if  i==14 % the ground electrode itself ||i==electrode
    %     % BlankingEnable: false: do not blank the ADC signal while stimulation is running
    %     cStgDevice.SetBlankingEnable(i, false);
    %     % AmplifierProtectionSwitch: false: Keep ADC connected to electrode even while stimulation is running
    %     cStgDevice.SetEnableAmplifierProtectionSwitch(i, false);
    %     else
    %        cStgDevice.SetBlankingEnable(i, true);
    %        cStgDevice.SetEnableAmplifierProtectionSwitch(i, true);
    %     end
    % end
    % cStgDevice.SetBlankingEnable(electrode, true );
    % cStgDevice.SetEnableAmplifierProtectionSwitch(electrode, true);
    %

    if stimtype=='nA';
        % use current stimulation
        cStgDevice.SetCurrentMode();

    else
        % use voltage stimulation
        cStgDevice.SetVoltageMode();

    end

    if stimtype=='nA'
        cStgDevice.PrepareAndSendData(0, NET.convertArray(amplitude, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.channeldata_current);
    else
        cStgDevice.PrepareAndSendData(0, NET.convertArray(amplitude, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.channeldata_voltage);
    end

    % connect all stimulation channels to the first trigger and repeat the
    % pulse 1 times

    cStgDevice.SendStart(uint32(1));

    %% bring electrode back in activated state
    % A hack to bring back the electrode to reading mode
    % cStgDevice.SetElectrodeMode(electrode, Mcs.Usb.ElectrodeModeEnumNet.emAutomatic);
    % cStgDevice.SetElectrodeDacMux(electrode,0, Mcs.Usb.ElectrodeDacMuxEnumNet.Stg1);% (electrode, index=0, dac)
    % cStgDevice.SetElectrodeEnable(electrode, 0, true);
    % cStgDevice.SetBlankingEnable(electrode, true);
    % cStgDevice.SetEnableAmplifierProtectionSwitch(electrode, true);
    % amplitude =int32([0, 0]); % nA or uV
    % duration = uint64([0, 0]); % ??s
    % cStgDevice.SetVoltageMode();
    % cStgDevice.PrepareAndSendData(0, NET.convertArray(amplitude, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.channeldata_voltage);
    % cStgDevice.SetupTrigger(0, NET.convertArray(255, 'System.UInt32'), NET.convertArray(255, 'System.UInt32'), NET.convertArray(1, 'System.UInt32'));
    % cStgDevice.SendStart(uint32(1));

    %% end session and disconnect
    % TriggerInputs = cStgDevice.GetNumberOfTriggerInputs();
    stimEventTime=datetime;
    cStgDevice.Disconnect(); % ,1)

else
    disp ('connection failed');
    disp (dec2hex(status));
    disp (Mcs.Usb.CMcsUsbNet.GetErrorText(status));
end

end

ghost commented 1 year ago

I have added the option to stimulate in automatic mode to the C# example "MEA2100_Recording_and_Stimulation". To select this option choose emAutomatic here:

// Defining the Elektrodes. Choose if you want use automatic or manual mode for the stimulation electrode here:
ElectrodeModeEnumNet electrodeMode = ElectrodeModeEnumNet.emAutomatic;
//ElectrodeModeEnumNet electrodeMode = ElectrodeModeEnumNet.emManual;

Do not forget to define the correct headstage private int HeadStage = 1; from 0 to 3.

If you increase the stimulation voltage (or anyway) consider enabling the amplifier protection switch, setting the parameter to true:

stg.SetEnableAmplifierProtectionSwitch((uint)HeadStage, (uint)i, false); // Enable the switch if you want to protect the amplifier from an overload

The usage of the call CreateSideband and the meaning of the relevant bits 0, 3 and 4 is explained in the code:

// bit0 (blanking switch) activation duration prolongation in µs
uint Bit0Time = 40;

// bit3 (stimulation switch) activation duration prolongation in µs
uint Bit3Time = 800;

// bit4 (stimulus selection switch) activation duration prolongation in µs
uint Bit4Time = 40;

CStimulusFunctionNet.SidebandData SidebandData1 = stg.Stimulus.CreateSideband(StimulusActive1, sideband1, duration, Bit0Time, Bit3Time, Bit4Time);
gchassink commented 1 year ago

Dear Jens,

Thanks for your new example script to read and stimulate using the same electrode: I tested it on our system (MEA2100 IFB-Lite 90067) driver: MEA-2100 Lite (version date 30-9-2022, 6.10.11.0 ) using visual studio 2022. Forgive me If I do not understand the code completely, I am not an C# programmer.

It worked partially: I have several issues:

  1. scu.SetDacqLegacyMode(false); --> throws an error. We don't have an scu so I commented it out, Right?

  2. stg.SetElectrodeMode((uint)HeadStage, (uint)i...... --> the headstage parameter gives an overload error MCS-Lite has only 1 (0) so I left this parameter out in this and similar commands (see adapted Form1.cs), reading then works

  3. Stimulating shows the block pulses in the chart but not in our external oscilloscope, which is able to detect the same stimulation pattern when programmed in MC-Rack

    • (I tried both channel 0 and channel 39 which I mostly use for testing)
    • In other words though the pulse seems visible in the software it is not actually stimulation (in my opinion)
  4. I do not understand the meaning coding pattern {1, 3, 0, 1, 3,0} of the sideband in this script or{ 4, 12, 0, 4, 12, 0 } for that matter , I expected it to be {1,1,0,1,1,0} as in the McsUsbNet documentation "Each datapoint is represented by an signed 32bit integer value. A value 0 means that the stimulation is active during that time. A value 1 means that the stimulation is not active during that time."

So as said our main goal is to stimulate using a block pulse negative first 200 us, at a given time with only little stimulation artefacts, at any desired channel, while reading remains possible on that channel. I attached your script with my adaptations, to show how I interpreted it.

We can also organize a teams call or something if that solves the problem with less effort for you? Meanwhile I will try to translate the C# script and incorporate it into out matlab script, which I can send you as well if you want, but it is rather big.

Kind Regards,

Gerco Hassink

G.C. Hassink, PhD University of Twente Dep. Clinical Neurophysiology Drienerlolaan 5, 7522 NB Enschede The Netherlands +31534892762

From: JensMCS @.> Sent: maandag 5 december 2022 16:16 To: multichannelsystems/McsUsbNet_Examples @.> Cc: Hassink, Gerco (UT-TNW) @.>; Author @.> Subject: Re: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

I have added the option to stimulate in automatic mode to the C# example "MEA2100_Recording_and_Stimulation". To select this option choose emAutomatic here:

// Defining the Elektrodes. Choose if you want use automatic or manual mode for the stimulation electrode here:

ElectrodeModeEnumNet electrodeMode = ElectrodeModeEnumNet.emAutomatic;

//ElectrodeModeEnumNet electrodeMode = ElectrodeModeEnumNet.emManual;

Do not forget to define the correct headstage private int HeadStage = 1; from 0 to 3.

If you increase the stimulation voltage (or anyway) consider enabling the amplifier protection switch, setting the parameter to true:

stg.SetEnableAmplifierProtectionSwitch((uint)HeadStage, (uint)i, false); // Enable the switch if you want to protect the amplifier from an overload

The usage of the call CreateSideband and the meaning of the relevant bits 0, 3 and 4 is explained in the code:

// bit0 (blanking switch) activation duration prolongation in µs

uint Bit0Time = 40;

// bit3 (stimulation switch) activation duration prolongation in µs

uint Bit3Time = 800;

// bit4 (stimulus selection switch) activation duration prolongation in µs

uint Bit4Time = 40;

CStimulusFunctionNet.SidebandData SidebandData1 = stg.Stimulus.CreateSideband(StimulusActive1, sideband1, duration, Bit0Time, Bit3Time, Bit4Time);

- Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmultichannelsystems%2FMcsUsbNet_Examples%2Fissues%2F13%23issuecomment-1337555245&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C5823e97c241b4c82093b08dad6d39433%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638058501457871587%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=sp%2BoFs%2BP6LJXQvnofCeB6mZus47Bdx94wyKIPaB1puU%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAYVFLOPOZ5I5QXA376TMPB3WLYBJ5ANCNFSM6AAAAAASNQQCYY&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C5823e97c241b4c82093b08dad6d39433%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638058501457871587%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=ufgf6X4O%2BsJcZqLT6Za4tRBgkMPxvbNJeJWWEKXMl1c%3D&reserved=0. You are receiving this because you authored the thread.Message ID: @.**@.>>

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Mcs.Usb;

namespace MEA2100_Recording_and_Stimulation { public partial class Form1 : Form { private CMcsUsbListNet list = new CMcsUsbListNet(DeviceEnumNet.MCS_DEVICE_USB);

    private CMeaUSBDeviceNet dacq = new CMeaUSBDeviceNet();
    private CStg200xDownloadNet stg = new CStg200xDownloadNet();

    public Form1()
    {
        InitializeComponent();

        list.DeviceArrival += List_DeviceArrivalRemoval;
        list.DeviceRemoval += List_DeviceArrivalRemoval;

        dacq.ErrorEvent += Dacq_ErrorEvent;
        dacq.ChannelDataEvent += Dacq_ChannelDataEvent;

        stg.Stg200xPollStatusEvent += Stg_Stg200xPollStatusEvent;

        RefreshDeviceList();
    }

    private void Stg_Stg200xPollStatusEvent(uint status, StgStatusNet stgStatusNet, int[] index_list)
    {
        if (InvokeRequired)
        {
            BeginInvoke(new OnStgPollStatus(Stg_Stg200xPollStatusEvent), status, stgStatusNet, index_list);
        }
        else
        {
            listBox1.Items.Add("STG: " + status.ToString("X8"));
        }
    }

    private void RefreshDeviceList()
    {
        cbDeviceList.Items.Clear();
        cbDeviceList.Items.AddRange(list.GetUsbListEntries());
        if (cbDeviceList.Items.Count > 0)
        {
            cbDeviceList.SelectedIndex = 0;
        }
    }

    private void List_DeviceArrivalRemoval(CMcsUsbListEntryNet entry)
    {
        RefreshDeviceList();
    }

    private void Dacq_ErrorEvent(string msg, int action)
    {
        if (InvokeRequired)
        {
            BeginInvoke(new OnError(Dacq_ErrorEvent), msg, action);
        }
        else
        {
            listBox1.Items.Add(msg);
        }
    }
    // using the IFB Lite on to stimulate and read on channel 39 in automatic mode
    private int Electrode = 39;
    private int HeadStage = 0;

    private int channelsInBlock = 60;
    private int threshold = 0;
    private void Dacq_ChannelDataEvent(CMcsUsbDacqNet dacq, int CbHandle, int numFrames)
    {
        List<int[]> data = new List<int[]>();
        for (int i = 0; i < channelsInBlock / 2; i++)
        {
            data.Add(dacq.ChannelBlock_ReadFramesI32(i, threshold, out int frames_ret));
        }
        // 0 - 59: Elektrode Channels
        // 60 - 67 IFB Analog IFB channels
        // 68 Digital In/Out
        // 69 - 74 Sideband channels
        // 75 - 76 Checksum channels
        BeginInvoke(new HandleDataDelegate(HandleData), data[Electrode], data[69 + HeadStage]);
    }

    delegate void HandleDataDelegate(int[] data1, int[] data2);

    private void HandleData(int[] data1, int[] data2)
    {
        FillSeried(0, data1);
        FillSeried(1, data2);
    }

    private void FillSeried(int serie, int[] data)
    {
        chart1.Series[serie].Points.Clear();
        for (int i = 0; i < data.Length; i++)
        {
            chart1.Series[serie].Points.AddXY(i, data[i]);
        }
    }

    private void btRecordingStart_Click(object sender, EventArgs e)
    {
        CMcsUsbListEntryNet deviceEntry = (CMcsUsbListEntryNet) cbDeviceList.SelectedItem;
        uint status = dacq.Connect(deviceEntry);
        if (status == 0)
        {
            stg.Connect(deviceEntry);

            dacq.StopDacq(0); // if software hat not stopped sampling correctly before

            CSCUFunctionNet scu = new CSCUFunctionNet(dacq);
            //scu.SetDacqLegacyMode(false);

            int samplerate = 25000;
            dacq.SetSamplerate(samplerate, 0, 0);

            dacq.SetDataMode(DataModeEnumNet.Signed_32bit, 0);
            // for MEA2100-Mini it is assumed that only one HS is connected
            dacq.SetNumberOfAnalogChannels(60, 0, 0, 8, 0);

            dacq.EnableDigitalIn(DigitalDatastreamEnableEnumNet.DigitalIn | DigitalDatastreamEnableEnumNet.DigitalOut |
                                 DigitalDatastreamEnableEnumNet.Hs1SidebandLow | DigitalDatastreamEnableEnumNet.Hs1SidebandHigh, 1);
            dacq.EnableChecksum(true, 0);

            // numbers are in 16bit
            dacq.GetChannelLayout(out int analogChannels, out int digitalChannels, out int checksumChannels, out int timestampChannels, out channelsInBlock, 0);

            int queuesize = samplerate;
            threshold = samplerate / 10;
            // channelsInBlock / 2 gives the number of channels in 32bit
            dacq.SetSelectedChannels(channelsInBlock / 2, queuesize, threshold, SampleSizeNet.SampleSize32Signed, channelsInBlock);

            dacq.ChannelBlock_SetCommonThreshold(threshold);

            dacq.ChannelBlock_SetCheckChecksum((uint) checksumChannels, (uint) timestampChannels);

            dacq.StartDacq();
        }
        else
        {
            MessageBox.Show("Connection failed: " + CMcsUsbNet.GetErrorText(status));
        }
    }

    private void btRecordingStop_Click(object sender, EventArgs e)
    {
        dacq.StopDacq(0);
        dacq.Disconnect();
        stg.Disconnect();
    }

    private void btStimulationStart_Click(object sender, EventArgs e)
    {
        int[] amplitude1 = new int[] { 100000, -100000, 0};
       // int[] amplitude2 = new int[] { -10000, 10000, 0, -20000, 20000, 0 };
        int[] sideband1 = new int[] { 1, 3, 0 };
       // int[] sideband2 = new int[] { 4, 12, 0, 4, 12, 0 };
        ulong[] duration = new ulong[] {200, 200, 10000}; // could be different in length an numbers for each amplitude and sideband, it only needs to be equal in length for the individual amplitude and sideband 

        //for (int i = 0; i < 60; i++)
        //{
            stg.SetElectrodeMode((uint)Electrode,ElectrodeModeEnumNet.emAutomatic); //? ElectrodeModeEnumNet.emManual : ElectrodeModeEnumNet.emAutomatic);
            stg.SetElectrodeEnable((uint)Electrode, 0, true);// ? true : false) ;//(uint)HeadStage,
            stg.SetElectrodeDacMux((uint)Electrode, 0, ElectrodeDacMuxEnumNet.Stg1);// ? ElectrodeDacMuxEnumNet.Stg1 : ElectrodeDacMuxEnumNet.Ground);
            stg.SetEnableAmplifierProtectionSwitch((uint)Electrode, true);
        //}

        stg.SetVoltageMode(0);

        stg.PrepareAndSendData(2 * (uint)HeadStage + 0, amplitude1, duration, STG_DestinationEnumNet.channeldata_voltage);
        stg.PrepareAndSendData(2 * (uint)HeadStage + 0, sideband1, duration, STG_DestinationEnumNet.syncoutdata);

        //stg.PrepareAndSendData(2 * (uint)HeadStage + 1, amplitude2, duration, STG_DestinationEnumNet.channeldata_voltage);
        //stg.PrepareAndSendData(2 * (uint)HeadStage + 1, sideband2, duration, STG_DestinationEnumNet.syncoutdata);

        stg.SetupTrigger(0, new uint[]{255}, new uint[]{255}, new uint[]{10});

        stg.SendStart(1);
    }

    private void btStimulatinStop_Click(object sender, EventArgs e)
    {
        stg.SendStop(1);
    }
}

}

ghost commented 1 year ago
  1. scu.SetDacqLegacyMode(false); --> throws an error. We don't have an scu so I commented it out, Right?

Yes, this command is only valid for SCU based MEA systems.

  1. stg.SetElectrodeMode((uint)HeadStage, (uint)i...... --> the headstage parameter gives an overload error MCS-Lite has only 1 (0) so I left this parameter out in this and similar commands (see adapted Form1.cs), reading then works

Yes, these commands are overloaded with and without Headstage parameter

  1. Stimulating shows the block pulses in the chart but not in our external oscilloscope, which is able to detect the same stimulation pattern when programmed in MC-Rack

There could be serveral reasons:

  1. I do not understand the meaning coding pattern {1, 3, 0, 1, 3,0} of the sideband in this script or{ 4, 12, 0, 4, 12, 0 } for that matter , I expected it to be {1,1,0,1,1,0} as in the McsUsbNet documentation "Each datapoint is represented by an signed 32bit integer value. A value 0 means that the stimulation is active during that time. A value 1 means that the stimulation is not active during that time."

In automatic mode bit 0, 3 and 4 are controlling the switches and should not be use by the user. For that reason I moved the user bits above bit 8 (do you have the latest version of the example where I have done this). Bit 0, 3, and 4 are deduced from the StimulusActivearray with the call of CreateSideband. But you could also set it by hand. Bit 0,3 and 4 set gives 25. So set the sideband to 25 during stimulation and to 0 outside.

gchassink commented 1 year ago

Hi Jens,

  1. The new version of your script from github, indeed works on my system.

  2. I also translated the for us relevant part in Matlab. And it results in signal at scope as well. please see attached matlab script.

  3. what still does not work is the CreateSideband. I think there is problem withe formatting of the arguments

         sideband = [25,25,0];%[bitsll(1, 8), bitsll(3, 8),0] does not seem to work matlab equevallent of << operator StimulusActive= [1,1,0] ; duration = [duration, 0]; %in our case duration is normally [200,200] amplitude= [amplitude,0]; %in our case amplitude is for now set to [-200000,200000], normally way lower and ultimately we will use 8-24 uA current in stead of voltage Bit0Time=40 ;% bit0 (blanking switch) activation duration prolongation in µs (multiples of 20) Bit3Time=800; %bit3 (stimulation switch) activation duration prolongation in µs Bit4Time=40; %bit4 (stimulus selection switch) activation duration prolongation in µs

  1. SidebandData = Devices.stg.Stimulus.CreateSideband(... NET.convertArray(StimulusActive, 'System.Int32'),... NET.convertArray(sideband, 'System.Int32'),... NET.convertArray(duration, 'System.UInt64'),... NET.convertArray(Bit0Time, 'System.UInt32'),... NET.convertArray(Bit3Time, 'System.UInt32'),... NET.convertArray(Bit4Time, 'System.UInt32'));

I think the bitshift procedure is not going well. I am not used to think in bits, 25 binary = 11001 so bit 0, 3 amd 4 from left to right. and when use this number as sideband so [25,25,0] it works but it is probably not how it should be done. since you use different 256 and 768 respecticely for the negative and positive. Can you explain the relation between sideband and amplitude? or is it not related? Hope my question is clear enough. Gerco


Van: JensMCS @.> Verzonden: dinsdag 13 december 2022 8:49 Aan: multichannelsystems/McsUsbNet_Examples @.> CC: Hassink, Gerco (UT-TNW) @.>; Author @.> Onderwerp: Re: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

  1. scu.SetDacqLegacyMode(false); --> throws an error. We don't have an scu so I commented it out, Right?

Yes, this command is only valid for SCU based MEA systems.

  1. stg.SetElectrodeMode((uint)HeadStage, (uint)i...... --> the headstage parameter gives an overload error MCS-Lite has only 1 (0) so I left this parameter out in this and similar commands (see adapted Form1.cs), reading then works

Yes, these commands are overloaded with and without Headstage parameter

  1. Stimulating shows the block pulses in the chart but not in our external oscilloscope, which is able to detect the same stimulation pattern when programmed in MC-Rack

There could be serveral reasons:

In automatic mode bit 0, 3 and 4 are controlling the switches and should not be use by the user. For that reason I moved the user bits above bit 8 (do you have the latest version of the example where I have done this). Bit 0, 3, and 4 are deduced from the StimulusActive array with the call of CreateSideband. But you could also set it by hand. Bit 0,3 and 4 set gives 25. So set the sideband to 25 during stimulation and to 0 outside.

— Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmultichannelsystems%2FMcsUsbNet_Examples%2Fissues%2F13%23issuecomment-1347879385&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C3875c02095cd423a9dfe08dadcde84a7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638065145507621275%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=mQze6wL%2BivCYmq8Kwj6dS36daFvx87IVpPVjw1BF%2BdQ%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAYVFLOPVNGU6IUTPTQEWHQTWNAS7JANCNFSM6AAAAAASNQQCYY&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C3875c02095cd423a9dfe08dadcde84a7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638065145507621275%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=dXQb0gYFS0M%2FJOlU%2FZBRpmLwOm67Ag4EYh%2BCFF0TY%2Fc%3D&reserved=0. You are receiving this because you authored the thread.Message ID: @.***>

% From the documentation of the DLL % The STG200x & STG400x Series Stimulus Generators have two distinct modes of operation, % the Download mode and the Streaming mode:The Download mode is the "classic" mode of % operation, as used by the MC Stimulus software. In this mode, one or multiple % waveforms are defined in PC memory and downloaded to the STG. The waveforms % are stored in STG device onboard memory and can be sent to the analog and % sync outputs once or multiple times. The STG can operate independantly from % the PC (without computer connection) after the download. Output is triggered % either by the front panel start/stop button, the digital trigger inputs or under software control. % The other mode of operation is the Streaming mode. Here the analog output % is sent to the STG device in "real time". The PC has to be connected to the % STG all the time. The data that is sent to the analog output is downloaded % from the PC to the STG on the fly. The Streaming mode is useful for applications % where flexible feedback is needed as well for applications where very long % waveforms which are not repeated (such as white noise) are used.

function stimEventTime=stimulate_electrode( electrode, channelorder, stimtype, amplitude, duration) global Devices FileInfo electrode=find(channelorder==electrode)-1;% MCS=0:59, matlab=1:60 if Devices.devicelist.Count == 0

            Msgbox('No MEA USB Device connected!', 'Error Connecting To Device', 'error');
        end

        if Devices.status == 0

            sideband = [25,25,0];%[bitsll(1, 8), bitsll(3, 8),0];
            StimulusActive= [1,1,0] ;
            duration = [duration, 0]; %in our case duration is normally [200,200]
            amplitude= [amplitude,0]; %in our case amplitude is for testing purposes set to [-200000,200000]
            Bit0Time=40 ;% bit0 (blanking switch) activation duration prolongation in ?s (multiples of 20)
            Bit3Time=800; %bit3 (stimulation switch) activation duration prolongation in ?s
            Bit4Time=40; %bit4 (stimulus selection switch) activation duration prolongation in ?s

            Devices.stg.SetElectrodeMode(electrode, Mcs.Usb.ElectrodeModeEnumNet.emAutomatic);

            Devices.stg.SetElectrodeEnable( electrode, 0, true);
            %electrodeMode=Devices.stg.GetElectrodeMode(electrode) 
            Devices.stg.SetElectrodeDacMux( electrode,0, Mcs.Usb.ElectrodeDacMuxEnumNet.Stg1);% (electrode, index=0, dac)

            for i=0:59%find(channelorder==32)-1

                    Devices.stg.SetEnableAmplifierProtectionSwitch( i, true);
                    Devices.stg.SetBlankingEnable( i, true);

%
end %

            if stimtype=='nA';
                % use current stimulation
                Devices.stg.SetCurrentMode();
                %disp('currentmode');
            else
                % use voltage stimulation
                Devices.stg.SetVoltageMode();
                 %disp('voltagemode');
            end

            %StimulusActive=zeros(length(amplitude),1);

      %cStgStimulusFunctions

% SidebandData = CStimulusFunctionNet( CreateSideband(... %
%

            if stimtype=='nA'
                Devices.stg.PrepareAndSendData(0, NET.convertArray(amplitude, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.channeldata_current);
                Devices.stg.PrepareAndSendData(0, NET.convertArray(sideband, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.syncoutdata);
            else
                Devices.stg.PrepareAndSendData(0, NET.convertArray(amplitude, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.channeldata_voltage);

% SidebandData = Devices.stg.Stimulus.CreateSideband(... % NET.convertArray(StimulusActive, 'System.Int32'),...
% NET.convertArray(sideband, 'System.Int32'),... % NET.convertArray(duration, 'System.UInt64'),... % NET.convertArray(Bit0Time, 'System.UInt32'),... % NET.convertArray(Bit3Time, 'System.UInt32'),... % NET.convertArray(Bit4Time, 'System.UInt32')); Devices.stg.PrepareAndSendData(0, NET.convertArray(sideband, 'System.Int32'), NET.convertArray(duration, 'System.UInt64'), Mcs.Usb.STG_DestinationEnumNet.syncoutdata); end

            % connect all stimulation channels to the first trigger and repeat the
            % pulse 1 times

% first_trigger= 0 ; % channelmap=255 ; % syncoutmap=255; % repeat =1;

            Devices.stg.SetupTrigger(0, NET.convertArray(255, 'System.UInt32'), NET.convertArray(255, 'System.UInt32'), NET.convertArray(1, 'System.UInt32'));

            % very important to use the Net.convertArray functiosn, since Matlab Uint32 itself does not translate correctly apparantly
            % start the first trigger

            Devices.stg.SendStart(uint32(1));

            stimEventTime=datetime;
             Devices.stg.SendStop(uint32(1));%Devices.stg.Disconnect(); % ,1)
            %disp(['electrode ' num2str(electrode+1) ' (' num2str(channelorder(electrode+1)) ') has been reactivated as well!']);

        else
            disp ('connection failed');
            disp (dec2hex(Devices.status));
            disp (Mcs.Usb.CMcsUsbNet.GetErrorText(Devices.status));
        end

end

gchassink commented 1 year ago

Dear Jens,

I found the problem.

sideband = int32([bitsll(3, 8), bitsll(1, 8),0]);%[25,25,0]; StimulusActive= int32([1,1,0]) ; duration = uint64([duration, 10000]); %in our case duration is normally [200,200] amplitude= int32([amplitude,0]); %in our case amplitude is for testing purposes set to [-200000,200000] Bit0Time=uint32(40) ;% bit0 (blanking switch) activation duration prolongation in µs (multiples of 20) Bit3Time=uint32(800); %bit3 (stimulation switch) activation duration prolongation in µs Bit4Time=uint32(40); %bit4 (stimulus selection switch) activation duration prolongation in µs SidebandData = Devices.stg.Stimulus.CreateSideband(StimulusActive, sideband, duration, Bit0Time, Bit3Time, Bit4Time); Devices.stg.PrepareAndSendData(0, SidebandData.Sideband, SidebandData.Duration, Mcs.Usb.STG_DestinationEnumNet.syncoutdata);

is accepted by matlab

two questions left:

  1. is it correct that sideband 3<<8 is used for negative amplitudes and 1<<8 for postive numbers, or is there a different relation between amplitude and sideband?
  2. Is it true that you should always end your amplitude sequence with a 0 amplitude, with a duration of larger than 0 us?

Gerco


Van: JensMCS @.> Verzonden: dinsdag 13 december 2022 8:49 Aan: multichannelsystems/McsUsbNet_Examples @.> CC: Hassink, Gerco (UT-TNW) @.>; Author @.> Onderwerp: Re: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

  1. scu.SetDacqLegacyMode(false); --> throws an error. We don't have an scu so I commented it out, Right?

Yes, this command is only valid for SCU based MEA systems.

  1. stg.SetElectrodeMode((uint)HeadStage, (uint)i...... --> the headstage parameter gives an overload error MCS-Lite has only 1 (0) so I left this parameter out in this and similar commands (see adapted Form1.cs), reading then works

Yes, these commands are overloaded with and without Headstage parameter

  1. Stimulating shows the block pulses in the chart but not in our external oscilloscope, which is able to detect the same stimulation pattern when programmed in MC-Rack

There could be serveral reasons:

In automatic mode bit 0, 3 and 4 are controlling the switches and should not be use by the user. For that reason I moved the user bits above bit 8 (do you have the latest version of the example where I have done this). Bit 0, 3, and 4 are deduced from the StimulusActive array with the call of CreateSideband. But you could also set it by hand. Bit 0,3 and 4 set gives 25. So set the sideband to 25 during stimulation and to 0 outside.

— Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmultichannelsystems%2FMcsUsbNet_Examples%2Fissues%2F13%23issuecomment-1347879385&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C3875c02095cd423a9dfe08dadcde84a7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638065145507621275%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=mQze6wL%2BivCYmq8Kwj6dS36daFvx87IVpPVjw1BF%2BdQ%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAYVFLOPVNGU6IUTPTQEWHQTWNAS7JANCNFSM6AAAAAASNQQCYY&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C3875c02095cd423a9dfe08dadcde84a7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638065145507621275%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=dXQb0gYFS0M%2FJOlU%2FZBRpmLwOm67Ag4EYh%2BCFF0TY%2Fc%3D&reserved=0. You are receiving this because you authored the thread.Message ID: @.***>

ghost commented 1 year ago
  1. is it correct that sideband 3<<8 is used for negative amplitudes and 1<<8 for postive numbers, or is there a different relation between amplitude and sideband?

There is no special meaning in the numbers of the sideband. It should just illustrate, that you can include here any information, that might be useful for you. Shifting the information to bits graeter equal 8 is just done, because bit 0,3 and 4 of the lowerest byte is reserved for the switches in automatic mode.

  1. Is it true that you should always end your amplitude sequence with a 0 amplitude, with a duration of larger than 0 us?

I think it is fine without a 0 voltage stimulation. Although for automatic mode there will be some extra time added again, if you use CreateSideband.

gchassink commented 1 year ago

Hi Jens,

A few question considering obtaining a timestamp of the stimulation:

  1. I expected channel [69 + Headstage] to contain information of the sideband, which I could then use as a marker for stimulation timestamp. Unfortunately this channel remains 0 at all times, when retrieved with channelblock_readframes. To be sure I checked all channels between 60 and 74 and none seem to spike at the time of stimulation. So how to retrieve this sideband information then?
  2. Related to this; In your C# example I am also unable to see "data2" in the plot so it seems empty or 0 as well? Which I could se to check if my matlab script does something wrong.
  3. There is delay of about 60 ms between the sendstart command and the actual start of stimulus (I now detect by a period of 0 signal followed by the remainder of the artifact). Is this to be expected?

I guess holiday season starts soon also for you. So happy holidays if I don't hear from you before the new year,

Gerco

From: JensMCS @.> Sent: donderdag 15 december 2022 12:02 To: multichannelsystems/McsUsbNet_Examples @.> Cc: Hassink, Gerco (UT-TNW) @.>; Author @.> Subject: Re: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

  1. is it correct that sideband 3<<8 is used for negative amplitudes and 1<<8 for postive numbers, or is there a different relation between amplitude and sideband?

There is no special meaning in the numbers of the sideband. It should just illustrate, that you can include here any information, that might be useful for you. Shifting the information to bits graeter equal 8 is just done, because bit 0,3 and 4 of the lowerest byte is reserved for the switches in automatic mode.

2> . Is it true that you should always end your amplitude sequence with a 0 amplitude, with a duration of larger than 0 us?

I think it is fine without a 0 voltage stimulation. Although for automatic mode there will be some extra time added again, if you use CreateSideband.

- Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmultichannelsystems%2FMcsUsbNet_Examples%2Fissues%2F13%23issuecomment-1352896542&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C52e6f05d828446a3fcc908dade8bbaf7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638066988962350555%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=u25up2vJ68ltX9MSeag3k74hSnS7jBDBaOV75SWWe3U%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAYVFLOMGAXWPDTYHKRE4NO3WNL3A3ANCNFSM6AAAAAASNQQCYY&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C52e6f05d828446a3fcc908dade8bbaf7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638066988962365547%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=V%2Brymlx4xcLtCcLB0j%2Fb8Z0dRxwOAVER3CFbvGpJHbI%3D&reserved=0. You are receiving this because you authored the thread.Message ID: @.**@.>>

gchassink commented 1 year ago

Update:

Hi Jens,

A few question considering obtaining a timestamp of the stimulation:

  1. I expected channel [69 + Headstage] to contain information of the sideband, which I could then use as a marker for stimulation timestamp. Unfortunately this channel remains 0 at all times, when retrieved with channelblock_readframes. To be sure I checked all channels between 60 and 74 and none seem to spike at the time of stimulation. So how to retrieve this sideband information then?
  2. Related to this; In your C# example I am also unable to see "data2" in the plot so it seems empty or 0 as well? Which I could se to check if my matlab script does something wrong. It does work when using headstage=1 instead of 0. Now I need to check whether it works in matlab as well.
  3. There is delay of about 60 ms between the sendstart command and the actual start of stimulus (I now detect by a period of 0 signal followed by the remainder of the artifact). Is this to be expected?

I guess holiday season starts soon also for you. So happy holidays if I don't hear from you before the new year,

Gerco

From: JensMCS @.**@.>> Sent: donderdag 15 december 2022 12:02 To: multichannelsystems/McsUsbNet_Examples @.**@.>> Cc: Hassink, Gerco (UT-TNW) @.**@.>>; Author @.**@.>> Subject: Re: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

  1. is it correct that sideband 3<<8 is used for negative amplitudes and 1<<8 for postive numbers, or is there a different relation between amplitude and sideband?

There is no special meaning in the numbers of the sideband. It should just illustrate, that you can include here any information, that might be useful for you. Shifting the information to bits graeter equal 8 is just done, because bit 0,3 and 4 of the lowerest byte is reserved for the switches in automatic mode.

2> . Is it true that you should always end your amplitude sequence with a 0 amplitude, with a duration of larger than 0 us?

I think it is fine without a 0 voltage stimulation. Although for automatic mode there will be some extra time added again, if you use CreateSideband.

- Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmultichannelsystems%2FMcsUsbNet_Examples%2Fissues%2F13%23issuecomment-1352896542&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C52e6f05d828446a3fcc908dade8bbaf7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638066988962350555%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=u25up2vJ68ltX9MSeag3k74hSnS7jBDBaOV75SWWe3U%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAYVFLOMGAXWPDTYHKRE4NO3WNL3A3ANCNFSM6AAAAAASNQQCYY&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C52e6f05d828446a3fcc908dade8bbaf7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638066988962365547%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=V%2Brymlx4xcLtCcLB0j%2Fb8Z0dRxwOAVER3CFbvGpJHbI%3D&reserved=0. You are receiving this because you authored the thread.Message ID: @.**@.>>

gchassink commented 1 year ago

Update2: Yes! It worked finally I had to set ChannelBlock_ReadFrames to I32 and all related stuff. Then indeed channel 60+9 or 59+9 (depending on how you calculate) give value around 800 at time of stimulation. So that is digital out I guess. This I can use to create my timestamp

Thanks a lot! And happy holidays

Gerco

From: Hassink, Gerco (UT-TNW) Sent: donderdag 22 december 2022 11:44 To: multichannelsystems/McsUsbNet_Examples @.***> Subject: RE: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

Update:

Hi Jens,

A few question considering obtaining a timestamp of the stimulation:

  1. I expected channel [69 + Headstage] to contain information of the sideband, which I could then use as a marker for stimulation timestamp. Unfortunately this channel remains 0 at all times, when retrieved with channelblock_readframes. To be sure I checked all channels between 60 and 74 and none seem to spike at the time of stimulation. So how to retrieve this sideband information then?
  2. Related to this; In your C# example I am also unable to see "data2" in the plot so it seems empty or 0 as well? Which I could se to check if my matlab script does something wrong. It does work when using headstage=1 instead of 0. Now I need to check whether it works in matlab as well.
  3. There is delay of about 60 ms between the sendstart command and the actual start of stimulus (I now detect by a period of 0 signal followed by the remainder of the artifact). Is this to be expected?

I guess holiday season starts soon also for you. So happy holidays if I don't hear from you before the new year,

Gerco

From: JensMCS @.**@.>> Sent: donderdag 15 december 2022 12:02 To: multichannelsystems/McsUsbNet_Examples @.**@.>> Cc: Hassink, Gerco (UT-TNW) @.**@.>>; Author @.**@.>> Subject: Re: [multichannelsystems/McsUsbNet_Examples] How to stimulate using ElectrodeModeEnumNet.emAutomatic on internal stimulator of MEA2100 IFB-Lite (Issue #13)

  1. is it correct that sideband 3<<8 is used for negative amplitudes and 1<<8 for postive numbers, or is there a different relation between amplitude and sideband?

There is no special meaning in the numbers of the sideband. It should just illustrate, that you can include here any information, that might be useful for you. Shifting the information to bits graeter equal 8 is just done, because bit 0,3 and 4 of the lowerest byte is reserved for the switches in automatic mode.

2> . Is it true that you should always end your amplitude sequence with a 0 amplitude, with a duration of larger than 0 us?

I think it is fine without a 0 voltage stimulation. Although for automatic mode there will be some extra time added again, if you use CreateSideband.

- Reply to this email directly, view it on GitHubhttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fmultichannelsystems%2FMcsUsbNet_Examples%2Fissues%2F13%23issuecomment-1352896542&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C52e6f05d828446a3fcc908dade8bbaf7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638066988962350555%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=u25up2vJ68ltX9MSeag3k74hSnS7jBDBaOV75SWWe3U%3D&reserved=0, or unsubscribehttps://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAYVFLOMGAXWPDTYHKRE4NO3WNL3A3ANCNFSM6AAAAAASNQQCYY&data=05%7C01%7Cg.c.hassink%40utwente.nl%7C52e6f05d828446a3fcc908dade8bbaf7%7C723246a1c3f543c5acdc43adb404ac4d%7C0%7C0%7C638066988962365547%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=V%2Brymlx4xcLtCcLB0j%2Fb8Z0dRxwOAVER3CFbvGpJHbI%3D&reserved=0. You are receiving this because you authored the thread.Message ID: @.**@.>>