function [ newPositionData ] = interpolatenewpositiondata( positionData, startTime, endTime, posObsInterval )
    % This function takes position observations on arbitrary time points
    % and produces a new set of observations linearly interpolated from the
    % originals to fixed interval time points.
    %
    % Positions are in continuous space, two dimensions.
    %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % positionData - has 3 columns: 1st time, 2nd, 3rd y coordinate and x
    % coordinate respectively.
    %
    % endTime - should be no greater than the time of the last position
    % observation we have.
    %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % CHANGELOG
    % 23.10.2014 - created, coded functionality.
    %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    if nargin < 4,
        error('Insufficient arguments!')
    end
    if posObsInterval < 0,
        error('posObsInterval must be positive!')
    end
    if startTime >= endTime,
        error('startTime must be less than endTime!')
    end
    if size(positionData, 2) ~= 3,
        error('positionData must have three columns!')
    end
    if ~isequal(positionData(:, 1), sort(positionData(:, 1))),
        error('First column of positionData must be times of observations in ascending order!')
    end
    if startTime < positionData(1, 1),
        error('startTime must be no less than the time of the first position observation!')
    end
    if endTime > positionData(end, 1),
        error('endTime must be no greater than the time of the last position observation!')
    end

    newPositionData = (startTime:posObsInterval:endTime)';
    nNewTimePoints = length(newPositionData);
    newPositionData = [newPositionData, nan([nNewTimePoints, 2])];
    
    % Get count of new time points landing in each old interval. Has length
    % of n old position data observations.
    % The last count should be zero because we do not allow any new time
    % points greater than the last position observation, unless the last
    % time is equal to the last position observation in which case this
    % will be 1.
    requiredIntervalsCount = histc(newPositionData(:, 1), positionData(:, 1));
    
    % Has length of n new time points.
    repeatedRequiredIntervalsInds = repeatindicesbycounts(requiredIntervalsCount);

    % Time from each required point to nearest old point before it.
    timeInInterval = newPositionData(:, 1) - positionData(repeatedRequiredIntervalsInds, 1);
    
    % The last interval has duration zero: this is only used for new time
    % points that coincide exactly with the time of the last position
    % observation. It is the time after the last position observation.
    intervalDurations = [positionData(2:end, 1) - positionData(1:(end - 1), 1); 0];
    
    % Proportion of old interval covered up to new time point.
    intervalProp = timeInInterval ./ intervalDurations(repeatedRequiredIntervalsInds);
    % Any zero intervals?
    zeroIntervalBinInds = intervalDurations(repeatedRequiredIntervalsInds) == 0;
    intervalProp(zeroIntervalBinInds) = 0;

    % The zero at the end is the displacement from the last position
    % observation to itself: used only for new time points that coincide
    % exactly with the last observation.
    successiveObsDisplacement = [positionData(2:end, 2:3) - positionData(1:(end - 1), 2:3); 0, 0];
    
    % Interpolated positions at new time points are old observations at
    % nearest time point before, plus proportion of the displacement to the
    % next old observation as given by proportion of new time point into
    % the interval.
    newPositionData(:, 2:3) = positionData(repeatedRequiredIntervalsInds, 2:3) + bsxfun(@times, intervalProp, successiveObsDisplacement(repeatedRequiredIntervalsInds, :));
end