6. Demo Scripts

Important

Please note that some of the features in the demo scripts may not be work proprely due to a transition from Matlab 2017b to Matlab 2021a. If you identify any difficulty in the respect, please contact the support team of sync2brain.

6.1. Open Loop Jittered TTL Output

This demo script uses bossdevice RESEARCH and 2 different approaches to generate jittered open loop TTL output.

Resources: 1) bossdevice Switched On, 2) bossdevice Open Source MATLAB API

NumberOfTrials=10;
 ITI=[4 6]; %ITI is seconds - a random number between these two values
 %% Initializing bossdevice RESEARCH API
 bd=bossdevice;
 %% Approach 1 - For Loop Based Open Loop TTL output generation
 bd.configure_time_port_marker([0 1 1]); %Configuring TTL output Sequence in [Time Port Marker] format
 for TrialNumber=1:NumberOfTrials
     bd.manualPulse
     disp(['Generated Trial #' num2str(TrialNumber)])
     min_inter_pulse_interval= ITI(1)+ (ITI(2)-ITI(1)).*rand(1,1); %Assigning New Random ITI for this Trial to the bossdevice RESEARCH
     pause(min_inter_pulse_interval) %Wait for next trial start
 end
 disp('Trials Completed')
 %% Approach 2 - bossdevice RESEARCH Sequence Generator Based Open Loop TTL output
 time_port_marker_sequence=[];
 Time=0;
 time_port_marker_sequence(NumberOfTrials,3)=0; %Pre filling the array
 for TrialNumber=1:NumberOfTrials
     Time=Time+ITI(1)+ (ITI(2)-ITI(1)).*rand(1,1); %Generating Sequence of Jittered ITIs for all Trials
     Port=1; %In order to generatre TTL output always on first port , use 2 for 2nd port and 3 for third port
     Marker=TrialNumber;
     time_port_marker_sequence(TrialNumber,:)=[Time Port Marker];
 end
 bd.configure_time_port_marker(time_port_marker_sequence); %Assigning Pregenerated sequence to the bossdevice RESEARCH
 bd.manualPulse % Generating the TTL output of the bossdevice RESEARCH to start sequence TTL Output Generation
 disp('TTL output Sequence Started by bossdevice RESEARCH')

6.2. mu-Rhythm Phase Locked TTL Output

This demo script uses bossdevice to deliver mu Rising or Falling Flank Phase locked TTL outputs Resources: 1) bossdevice Switched On 2) bossdevice Open Source MATLAB API 3) Biosignal Amplifier streaming atleast 5 EEG Channels

no_of_trials=10;
 minimium_inter_pulse_interval=5; %s
 phase=[+pi/2 -pi/2]; %[falling_flank rising_flank]
 phase_tolerance=pi/40;
 individual_peak_frequency=11; % Hz
 bandpassfilter_order= 75;
 eeg_channels=5; %Assigning Number of channels as equivalent to Num of Channels streamed by Biosignal Processor
 spatial_filter_weights=[1 -0.25 -0.25 -0.25 -0.25]'; %Column Vector of Spatial Filter Indexed wrt corrosponding Channels

 time=0;
 plasticity_protocol_sequence=[];

 %% Initializing bossdevice RESEARCH API
 bd=bossdevice;
 bd.sample_and_hold_period=0;
 bd.calibration_mode = 'no';
 bd.armed = 'no';
 bd.sample_and_hold_period=0;
 bd.theta.ignore; pause(0.1)
 bd.beta.ignore; pause(0.1)
 bd.alpha.ignore; pause(0.1)
 bd.eeg_channels=eeg_channels;

 %% Preparing an Individual Peak Frequency based Band Pass Filter for mu Alpha
 bpf_fir_coeffs = firls(bandpassfilter_order, [0 (individual_peak_frequency + [-5 -2 +2 +5]) (500/2)]/(500/2), [0 0 1 1 0 0], [1 1 1] );

 %% Setting Filters on bossdevice RESEARCH
 bd.spatial_filter_weights=spatial_filter_weights;
 bd.alpha.bpf_fir_coeffs = bpf_fir_coeffs;

 %% Controlling bossdevice RESEARCH for mu Alpha Phase Locked TTL Output
 condition_index=0;
 while (condition_index <= no_of_trials)
     if(strcmp(bb.armed, 'no'))
         bb.pulses_remaining = 1;
         bb.alpha.phase_target(1) = phase(randi(1:numel(phase), 1));
         bb.alpha.phase_plusminus(1) = phase_tolerance;
         bb.configure_time_port_marker(([0, 1, 0]))
         bb.min_inter_pulse_interval = minimium_inter_pulse_interval;
         pause(0.1)
         bb.arm;
     end
     % trigger has been executed, move to the next condition
     if(bb.pulses_remaining == 0)
         condition_index = condition_index + 1;
         bb.disarm;
         disp (['Triggered around ' (num2str(rad2deg(bb.alpha.phase_target(1)))) ' degrees Phase angle.'])
         pause(minimium_inter_pulse_interval)
     end
     pause(0.01);
 end

 %% End
 disp ('Protocol has been completed');

