function tchFrame = tchReadFrame(fid, tchType)
% tchFrame = tchReadFrame(fid, tchType)
%   Reads a single encoded speech frame from file identifier fid. The frame
%   must be a valid type expected for traffic channel tchType. If EOF has been
%   reached, tchFrame is returned empty. Otherwise, a struct containing the
%   frame is returned.
%
%   Valid channel types are:
%       'afs'       AMR full-rate speech
%       'fs'        GSM full-rate speech 
%
%   This function converts frames conforming to an augmented version of 3GPP
%   26.101 IF1 (read from fid) into frames with class bits partitioned
%   according to 3GPP 05.03 (tchFrame struct). IF1 is the 'file format' for
%   frame storage, while the particular tchType defines the 'working format'
%   for the frame.
%
%   See the command-line help for any of the codec executables for more details
%   on augmented of IF1.
%
% See also TCHWRITEFRAME.
%
% Copyright 2008, Research In Motion Limited.

    % Cache frame parameters for speed
    persistent param;
    if isempty(param)
        param = tchFrameParam();
    end
   
    bits = reshape(dec2bin(fread(fid, 2, 'uint8'), 8)', 1, []);
    if feof(fid) 
        tchFrame = [];
        return;
    end
    
    frameType = bin2dec(bits(param.frameTypeBits));
    spare = bin2dec(bits(param.spareBits));
    if frameType == 12
        % Extended frame type (uses spare field)
        frameType = 16 + spare;
    elseif spare ~= 0
        % Normal frame type
        error('Frame appears corrupt (non-zero spare bits)');
    end

    if ~isfield(param.frameTypes, tchType)
        error('Invalid tchType ''%s''', tchType);
    end
    tchName = param.frameTypes.(tchType){frameType + 1};
    if isempty(tchName) 
        error('Invalid frame type %d for tchType ''%s''', frameType, tchType);
    end
    p = param.tchTypes.(tchName);

    % Now that frameType is known, read the rest of the frame
    data = reshape(dec2bin(fread(fid, p.nBytes, 'uint8'), 8)', 1, []);
    bits = [bits data];

    % Unpack frame into struct
    tchFrame = struct();
    tchFrame.tchType = p.tchType;
    tchFrame.tchMode = p.tchMode;
    modes = param.frameModes.(p.tchType);
    tchFrame.modeInd = modes{bin2dec(bits(p.modeIndBits)) + 1};
    tchFrame.modeReq = modes{bin2dec(bits(p.modeReqBits)) + 1};
    tchFrame.sti = bin2dec(bits(p.stiBits));
    tchFrame.bfi = ~logical(bin2dec(bits(p.fqiBits)));
    tchFrame.classBits{1} = bits(p.classBits{1}) - '0';
    tchFrame.classBits{2} = bits(p.classBits{2}) - '0';
    tchFrame.classBits{3} = bits(p.classBits{3}) - '0';
    if bin2dec(bits(p.crcBits)) ~= 0
        error('Frame CRC is bad (non-zero)');
    end
end
