function tchWriteFrame(fid, tchFrame)
% tchWriteFrame(fid, tchFrame)
%   Writes a single encoded speech frame to file identifier fid. The struct
%   tchFrame contains the frame data in the following fields:
%
%       tchType     one of the channel types listlisted below 
%       tchMode     one of the modes listed below for the given channel type
%       modeInd     mode indication
%       modeReq     mode request
%       sti         SID Type Indicator (valid when tchMode is 'SID')
%                   (0 = SID_FIRST, 1 = SID_UPDATE)
%       bfi         Bad Frame Indicator 
%                   (0 = GOOD, 1 = BAD) 
%       classBits   cell array of 3 vectors (class bits A, B, and C)
%
%   Valid channel types are:
%       'afs'       AMR full-rate speech
%       'fs'        GSM full-rate speech 
%
%   Valid modes for 'afs' are:
%       '4p75', '5p15', '5p9', '6p7', '7p4', 
%       '7p95', '10p2', '12p2', 'SID', 'NODATA'
%
%   Valid modes for 'fs' are:
%       '13p0', 'SID', 'NODATA'
%
%   This function converts frames with class bits partitioned according to 3GPP
%   05.03 (tchFrame struct) into frames conforming to an augmented version of
%   3GPP 26.101 IF1 (written to fid). 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 IF1.
%
% See also TCHREADFRAME.
%
% Copyright 2008, Research In Motion Limited.

    % Cache frame parameters for speed
    persistent param;
    if isempty(param)
        param = tchFrameParam();
    end

    if ~isfield(param.frameModes, tchFrame.tchType)
        error('Invalid tchType ''%s''', tchFrame.tchType)
    end
    if length(param.frameModes.(tchFrame.tchType)) == 1
        guessTchMode = param.frameModes.(tchFrame.tchType){1};
    elseif isfield(tchFrame, 'tchMode') && ...
           ~isequal(tchFrame.tchMode, 'SID') && ...
           ~isequal(tchFrame.tchMode, 'NODATA')
        guessTchMode = tchFrame.tchMode;
    else
        guessTchMode = '';
    end

    % Defaults for fields that are not always used
    if ~isfield(tchFrame, 'tchMode')
        tchFrame.tchMode = guessTchMode;
    end
    if ~isfield(tchFrame, 'sti')
        tchFrame.sti = 0;
    end
    if ~isfield(tchFrame, 'bfi')
        tchFrame.bfi = 0;
    end
    if ~isfield(tchFrame, 'modeInd') 
        tchFrame.modeInd = guessTchMode;
    end
    if ~isfield(tchFrame, 'modeReq') 
        tchFrame.modeReq = guessTchMode;
    end

    % Lookup tchType/tchMode
    tchType = [tchFrame.tchType tchFrame.tchMode];
    if ~isfield(param.tchTypes, tchType)
        error('Invalid tchMode ''%s''', tchFrame.tchMode);
    end
    p = param.tchTypes.(tchType);
    
    frameType = p.frameType;
    if frameType > 15
        % Extended frame type (uses spare field)
        spare = frameType - 16;
        frameType = 12;
    else
        % Normal frame type
        spare = 0;
    end

    % Lookup tchType/modeInd and tchType/modeReq
    modeInd = [tchFrame.tchType tchFrame.modeInd];
    if ~isfield(param.tchModes, modeInd)
        error('Invalid modeInd ''%s''', tchFrame.modeInd);
    end
    modeInd = param.tchModes.(modeInd);
    modeReq = [tchFrame.tchType tchFrame.modeReq];
    if ~isfield(param.tchModes, modeReq)
        error('Invalid modeReq ''%s''', tchFrame.modeReq);
    end
    modeReq = param.tchModes.(modeReq);

    % Pack frame into bits
    bits = reshape(dec2bin(zeros(1, 2 + p.nBytes), 8)', 1, []);
    bits(param.frameTypeBits) = ...
        dec2bin(frameType, length(param.frameTypeBits));
    bits(p.fqiBits) = dec2bin(~logical(tchFrame.bfi), length(p.fqiBits));
    bits(param.spareBits) = dec2bin(spare, length(param.spareBits));
    bits(p.modeIndBits) = dec2bin(modeInd, length(p.modeIndBits));
    bits(p.modeReqBits) = dec2bin(modeReq, length(p.modeReqBits));
    bits(p.crcBits) = dec2bin(0, length(p.crcBits));
    bits(p.classBits{1}) = dec2bin(tchFrame.classBits{1}, 1);
    bits(p.classBits{2}) = dec2bin(tchFrame.classBits{2}, 1);
    bits(p.classBits{3}) = dec2bin(tchFrame.classBits{3}, 1);
    bits(p.stiBits) = dec2bin(tchFrame.sti, length(p.stiBits));

    bits = reshape(bits, 8, [])';
    fwrite(fid, bin2dec(bits), 'uint8');
 end