6.3. Phase Locked Plasticity Protocol

This demo script uses bossdevice RESEARCH and 2 different approaches to generate jittered open loopTTL output

Resources: 1) bossdevice Switched On, 2) bossdevice Open Source MATLAB API

no_of_trials=25;
 no_of_pulses=100;
 pulse_frequency=100; %Hz
 minimium_inter_pulse_interval=5; %s
 phase=0; %peak
 phase_tolerance=pi/40;
 individual_peak_frequency=11; % Hz
 bandpassfilter_order= 75;
 eeg_channels=5; %Assigning Number of channels as equivalent to Num of Channels streamed by Biosignal Processor
 spatial_filter_weights=[1 -0.25 -0.25 -0.25 -0.25]'; %Column Vector of Spatial Filter Indexed wrt corrosponding Channels

 time=0;
 plasticity_protocol_sequence=[];

 %% Initializing bossdevice RESEARCH API
 bd=bossdevice;
 bd.sample_and_hold_period=0;
 bd.calibration_mode = 'no';
 bd.armed = 'no';
 bd.sample_and_hold_period=0;
 bd.theta.ignore; pause(0.1)
 bd.beta.ignore; pause(0.1)
 bd.alpha.ignore; pause(0.1)
 bd.eeg_channels=eeg_channels;

 %% Preparing a Plasticity Protocol Seqeuence for bossdevice RESEARCH
 plasticity_protocol_sequence(no_of_pulses,3)=0; %Pre filling the array
 for iPulse=1:no_of_pulses
     time=time+0.01;
     port=1;
     marker=iPulse;
     plasticity_protocol_sequence(iPulse,:)=[time port marker];
 end

 %% Preparing an Individual Peak Frequency based Band Pass Filter for mu Alpha
 bpf_fir_coeffs = firls(bandpassfilter_order, [0 (individual_peak_frequency + [-5 -2 +2 +5]) (500/2)]/(500/2), [0 0 1 1 0 0], [1 1 1] );

 %% Setting Filters on bossdevice RESEARCH
 bd.spatial_filter_weights=spatial_filter_weights;
 bd.alpha.bpf_fir_coeffs = bpf_fir_coeffs;



 %% For plasticitz, we have the same condition, multiple times, we can run everything on the device:
         bd.pulses_remaining = 100;
         bd.alpha.phase_target(1) = phase;
         bd.alpha.phase_plusminus(1) = phase_tolerance;
         bd.configure_time_port_marker(plasticity_protocol_sequence)
         bd.min_inter_pulse_interval = minimium_inter_pulse_interval;
         pause(0.1)
         bd.arm;

         fprintf('\nSystem running, pulses remaining: %03i', bd.pulses_remaining)
         while (bd.pulses_remaining > 0)
             fprintf('\b\b\b%03i', bd.pulses_remaining);
             pause(0.1)
         end
         fprintf('\b\b\bDone\n')


 %% Controlling bossdevice RESEARCH for mu Alpha Phase Locked TTL Ouput % this could be for excitability, where we have interleaved different conditions
 condition_index=0;
 while (condition_index <= no_of_trials)
     if(strcmp(bd.armed, 'no'))
         bd.pulses_remaining = 1;
         bd.alpha.phase_target(1) = phase;
         bd.alpha.phase_plusminus(1) = phase_tolerance;
         bd.configure_time_port_marker(plasticity_protocol_sequence)
         bd.min_inter_pulse_interval = minimium_inter_pulse_interval;
         pause(0.1)
         bd.arm;
     end
     % TTL output has been generated, move to the next condition
     if(bd.pulses_remaining == 0)
         condition_index = condition_index + 1;
         bd.disarm;
         disp TTL_Output_Generated!
         pause(minimium_inter_pulse_interval)
     end
     pause(0.01);
 end

 %% End
 disp ('Plasticity Protocol has been completed');

