function data = readDataFromFile( fid, samplesToRead, settings )

%==============================================================================%
%                                                                              %
%   data = readDataFromFile( fid, samplesToRead, settings )                    %
%                                                                              %
%   Read binary data from disk                                                 %
%   David S. De Lorenzo                                                        %
%   28 November 2005                                                           %
%                                                                              %
%==============================================================================%
%                       GPS Software Receiver in Matlab                        %
%     Copyright (c) 2003-2017 David S. De Lorenzo / 2018-2023 Trimble Inc.     %
%==============================================================================%

% Assign temporary variables, making code more readable
dataSource = settings.dataSource;
dataType   = settings.dataType;
dataFormat = settings.dataFormat;

% Set the number of fread bits/bytes to skip after each PRECISION value is read
skip = 0;

% Special handling instructions
switch dataSource
    case 'Spirent'
        if strcmp(dataFormat, '8*bit4')
            % Make sure 'samplesToRead' is a multiple of 4
            samplesToRead = 4*ceil(samplesToRead/4);
        elseif strcmp(dataFormat, '16*bit2')
            % Make sure 'samplesToRead' is a multiple of 8
            samplesToRead = 8*ceil(samplesToRead/8);
        else
            error('This is not a valid Spirent data format!!');
        end
        % Set the number of fread bits to skip based on the signals in this file
        if (settings.number_of_spirent_signals == 1)
            % There is only one signal in this file
            skip = 0;
        elseif (settings.number_of_spirent_signals == 2)
            % There are two signals in this file
            skip = 32;  % skip the 32-bit data frame for the other channel
        else
            error('We only support 1 or 2 signals in a Spirent data file!!');
        end
        if ~strcmp(dataType, 'complex')
            error('Spirent data are stored as complex dataType!!');
        end
    case {'USRP', 'Ettus'}
        if ~strcmp(dataFormat, 'bit2')
            error('TBD:  Ettus value re-mapping only works for bit2 data!!');
        end
        if ~strcmp(dataType, 'complex')
            error('Ettus data are stored as complex dataType!!');
        end
    otherwise
        % do nothing -- fall through
end

% Read data from file
if strcmp(dataType, 'real')
    [data,count] = fread(fid, [1 samplesToRead], dataFormat, skip);
    if (count ~= samplesToRead)
        error('***  READ ERROR - End-of-file reached!!  ***');
    end
elseif strcmp(dataType, 'complex')
    [data,count] = fread(fid, [2 samplesToRead], dataFormat, skip);
    if (count ~= 2*samplesToRead)
        error('***  READ ERROR - End-of-file reached!!  ***');
    end
else
    error('*** Invalid dataType string!! ***');
end

% Special handling instructions
switch dataSource
    case 'Spirent'
        if strcmp(dataFormat, '8*bit4')
            % Special handling for Spirent 'bit4' data
            %   When reading Spirent data, the byte order is reversed
            %   e.g., when reading a 4 byte word swap bytes 1 & 4 and
            %   2 & 3 - see pgs. 9-10 of "GSS6450 Data Format"
            %     swap bytes 1 <--> 4
            %     swap bytes 2 <--> 3
            temp = zeros(size(data));
            temp(:,1:4:end) = data(:,4:4:end);
            temp(:,2:4:end) = data(:,3:4:end);
            temp(:,3:4:end) = data(:,2:4:end);
            temp(:,4:4:end) = data(:,1:4:end);
            data = temp;
            clear('temp');
        elseif strcmp(dataFormat, '16*bit2')
            % Special handling for Spirent 'bit2' data
            %   When reading Spirent data, the byte order is reversed
            temp = zeros(size(data));
            temp(:,1:8:end) = data(:,8:8:end);
            temp(:,2:8:end) = data(:,7:8:end);
            temp(:,3:8:end) = data(:,6:8:end);
            temp(:,4:8:end) = data(:,5:8:end);
            temp(:,5:8:end) = data(:,4:8:end);
            temp(:,6:8:end) = data(:,3:8:end);
            temp(:,7:8:end) = data(:,2:8:end);
            temp(:,8:8:end) = data(:,1:8:end);
            data = temp;
            clear('temp');
        else
            error('TBD:  Spirent byte swapping only works for bit4 data!!');
        end
    case {'USRP', 'Ettus'}
        if strcmp(dataFormat, 'bit2')
            % Special handling for Ettus 'bit2' data
            %   Ettus 2-bit data is written to the binary data file
            %   in a bit stream of little-endian U16s - note that the
            %   quadrature data word leads the in-phase data word.
            %     {QM0, QS0, IM0, IS0, QM1, QS1, IM1, IS1, ...
            %      QM2, QS2, IM2, IS2, QM3, QS3, IM3, IS3}
            %   Remedy is to swap the data rows, real <--> imag.
            data = [data(2,:); data(1,:)];
            %   Ettus 2-bit data is written to the binary data file
            %   in a format that agrees with Trimble A/D protocol.
            %     Value from 'fread'      Value after re-mapping
            %            +1  ------------------->  +1
            %             0  ------------------->   0
            %            -1  ------------------->  -2  \ these values
            %            -2  ------------------->  -1  / get switched
            temp = data;
            temp(data==-1) = -2;
            temp(data==-2) = -1;
            data = temp;
            clear('temp');
        else
            error('TBD:  Ettus value re-mapping only works for bit2 data!!');
        end
    otherwise
        % do nothing -- fall through
