%% License Info
%A Model of Feedforward, Global, and Lateral Inhibition in the Locust Visual System.

%This model examines the architecture and function of inhibitory mechanisms in the 
%visual system of locusts, namely those involved in the processing of inputs to a
%key looming-sensitive neuron, the lobula giant movement detector (LGMD).

%Copyright (c) 2026, Erik Olson, Travis Wiens, and Jack Gray

%CITATION:
%When using the model code for scientific publications, cite the following work:
%Olson EGN, Wiens TK, Gray JR. A model of feedforward, global, and lateral inhibition 
%in the locust visual system predicts responses to looming stimuli. Biol Cybern. 2021 
%Jun;115(3):245-265. doi: 10.1007/s00422-021-00876-8. Epub 2021 May 16. PMID: 33997912.

%This program is free software: you can redistribute it and/or modify
%it under the terms of the GNU General Public License as published by
%the Free Software Foundation, either version 3 of the License, or
%(at your option) any later version.

%This program is distributed in the hope that it will be useful,
%but WITHOUT ANY WARRANTY; without even the implied warranty of
%MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%GNU General Public License for more details.

%You should have received a copy of the GNU General Public License
%along with this program.  If not, see <https://www.gnu.org/licenses/>.

%Contact: erik.olson@usask.ca

%% Code
function [I_out,t_out,max_f,f_base,delay_final] = VoltageClampTrialFunction(V_clamp,parFile,smoothing)
%Performs a simulated voltage clamp experiment on the LGMD, showing the
%response to a single-facet stimulus (i.e. input from one TmA).  The
%recording site is proximal to the SIZ.  Results are generated for four
%different luminance change durations: 0 ms, 83 ms, 183 ms and 350 ms.
%Current traces are offset temporally based on cross-correlation with
%experimental data from Jones & Gabbiani (2010).
%Inputs:
%parfile = name of the parameter file containing model parameters
%V_clamp = the clamp voltage [V]
%smoothing = the standard deviation of the Gaussian filter used to
%determine maximum firing rate [ms], which must be given as an integer
%Outputs:
%I_out = the current traces for each stimulus duration [A], averaged across
%ten trials. Each column corresponds to a single stimulus duration, going
%from shortest to longest, left to right
%t_out = the corresponding time values [s]
%max_f = the maximum firing rate of the TmA [spikes/s]
%f_base = the baseline firing rate of an unstimulated TmA, due to noise
%[Hz]
%delay_final = the delay added to current outputs [s]

load('Jones_and_Gabbiani_2010_Voltage_Clamp_Data');
load(parFile);

options = simset('srcworkspace','current','ReturnWorkspaceOutputs','on');
results = sim("VoltageClampTrial.slx",0.449,options);
t = results.yout{1}.Values.Time; %Access time data
y1 = results.yout{1}.Values.Data; %Access dendritic branch current data
y2 = results.yout{2}.Values.Data;
y3 = results.yout{3}.Values.Data;
y4 = results.yout{4}.Values.Data;
spiketrain = results.yout{5}.Values.Data;
Simulink.sdi.clear;

for i = 1:9
    options = simset('srcworkspace','current','ReturnWorkspaceOutputs','on');
    results = sim("VoltageClampTrial.slx",0.449,options);
    t = results.yout{1}.Values.Time; %Access time data
    y1 = y1+results.yout{1}.Values.Data; %Access dendritic branch current data
    y2 = y2+results.yout{2}.Values.Data;
    y3 = y3+results.yout{3}.Values.Data;
    y4 = y4+results.yout{4}.Values.Data;
    spiketrain = spiketrain+results.yout{5}.Values.Data;
    Simulink.sdi.clear;        
end

I_model1 = 0.1*y1;
I_model2 = 0.1*y2;
I_model3 = 0.1*y3;
I_model4 = 0.1*y4;

I_exp1 = interp1(t_exp,I_exp1,t);
I_exp2 = interp1(t_exp,I_exp2,t);
I_exp3 = interp1(t_exp,I_exp3,t);
I_exp4 = interp1(t_exp,I_exp4,t);

cross_corr1 = xcorr(I_model1,I_exp1);
cross_corr2 = xcorr(I_model2,I_exp2);
cross_corr3 = xcorr(I_model3,I_exp3);
cross_corr4 = xcorr(I_model4,I_exp4);
best_corr1 = max(cross_corr1);
best_corr2 = max(cross_corr2);
best_corr3 = max(cross_corr3);
best_corr4 = max(cross_corr4);

indices1 = find(cross_corr1 == best_corr1);
indices2 = find(cross_corr2 == best_corr2);
indices3 = find(cross_corr3 == best_corr3);
indices4 = find(cross_corr4 == best_corr4);
delays1 = indices1-max(size(I_model1));
delays2 = indices2-max(size(I_model2));
delays3 = indices3-max(size(I_model3));
delays4 = indices4-max(size(I_model4));