6.4. Real-Time Oscillation Amplitude Threshold Tracking

This demo script uses bossdevice RESEARCH and 2 different approaches to generate jittered open loop TTL output

Resources: 1) bossdevice Switched On, 2) bossdevice Open Source MATLAB API

no_of_trials=25;
minimium_inter_pulse_interval=4; %s
phase=0; %[positive]
phase_tolerance=pi/40;
amplitude_threshold=[25 75]; %[min max] in percentile
amplitude_assignment_period=60*2; %s
individual_peak_frequency=11; % Hz
bandpassfilter_order= 75;
eeg_channels=5; %Assigning Number of channels as equivalent to Num of Channels streamed by Biosignal Processor
spatial_filter_weights=[1 -0.25 -0.25 -0.25 -0.25]'; %Column Vector of Spatial Filter Indexed wrt corrosponding Channels
time=0;
plasticity_protocol_sequence=[];

%% Initializing bossdevice RESEARCH API
bd=bossdevice;
bd.sample_and_hold_period=0;
bd.calibration_mode = 'no';
bd.armed = 'no';
bd.sample_and_hold_period=0;
bd.theta.ignore; pause(0.1)
bd.beta.ignore; pause(0.1)
bd.alpha.ignore; pause(0.1)
bd.eeg_channels=eeg_channels;

%% Preparing an Individual Peak Frequency based Band Pass Filter for mu Alpha
bpf_fir_coeffs = firls(bandpassfilter_order, [0 (individual_peak_frequency + [-5 -2 +2 +5]) (500/2)]/(500/2), [0 0 1 1 0 0], [1 1 1] );

%% Setting Filters on bossdevice RESEARCH
bd.spatial_filter_weights=spatial_filter_weights;
bd.alpha.bpf_fir_coeffs = bpf_fir_coeffs;

%% Configuring Real-Time Scopes for Amplitude Tracking
AMP_TRACING_SCOPES_IDS = [101 102];

% remove any pre-existing scopes with these ids
for id = AMP_TRACING_SCOPES_IDS
    if(find(bd.tg.Scopes == id))
        fprintf('\nRemoving scope %i', id)
        remscope(bd.tg, id);
    end
end

sig_id_amp = getsignalid(bd.tg, 'OSC/alpha/IA'); %amplitude
sig_id_qly = getsignalid(bd.tg, 'QLY/Logical Operator2'); %eeg_is_clean

sc = addscope(bd.tg, 'host', AMP_TRACING_SCOPES_IDS);
addsignal(sc, [sig_id_amp sig_id_qly]);

sc(1).NumSamples = 500;
sc(1).Decimation = 10;
sc(1).TriggerSample = -1;

sc(2).NumSamples = 500;
sc(2).Decimation = 10;
sc(2).TriggerSample = -1;

sc(1).TriggerMode = 'Scope';
sc(1).TriggerScope = AMP_TRACING_SCOPES_IDS(2);

sc(2).TriggerMode = 'Scope';
sc(2).TriggerScope = AMP_TRACING_SCOPES_IDS(1);

start(sc); % now they are ready for TTL output generation

activeScope = 1;
mAmplitudeScopeCircBufTotalBlocks = amplitude_assignment_period;
mAmplitudeScopeCircBufCurrentBlock = 1;
mAmplitudeScopeCircBuf = [];
hAmplitudeHistoryAxes = subplot(1,2,1);
hAmplitudeDistributionAxes = subplot(1,2,2);

trigger(sc(activeScope));