end

% Re-scaling to go from storage format to A/D levels
if ~(exist('OCTAVE_VERSION','builtin') > 0)
    if contains(dataFormat, 'ubit')
        % Extract the number of quantization levels
        N = regexp(dataFormat, 'ubit(\d+)', 'tokens');
        if (numel(N) ~= 1)
            error('You have a problem with your UBIT data format!!');
        else
            N = str2double(N{1});
        end
        % Re-scaling to go from ADC values to decimal data
        %   For example, 'ubit2' ADC values store as {0,1,2,3}
        %   but the signal amplitudes should be {-3,-1,+1,+3}
        %   Likewise, 'ubit1' stores as {0,1} but should be {-1,+1}
        %   And 'ubit3' stores as {0,...,7} but should be {-7,-5,...,+7}
        data = 2*data - (2^N - 1);
    % Re-scaling to go from storage format to A/D levels
    elseif contains(dataFormat, 'bit')
        % Re-scaling to go from ADC values to decimal data
        %   For example, 2-bit ADC values store as {-2,-1,0,+1}
        %   but the signal amplitudes should be {-3,-1,+1,+3}
        %   Likewise, 1-bit stores as {-1,0} but should be {-1,+1}
        %   And 4-bit ADC values store as {-8,-7,...-1,0,1,...6,7}
        %   but amplitudes should be {-15,-13,...-1,1,...13,15}
        data = 2*data + 1;
    % No re-scaling
    else
        % do nothing -- fall through
    end
else
    if ~isempty(strfind(dataFormat, 'ubit')) %#ok<STREMP>
        % Extract the number of quantization levels
        N = regexp(dataFormat, 'ubit(\d+)', 'tokens');
        if (numel(N) ~= 1)
            error('You have a problem with your UBIT data format!!');
        else
            N = str2double(N{1});
        end
        % Re-scaling to go from ADC values to decimal data
        %   For example, 'ubit2' ADC values store as {0,1,2,3}
        %   but the signal amplitudes should be {-3,-1,+1,+3}
        %   Likewise, 'ubit1' stores as {0,1} but should be {-1,+1}
        %   And 'ubit3' stores as {0,...,7} but should be {-7,-5,...,+7}
        data = 2*data - (2^N - 1);
    % Re-scaling to go from storage format to A/D levels
    elseif ~isempty(strfind(dataFormat, 'bit')) %#ok<STREMP>
        % Re-scaling to go from ADC values to decimal data
        %   For example, 2-bit ADC values store as {-2,-1,0,+1}
        %   but the signal amplitudes should be {-3,-1,+1,+3}
        %   Likewise, 1-bit stores as {-1,0} but should be {-1,+1}
        %   And 4-bit ADC values store as {-8,-7,...-1,0,1,...6,7}
        %   but amplitudes should be {-15,-13,...-1,1,...13,15}
        data = 2*data + 1;
    % No re-scaling
    else
        % do nothing -- fall through
    end
end

% Build complex data matrix from 2 rows of input data
if strcmp(dataType, 'complex')
    data = complex(data(1,:), data(2,:));
end

return
%===============================================================================