function Hq = coeread(filename, flag)
%COEREAD Read a XILINX CORE Generator(tm) coefficient (.COE) file.
%   Hd = COEREAD(FILENAME) extracts the Distributed Arithmetic FIR filter 
%   coefficients defined in a XILINX CORE Generator .COE file specified by 
%   FILENAME and creates a fixed-point DFILT filter object, Hd.
%
%   If no extension is given with FILENAME, the extension '.coe' is assumed.
%  
%   See also DFILT/COEWRITE.

%   Author(s): P. Costa
%   Copyright 1999-2019 The MathWorks, Inc.

narginchk(1,2);

filename = convertStringsToChars(filename);

if nargin > 1
    flag     = convertStringsToChars(flag);
end

% Left here as an undocumented way to create a QFILT from .COE files.
if nargin < 2, flag = 'dfilt'; end
if ~strcmpi(flag, 'dfilt')
    error(message('dsp:coeread:invalidInput'));
end

% Check for valid .COE file extension
if ~contains(filename,'.')
    filename=[filename,'.coe'];
else
    [~,~,ext] = fileparts(filename);
    if ~strcmpi(ext,'.coe')
        error(message('dsp:coeread:FixedPtErr1'));
    end
end

% Create a cell array with the contains of the entire file
fileID = fopen(filename);
filecontents = textscan(fileID,'%s','Delimiter',',');
filecontents = filecontents{:};
fclose(fileID);
% Remove comments from file contents
filecell = sptrmcharsfromcell(filecontents,';');

% Get the radix
radix = getkeywordval(filecell,'radix');
if isempty(radix)
    error(message('dsp:coeread:FixedPtErr2')); 
end

% Determine the CoefficientFormat and get the coefficients.  
[b, q] = getCoeffsNFormat(filecell,'coefficient_width',radix);

% Get the InputFormat & OutputFormat quantizers
qiw = getIOFormat(filecell,'input_width');
qow = getIOFormat(filecell,'output_width');


Hq = dfilt.dffir(b);
set(Hq, 'Arithmetic', 'Fixed');
specifyall(Hq);
set(Hq, 'CoeffWordLength', q.Format(1), ...
    'NumFracLength', q.Format(2));

% If the quantizers are none, there was no information in the file
% about how to set them.  Just ignore.
if ~strcmpi(qiw.Mode, 'none')
    set(Hq, ...
        'InputWordLength', qiw.Format(1), ...
        'InputFracLength', qiw.Format(2));
end
if ~strcmpi(qow.Mode, 'none')
    set(Hq, ...
        'OutputWordLength', qow.Format(1), ...
        'OutputFracLength', qow.Format(2));
end


%-------------------------------------------------------------------
function [b, q] = getCoeffsNFormat(C,keyword,radix)
% Determine the CoefficientFormat and get the coefficients.  

% Get a cell array with the coefficient data 
coeffcell = getsubcell(C,'coefdata'); 

% Look for the 'Coefficient_Width' keyword
wordlen = getkeywordval(C,keyword);

% Coefficient wordlength could not be determined, infer it from
% the coefficients 
if isempty(wordlen)
    switch radix    
    case 2  
        % Assume binary always shows leading zeros
        wordlen = length(coeffcell{1}); 
    case 10 
        wordlen = ceil(1+log2((abs(max(str2num(char(coeffcell)))))));%#ok
    case 16 
        wordlen = gethexcoeffwl(char(coeffcell));
    end
end

q = quantizer([wordlen 0]);

% Get the quantized coefficients
switch radix
case 2,  b = bin2num(q,char(coeffcell)); 
case 10, b = pow2(str2num(char(coeffcell)),-q.fractionlength); %#ok
case 16, b = hex2num(q,char(coeffcell));
end




%-------------------------------------------------------------------
function qw = getIOFormat(filecell,keyword)
% Build a quantizer object from the supplied Input/Output wordlength 
% if not supplied, create a default quantizer('none')

iowidth = getkeywordval(filecell,keyword);
if isempty(iowidth)
    qw = quantizer('none');
else
    qw = quantizer([iowidth 0]);
end


%-------------------------------------------------------------------
function wordlen = gethexcoeffwl(c)
% Get the Wordlength for Radix 16 coefficients

maxwl = length(c(1,:))*4;
u = quantizer('ufixed',[maxwl 0]);

try
    % g 194518
    x = hex2num(u,c);
catch
    c = [c repmat(',', size(c, 1), 1)]';
    error(message('dsp:coeread:FixedPtErr3', c));
end

if max(x)>=2^(maxwl-1)
    wordlen = maxwl;
else
    error(message('dsp:coeread:FixedPtErr4'));
end 


%-------------------------------------------------------------------
%                       Cell-array / File utility functions
%-------------------------------------------------------------------
function subcell = getsubcell(filecell,keyword)
% Build a cell array which starts with the element containing the keyword 
% and ends with the first occurrence of a semicolon.  If no keyword was 
% found, and empty cell array is returned.

% Get the index of the keyword followed by an '=' sign.
kwindex = search4str(filecell,keyword);

% Get the index of the first ; after the keyword
index_2 = search4str({filecell{kwindex:end}},';'); %#ok<CCAT1>

% Form a new cell array to extract extra strings
if isempty(kwindex) && isempty(index_2)
    subcell = {};
else
    subcell = extractfromcell({filecell{kwindex:(kwindex+index_2-1)}}); %#ok<CCAT1>
end


%-------------------------------------------------------------------
function index = search4str(C,str2find)
% Search cell array elements for a particular string
%
% Inputs:
%   C - Cell array 
%   STR2FIND - String containing a keyword to search for

index = [];

% Looking for different permutations of the keyword
if nargin == 3
    str2find = {[str2find '='],[str2find ' =']};
else
    str2find = {str2find};
end

for n = 1:length(C)
    for m = 1:length(str2find)
        index = strfind(lower(C{n}),str2find{m});
        if ~isempty(index), index = n; return; end
    end
end


%-------------------------------------------------------------------
function newstr = extractfromcell(C)
% Extract extra strings from cell array elements
newstr = cell(1,length(C));
for m = 1:length(C)
    [t,newstr{m}] = strtok(C{m},'=');
    
    % Return the original string if '=' is not found 
    if isempty(newstr{m}); newstr{m} = t; end
    
    % Strip out extra characters
    torm = {'=',' ',';'};
    for n = 1:length(torm)
        newstr{m} = strrep(newstr{m},torm{n},'');
    end
end


%-------------------------------------------------------------------
function val = getkeywordval(C,keyword)
% Returns an empty if no keyword is found
%
% Inputs:
%   C - Cell array
%   STR2FIND - String containing a keyword 

% Build a cell array containing keyword information
subcell = getsubcell(C,keyword);

% Ignore empty cells (for the case of multi-line keyword fields)
val = [];
if ~isempty(subcell)
    for n = 1:length(subcell)
        if ~isempty(subcell{n}) || ~strcmpi(subcell{n},keyword)
            val = str2num(subcell{n});%#ok
        end
    end
end

% [EOF]