%% Controlling bossdevice RESEARCH for mu Alpha Phase Locked TTL Output
condition_index=0;
while (condition_index <= no_of_trials)
    if (strcmp(sc(activeScope).Status, 'Finished') || ...
            strcmp(sc(activeScope).Status, 'Interrupted'))

        time = sc(activeScope).Time;
        data = sc(activeScope).Data;
        plot(hAmplitudeHistoryAxes, time, data(:,1));

        fprintf(['Restarting Scope ' num2str(activeScope)]);

        % Restart this scope.
        start(sc(activeScope));

        % Switch to the next scope.
        if(activeScope == 1)
            activeScope = 2;
        else
            activeScope = 1;
        end

        % append data in circular buffer
        mAmplitudeScopeCircBuf{mAmplitudeScopeCircBufCurrentBlock} = data';

        maxmindata = cell2mat(cellfun(@(data) quantile(data(1, data(2,:) == 1), [amplitude_threshold(1)/100 amplitude_threshold(2)/100])', mAmplitudeScopeCircBuf, 'UniformOutput', false))';
        maxmindata = circshift(maxmindata, mAmplitudeScopeCircBufCurrentBlock);
        plot(hAmplitudeHistoryAxes, maxmindata)
        xlim(hAmplitudeHistoryAxes, [1 mAmplitudeScopeCircBufTotalBlocks])
        set(hAmplitudeHistoryAxes, 'Xdir', 'reverse')

        circular_buffer_data = cell2mat(mAmplitudeScopeCircBuf);

        % Switch to the next data block
        if(mAmplitudeScopeCircBufCurrentBlock < mAmplitudeScopeCircBufTotalBlocks)
            mAmplitudeScopeCircBufCurrentBlock = mAmplitudeScopeCircBufCurrentBlock + 1;
        else
            mAmplitudeScopeCircBufCurrentBlock = 1;
        end

        %tic

        % remove post-TTL pulse data
        amplitude_clean = circular_buffer_data(1, circular_buffer_data(2,:) == 1);

        % calculate percentiles
        amplitude_sorted = sort(amplitude_clean);
        plot(hAmplitudeDistributionAxes, amplitude_sorted)

        amp_lower = quantile(amplitude_clean, amplitude_threshold(1)/100); % TODO: INCLUDE THIS IN INFO STRUCT
        amp_upper = quantile(amplitude_clean, amplitude_threshold(2)/100); % TODO: INCLUDE THIS IN INFO STRUCT

        hold(hAmplitudeDistributionAxes, 'on')
        plot(hAmplitudeDistributionAxes, [1 length(amplitude_clean)], [amp_lower amp_upper; amp_lower amp_upper]);
        hold(hAmplitudeDistributionAxes, 'off')

        if length(amplitude_clean) > 1
            xlim(hAmplitudeDistributionAxes, [1 length(amplitude_clean)]);
        end
        if (amplitude_sorted(end) > amplitude_sorted(1))
            ylim(hAmplitudeDistributionAxes, [amplitude_sorted(1) amplitude_sorted(end)]);
        end

        %toc

        % set amplitude threshold
        mDbsp.alpha.amplitude_min(1) = amp_lower;
        mDbsp.alpha.amplitude_max(1) = amp_upper;
        bd.alpha.amplitude_min(1)=amp_lower;
        bd.alpha.amplitude_max(1)=amp_upper;
        title(hAmplitudeDistributionAxes, ['Min Amplitude: ', num2str(amp_lower)]);

    end % handle the amplitude tracking
    if(strcmp(bd.armed, 'no'))
        bd.pulses_remaining = 1;
        bd.alpha.phase_target(1) = phase(randi(1:numel(phase), 1));
        bd.alpha.phase_plusminus(1) = phase_tolerance;
        bd.configure_time_port_marker(([0, 1, 0]))
        bd.min_inter_pulse_interval = minimium_inter_pulse_interval;
        pause(0.1)
        bd.arm;
    end
    % TTL output has been generated, move to the next condition
    if(bd.pulses_remaining == 0)
        condition_index = condition_index + 1;
        bd.disarm;
        disp TTL_Output_Has_Been_Generated!
    end
    pause(0.01);
end

%% End

6.5. Phase Prediction Error Measurement

This demo script uses bossdevice RESEARCH and 2 different approaches to generate jittered open loop TTL output

Resources: 1) bossdevice Switched On, 2) bossdevice Open Source MATLAB API