residuals_sq1 = [];
residuals_sq2 = [];
residuals_sq3 = [];
residuals_sq4 = [];

for i = 1:max(size(indices1))
    add_on = zeros(abs(delays1(i)),1);
    if delays1(i) < 0
        I_model_temp = [add_on;I_model1];
        I_model_temp = I_model_temp(1:max(size(I_exp1)));

        residual = I_model_temp-I_exp1;
        residuals_sq1 = [residuals_sq1,residual'*residual];
    else
        I_model_temp = [I_model1;add_on];
        I_model_temp = I_model_temp(1:max(size(I_exp1)));

        residual = I_model_temp-I_exp1;
        residuals_sq1 = [residuals_sq1,residual'*residual];
    end
end

for i = 1:max(size(indices2))
    add_on = zeros(abs(delays2(i)),1);
    if delays2(i) < 0
        I_model_temp = [add_on;I_model2];
        I_model_temp = I_model_temp(1:max(size(I_exp2)));

        residual = I_model_temp-I_exp2;
        residuals_sq2 = [residuals_sq2,residual'*residual];
    else
        I_model_temp = [I_model2;add_on];
        I_model_temp = I_model_temp(1:max(size(I_exp2)));

        residual = I_model_temp-I_exp2;
        residuals_sq2 = [residuals_sq2,residual'*residual];
    end
end

for i = 1:max(size(indices3))
    add_on = zeros(abs(delays3(i)),1);
    if delays3(i) < 0
        I_model_temp = [add_on;I_model3];
        I_model_temp = I_model_temp(1:max(size(I_exp3)));

        residual = I_model_temp-I_exp3;
        residuals_sq3 = [residuals_sq3,residual'*residual];
    else
        I_model_temp = [I_model3;add_on];
        I_model_temp = I_model_temp(1:max(size(I_exp3)));

        residual = I_model_temp-I_exp3;
        residuals_sq3 = [residuals_sq3,residual'*residual];
    end
end

for i = 1:max(size(indices4))
    add_on = zeros(abs(delays4(i)),1);
    if delays4(i) < 0
        I_model_temp = [add_on;I_model4];
        I_model_temp = I_model_temp(1:max(size(I_exp4)));

        residual = I_model_temp-I_exp4;
        residuals_sq4 = [residuals_sq4,residual'*residual];
    else
        I_model_temp = [I_model4;add_on];
        I_model_temp = I_model_temp(1:max(size(I_exp4)));

        residual = I_model_temp-I_exp4;
        residuals_sq4 = [residuals_sq4,residual'*residual];
    end
end

[~,index1] = min(residuals_sq1);
[~,index2] = min(residuals_sq1);
[~,index3] = min(residuals_sq1);
[~,index4] = min(residuals_sq1);
delay1 = delays1(index1);
delay2 = delays2(index2);
delay3 = delays3(index3);
delay4 = delays4(index4);

delay_int = round(mean([delay1,delay2,delay3,delay4]));

delay_final = -1*delay_int*(t(2)-t(1));

add_on = zeros(abs(delay_int),1);
if delay_int < 0
    I_model_temp = [add_on;I_model1];
    I_model1 = I_model_temp(1:max(size(I_exp1)));
    I_model_temp = [add_on;I_model2];
    I_model2 = I_model_temp(1:max(size(I_exp1)));
    I_model_temp = [add_on;I_model3];
    I_model3 = I_model_temp(1:max(size(I_exp1)));
    I_model_temp = [add_on;I_model4];
    I_model4 = I_model_temp(1:max(size(I_exp1)));
else
    I_model_temp = [I_model1;add_on];
    I_model1 = I_model_temp(1:max(size(I_exp1)));
    I_model_temp = [I_model2;add_on];
    I_model2 = I_model_temp(1:max(size(I_exp1)));
    I_model_temp = [I_model3;add_on];
    I_model3 = I_model_temp(1:max(size(I_exp1)));
    I_model_temp = [I_model4;add_on];
    I_model4 = I_model_temp(1:max(size(I_exp1)));
end

t_out = t;
I_out = [I_model1,I_model2,I_model3,I_model4];

max_f = 0.1*max(histoFiringRate(spiketrain,t,0.001,smoothing));

spikecount = 0;

for i = 1:10
    options = simset('srcworkspace','current','ReturnWorkspaceOutputs','on');
    T_final = 500;
    results = sim("TmABaselineModel.slx",T_final,options);
    y1 = results.yout{1}.Values.Data;
    spikecount = spikecount+sum(y1);
end

f_base = spikecount/(T_final*10);
end