ang_diff = @(x, y) angle(exp(1i*x)./exp(1i*y));
ang_var = @(x) 1-abs(mean(exp(1i*x)));
%ang_var2dev = @(v) sqrt(2*v); % circstat preferred formula uses angular deviation (bounded from 0 to sqrt(2)) which is sqrt(2*(1-r))
ang_var2dev = @(v) sqrt(-2*log(1-v)); % formula for circular standard deviation is sqrt(-2*ln(r))

%%  Initializing bossdevice RESEARCH API
bb = bossdevice;
bd.eeg_channels = 1;
bd.aux_channels = 1;
bd.spatial_filter_weights = 1;

bd.alpha.offset_samples = 5; %this depends on the loop-delay

%% Setting Filters to bossdevice RESEARCH
% this allows calibrating the oscillation analysis to an individual peak frequency
bd.alpha.bpf_fir_coeffs = firls(70, [0 6 9 13 16 (500/2)]/(500/2), [0 0 1 1 0 0], [1 1 1]);
%fvtool(bd.alpha.bpf_fir_coeffs, 'Fs', 500) % visualize filter

%% Configuring a scope to acquire data
sc = addscope(bd.tg, 'host', 101);
addsignal(sc, getsignalid(bd.tg, 'SPF/Matrix Multiply')); % this signals goes into the oscillation analysis
addsignal(sc, getsignalid(bd.tg, 'OSC/alpha/IP')); % instantaneous phase estimate for alpha

sc.NumSamples = 10 * 5000;
sc.Decimation = 1;

fprintf('\nAcquiring data ...'), start(sc);
while(strcmp(sc.Status, 'Acquiring')), fprintf('.'), pause(1), end
fprintf(' done')

data = sc.Data(:,1);
ip_estimate_causal = sc.Data(:,end);
fs = 1/mean(diff(sc.Time));

fprintf('\nDetermining phase using standard non-causal methods ...')
% demean
data = data - mean(data);
% zero phase band-pass filter
D = designfilt('bandpassfir', 'FilterOrder', round(1*fs), 'CutoffFrequency1', 9, 'CutoffFrequency2', 13, 'SampleRate', fs);
data = filtfilt(D, data); %demean

ip_estimate_noncausal = angle(hilbert(data));
phase_error = ang_diff(ip_estimate_noncausal, ip_estimate_causal);

fprintf('\n')

%% Visualize
figure
ax1 = subplot(2,2,1);
plot(sc.Time-sc.Time(1), [sc.Data(:,1) data])
ax2 = subplot(2,2,2);
plot(sc.Time-sc.Time(1), [ip_estimate_causal ip_estimate_noncausal])
linkaxes([ax1 ax2], 'x')
subplot(2,2,4,polaraxes);
polarhistogram(phase_error, 'Normalization', 'probability', 'BinWidth', pi/36);
title(sprintf('circular standard deviation = %.1f°', rad2deg(ang_var2dev(ang_var(phase_error)))))

% remove the scope
remscope(bd.tg, 101)

6.6. Loop Latency Measurement

This demo script uses bossdevice RESEARCH and 2 different approaches to generate jittered open loop TTL output

Resources: 1) bossdevice Switched On, 2) bossdevice Open Source MATLAB API

bd = bossdevice;

%% Configuring Scope
sc = addscope(bd.tg, 'host', 255);

mrk_signal_id = getsignalid(bd.tg, 'MRK/mrk_masked') + int32([0]);

addsignal(sc, mrk_signal_id);
sc.NumSamples = 100;
sc.NumPrePostSamples = -50;
sc.Decimation = 1;
sc.TriggerMode = 'Signal';
sc.TriggerSignal = getsignalid(bd.tg, 'gen_running');
sc.TriggerLevel = 0.5;
sc.TriggerSlope = 'Rising';

%% Generating TTL Output

fprintf('\nTesting... ')
start(sc);
pause(0.5); % give the scope time to pre-aquire

bd.configure_time_port_marker([0 1 1])
    bd.manualTrigger;

pause(0.5)
assert(strcmp(sc.Status, 'Finished'))

fprintf('loop delay is %2.1f ms\n', (find(sc.Data(:,1), 1)-50)/5)