source: ResearchApps/PHY/WARPLAB/WARPLab7/M_Code_Reference/classes/wl_baseband_buffers.m

Last change on this file was 4975, checked in by welsh, 8 years ago

fixed typos.

File size: 125.1 KB
RevLine 
[4330]1%-------------------------------------------------------------------------
[4332]2% WARPLab Framework
[4330]3%
4% Copyright 2013, Mango Communications. All rights reserved.
5%           Distributed under the WARP license  (http://warpproject.org/license)
6%
7% Chris Hunter (chunter [at] mangocomm.com)
8% Patrick Murphy (murphpo [at] mangocomm.com)
9% Erik Welsh (welsh [at] mangocomm.com)
10%-------------------------------------------------------------------------
11
[1915]12classdef wl_baseband_buffers < wl_baseband
[4320]13% Baseband object for the warplab_buffers reference baseband
14%     User code should not use this object directly-- the parent wl_node will
15%     instantiate the appropriate baseband object for the hardware in use
16
[4416]17    properties (SetAccess = protected, Hidden = true)
[4320]18        description;                % Description of this baseband object
[4416]19
20        sample_write_iq_id;         % IQ ID for Write IQ
21       
[4320]22        rx_iq_warning_needed;       % RX IQ warning:  Rx Length should be set explicitly before Rx buff is enabled
[4334]23        tx_iq_warning_needed;       % TX IQ warning:  Tx Length should be set explicitly before Tx buff is enabled
[4815]24       
25        seq_num_tracker;            % Sequence number tracker
26                                    %     Array of 8 entries:  [RFA_IQ, RFA_RSSI, RFB_IQ, RFB_RSSI, RFC_IQ, RFC_RSSI, RFD_IQ, RFD_RSSI]
[1915]27    end
[4320]28
[4334]29    % NOTE:  These properties will be removed in future releases
[1915]30    properties (SetAccess = public)
[4320]31        txIQLen;                    % Buffer length for transmit I/Q
32        rxIQLen;                    % Buffer length for receive I/Q
33        rxRSSILen;                  % Buffer length for receive RSSI
[4334]34       
35        txIQLen_warning;
36        rxIQLen_warning;
37        rxRSSILen_warning;
[4815]38       
39        seq_num_match_severity;     % Severity of the Read IQ sequence number error
[4334]40    end
41   
42    properties (SetAccess = public)
[4320]43        readTimeout;                % Maximum time spent waiting on board to send sample packets
44        check_write_iq_clipping;    % Enable checking for input sample range
[1915]45    end
[4687]46
47    properties (SetAccess = public, Hidden = false, Constant = true)
48        % Buffer State
49        STANDBY                        = 0;
50        RX                             = 1;
51        TX                             = 2;
[4815]52       
53        % Read IQ sequence number error severity
54        SEQ_NUM_MATCH_IGNORE           = 'ignore';
55        SEQ_NUM_MATCH_WARNING          = 'warning';
56        SEQ_NUM_MATCH_ERROR            = 'error';
[4687]57    end
[4320]58   
[4374]59    properties (SetAccess = public, Hidden = true)
60        % Due to performance limitation of the java transport, we are
61        %   imposing a soft maximum on the number of IQ samples that
62        %   can be requested using the java transport.  Based on the
63        %   performance data, 2^20 samples is about the max number of
64        %   samples before the performance degrades due to the way
65        %   samples are being tracked.  Similarly, we have a maximum
66        %   sample limit for the MEX transport due to memory constraints
67        %   within Matlab.
68        %
69        % THIS NEEDS TO BE REEVALUATED IN FUTURE RELEASES
70        %
71        JAVA_TRANSPORT_MAX_IQ          = 2^20;
[4687]72        MEX_TRANSPORT_MAX_IQ           = 2^25;       
[4374]73    end
74   
[4320]75    properties(Hidden = true, Constant = true)
[4330]76        % These constants define specific command IDs used by this object.
77        % Their C counterparts are found in wl_baseband.h
[2919]78        GRP                            = 'baseband';
[4812]79        CMD_TX_DELAY                   = 1;                % 0x000001
80        CMD_TX_LENGTH                  = 2;                % 0x000002
81        CMD_TX_MODE                    = 3;                % 0x000003
82        CMD_TX_BUFF_EN                 = 4;                % 0x000004
83        CMD_RX_BUFF_EN                 = 5;                % 0x000005
84        CMD_TX_RX_BUFF_DIS             = 6;                % 0x000006
85        CMD_TX_RX_BUFF_STATE           = 7;                % 0x000007
86        CMD_WRITE_IQ                   = 8;                % 0x000008
87        CMD_READ_IQ                    = 9;                % 0x000009
88        CMD_READ_RSSI                  = 10;               % 0x00000A
89        CMD_RX_LENGTH                  = 11;               % 0x00000B
90        CMD_WRITE_IQ_CHKSUM            = 12;               % 0x00000C
91        CMD_MAX_NUM_SAMPLES            = 13;               % 0x00000D
[2919]92
[4812]93        CMD_TXRX_COUNT_RESET           = 16;               % 0x000010
94        CMD_TXRX_COUNT_GET             = 17;               % 0x000011
[4330]95       
[4812]96        CMD_AGC_STATE                  = 256;              % 0x000100
97        CMD_AGC_DONE_ADDR              = 257;              % 0x000101
98        CMD_AGC_RESET                  = 258;              % 0x000102
99        CMD_AGC_RESET_MODE             = 259;              % 0x000103
[4784]100       
[4812]101        CMD_AGC_TARGET                 = 272;              % 0x000110
102        CMD_AGC_DCO_EN_DIS             = 273;              % 0x000111
[4784]103       
[4812]104        CMD_AGC_CONFIG                 = 288;              % 0x000120
105        CMD_AGC_IIR_HPF                = 289;              % 0x000121
106        CMD_RF_GAIN_THRESHOLD          = 290;              % 0x000122
107        CMD_AGC_TIMING                 = 291;              % 0x000123
108        CMD_AGC_DCO_TIMING             = 292;              % 0x000124
109       
[4416]110        % Sample defines
111        SAMPLE_IQ_SUCCESS              = 0;
112        SAMPLE_IQ_ERROR                = 1;
113        SAMPLE_IQ_NOT_READY            = 2;
114        SAMPLE_IQ_CHECKSUM_FAILED      = 3;
[4815]115       
116        % Baseband defines
117        BB_SEL_RFA                     = 1;                % 0x00000001
118        BB_SEL_RFB                     = 2;                % 0x00000002
119        BB_SEL_RFC                     = 4;                % 0x00000004
120        BB_SEL_RFD                     = 8;                % 0x00000008
[1948]121    end
[4330]122   
[1915]123    methods
[4334]124        function [varargout] = subsref(obj, S)
125            % Display deprecation warnings for the baseband parameters that will be removed.
126            %
127            if((obj.txIQLen_warning) && (numel(S) == 1) && (S.type == '.') && strcmp(S.subs, 'txIQLen'))
128                fprintf('\n\nWARNING:  Param txIQLen is deprecated and will be removed!\n\n\n')
129                obj.txIQLen_warning = false;
130            end
131           
132            if((obj.rxIQLen_warning) && (numel(S) == 1) && (S.type == '.') && strcmp(S.subs, 'rxIQLen'))
133                fprintf('\n\nWARNING:  Param rxIQLen is deprecated and will be removed!\n\n\n')
134                obj.rxIQLen_warning = false;
135            end
136           
137            if((obj.rxRSSILen_warning) && (numel(S) == 1) && (S.type == '.') && strcmp(S.subs, 'rxRSSILen'))
138                fprintf('\n\nWARNING:  Param rxRSSILen is deprecated and will be removed!\n\n\n')
139                rxRSSILen_warning = false;
140            end
141           
142            varargout{:} = builtin('subsref', obj, S);
143        end       
144
145       
[1915]146        function obj = wl_baseband_buffers()
[4320]147            obj.description             = 'WARPLab Baseband for wl_buffers';
148            obj.readTimeout             = 0.1;                                      % Default read timeout (in seconds)
[4416]149           
[4320]150            obj.check_write_iq_clipping = 1;                                        % Default to check for write IQ clipping
[4416]151           
[4320]152            obj.rx_iq_warning_needed    = true;                                     % Default to issue a rx_iq warning
[4334]153            obj.tx_iq_warning_needed    = true;                                     % Default to issue a tx_iq warning
154           
155            obj.txIQLen_warning         = true;
156            obj.rxIQLen_warning         = true;
157            obj.rxRSSILen_warning       = true;
[4416]158           
159            obj.sample_write_iq_id      = uint8(0);
[4815]160           
161            obj.seq_num_tracker         = uint32(zeros(1, 8));                      % Initialize all 8 trackers to zero
162            obj.seq_num_match_severity  = obj.SEQ_NUM_MATCH_WARNING;
[1915]163        end 
164       
165        function out = procCmd(obj, nodeInd, node, varargin)
[4330]166            % wl_baseband_buffers procCmd(obj, nodeInd, node, varargin)
167            %     obj:       Node object (when called using dot notation)
168            %     nodeInd:   Index of the current node, when wl_node is iterating over nodes
169            %     node:      Current node object (the owner of this baseband)
170            %     cmdStr:    Command string of the interface command
171            %     varargin:
172            %         Two forms of arguments for baseband commands:
173            %             (...,'theCmdString', someArgs) - for commands that affect all buffers
174            %             (...,BUFF_SEL, 'theCmdString', someArgs) - for commands that affect specific buffers
175            %       
176            out       = [];
[1915]177            transport = node.transport;
178           
[4330]179            % Process initial command varargin so commands can have consistent inputs
180            if(strcmp(varargin{1}, 'RF_ALL'))
[4687]181                buffSel     = rfSel_to_bbSel(sum(node.interfaceGroups{1}.ID));
182                num_buffers = 1;
183                cmdStr      = varargin{2};
[1959]184
[4330]185                if(length(varargin) > 2)
186                    varargin = varargin(3:end);
187                else
188                    varargin = {};
189                end
190               
[1959]191            elseif(ischar(varargin{1}))
[4330]192               % No buffers specified
[4687]193               cmdStr      = varargin{1};
[4330]194
195               if(length(varargin) > 1)
[1915]196                   varargin = varargin(2:end);
197               else
198                   varargin = {};
199               end
[4330]200               
[1915]201            else
[4330]202               % Buffers specified
203               %     Reference implementation uses the same RF_x values to identify RF interfaces
204               %     and their associated buffers. A more sophisticated baseband (where interfaces:buffers
205               %     aren't 1:1) would need a different scheme for identifying buffers from user code
[4687]206               %
[4686]207               buffSel       = rfSel_to_bbSel(varargin{1});
208               num_buffers   = length(buffSel);
209               cmdStr        = varargin{2};
210               
[4330]211               if(length(varargin) > 2)
[1915]212                   varargin = varargin(3:end);
213               else
214                   varargin = {};
215               end
216            end
217           
218            cmdStr = lower(cmdStr);
219            switch(cmdStr)
[4330]220           
221                %---------------------------------------------------------
[1915]222                case 'tx_delay'
[4330]223                    % Transmit delay- gets or sets the number of sample periods the baseband
[1915]224                    % waits between the trigger and starting the transission
225                    %
226                    % Requires BUFF_SEL: No
[1991]227                    % Arguments: none or (uint32 TX_DLY)
228                    % Returns: (uint32 TX_DLY) or none
[1915]229                    %
[1991]230                    % If an argument is specified, this command enters a write mode where
231                    % that argument is written to the board. If no argument is specified,
232                    % the current value of TX_DLY is read from the board.
[4686]233                    %
[4330]234                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TX_DELAY));
235                   
236                    if(isempty(varargin))                  % Read Mode
[4334]237                        myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
238                       
[1915]239                        resp = node.sendCmd(myCmd);
[4330]240                        ret  = resp.getArgs();
[4334]241                        out  = ret(2);
[4330]242                    else                                   % Write Mode
[4334]243                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
244                       
[1915]245                        delay = varargin{1};
[4330]246                       
[4687]247                        % Check arguments
248                        if(length(delay) ~= 1)
249                           error('%s:  Requires scalar argument.  Delay is per-node, not per-interface or per-buffer.', cmdStr);
[1915]250                        end
[4330]251                       
[4334]252                        if(delay < 0)
253                            error('%s: Tx delay must be greater than or equal to zero.\n', cmdStr);
[1915]254                        end
[4687]255
256                        % Send command to the node
[4334]257                        myCmd.addArgs(delay);
258                        resp = node.sendCmd(myCmd);
259                       
260                        % Process response from the node.  Return arguments:
261                        %     [1] - Status
262                        %     [2] - Tx Delay
263                        %
[4358]264                        for i = 1:numel(resp)                   % Needed for unicast node_group support
265                            ret   = resp(i).getArgs();
266
267                            if (delay ~= ret(2))
[4687]268                                msg = sprintf('%s: Tx delay error in node %d.\n    Requested delay of %d samples.\n    Max delay of %d samples.\n', ...
269                                    cmdStr, nodeInd, delay, ret(2));
[4358]270                                error(msg);
271                            end
[4334]272                        end
[1915]273                    end
[2919]274
[4330]275                %---------------------------------------------------------
[1915]276                case 'tx_length'
[4334]277                    % Transmit length- sets the duration of each transmit cycle, in sample periods
[1915]278                    %
279                    % Requires BUFF_SEL: No
[4334]280                    % Arguments: (uint32 TX_LEN)
281                    % Returns:   none
282                    %
283                    % NOTE:  This will error if the user tries to read tx_length from the board.
284                    %   the 'tx_buff_max_num_samples' command should be used to determine the
285                    %   capabilities of the board.
[1915]286                    %
[4330]287                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TX_LENGTH));
288                   
289                    if(isempty(varargin))                  % Read Mode
[4334]290                        error('%s: ''tx_length'' does not support read.  Use ''tx_buff_max_num_samples''.', cmdStr);
[4330]291                    else                                   % Write Mode
[4334]292                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
293                       
[1915]294                        len = varargin{1};
[4330]295                       
[4687]296                        % Check arguments
297                        if(length(len) ~= 1)
298                           error('%s:  Requires scalar argument.  Length is per-node, not per-interface or per-buffer.', cmdStr);
[1915]299                        end
[4330]300                       
[4687]301                        if(len < 0)
[4334]302                            error('%s: Tx length must be greater than or equal to zero.\n', cmdStr);
[1915]303                        end
[4330]304                       
[4687]305                        % Send command to the node
[1915]306                        myCmd.addArgs(len);
[4334]307                        resp = node.sendCmd(myCmd);
[4330]308                       
[4334]309                        % Process response from the node.  Return arguments:
310                        %     [1] - Status
311                        %     [2] - Tx Length
312                        %
[4358]313                        for i = 1:numel(resp)                   % Needed for unicast node_group support
314                            ret   = resp(i).getArgs();
315                           
316                            if (len ~= ret(2))
[4687]317                                msg = sprintf('%s: Tx length error in node %d.\n    Requested length of %d samples.\n    Max length of %d samples.\n', ...
318                                    cmdStr, nodeInd, len, ret(2));
[4358]319                                error(msg);
320                            end
[4334]321                        end
322                       
[4330]323                        % Update internal object values
[4334]324                        obj.tx_iq_warning_needed = false;       % Since we have explicitly set the tx IQ length, we do not need a warning
[1915]325                    end
[2919]326
[4330]327                %---------------------------------------------------------
[1915]328                case 'rx_length'
[4330]329                    % Receive length- reads or sets the duration of each receive cycle, in sample periods
[1915]330                    %
331                    % Requires BUFF_SEL: No
[4334]332                    % Arguments: (uint32 RX_LEN)
333                    % Returns: none
[1915]334                    %
[4334]335                    % NOTE:  This will error if the user tries to read tx_length from the board.
336                    %   the 'tx_buff_max_num_samples' command should be used to determine the
337                    %   capabilities of the board.
[4330]338                    %
339                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_RX_LENGTH));
340                   
341                    if(isempty(varargin))                  % Read Mode
[4334]342                        error('%s: ''rx_length'' does not support read.  Use ''rx_buff_max_num_samples''.', cmdStr);
[4330]343                    else                                   % Write Mode
[4334]344                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
345                       
[1915]346                        len = varargin{1};
[4330]347
[4687]348                        % Check arguments
[4320]349                        if(length(len) > 1)
[4687]350                           error('%s:  Requires scalar argument.  Length is per-node, not per-interface or per-buffer.', cmdStr);
[1915]351                        end
[4330]352
[4334]353                        if(len  < 0)
354                            error('%s: Rx length must be greater than or equal to zero.\n', cmdStr);
[1915]355                        end
[4330]356                       
[4687]357                        % Send command to the node
[1915]358                        myCmd.addArgs(len);
[4334]359                        resp = node.sendCmd(myCmd);
[4330]360                       
[4334]361                        % Process response from the node.  Return arguments:
362                        %     [1] - Status
363                        %     [2] - Rx Length
364                        %
[4358]365                        for i = 1:numel(resp)                   % Needed for unicast node_group support
366                            ret   = resp(i).getArgs();
367
368                            if (len > ret(2))
[4687]369                                msg = sprintf('%s: Rx length error in node %d.\n    Requested length of %d samples.\n    Max length of %d samples.\n', ...
370                                    cmdStr, nodeInd, len, ret(2));
[4358]371                                error(msg);
372                            end
[4334]373                        end
374                       
[4330]375                        % Update internal object values
[4320]376                        obj.rx_iq_warning_needed = false;       % Since we have explicitly set the rx IQ length, we do not need a warning
[1915]377                    end
[2919]378
[4330]379                %---------------------------------------------------------
[4334]380                case 'tx_buff_max_num_samples'
381                    % Maximum number of TX samples
382                    %
[4812]383                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
[4334]384                    % Arguments: none
385                    % Returns: (uint32 MAX_TX_LEN)
386                    %
[4687]387                    out = zeros(1, num_buffers);
[4686]388                   
[4687]389                    % Iterate over the provided interfaces
390                    for n = 1:num_buffers
[4334]391                   
[4687]392                        % Check buffer selection
393                        if(isSingleBuffer(buffSel(n)) == 0)
394                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
395                        end
[4334]396                   
[4687]397                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_MAX_NUM_SAMPLES));
[4334]398                       
[4687]399                        if(isempty(varargin))                  % Read Mode
400                            myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
401                            myCmd.addArgs(buffSel(n));
402                       
403                            resp = node.sendCmd(myCmd);
404                           
405                            % Process response from the node.  Return arguments:
406                            %     [1] - Status
407                            %     [2] - Max Tx Length
408                            %     [3] - Max Rx Length
409                            %
410                            ret    = resp.getArgs();
411                            out(n) = double(ret(2));
412                           
413                        else                                   % Write Mode
414                            error('%s: ''tx_buff_max_num_samples'' does not support write.  Use ''tx_length''.', cmdStr);
415                        end
[4334]416                    end
417                   
418                %---------------------------------------------------------
419                case 'rx_buff_max_num_samples'
420                    % Maximum number of RX samples
421                    %
[4812]422                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
[4334]423                    % Arguments: none
424                    % Returns: (uint32 MAX_RX_LEN)
425                    %
[4687]426                    out = zeros(1, num_buffers);
[4686]427                   
[4687]428                    % Iterate over the provided interfaces
429                    for n = 1:num_buffers
[4686]430                   
[4687]431                        % Check buffer selection
432                        if(isSingleBuffer(buffSel(n)) == 0)
433                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
434                        end
[4334]435                   
[4687]436                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_MAX_NUM_SAMPLES));
[4334]437                       
[4687]438                        if(isempty(varargin))                  % Read Mode
439                            myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
440                            myCmd.addArgs(buffSel(n));
441                       
442                            resp = node.sendCmd(myCmd);
443                           
444                            % Process response from the node.  Return arguments:
445                            %     [1] - Status
446                            %     [2] - Max Tx Length
447                            %     [3] - Max Rx Length
448                            %
449                            ret    = resp.getArgs();
450                            out(n) = double(ret(3));
451                           
452                        else                                   % Write Mode
453                            error('%s: ''rx_buff_max_num_samples'' does not support write.  Use ''rx_length''.', cmdStr);
454                        end
[4334]455                    end
456                   
457                %---------------------------------------------------------
[2919]458                case 'continuous_tx'
[1915]459                    % Enable/disable continuous transmit mode
460                    %
461                    % Requires BUFF_SEL: No
462                    % Arguments: (boolean CONT_TX)
[4330]463                    %     CONT_TX:
464                    %         true enables continuous transmit mode
465                    %         false disable continuous transmit mode
[1915]466                    % Returns: none
[4330]467                    %
[4687]468                    % Restrictions on continuous transmit waveform length:
[4681]469                    %     WARPLab 7.6.0:
470                    %         0 to 2^15   --> Waveform will be transmitted for the exact number of samples
471                    %         > 2^15      --> Waveform must be a multiple of 2^14 samples for the waveform
472                    %                         to be transmitted exactly.  Otherwise, waveform will be appended
473                    %                         with whatever IQ data is in the transmit buffer to align the
474                    %                         waveform to be a multiple of 2^14 samples.
475                    %
476                    %     WARPLab 7.5.x:
477                    %         Example not supported
478                    %
479                    %     WARPLab 7.4.0 and prior:
480                    %         0 to 2^15   --> Waveform will be transmitted for the exact number of samples
481                    %         > 2^15      --> Not supported
482                    %
[1948]483                    if(length(varargin) ~= 1)
[2865]484                        error('%s: requires one boolean argument',cmdStr);
[1948]485                    end
[4334]486                   
[4330]487                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TX_MODE), uint32(boolean(varargin{1})));
[1915]488                    node.sendCmd(myCmd);
[2919]489
[4330]490                %---------------------------------------------------------
[1915]491                case 'tx_buff_en'
492                    % Enable transmit buffer for one or more interfaces. When a buffer is enabled it will
[4330]493                    %     drive samples into its associated interface when a trigger is received. The interface
494                    %     itself must also be enabled (wl_interfaceCmd(...,'tx_en')) to actually transmit the samples
[1915]495                    %
[4812]496                    % Requires BUFF_SEL: Yes (Scalar notation [RFA + RFB])
[1915]497                    % Arguments: none
498                    % Returns: none
[4330]499                    %
[4686]500
501                    % Check buffer selection
502                    if(num_buffers ~= 1)
503                        error('%s: Length of buffer selection vector must be 1', cmdStr);
504                    end
505                   
[4334]506                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TX_BUFF_EN), buffSel);
[1915]507                    node.sendCmd(myCmd);
[4334]508                   
509                    % Print warning if TX length was not set before the TX buffer was enabled                   
510                    try
511                        temp = evalin('base', 'wl_tx_iq_length_did_warn');
512                    catch
513                        if ( obj.tx_iq_warning_needed ) 
514                            fprintf('WARNING:   Currently in WARPLab, the default value transmit length is %d.\n', obj.txIQLen);
515                            fprintf('WARNING:   In the future, this may change.  Therefore, you need to explicitly\n');
516                            fprintf('WARNING:   set the transmit IQ length:\n');
[4351]517                            fprintf('WARNING:       wl_basebandCmd(nodes, ''tx_length'', my_tx_length);\n');
[4334]518                        end
519                        assignin('base', 'wl_tx_iq_length_did_warn', 1)
520                    end
[2919]521
[4330]522                %---------------------------------------------------------
[1915]523                case 'rx_buff_en'
524                    % Enable receive buffer for one or more interfaces. When a buffer is enabled it will
[4330]525                    %     capture samples from its associated interface when a trigger is received. The interface
526                    %     itself must also be enabled (wl_interfaceCmd(...,'rx_en'))
[1915]527                    %
[4812]528                    % Requires BUFF_SEL: Yes (Scalar notation [RFA + RFB])
[1915]529                    % Arguments: none
530                    % Returns: none
[4334]531                    %
[4686]532                   
533                    % Check buffer selection
534                    if(num_buffers ~= 1)
535                        error('%s: Length of buffer selection vector must be 1', cmdStr);
536                    end
537                   
[4330]538                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_RX_BUFF_EN), buffSel);
[1915]539                    node.sendCmd(myCmd);
[4330]540
541                    % Print warning if RX length was not set before the RX buffer was enabled                   
[4320]542                    try
[4334]543                        temp = evalin('base', 'wl_rx_iq_length_did_warn');
[4320]544                    catch
545                        if ( obj.rx_iq_warning_needed ) 
[4334]546                            fprintf('WARNING:   Currently in WARPLab, the default value receive length is %d.\n', obj.rxIQLen);
547                            fprintf('WARNING:   In the future, this may change.  Therefore, you need to explicitly\n');
[4320]548                            fprintf('WARNING:   set the receive IQ length:\n');
[4351]549                            fprintf('WARNING:       wl_basebandCmd(nodes, ''rx_length'', my_rx_length);\n');
[4320]550                        end
[4334]551                        assignin('base', 'wl_rx_iq_length_did_warn', 1)
552                    end
[2919]553
[4330]554                %---------------------------------------------------------
[1915]555                case 'tx_rx_buff_dis'
[2009]556                    % Disable the Tx and Rx buffers for one or more interfaces. When a buffer is disabled it will not
[4330]557                    %     output/capture samples when a trigger is received, even if the associated interface is enabled
[1915]558                    %
[4812]559                    % Requires BUFF_SEL: Yes (Scalar notation [RFA + RFB])
[1915]560                    % Arguments: none
561                    % Returns: none
[4330]562                    %
[4686]563                   
564                    % Check buffer selection
565                    if(num_buffers ~= 1)
566                        error('%s: Length of buffer selection vector must be 1', cmdStr);
567                    end
568                   
[4334]569                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TX_RX_BUFF_DIS), buffSel);
[1915]570                    node.sendCmd(myCmd);
[2919]571
[4330]572                %---------------------------------------------------------
[4687]573                case 'read_buff_state'
574                    % Read the current state of the buffer
575                    %
[4812]576                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
[4687]577                    % Arguments: none
578                    % Returns: Current state of the buffer:  TX, RX or STANDBY
579                    %
580                   
581                    for buff_index = 1:num_buffers
582                   
583                        if(isSingleBuffer(buffSel(buff_index)) == 0)
584                            error('%s: Buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
585                        end
586
587                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TX_RX_BUFF_STATE));
588                        myCmd.addArgs(buffSel(buff_index));
589                       
590                        resp  = node.sendCmd(myCmd);
591                        ret   = resp.getArgs();
592                       
593                        switch (ret(1))
594                            case 0
595                                out(buff_index) = obj.STANDBY;
596                            case 1
597                                out(buff_index) = obj.RX;
598                            case 2
599                                out(buff_index) = obj.TX;
600                            otherwise
601                                error('%s: Node returned an unknown buffer state.', cmdStr);
602                        end
603                    end
604               
605                %---------------------------------------------------------
[1915]606                case 'tx_buff_clk_freq'
607                    % Read the transmit sample clock frequency out of the buffer core.
608                    %
[1979]609                    % Requires BUFF_SEL: No
[1915]610                    % Arguments: none
611                    % Returns: (uint32 Fs_Tx)
[4330]612                    %     Fs_Tx: Tx sample frequency of buffer core in Hz
613                    %
614                    out = 40e6;        % Currently, this value is hardcoded. It will eventually be read from the board.
[2919]615
[4330]616                %---------------------------------------------------------
[1972]617                case 'rx_buff_clk_freq'
618                    % Read the receive sample clock frequency out of the buffer core.
619                    %
[1979]620                    % Requires BUFF_SEL: No
[1972]621                    % Arguments: none
622                    % Returns: (uint32 Fs_Rx)
[4330]623                    %     Fs_Rx: Rx sample frequency of buffer core in Hz
624                    %
625                    out = 40e6;        % Currently, this value is hardcoded. It will eventually be read from the board.
[2919]626
[4330]627                %---------------------------------------------------------
[1972]628                case 'rx_rssi_clk_freq'
629                    % Read the receive RSSI sample clock frequency out of the buffer core.
630                    %
[1979]631                    % Requires BUFF_SEL: No
[1972]632                    % Arguments: none
633                    % Returns: (uint32 Fs_RxRSSI)
[4330]634                    %     Fs_RxRSSI: Rx RSSI sample frequency of buffer core in Hz
635                    %
636                    out = 10e6;        % Currently, this value is hardcoded. It will eventually be read from the board.                         
[2919]637
[4330]638                %---------------------------------------------------------
[1915]639                case 'write_iq'
640                    % Write I/Q samples to the specified buffers. The dimensions of the buffer selection and samples matrix
[4330]641                    %     must agree. The same samples can be written to multiple buffers by combining buffer IDs
[1915]642                    %
643                    % Requires BUFF_SEL: Yes (combined BUFF_SEL values ok)
644                    % Arguments: (complex double TX_SAMPS, int OFFSET)
[4330]645                    %     TX_SAMPS: matrix of complex samples. The number of columns must match the length of BUFF_SEL
646                    %     OFFSET: buffer index of first sample to write (optional; defaults to 0)
[1915]647                    %
648                    % Examples:
[4330]649                    %     TxLength = 2^14;
650                    %     Ts = 1/(wl_basebandCmd(node0,'tx_buff_clk_freq'));
651                    %     t  = [0:Ts:(TxLength-1)*Ts].';                       % column vector
652                    %     X  = exp(t*1i*2*pi*3e6);                             % 3MHz sinusoid
653                    %     Y  = exp(t*1i*2*pi*5e6);                             % 5MHz sinusoid
[1915]654                    %
[4330]655                    %     % Write X to RFA
656                    %     wl_basebandCmd(node, RFA, 'write_IQ', X);
[1915]657                    %
[4330]658                    %     % Write X to RFA and RFB
659                    %     wl_basebandCmd(node, (RFA + RFB), 'write_IQ', X);
[1915]660                    %
[4330]661                    %     % Write X to RFA, Y to RFB
662                    %     wl_basebandCmd(node, [RFA RFB], 'write_IQ', [X Y]);
663                    %
[1948]664                    writeIQ(obj, node, transport, buffSel, cmdStr, varargin{:});
[2919]665
[4330]666                %---------------------------------------------------------
[2919]667                case 'write_iq_checksum'
[4330]668                    % Write IQ checksum - gets the current Write IQ checksum from the node.
[2919]669                    %
670                    % Requires BUFF_SEL: No
671                    % Arguments: none
672                    % Returns: (uint32 WRITE_IQ_CHECKSUM)
673                    %
[4330]674                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_WRITE_IQ_CHKSUM));
675                    resp  = node.sendCmd(myCmd);
676                    ret   = resp.getArgs();
677                    out   = ret(1);
[2919]678
[4330]679                %---------------------------------------------------------
[1915]680                case 'read_iq'
681                    % Read I/Q samples from the specified buffers. The elements of the buffer selection must be scalers which
[4330]682                    %     identify a single buffer. To read multiple buffers in one call, pass a vector of individual buffer IDs
[1915]683                    %
684                    % Requires BUFF_SEL: Yes (combined BUFF_SEL values not allowed)
685                    % Arguments: (int OFFSET, int NUM_SAMPS)
[4330]686                    %     OFFSET: buffer index of first sample to read (optional; defaults to 0)
687                    %     NUM_SAMPS: number of complex samples to read (optional; defaults to length(OFFSET:rxIQLen-1))
[1915]688                    %
689                    % Examples:
[4330]690                    %     % Read full buffer for RFA
691                    %     %     NOTE:  size(X) will be [rxIQLen, 1]
692                    %     X = wl_basebandCmd(node, RFA, 'read_IQ');
[1915]693                    %
[4330]694                    %     % Read partial buffer for RFA (samples 1000:4999)
695                    %     %     NOTE:  size(X) will be [5000, 1]
696                    %     X = wl_basebandCmd(node, RFA, 'read_IQ', 1000, 5000);
[1915]697                    %
[4330]698                    %     % Read full buffers for RFA and RFB
699                    %     %     NOTE:  size(X) will be [rxIQLen, 2]
700                    %     X = wl_basebandCmd(node, [RFA RFB], 'read_IQ');
[1915]701                    %
[1948]702                    out = readIQ(obj, node, buffSel, cmdStr, varargin{:});
[2919]703
[4330]704                %---------------------------------------------------------
[1915]705                case 'read_rssi'
706                    % Read RSSI samples from the specified buffers. The elements of the buffer selection must be scalers which
[4330]707                    %     identify a single buffer. To read multiple buffers in one call, pass a vector of individual buffer IDs.
[1915]708                    %
[2009]709                    % See 'read_iq' for arguments/returns
[4330]710                    %
[1948]711                    out = readRSSI(obj, node, buffSel, cmdStr, varargin{:});
[2919]712
[4330]713                %---------------------------------------------------------
[4812]714                case 'get_tx_count'
715                    % For the given buffers, get the number of times the TX state machine has run
716                    %
717                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
718                    % Arguments: none
719                    % Returns: [uint32 BUFFER_COUNTER]
720                    %
721                    out = zeros(1, num_buffers);
722                   
723                    % Iterate over the provided interfaces
724                    for n = 1:num_buffers
725                   
726                        % Check buffer selection
727                        if(isSingleBuffer(buffSel(n)) == 0)
728                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
729                        end
730                   
731                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TXRX_COUNT_GET));
732                       
733                        if(isempty(varargin))                  % Read Mode
734                            myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
735                            myCmd.addArgs(buffSel(n));
736                            myCmd.addArgs(0);                  % Select TX counter
737                       
738                            resp = node.sendCmd(myCmd);
739                           
740                            % Process response from the node.  Return arguments:
741                            %     [1] - Status
742                            %     [2] - Counter value
743                            %
744                            ret    = resp.getArgs();
745                           
746                            if (ret(1) == myCmd.CMD_PARAM_SUCCESS)
747                                out(n) = double(ret(2));
748                            else 
749                                msg = sprintf('%s: Get TX count error in node %d.\n', cmdStr, nodeInd);
750                                error(msg);
751                            end
752                        else                                   % Write Mode
753                            error('%s: ''get_tx_count'' does not support write.', cmdStr);
754                        end
755                    end
756                   
757                %---------------------------------------------------------
758                case 'get_rx_count'
759                    % For the given buffers, get the number of times the RX state machine has run
760                    %
761                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
762                    % Arguments: none
763                    % Returns: [uint32 BUFFER_COUNTER]
764                    %
765                    out = zeros(1, num_buffers);
766                   
767                    % Iterate over the provided interfaces
768                    for n = 1:num_buffers
769                   
770                        % Check buffer selection
771                        if(isSingleBuffer(buffSel(n)) == 0)
772                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
773                        end
774                   
775                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TXRX_COUNT_GET));
776                       
777                        if(isempty(varargin))                  % Read Mode
778                            myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
779                            myCmd.addArgs(buffSel(n));
780                            myCmd.addArgs(1);                  % Select RX counter
781                       
782                            resp = node.sendCmd(myCmd);
783                           
784                            % Process response from the node.  Return arguments:
785                            %     [1] - Status
786                            %     [2] - Counter value
787                            %
788                            ret    = resp.getArgs();
789                           
790                            if (ret(1) == myCmd.CMD_PARAM_SUCCESS)
791                                out(n) = double(ret(2));
792                            else 
793                                msg = sprintf('%s: Get RX count error in node %d.\n', cmdStr, nodeInd);
794                                error(msg);
795                            end
796                        else                                   % Write Mode
797                            error('%s: ''get_rx_count'' does not support write.', cmdStr);
798                        end
799                    end
800                   
801                %---------------------------------------------------------
802                case 'reset_tx_count'
803                    % For the given buffers, reset the counter that records the number of times the TX state machine has run
804                    %
805                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
806                    % Arguments: none
807                    % Returns: [uint32 BUFFER_COUNTER]
808                    %
809                   
810                    % Iterate over the provided interfaces
811                    for n = 1:num_buffers
812                   
813                        % Check buffer selection
814                        if(isSingleBuffer(buffSel(n)) == 0)
815                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
816                        end
817                   
818                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TXRX_COUNT_RESET));
819                       
820                        if(isempty(varargin))
821                            myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
822                            myCmd.addArgs(buffSel(n));
823                            myCmd.addArgs(0);                  % Select TX counter
824                       
825                            resp = node.sendCmd(myCmd);
826                           
827                            % Process response from the node.  Return arguments:
828                            %     [1] - Status
829                            %
830                            ret    = resp.getArgs();
831                           
832                            if (ret(1) ~= myCmd.CMD_PARAM_SUCCESS)
833                                msg = sprintf('%s: Get TX count reset error in node %d.\n', cmdStr, nodeInd);
834                                error(msg);
835                            end
836                        else
837                            error('%s: ''reset_tx_count'' does not support additional arguments.', cmdStr);
838                        end
839                    end
840                   
841                %---------------------------------------------------------
842                case 'reset_rx_count'
843                    % For the given buffers, reset the counter that records the number of times the RX state machine has run
844                    %
845                    % Requires BUFF_SEL: Yes (Vector notation [RFA, RFB])
846                    % Arguments: none
847                    % Returns: [uint32 BUFFER_COUNTER]
848                    %
849                   
850                    % Iterate over the provided interfaces
851                    for n = 1:num_buffers
852                   
853                        % Check buffer selection
854                        if(isSingleBuffer(buffSel(n)) == 0)
855                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA, RFB]', cmdStr);
856                        end
857                   
858                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TXRX_COUNT_RESET));
859                       
860                        if(isempty(varargin))
861                            myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
862                            myCmd.addArgs(buffSel(n));
863                            myCmd.addArgs(1);                  % Select RX counter
864                       
865                            resp = node.sendCmd(myCmd);
866                           
867                            % Process response from the node.  Return arguments:
868                            %     [1] - Status
869                            %
870                            ret    = resp.getArgs();
871                           
872                            if (ret(1) ~= myCmd.CMD_PARAM_SUCCESS)
873                                msg = sprintf('%s: Get RX count reset error in node %d.\n', cmdStr, nodeInd);
874                                error(msg);
875                            end
876                        else
877                            error('%s: ''reset_rx_count'' does not support additional arguments.', cmdStr);
878                        end
879                    end
880                   
881                %---------------------------------------------------------
[1915]882                case 'agc_state'
[1973]883                    % Read AGC state from the specified buffers. The elements of the buffer selection must be scalers which
[4330]884                    %     identify a single buffer. To read multiple buffers in one call, pass a vector of individual buffer IDs
[1973]885                    %
[4812]886                    % Requires BUFF_SEL: Yes  (Vector notation [RFA, RFB])
[1973]887                    % Arguments: none
888                    %
889                    % Returns: agc_state -- column vector per buffer BUFF_SEL
890                    %   agc_state(1): RF gain chosen by AGC
891                    %   agc_state(2): BB gain chosen by AGC
892                    %   agc_state(3): RSSI observed by AGC at time of lock
[4330]893                    %
[4687]894                    for ifcIndex = length(buffSel):-1:1
[4330]895                        currBuffSel = buffSel(ifcIndex);
[1915]896                       
[4330]897                        if(isSingleBuffer(currBuffSel) == 0)
[2865]898                            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA,RFB]',cmdStr);
[1915]899                        end
900
[4330]901                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_AGC_STATE));
[1915]902                        myCmd.addArgs(bbSel_to_rfSel(currBuffSel));
903                       
[4330]904                        resp  = node.sendCmd(myCmd);
905                        ret   = resp.getArgs();
906                        k     = 1;
907                        gains = uint16(bitand(ret(2*(k - 1) + 1), hex2dec('000000FF')));
908                       
909                        out(1, ifcIndex) = uint16(bitand(gains, hex2dec('03')));
910                        out(2, ifcIndex) = uint16(bitshift(gains, -2));
911                        out(3, ifcIndex) = uint16(ret(2*(k - 1) + 2));
[1915]912                    end
913                   
[4330]914                %---------------------------------------------------------
[1915]915                case 'agc_target'
[1973]916                    % Set the AGC target
917                    %
918                    % Requires BUFF_SEL: No. Values apply to all RF paths
[4784]919                    % Arguments: (int32 target) (Integer value in [-32, 31])
[4330]920                    %     target: target receive power (in dBm)
[4803]921                    %             default value: -13
[1973]922                    % Returns: none
923                    %
924                    % This command is the best way to tweak AGC behavior
925                    % to apply more or less gain. For example, a target of
[4784]926                    % -5dBm will apply more gain than a target of -10dBm,
[1973]927                    % so the waveform will be larger at the inputs of the I
928                    % and Q ADCs.
[4330]929                    %
930                    myCmd  = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_AGC_TARGET));
[4784]931                   
932                    % Check arguments
933                    if(length(varargin) ~= 1)
934                       error('%s:  Requires one argument:  agc target', cmdStr);
935                    end
936
[1915]937                    target = varargin{1};
938                   
[4687]939                    % Check arguments
940                    if(length(target) > 1)
941                       error('%s:  Requires scalar argument.  AGC target is per-node, not per-interface or per-buffer.', cmdStr);
[1915]942                    end
[4784]943                                       
944                    if ((target < -32) && (target > 31))
945                        error('%s: AGC target must be in [-32, 31].\n', cmdStr);
[1915]946                    end
[4186]947
[4784]948                    % Convert to UFix_6_0
949                    %   NOTE: this method of converting to two's compliment works because of the bounds checking above
950                    %
951                    target = uint32(mod(target, 2^6));
[4186]952                   
[1915]953                    myCmd.addArgs(target);
[4784]954                   
[1973]955                    node.sendCmd(myCmd);
[2919]956
[4330]957                %---------------------------------------------------------
[1915]958                case 'agc_dco'
[1973]959                    % Enable/disable DC offset correction
960                    %
961                    % Requires BUFF_SEL: No
962                    % Arguments: (boolean DCO)
[4330]963                    %     DCO:
964                    %         true enables DC offset correction
965                    %         false disable DC offset correction
[1973]966                    % Returns: none
[4330]967                    %
[4687]968
969                    % Check arguments                   
970                    if(length(varargin) ~= 1)
971                        error('%s: Requires one boolean argument',cmdStr);
972                    end
973                   
[1915]974                    switch(varargin{1})
975                        case true
[4330]976                            myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_AGC_DCO_EN_DIS), 1);
[1915]977                        case false
[4330]978                            myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_AGC_DCO_EN_DIS), 0);
[1915]979                    end
[4330]980                   
[1973]981                    node.sendCmd(myCmd);
[1915]982               
[4330]983                %---------------------------------------------------------
[4784]984                case 'agc_done_addr'
985                    % Sample index where AGC finished
[1973]986                    %
[4784]987                    % Value applies to all RF paths
[4330]988                    %
[4784]989                    % Requires BUFF_SEL: No.
990                    % Arguments:
991                    % Returns: (uint32) sample_index
992                    %
993                    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_AGC_DONE_ADDR));
994                    resp  = node.sendCmd(myCmd);
995                    ret   = resp.getArgs();
996                    out   = ret(1);
[1915]997                   
[4330]998                %---------------------------------------------------------
[1915]999                case 'agc_reset'
[1973]1000                    % Resets the AGC to its default state
1001                    %
1002                    % Requires BUFF_SEL: No. Values apply to all RF paths
1003                    % Arguments: none
1004                    % Returns: none
[4330]1005                    %
[1948]1006                    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_AGC_RESET));
[1973]1007                    node.sendCmd(myCmd);
[1915]1008                   
[4330]1009                %---------------------------------------------------------
[4806]1010                case 'agc_reset_per_rx'
1011                    % Get / Set whether the AGC will reset on per RX or hold gains across RX
[2853]1012                    %
1013                    % Arguments: 'true' or 'false'; none on read
1014                    % Returns:   none on write; 'true' or 'false'
[4330]1015                    %
[4806]1016                    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_AGC_RESET_MODE));
1017                   
1018                    if(isempty(varargin))                  % Read Mode
1019                        myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
1020                       
1021                    else                                   % Write Mode
1022                        % Check arguments
[4687]1023                        if(length(varargin) ~= 1)
[4806]1024                            error('%s: Requires one argument:  true/false', cmdStr);
[4687]1025                        end
1026                   
[4806]1027                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1028
1029                        if (boolean(varargin{1}))
1030                           reset_per_rx = uint32(hex2dec('00000001'));;
[2853]1031                        else
[4806]1032                           reset_per_rx = uint32(hex2dec('00000000'));
[2853]1033                        end
1034                       
[4806]1035                        myCmd.addArgs(reset_per_rx);
[2853]1036                    end
[4806]1037                   
1038                    resp = node.sendCmd(myCmd);
[2853]1039
[4806]1040                    % Process response from the node.  Return arguments:
1041                    %     [1] - Status
1042                    %     [2] - Value
[2853]1043                    %
[4806]1044                    for i = 1:numel(resp)                   % Needed for unicast node_group support
1045                        ret   = resp(i).getArgs();
[2853]1046
[4806]1047                        if (ret(1) == myCmd.CMD_PARAM_SUCCESS)
1048                            if (ret(2) == hex2dec('00000001')) 
1049                                out = true;
1050                            else
1051                                out = false;
1052                            end
1053                        else 
1054                            msg = sprintf('%s: AGC reset per rx error in node %d.\n', cmdStr, nodeInd);
1055                            error(msg);                           
[2853]1056                        end
[4806]1057                    end
[2853]1058
[4330]1059                %---------------------------------------------------------
[4784]1060                case 'agc_config'
1061                    % Set the configuration of the AGC
1062                    %
1063                    % This function will set the following AGC configuration fields:
1064                    %   - RSSI averaging length
1065                    %   - Voltage DB Adjust
1066                    %   - Initial BB Gain
1067                    %
1068                    % Requires BUFF_SEL: No
1069                    % Arguments: RSSI Averaging length (Integer value in [0, 3])
1070                    %            Voltage DB Adjust (Integer value in [0, 63])
1071                    %            Initial BB Gain (RX) (Integer value in [0, 31])
1072                    % Returns  : None
1073                    %
1074                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_AGC_CONFIG));
1075                   
1076                    if(isempty(varargin))                  % Read Mode
1077                        error('%s: Read mode not supported', cmdStr);
1078                       
1079                    else                                   % Write Mode
1080                        % Check arguments
1081                        if(length(varargin) ~= 3)
1082                            error('%s: Requires three arguments:  RSSI_Avg, VDB_Adj, Init_BB_Gain', cmdStr);
1083                        end
1084                   
1085                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1086
1087                        rssi_avg_length = varargin{1};
1088                        v_db_adjust     = varargin{2};
1089                        init_bb_gain    = varargin{3};
1090                       
1091                        % Check arguments
[4788]1092                        if ((rssi_avg_length < 0) || (rssi_avg_length > 3))
[4784]1093                            error('%s: RSSI Averaging length must be in [0, 3].\n', cmdStr);
1094                        end
1095
[4788]1096                        if ((v_db_adjust < 0) || (v_db_adjust > 63))
[4784]1097                            error('%s: Voltage DB Adjust must be in [0, 63].\n', cmdStr);
1098                        end
1099                       
[4788]1100                        if ((init_bb_gain < 0) || (init_bb_gain > 31))
[4784]1101                            error('%s: Initial BB gain must be in [0, 31].\n', cmdStr);
1102                        end
1103                       
1104                        % Send command to the node
1105                        myCmd.addArgs(rssi_avg_length);
1106                        myCmd.addArgs(v_db_adjust);
1107                        myCmd.addArgs(init_bb_gain);
1108                       
1109                        resp = node.sendCmd(myCmd);
1110                       
1111                        % Process response from the node.  Return arguments:
1112                        %     [1] - Status
1113                        %
1114                        for i = 1:numel(resp)                   % Needed for unicast node_group support
1115                            ret   = resp(i).getArgs();
1116
[4788]1117                            if (ret(1) == myCmd.CMD_PARAM_ERROR)
[4784]1118                                msg = sprintf('%s: AGC config error in node %d.\n', cmdStr, nodeInd);
1119                                error(msg);
1120                            end
1121                        end
1122                    end
1123                   
1124                %---------------------------------------------------------
1125                case 'agc_iir_hpf'
1126                    % Set the Infinite Impulse Response (IIR) High Pass Filter (HPF) coefficients
1127                    %
1128                    % This function will set the following IIR HPF coefficients:
1129                    %   - A1
1130                    %   - B0
1131                    %
1132                    % NOTE:  By default the reference design uses a filter with a 3 dB cutoff at
1133                    %     20 kHz with 40 MHz sampling.  This results in coefficients:
1134                    %         A1 = -0.996863331833438
1135                    %         B0 = 0.99843166591671906
1136                    %
1137                    % Requires BUFF_SEL: No
1138                    % Arguments: A1 coefficient (Value in [-1, 1]; range represented by Fix_18_17)
1139                    %            B0 coefficient (Value in [0, 2]; range represented by UFix_18_17)
1140                    % Returns  : None
1141                    %
1142                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_AGC_IIR_HPF));
1143                   
1144                    if(isempty(varargin))                  % Read Mode
1145                        error('%s: Read mode not supported', cmdStr);
1146                       
1147                    else                                   % Write Mode
1148                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1149                       
1150                        % Check arguments
1151                        if(length(varargin) ~= 2)
1152                            error('%s: Requires two arguments:  A1 coefficient, B0 coefficient', cmdStr);
1153                        end
1154                   
1155                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1156
1157                        a1_coeff = varargin{1};
1158                        b0_coeff = varargin{2};
1159                       
1160                        % Check arguments
[4788]1161                        if ((a1_coeff < -1) || (a1_coeff > 1))
[4784]1162                            error('%s: A1 coefficient must be in [-1, 1].\n', cmdStr);
1163                        end
1164
[4788]1165                        if ((b0_coeff < 0) || (b0_coeff > 2))
[4784]1166                            error('%s: B0 coefficient must be in [0, 2].\n', cmdStr);
1167                        end
1168
1169                        % Convert A1 to Fix_18_17
1170                        %   NOTE: this method of converting to two's compliment works because of the bounds checking above
1171                        %
1172                        a1_coeff = bitand(uint32(mod((a1_coeff * 2^17), 2^18)), hex2dec('0003FFFF'));
1173                       
1174                        % Convert B0 to UFix_18_17
1175                        b0_coeff = bitand(uint32(b0_coeff * 2^17), hex2dec('0003FFFF'));
1176
1177                        % Send command to the node
1178                        myCmd.addArgs(a1_coeff);
1179                        myCmd.addArgs(a1_coeff);
1180                       
1181                        resp = node.sendCmd(myCmd);
1182                       
1183                        % Process response from the node.  Return arguments:
1184                        %     [1] - Status
1185                        %
1186                        for i = 1:numel(resp)                   % Needed for unicast node_group support
1187                            ret   = resp(i).getArgs();
1188
[4788]1189                            if (ret(1) == myCmd.CMD_PARAM_ERROR)
[4784]1190                                msg = sprintf('%s: IIR HPF error in node %d.\n', cmdStr, nodeInd);
1191                                error(msg);
1192                            end
1193                        end
1194                    end
1195                   
1196                %---------------------------------------------------------
1197                case 'agc_rf_gain_threshold'
1198                    % Set the RF gain thresholds
1199                    %
1200                    % This function will set the following fields:
1201                    %   - 3 -> 2 RF gain threshold
1202                    %   - 2 -> 1 RF gain threshold
1203                    %
1204                    % After the AGC has converted RSSI to power (dBm), this will select the
1205                    % the thresholds used to set the RF (LNA) gain in the MAX2829. 
1206                    %
1207                    % Requires BUFF_SEL: No
1208                    % Arguments: 3 -> 2 RF gain threshold (Integer value in [-128, 127])
1209                    %            2 -> 1 RF gain threshold (Integer value in [-128, 127])
1210                    % Returns  : None
1211                    %
1212                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_RF_GAIN_THRESHOLD));
1213                   
1214                    if(isempty(varargin))                  % Read Mode
1215                        error('%s: Requires argument(s)', cmdStr);
1216                       
1217                    else                                   % Write Mode
1218                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1219                       
1220                        % Check arguments
1221                        if(length(varargin) ~= 2)
1222                            error('%s: Requires two arguments:  3 -> 2 threshold, 2 -> 1 threshold', cmdStr);
[4790]1223                        end                 
[4784]1224
1225                        threshold_3_2 = varargin{1};
1226                        threshold_2_1 = varargin{2};
1227                       
1228                        % Check arguments
[4788]1229                        if ((threshold_3_2 < -128) || (threshold_3_2 > 127))
[4784]1230                            error('%s: 3 -> 2 threshold must be in [-128, 127].\n', cmdStr);
1231                        end
1232
[4788]1233                        if ((threshold_2_1 < -128) || (threshold_2_1 > 127))
[4784]1234                            error('%s: 3 -> 2 threshold must be in [-128, 127].\n', cmdStr);
1235                        end
1236
1237                        % Convert to UFix_8_0
1238                        %   NOTE: this method of converting to two's compliment works because of the bounds checking above
1239                        %
[4791]1240                        threshold_3_2 = uint32(mod(threshold_3_2, 2^8));
1241                        threshold_2_1 = uint32(mod(threshold_2_1, 2^8));
[4784]1242                       
1243                        % Send command to the node
1244                        myCmd.addArgs(threshold_3_2);
1245                        myCmd.addArgs(threshold_2_1);
1246                       
1247                        resp = node.sendCmd(myCmd);
1248                       
1249                        % Process response from the node.  Return arguments:
1250                        %     [1] - Status
1251                        %
1252                        for i = 1:numel(resp)                   % Needed for unicast node_group support
1253                            ret   = resp(i).getArgs();
1254
[4788]1255                            if (ret(1) == myCmd.CMD_PARAM_ERROR)
[4784]1256                                msg = sprintf('%s: IIR HPF error in node %d.\n', cmdStr, nodeInd);
1257                                error(msg);
1258                            end
1259                        end
1260                    end
1261                   
1262                %---------------------------------------------------------
1263                case 'agc_timing'
1264                    % Set the AGC timing
1265                    %
1266                    % This function will set the following fields:
1267                    %   - Sample to take first RSSI capture
1268                    %   - Sample to take second RSSI capture
1269                    %   - Sample to take the Voltage DB capture
1270                    %   - Sample to complete the AGC
1271                    %
1272                    % Requires BUFF_SEL: No
1273                    % Arguments: Capture RSSI 1 (Integer value in [0, 255])
1274                    %            Capture RSSI 2 (Integer value in [0, 255])
1275                    %            Capture Voltage DB (Integer value in [0, 255])
1276                    %            AGC Done (Integer value in [0, 255])
1277                    % Returns  : None
1278                    %
[4792]1279                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_AGC_TIMING));
[4784]1280                   
1281                    if(isempty(varargin))                  % Read Mode
1282                        error('%s: Read mode not supported', cmdStr);
1283                       
1284                    else                                   % Write Mode
1285                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1286                       
1287                        % Check arguments
1288                        if(length(varargin) ~= 4)
1289                            error('%s: Requires four arguments:  capture rssi 1, capture rssi 2, capture voltage db, agc done', cmdStr);
[4792]1290                        end                   
[4784]1291
1292                        capture_rssi_1 = varargin{1};
1293                        capture_rssi_2 = varargin{2};
1294                        capture_v_db   = varargin{3};
1295                        agc_done       = varargin{4};
1296                       
1297                        % Check arguments
[4788]1298                        if ((capture_rssi_1 < 0) || (capture_rssi_1 > 255))
[4784]1299                            error('%s: Capture RSSI 1 must be in [0, 255].\n', cmdStr);
1300                        end
1301
[4788]1302                        if ((capture_rssi_2 < 0) || (capture_rssi_2 > 255))
[4784]1303                            error('%s: Capture RSSI 2 must be in [0, 255].\n', cmdStr);
1304                        end
1305
[4788]1306                        if ((capture_v_db < 0) || (capture_v_db > 255))
[4784]1307                            error('%s: Capture Voltage DB must be in [0, 255].\n', cmdStr);
1308                        end
1309
[4788]1310                        if ((agc_done < 0) || (agc_done > 255))
[4784]1311                            error('%s: AGC done must be in [0, 255].\n', cmdStr);
1312                        end
1313                       
1314                        % Send command to the node
1315                        myCmd.addArgs(capture_rssi_1);
1316                        myCmd.addArgs(capture_rssi_2);
1317                        myCmd.addArgs(capture_v_db);
1318                        myCmd.addArgs(agc_done);
1319                       
1320                        resp = node.sendCmd(myCmd);
1321                       
1322                        % Process response from the node.  Return arguments:
1323                        %     [1] - Status
1324                        %
1325                        for i = 1:numel(resp)                   % Needed for unicast node_group support
1326                            ret   = resp(i).getArgs();
1327
[4788]1328                            if (ret(1) == myCmd.CMD_PARAM_ERROR)
[4784]1329                                msg = sprintf('%s: AGC timing error in node %d.\n', cmdStr, nodeInd);
1330                                error(msg);
1331                            end
1332                        end
1333                    end
1334                   
1335                %---------------------------------------------------------
1336                case 'agc_dco_timing'
1337                    % Set the AGC DC Offset (DCO) timing
1338                    %
1339                    % This function will set the following fields:
1340                    %   - Sample to start the DCO
1341                    %   - Sample to start the IIR HPF
1342                    %
1343                    % Requires BUFF_SEL: No
1344                    % Arguments: Start DCO (Integer value in [0, 255])
1345                    %            Start IIR HPF (Integer value in [0, 255])
1346                    % Returns  : None
1347                    %
1348                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_AGC_DCO_TIMING));
1349                   
1350                    if(isempty(varargin))                  % Read Mode
1351                        error('%s: Read mode not supported', cmdStr);
1352                       
1353                    else                                   % Write Mode
1354                        myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1355                       
1356                        % Check arguments
1357                        if(length(varargin) ~= 2)
1358                            error('%s: Requires two arguments:  start DCO, start IIR HPF', cmdStr);
[4792]1359                        end                   
[4784]1360
1361                        start_dco     = varargin{1};
1362                        start_iir_hpf = varargin{2};
1363                       
1364                        % Check arguments
[4788]1365                        if ((start_dco < 0) || (start_dco > 255))
[4784]1366                            error('%s: Start DCO must be in [0, 255].\n', cmdStr);
1367                        end
1368
[4788]1369                        if ((threshold_2_1 < 0) || (threshold_2_1 > 255))
[4784]1370                            error('%s: Start IIR HPF must be in [0, 255].\n', cmdStr);
1371                        end
1372                       
1373                        if (start_dco < start_iir_hpf)
1374                            error('%s: Start DCO must be before start IIR HPF.\n', cmdStr);
1375                        end
1376                       
1377                        % Send command to the node
1378                        myCmd.addArgs(start_dco);
1379                        myCmd.addArgs(start_iir_hpf);
1380                       
1381                        resp = node.sendCmd(myCmd);
1382                       
1383                        % Process response from the node.  Return arguments:
1384                        %     [1] - Status
1385                        %
1386                        for i = 1:numel(resp)                   % Needed for unicast node_group support
1387                            ret   = resp(i).getArgs();
1388
[4788]1389                            if (ret(1) == myCmd.CMD_PARAM_ERROR)
[4784]1390                                msg = sprintf('%s: AGC DCO timing error in node %d.\n', cmdStr, nodeInd);
1391                                error(msg);
1392                            end
1393                        end
1394                    end
1395                   
1396                %---------------------------------------------------------
[1915]1397                otherwise
[2865]1398                    error('unknown command ''%s''',cmdStr);
[1915]1399            end
1400           
1401            if(iscell(out)==0 && numel(out)~=1)
1402                out = {out}; 
1403            end
1404         end
1405    end
1406end
1407
1408
1409function out = rfSel_to_bbSel(sel)
[4330]1410    out = bitshift(uint32(sel), -28);
[1915]1411end
1412
[4309]1413
[1915]1414function out = bbSel_to_rfSel(sel)
[4330]1415    out = bitshift(uint32(sel), 28);
[1915]1416end
1417
[4309]1418
[1915]1419function out = isSingleBuffer(sel)
[4330]1420    out = (length(strfind(dec2bin(sel), '1')) == 1);
[1915]1421end
1422
[4309]1423
[4416]1424function out = updateChecksum(newdata, varargin)
[1915]1425    persistent sum1;
1426    persistent sum2;
1427   
1428    if(isempty(sum1))
1429        sum1 = uint32(0);
1430    end
1431    if(isempty(sum2))
1432        sum2 = uint32(0);
1433    end
1434
[4416]1435    if(length(varargin) == 1)
1436       if(strcmpi(varargin{1}, 'reset'))
[1915]1437           sum1 = uint32(0);
1438           sum2 = uint32(0);
1439       end
1440    end
1441
[1979]1442    newdata = uint32(newdata);
[1915]1443
[4416]1444    sum1 = mod((sum1 + newdata), 65535);
1445    sum2 = mod(sum2 + sum1, 65535);
[1915]1446   
[4416]1447    out = bitshift(sum2, 16) + sum1;
[1915]1448end
1449
[4309]1450
1451
[1948]1452function writeIQ(obj, node, transport, buffSel, cmdStr, varargin)
[4330]1453% writeIQ Helper function for baseband object to write IQ samples to node
[1915]1454% IMPORTANT: user code should never call this function; always use the
1455%  'writeIQ' baesband command (which will call this function with the right arguments)
1456%
1457% Writing a full buffer of IQ samples requires many host-to-node packets
1458% This function uses the minimum number of packets possible, given the payload
1459% limitations of the node's transport object
1460%
1461% This write IQ implementation has two modes:
1462%  fast: node sends ACK only after first and last samples packet
1463%  slow: node sends ACK after every samples packet
1464%
1465% This implementation always attempts fast mode first. If the node fails to
1466%  receive any packet in fast mode, this funciton reverts to slow mode and
1467%  re-sends all samples.
1468% A failure in fast mode is detected using a simple checksum scheme. The node
1469%  returns its computed checksum over all received samples packets in its ACK
1470%  of the final packet. If the node's checksum does not match the one computed here,
1471%  this function reverts to slow mode and tries again. This function raises an
1472%  error if slow mode fails.
1473
[4416]1474    command       = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_WRITE_IQ));
[1915]1475
[2124]1476    samps         = varargin{1};
[4416]1477    num_samples   = size(samps, 1);
[1915]1478
[1974]1479    num_interface = length(buffSel);
[2865]1480   
[2124]1481    if( num_interface ~= size(samps,2) )
[4687]1482        if (min(size(samps)) > num_interface)
1483            error_str = sprintf('%s: Length of buffer selection vector smaller than the number of columns in the sample matrix.', cmdStr);
1484            error_str = strcat(error_str, '  If trying to write to multiple interfaces, use vector notation vs bitwise addition:  [RFA, RFB] vs RFA + RFB');
1485        else
1486            error_str = sprintf('%s: Length of buffer selection vector greater than the number of columns in the sample matrix.', cmdStr);
1487            error_str = strcat(error_str, '  Make sure that the sample matrix has one IQ vector per specified interface.');
1488        end
1489       
1490        error(error_str);
[1915]1491    end
1492   
[4393]1493    % Check if user provided a first sample index for any interface
[1915]1494    if(length(varargin)==2)
1495        offset = varargin{2};
1496        if(length(offset)==1)
[4393]1497            offset = offset(:,ones(1,num_interface));
[1915]1498        end
1499    else
[4393]1500        % No offsets specified; write to index 0 for all interfaces
1501        offset = zeros(1, num_interface);
[1915]1502    end
1503
[4393]1504    % If we have the WARPLab MEX transport, then call the transport function with the raw data
[4416]1505    if ( strcmp( class(transport), 'wl_transport_eth_udp_mex' ) )
[4334]1506   
[4416]1507        if ( num_samples > obj.MEX_TRANSPORT_MAX_IQ )
1508            msg0 = sprintf('%s: Requested %d samples.  Due to Matlab memory limitations, the mex transport only supports %d samples.', cmdStr, num_samples, obj.MEX_TRANSPORT_MAX_IQ);
[4334]1509            msg1 = sprintf('\n    If your computer has enough physical memory, you can adjust this limit using node.baseband.MEX_TRANSPORT_MAX_IQ.');
1510            msg2 = sprintf('\n\n');
1511            msg  = strcat(msg0, msg1, msg2);
1512            error(msg);
1513        end
[1915]1514
[4475]1515        % write_buffers(obj, func, num_samples, samples, buffer_ids, start_sample, hw_ver, wl_command, check_chksum, input_type)
1516        %     NOTE:  Currently the only input type supported is 'double' which has a value of 0
1517        %
[4416]1518        transport.write_buffers('IQ', num_samples, samps, buffSel, offset, node.hwVer, command, 1, 0);
[1915]1519
[4416]1520    elseif( strcmp( class(transport), 'wl_transport_eth_udp_mex_bcast') )
[4334]1521   
[4416]1522        if ( num_samples > obj.MEX_TRANSPORT_MAX_IQ )
1523            msg0 = sprintf('%s: Requested %d samples.  Due to Matlab memory limitations, the mex transport only supports %d samples.', cmdStr, num_samples, obj.MEX_TRANSPORT_MAX_IQ);
[4334]1524            msg1 = sprintf('\n    If your computer has enough physical memory, you can adjust this limit using node.baseband.MEX_TRANSPORT_MAX_IQ.');
1525            msg2 = sprintf('\n\n');
1526            msg  = strcat(msg0, msg1, msg2);
1527            error(msg);
1528        end
[2919]1529       
[4475]1530        % write_buffers(obj, func, num_samples, samples, buffer_ids, start_sample, hw_ver, wl_command, check_chksum, input_type)
1531        %     NOTE:  Currently the only input type supported is 'double' which has a value of 0
1532        %
[4416]1533        checksum = transport.write_buffers('IQ', num_samples, samps, buffSel, offset, node.hwVer, command, 0, 0);
[2919]1534
1535        % Call the node to verify the checksum from the WriteIQ
1536        node.verify_writeIQ_checksum(checksum);
[2124]1537    else
[4393]1538   
[4416]1539        if ( num_samples > obj.JAVA_TRANSPORT_MAX_IQ )
1540            msg0 = sprintf('%s: Requested %d samples.  Due to performance reasons, the java transport only supports %d samples.', cmdStr, num_samples, obj.JAVA_TRANSPORT_MAX_IQ);
[4334]1541            msg1 = sprintf('\n    Please use the MEX transport for larger requests.');
1542            msg2 = sprintf('\n\n');
1543            msg  = strcat(msg0, msg1, msg2);
1544            error(msg);
[4312]1545        end
[1915]1546
[4416]1547        % Determine if we need to check the Write IQ checksum
1548        if (strcmp(class(transport), 'wl_transport_eth_udp_java')) 
1549            check_chksum       = 1;
1550        else
1551            check_chksum       = 0;
1552        end
1553       
1554        % Set default value to warn when issuing a Write IQ and the node is not ready
1555        write_iq_ready_warn    = 1;
1556       
1557        % Define some constants (*_np variables do not have transport padding)
1558        TRANSPORT_PADDING_SIZE = 2;                                       % In bytes
1559        TRANSPORT_MAX_RETRY    = 2;                                       % In packet transmissions
1560        TRANSPORT_TIMEOUT      = 1;                                       % In seconds
1561        TRANSPORT_SEND_PKT_LEN = transport.getMaxPayload();               % In bytes
1562
1563        tport_hdr_size_np      = sizeof( transport.hdr );
1564        cmd_hdr_size_np        = tport_hdr_size_np + sizeof( wl_cmd );
1565        all_hdr_size_np        = cmd_hdr_size_np + sizeof( wl_samples );
1566       
[4393]1567        % Check the bounds on the data
1568        if(obj.check_write_iq_clipping)
[4416]1569            if(any(any((real(samps).^2) > 1)) || any(any((imag(samps).^2) > 1)))               
1570                warning('Sample vector contains values outside the range of [-1,+1]');
[4393]1571            end
1572        end
1573
1574        % Convert the user-supplied floating point I/Q values to Fix16_15
1575        samp_I_fi = int16(real(samps)*2^15);
1576        samp_Q_fi = int16(imag(samps)*2^15);
1577   
1578        % Combine the Fix16_15 I/Q values into one 32-bit word
[4416]1579        %     The typecast call preserves the bits of the Fix16_15 I/Q values, so they can be safely
1580        %     handled as unsigned 32-bit integers until re-interpreted by the node's C code
[2124]1581        samp_fi = uint32(zeros(size(samps)));
[4393]1582       
[4416]1583        for col = 1:size(samps, 2)
1584            samp_fi(:, col) = (2^16 .* uint32(typecast(samp_I_fi(:, col), 'uint16'))) + uint32(typecast(samp_Q_fi(:, col), 'uint16'));
[2124]1585        end
[1915]1586   
[4416]1587        % Create a wl_samples object to help serialize chunks of samples for each Ethernet packet
1588        samples = wl_samples();
[2069]1589
[4416]1590        % Compute the maximum number of samples in each Ethernet packet
1591        %     Starts with transport.maxPayload is the max number of bytes the node's transport can handle per packet (nominally the Ethernet MTU)
1592        %     Subtracts sizes of the transport header, command header and samples header
1593        %     Makes sure that it is 4 sample aligned (ie 16 byte aligned) for node DMA transfers
1594        %
1595        max_samples = double(bitand(((floor(double(TRANSPORT_SEND_PKT_LEN)/4) - sizeof(transport.hdr)/4 - sizeof(wl_cmd)/4) - (sizeof(wl_samples)/4)), 4294967292));
[2069]1596
[4416]1597        % Calculate the number of transport packets required to write all I/Q samples
1598        num_pkts = ceil(num_samples / max_samples);
[2069]1599       
[4416]1600        % User species sample offsets zero-indexed; adjust here for MATLAB indexing
1601        offset = offset + 1;
[1915]1602
[4416]1603        % Initialize the checksum
1604        currChecksum = zeros(1, num_interface);
1605
1606        % Initialize loop variables for transmission of all samples
[2290]1607        transmitted = 0;
1608        sendErrors  = 0;
[4416]1609       
[2290]1610        % Loop over the send command and use try/catch to make sure there are no issues in the send buffer       
1611        while (transmitted == 0)
1612            try
1613                for ifcIndex = 1:num_interface
[1915]1614
[4416]1615                    % Set values that do not change within each transmission
1616                    %
1617                    % Command header
1618                    %     NOTE:  Since there is one sample packet per command, we set the number number of command arguments to be 1.
1619                    command.numArgs         = 1;
1620
1621                    % Sample header
1622                    samples.buffSel         = buffSel(ifcIndex);
1623                    samples.sample_iq_id    = obj.sample_write_iq_id;
1624                   
1625                    % Increment write IQ ID
1626                    obj.sample_write_iq_id  = mod((obj.sample_write_iq_id + 1), 256);
1627
1628                   
1629                    % Initialize loop variables for a transmission of samples for 1 interface
1630                    startSampOffset       = offset(ifcIndex);
1631                    startSampOffsetMinus1 = offset(ifcIndex) - 1;    % Due to 0 vs 1 indexing, we use this often; so compute it once
1632                    done                  = 0;
1633                    pktIndex              = 1;
1634                    slow_write            = 0;                       % Try a fast write first (ACK only last packet)
1635                   
1636                    while(done == 0)
[2290]1637                       
[4416]1638                        % Determine the samples to transmit
1639                        if((startSampOffset + max_samples) <= num_samples)
1640                            stopSampOffset = startSampOffsetMinus1 + max_samples;
1641                            xfer_samples   = max_samples;
[2290]1642                        else
[4416]1643                            % Last packet may not require max payload size
1644                            stopSampOffset = (num_samples);
1645                            xfer_samples   = stopSampOffset - startSampOffset + 1;
[2290]1646                        end
[4416]1647 
1648                        % Determine the total length of the transmission
1649                        pkt_length = all_hdr_size_np + (xfer_samples * 4);                     % 4 bytes / sample
1650
1651                        % Request that the board responds:
1652                        %     - For slow_write == 1, all packets should be acked
1653                        %     - For the last packet, if we are checking the checksum, then it needs to be acked
1654                        if (slow_write == 1)
1655                            need_resp               = 1;
1656                            transport.hdr.flags     = bitset(transport.hdr.flags, 1, 1);      % We do need a response for the command
1657                        elseif ((pktIndex == num_pkts) && (check_chksum == 1))
1658                            need_resp               = 1;
1659                            transport.hdr.flags     = bitset(transport.hdr.flags, 1, 1);      % We do need a response for the command
1660                        else
1661                            need_resp               = 0;
1662                            transport.hdr.flags     = bitset(transport.hdr.flags, 1, 0);      % We do not need a response for the command
1663                        end
1664
1665                        % Construct the WARPLab transport header that will be used used to write the samples
[2290]1666                        %
[4416]1667                        transport.hdr.msgLength = pkt_length - tport_hdr_size_np;             % Length of the packet without the transport header
1668                        transport.hdr.seqNum    = (transport.hdr.seqNum + 1) & 255;
1669                        % transport.hdr.increment;
1670                               
1671                        % Construct the WARPLab command that will be used used to write the samples
1672                        %
1673                        command.len             = pkt_length - cmd_hdr_size_np;               % Length of the packet without the transport and command headers 
1674
1675                        % Construct the WARPLab sample structure that will be used to write the samples
1676                        samples.startSamp       = startSampOffsetMinus1;
1677                        samples.numSamp         = xfer_samples;
1678
1679                        if (pktIndex == 1)
1680                            % First packet
1681                            if (num_pkts > 1)
[4309]1682                                % This is the first packet of a multi-packet transfer
[4416]1683                                samples.flags   = samples.FLAG_CHKSUM_RESET;
[4309]1684                            else
1685                                % There is only one packet so mark both flags
[4416]1686                                samples.flags   = samples.FLAG_CHKSUM_RESET + samples.FLAG_LAST_WRITE;
[4309]1687                            end
[4416]1688                        elseif (pktIndex == num_pkts)
1689                            samples.flags       = samples.FLAG_LAST_WRITE;
[2290]1690                        else
[4416]1691                            samples.flags       = 0;
[2290]1692                        end
[4416]1693                           
1694                        % Construct the data array to send
1695                        tport_header            = transport.hdr.serialize;
1696                        cmd_header              = command.serialize;
1697                        sample_header           = samples.serialize;
1698                        data                    = [tport_header, cmd_header, sample_header, samp_fi(startSampOffset:stopSampOffset, ifcIndex).'];
1699                        data_swap               = swapbytes(uint32(data));
1700                        data8                   = [zeros(1,2,'uint8') typecast(data_swap, 'uint8')];
1701                                               
1702                        % Send the packet
1703                        node.transport.send_raw(data8, (pkt_length + TRANSPORT_PADDING_SIZE));
1704                       
1705                        % Updated checksum
1706                        %     NOTE:  Due to a weakness in the Fletcher 32 checksum (ie it cannot distinguish between
1707                        %         blocks of all 0 bits and blocks of all 1 bits), we need to add additional information
1708                        %         to the checksum so that we will not miss errors on packets that contain data of all
1709                        %         zero or all one.  Therefore, we add in the start sample for each packet since that
1710                        %         is readily available on the node.
1711                        %
1712                        newchkinput_32 = uint32(samp_fi(stopSampOffset, ifcIndex));
1713                        newchkinput_16 = bitxor(bitshift(newchkinput_32, -16), bitand(newchkinput_32, 65535));
1714                       
1715                        % Reset the checksum on the first packet
1716                        if(pktIndex == 1)
1717                            currChecksum(ifcIndex) = updateChecksum(bitand(startSampOffsetMinus1, 65535), 'reset');
1718                        else
1719                            currChecksum(ifcIndex) = updateChecksum(bitand(startSampOffsetMinus1, 65535));
1720                        end
1721                        currChecksum(ifcIndex) = updateChecksum(newchkinput_16);
[2124]1722
[4416]1723                       
1724                        % If we need a response, then wait for it
1725                        if (need_resp == 1)
1726                            num_retrys    = 1;
1727                            rcvd_response = 0;
1728                            curr_time     = tic;
1729
1730                            while (rcvd_response == 0)
1731                           
1732                                % Have we timed out
1733                                if ((toc(curr_time) > TRANSPORT_TIMEOUT) && (rcvd_response == 0))
1734                                    if(num_retrys == TRANSPORT_MAX_RETRY)
1735                                        error('Error:  Reached maximum number of retrys without a response... aborting.'); 
1736                                    end
1737                                   
1738                                    % Roll everything back and retransmit the packet
1739                                    num_retrys            = num_retrys + 1;
1740                                    stopSampOffset        = startSampOffsetMinus1;
1741                                    pktIndex              = pktIndex - 1;
1742                                    break;
1743                                end
1744
1745                                [recv_len, reply] = transport.receive_raw();
1746                               
1747                                % If we have a packet, then process the contents
1748                                if(recv_len > 0)
1749                                    reply = reply(((cmd_hdr_size_np / 4) + 1):end);       % Strip off transport and command headers
1750                               
1751                                    write_iq_response = process_write_iq_response(obj, reply, samples.sample_iq_id, currChecksum(ifcIndex), write_iq_ready_warn);
1752
1753                                    % Transmission failed between host and the node
1754                                    if (write_iq_response == obj.SAMPLE_IQ_CHECKSUM_FAILED)
1755                                        if (slow_write == 0)
[4706]1756                                            warning('%s: Checksum mismatch on fast write ... reverting to ''slow write''', cmdStr);
[4416]1757                                        else
1758                                            error('Error:  Checksums do not match when in slow write... aborting.');
1759                                        end
[2290]1760                                       
[4416]1761                                        % Start over with a slow write
1762                                        slow_write      = 1;
1763                                        stopSampOffset  = offset(ifcIndex) - 1;
1764                                        pktIndex        = 0;
1765                                        break;
1766                                    end
1767                                   
1768                                    % Node was not ready for the Write IQ
1769                                    if (write_iq_response == obj.SAMPLE_IQ_NOT_READY)
1770                                        write_iq_ready_warn = 0;
1771                                       
1772                                        % Start over; Maintain "fast write"
1773                                        stopSampOffset  = offset(ifcIndex) - 1;
1774                                        pktIndex        = 0;
1775                                        break;
1776                                    end
1777                   
1778                                    curr_time     = tic;
1779                                    rcvd_response = 1;
1780                                end
[2290]1781                            end
[4416]1782                        else
1783                            % For performance reasons, only check the socket once every 32 packets
1784                            if (mod(pktIndex, 32) == 0)
[4358]1785                           
[4416]1786                                % Check if the node has sent us a packet that we were not expecting
1787                                [recv_len, reply] = transport.receive_raw();
1788                               
1789                                % If we have a packet, then process the contents
1790                                if(recv_len > 0)
1791                                    reply = reply(((cmd_hdr_size_np / 4) + 1):end);       % Strip off transport and command headers
1792                                   
1793                                    write_iq_response = process_write_iq_response(obj, reply, samples.sample_iq_id, currChecksum(ifcIndex), write_iq_ready_warn);
[2290]1794
[4416]1795                                    % Node was not ready for the Write IQ
1796                                    if (write_iq_response == obj.SAMPLE_IQ_NOT_READY)
1797                                        write_iq_ready_warn = 0;
1798                                       
1799                                        % Start over; Maintain "fast write"
1800                                        stopSampOffset  = offset(ifcIndex) - 1;
1801                                        pktIndex        = 0;
1802                                    end
[2919]1803                                end
[2290]1804                            end
[4416]1805                           
1806                            % If this was the last packet and we did not need a response, then this must be a
1807                            % broadcast Write IQ.  Therefore, we need to use the node to verify the checksum
1808                            if (pktIndex == num_pkts)
1809                                node.verify_writeIQ_checksum(currChecksum(ifcIndex));
[2290]1810                            end
1811                        end
[4416]1812                       
1813                        if (pktIndex == num_pkts)
1814                            done = 1;
1815                        end
1816                       
1817                        % Update starting sample offset for next packet
1818                        startSampOffset       = stopSampOffset + 1;
1819                        startSampOffsetMinus1 = stopSampOffset;
1820                        pktIndex              = pktIndex + 1;
1821                       
[2290]1822                    end %end while !done sending packets
1823                end %end for ifcIndex
1824               
1825                % Exit out of the while loop
1826                transmitted = 1;
1827       
1828            catch sendError
1829                % If we have a socket exception, this could indicate that the send buffer was not large enough.
1830                % Therefore, we should use slow writes and see if that fixes the problem.
[4416]1831                if ~isempty(strfind(sendError.message, 'java.net.SocketException'))
1832                    slow_write  = 1;
[2290]1833                   
1834                    % If we have tried the slow write and still fail, then throw the error.
1835                    if ( sendErrors == 1 )
1836                        fprintf('%s.m--Failed to send writeIQ data after trying slow write.\n', mfilename);
1837                        throw( sendError );
[2124]1838                    end
[2290]1839                   
1840                    sendErrors = 1;
1841                else
1842                    throw( sendError );
[2124]1843                end
[2290]1844            end %end try
1845        end %end while( transmitted == 0 )     
[2124]1846    end %end if mex transport
[1915]1847end %end function writeIQ
1848
[2919]1849
1850
[4416]1851function out = process_write_iq_response(obj, args, sample_iq_id, checksum, iq_ready_warn)
1852% process_write_iq_response
1853%   Helper function to parse write IQ responses
1854%
1855
1856    % Initialize the response
1857    out = obj.SAMPLE_IQ_SUCCESS;
1858
1859    % Get the IQ ID from the response
1860    node_sample_iq_id = args(2);
1861   
1862    % Only process packets for the current sample_iq_id
1863    if (node_sample_iq_id == sample_iq_id)
1864        node_status = args(1);
1865       
1866        switch(node_status)
1867            case obj.SAMPLE_IQ_ERROR
1868                fprintf('SAMPLE_IQ_ERROR:\n');
1869                fprintf('    Due to limitations on the node, it is not possible to do a Write IQ while the\n');
1870                fprintf('    node is transmitting in ''Continuous Tx'' mode.  Please stop the current transmission\n');
1871                fprintf('    and try the Write IQ again\n');
1872           
1873                error('ERROR:  Node returned ''SAMPLE_IQ_ERROR''.  See above for debug information.');
1874           
1875            case obj.SAMPLE_IQ_NOT_READY
1876                % If the node is not ready, then we need to wait until the node is ready and try again from the
1877                % beginning of the Write IQ.
1878                %
1879                wait_time = compute_sample_wait_time(args(4:end));
1880           
1881                % Wait until the samples should be done
1882                if ( wait_time ~= 0 )
1883                    pause( wait_time + 0.001 );
1884                end
1885               
1886                % Print warning
1887                if (iq_ready_warn == 1)
1888                    fprintf('WARNING:  Node was not ready to process Write IQ request.  Waiting to request again.\n');
[4706]1889                    fprintf('    This warning can be removed by waiting until the node is not busy with a TX or RX\n');
1890                    fprintf('    operation.  To do this, please add ''pause(1.5 * NUM_SAMPLES * 1/(40e6));'' after\n');
1891                    fprintf('    any triggers and before the Write IQ request.\n\n');
[4416]1892                end
1893               
1894                out = obj.SAMPLE_IQ_NOT_READY;
1895
1896            case obj.SAMPLE_IQ_SUCCESS
1897                % If the response was a success, then check the checksum
1898                %
1899                node_checksum  = args(3);
1900
1901                % Compare the checksum values
1902                if ( node_checksum ~= checksum )
1903               
1904                    fprintf('Checksum mismatch:  0x%08x != 0x%08x\n', node_checksum, checksum);
1905               
1906                    % Reset the loop variables
1907                    out = obj.SAMPLE_IQ_CHECKSUM_FAILED;
1908                end
1909           
1910            otherwise
1911                error('ERROR:  Unknown write IQ response status = %d\n', node_status);
1912        end
1913    end
1914end
1915
1916
1917
1918function out = compute_sample_wait_time(args)
1919% compute_sample_wait_time
1920%   Function to compute the wait time based on the args:
1921%      args[1]       - Tx status
1922%      args[2]       - Current Tx read pointer
1923%      args[3]       - Tx length
1924%      args[4]       - Rx status
1925%      args[5]       - Current Rx write pointer
1926%      args[6]       - Rx length
1927%
1928
1929    node_tx_status    = args(1);
1930    node_tx_pointer   = args(2);
1931    node_tx_length    = args(3);
1932    node_rx_status    = args(4);
1933    node_rx_pointer   = args(5);
1934    node_rx_length    = args(6);
1935   
1936    % NOTE:  node_*_length and node_*_pointer are in bytes.  To convert the difference to microseconds,
1937    %    we need to divide by: 160e6  (ie 40e6 sample / sec * 4 bytes / sample => 160e6 bytes / sec)
1938    %
1939    if (node_tx_status == 1)
1940        tx_wait_time = ((node_tx_length - node_tx_pointer) / 160e6);
1941    else
1942        tx_wait_time = 0;
1943    end
1944   
1945    if (node_rx_status == 1)
1946        rx_wait_time = ((node_rx_length - node_rx_pointer) / 160e6);
1947    else
1948        rx_wait_time = 0;
1949    end
1950
1951    if (tx_wait_time > rx_wait_time)
1952        out = tx_wait_time;
1953    else
1954        out = rx_wait_time;
1955    end
1956end
1957
1958
1959
[1948]1960function out = readIQ(obj, node, buffSel, cmdStr, varargin)
[4330]1961% readIQ Helper function for baseband object to read IQ samples from node
[1915]1962% IMPORTANT: user code should never call this function; always use the
1963%  'readIQ' baesband command (which will call this function with proper arguments)
1964%
1965% Reading a full buffer of IQ samples requires many node-to-host packets
1966% This function uses the minimum number of packets possible, given the payload
1967% limitations of the node's transport object.
1968
[1948]1969    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_READ_IQ));
[1915]1970
1971    if(isempty(varargin))
[4330]1972        % User didn't specify a starting sample or num samples default to reading all samples (0:rxIQLen-1)
[4334]1973        offset   = 0;
1974        numSamps = obj.rxIQLen;
[4330]1975    elseif(length(varargin) == 2)
[4334]1976        offset   = varargin{1};
1977        numSamps = varargin{2};
[1915]1978    else
[2865]1979        error('%s: invalid arguments... user must provide an offset and a length',cmdStr);
[1915]1980    end
[4351]1981       
[2124]1982    % If we have the WARPLab MEX transport, then call the special function   
[4815]1983    if (strcmp(class(node.transport), 'wl_transport_eth_udp_mex'))
[4334]1984        if ( numSamps > obj.MEX_TRANSPORT_MAX_IQ )
1985            msg0 = sprintf('%s: Requested %d samples.  Due to Matlab memory limitations, the mex transport only supports %d samples.', cmdStr, numSamps, obj.MEX_TRANSPORT_MAX_IQ);
1986            msg1 = sprintf('\n    If your computer has enough physical memory, you can adjust this limit using node.baseband.MEX_TRANSPORT_MAX_IQ.');
1987            msg2 = sprintf('\n\n');
1988            msg  = strcat(msg0, msg1, msg2);
1989            error(msg);
1990        end
[1915]1991
[4475]1992        % read_buffers(obj, func, num_samples, buffer_ids, start_sample, wl_command, input_type)
1993        %     NOTE:  Currently the only input type supported is 'double' which has a value of 0
1994        %
[4815]1995        rxSamples_IQ = node.transport.read_buffers('IQ', numSamps, buffSel, offset, obj.seq_num_tracker, obj.seq_num_match_severity, node.repr(), myCmd, 0);
[1915]1996
[2124]1997    else
[4815]1998        if (numSamps > obj.JAVA_TRANSPORT_MAX_IQ)
[4334]1999            msg0 = sprintf('%s: Requested %d samples.  Due to performance reasons, the java transport only supports %d samples.', cmdStr, numSamps, obj.JAVA_TRANSPORT_MAX_IQ);
2000            msg1 = sprintf('\n    Please use the MEX transport for larger requests.');
2001            msg2 = sprintf('\n\n');
2002            msg  = strcat(msg0, msg1, msg2);
2003            error(msg);
[4312]2004        end
[4815]2005       
2006        num_interface = length(buffSel);
[2124]2007   
[4351]2008        % Use the readbuffer helper to handle network I/O
2009        %     The helper avoids repeating code for reading I/Q and RSSI
[4334]2010        rxSamples = read_baseband_buffer(obj, node, buffSel, myCmd, numSamps, offset, cmdStr);
[2124]2011
[4334]2012        rxSamples_IQ = double(zeros(numSamps, num_interface));
[4351]2013       
[2124]2014        for ifcIndex = 1:num_interface
[4186]2015            % Unpack the WARPLab sample
2016            %   NOTE:  This performs a conversion from an UFix_16_0 to a Fix_16_15
2017            %      Process:
2018            %          1) Treat the 16 bit unsigned value as a 16 bit two's compliment signed value
2019            %          2) Divide by range / 2 to move the decimal point so resulting value is between +/- 1
2020            %          3) Cast as complex doubles
2021            %   NOTE:  This verbose implementation avoids using the fixed-point toolbox
[4334]2022            rxSamples_I = uint16(bitand(bitshift(rxSamples(:, ifcIndex), -16), 65535));       % 16 MSB (Right shift by 16 bits; Mask by 0xFFFF)
[4309]2023            rxSamples_I = double(typecast(rxSamples_I, 'int16'))./2^15;                       % Cast as 'int16'; Divide by 0x8000
[2124]2024
[4334]2025            rxSamples_Q = uint16(bitand(rxSamples(:, ifcIndex), 65535));                      % 16 LSB (Mask by 0xFFFF)
[4309]2026            rxSamples_Q = double(typecast(rxSamples_Q, 'int16'))./2^15;                       % Cast as 'int16'; Divide by 0x8000
2027           
[2124]2028            rxSamples_IQ(:,ifcIndex) = complex(rxSamples_I, rxSamples_Q);
2029        end
[1915]2030    end
2031
2032    out = rxSamples_IQ;
2033end
2034
[4309]2035
2036
[1948]2037function out = readRSSI(obj, node, buffSel, cmdStr, varargin)
[1915]2038%readIQ Helper function for baseband object to read IQ samples from node
2039% IMPORTANT: user code should never call this function; always use the
2040%  'readRSSI' baseband command (which will call this function with proper arguments)
2041%
2042% Reading a full buffer of RSSI samples requires many node-to-host packets
2043% This function uses the minimum number of packets possible, given the payload
2044% limitations of the node's transport object.
2045
[1948]2046    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_READ_RSSI));
[1974]2047    num_interface = length(buffSel);
[1915]2048
2049    if(isempty(varargin))
[4334]2050        offset   = 0;
2051        numSamps = obj.rxRSSILen;
[1915]2052    elseif(length(varargin)==2)
[4334]2053        offset   = varargin{1};
2054        numSamps = varargin{2};
[1915]2055    else
[2865]2056        error('%s: invalid arguments... user must provide an offset and a length',cmdStr);
[1915]2057    end
2058   
[4309]2059    %RSSI is a unique buffer in that it stores pairs of RSSI samples in a single 32-bit word.
2060    % As such, from the board's perspective, the number of samples that we request is actually
2061    % half what the end-user really specifies.
[2124]2062   
2063    % If we have the WARPLab MEX transport, then call the special function   
2064    if ( strcmp( class(node.transport), 'wl_transport_eth_udp_mex' ) )
[4311]2065        % The underlying MEX function does not like odd number of samples;  This will potentially request one more sample
2066        %     than necessary.  That sample will be discarded when the vector is returned.
[4334]2067        samples_to_req = (numSamps/2) + mod((numSamps/2), 2);
[1915]2068
[4475]2069        % read_buffers(obj, func, num_samples, buffer_ids, start_sample, wl_command, input_type)
2070        %     NOTE:  Currently the only input type supported is 'double' which has a value of 0
2071        %
[4815]2072        rxSamples_RSSI = node.transport.read_buffers('RSSI', samples_to_req, buffSel, floor(double(offset)/2), obj.seq_num_tracker, obj.seq_num_match_severity, node.repr(), myCmd, 0);
[2124]2073
2074    else
2075
[4334]2076        rxSamples = read_baseband_buffer(obj, node, buffSel, myCmd, ceil(double(numSamps)./2), floor(double(offset)/2), cmdStr);
[2124]2077
2078        for ifcIndex = num_interface:-1:1
[4334]2079            rssi = [mod(bitshift(rxSamples(:, ifcIndex), -16), 1024), mod(rxSamples(:, ifcIndex), 1024)];
[2124]2080            rssi = rssi.';
2081            rxSamples_RSSI(:,ifcIndex) = rssi(:);
2082        end
[1915]2083    end
2084
[4309]2085    % Return the appropriate samples depending on the offset
[1915]2086    if(mod(offset,2)==0)
[4334]2087        out = rxSamples_RSSI(1:numSamps, :);
[1915]2088    else
[4334]2089        out = rxSamples_RSSI(2:(numSamps + 1), :);
[1915]2090    end
2091end
2092
[4309]2093
2094
[4416]2095function rx_samples = read_baseband_buffer(obj, node, buffSel, myCmd, num_samples, start_sample, cmdStr)
[4330]2096% read_baseband_buffer  Helper function to read a buffer from the node's baseband
[1915]2097% IMPORTANT: user code should never call this function; always use the 'readRSSI' or
[4416]2098%  'readIQ' baseband commands (which will call properly call this function)
[1915]2099%
2100% This function implements the process for reading large numbers of samples from a
[4416]2101%  baseband buffer, a transfer which requires multiple transport packets
[1915]2102%
[4416]2103% This function retrieves samples in order, starting at the user-specified offset and
[1915]2104%  requesting the largest contiguous block of not-yet-retrieved samples. Each reqest
2105%  may generate many node-to-host packets. In case a packet is lost, this function
2106%  will re-request only the missing samples. This function only returns successfully
2107%  if all requested samples are received from the node.
2108
[4416]2109    transport              = node.transport;               % Get the transport that we will use to send/receive
2110    curr_samples           = wl_samples();                 % Create a wl_samples object to deserialize the received samples packets
2111    num_interface          = length(buffSel);              % Determine the number of interfaces to transfer
2112    USEFULBUFFERSIZE       = .8;                           % Assume (1 - USEFULBUFFERSIZE) is used for Ethernet packet overhead;
2113    TIMEOUT                = 100;                          % Timeout time (in seconds)
2114    TRANSPORT_SEND_PKT_LEN = transport.getMaxPayload();    % In bytes
2115    not_ready_warn         = true;
[4309]2116   
[4416]2117    % Compute the maximum number of samples in each Ethernet packet
2118    %     Starts with transport.maxPayload is the max number of bytes the node's transport can handle per packet (nominally the Ethernet MTU)
2119    %     Subtracts sizes of the transport header, command header and samples header
2120    %     Makes sure that it is 4 sample aligned (ie 16 byte aligned) for node DMA transfers
2121    %
2122    max_samples = double(bitand(((floor(double(TRANSPORT_SEND_PKT_LEN)/4) - sizeof(transport.hdr)/4 - sizeof(wl_cmd)/4) - (sizeof(wl_samples)/4)), 4294967292));
2123    max_sample_length  = max_samples * 4;
[4315]2124
[4309]2125    % Pre-allocate an array to hold retrieved samples
[4416]2126    %     NOTE:  Since Matlab passes function arguments by value, we need to use the wl_samples object as a
2127    %            container for the samples to pass to read_samples().  However, for performance reasons, we
2128    %            are directly setting the internal properties vs using methods.  This could be remedied in
2129    %            the future by creating 'raw' methods that will not modify the data or do so in a "smart" way.
2130    %
2131    curr_samples.samps = uint32(zeros(num_samples, num_interface, 'double'));
[4309]2132   
[4416]2133    % Get the buffer size
2134    buffer_size        = bitand(uint32(USEFULBUFFERSIZE * transport.rxBufferSize), uint32(4294967280));       % Mask with 0xFFFF_FFF0 so requests are 16 byte aligned
[4309]2135   
2136    % Get the samples for each interface
[1974]2137    for ifcIndex = 1:num_interface
[1915]2138
[4416]2139        buffer_id = buffSel(ifcIndex);
[4309]2140
[4416]2141        if(isSingleBuffer(buffer_id) == 0)
[4309]2142            error('%s: buffer selection must be singular. Use vector notation for reading from multiple buffers e.g. [RFA,RFB]', cmdStr);
[1915]2143        end
2144
[4309]2145        % Initialize loop variables for timeout
2146        startTime   = tic;
2147        numloops    = 0;
2148        total_loops = 1;
[4416]2149        done        = 0;
[4815]2150        seq_num     = 0;
[4416]2151       
2152        start_sample_this_req = start_sample;
2153        rcvd_samples          = 0;
[4309]2154
[4416]2155        while (done == 0)
2156            % Each iteration of this loop retrieves the first contiguous block of samples
2157            %     that has not yet been received from the node
[4309]2158            numloops = numloops + 1;
[1915]2159
[4416]2160            % Due to limitations with the receive buffer, we need to chunk the read calls to
2161            % the node so that we do not overflow the receive buffer and lose packets
2162            %
2163            num_samples_this_req = num_samples - rcvd_samples;
[1915]2164           
[4416]2165            if((num_samples_this_req * 4) > (buffer_size))
2166                % If the number of bytes we need from the board exceeds the
2167                %     receive buffer size of our transport, we are going to drop
2168                %     packets. We should reduce our request to minimize dropping.
2169                num_samples_this_req = floor(buffer_size / 4);
2170                total_loops          = total_loops + 1;
[1961]2171            end
[1915]2172
[4416]2173            % Calculate how many transport packets are required for this request
2174            num_pkts = ceil(double(num_samples_this_req)/double(max_samples));
[1915]2175
[4416]2176            % fprintf('Useful buffer size = %10d (of %10d) for %10d pkt request\n', buffer_size, transport.rxBufferSize, num_pkts );
2177            % fprintf('  Num samples      = 0x%08x     Useful buffer samples = 0x%08x\n', num_samples, num_samples_this_req);
2178            % fprintf('  Offset           = 0x%08x     Payload samples       = 0x%08x\n', start_sample_this_req, max_sample_length);
[4309]2179           
[4416]2180            % Construct and send the argument to the node
2181            myCmd.setArgs(buffer_id, start_sample_this_req, num_samples_this_req, max_sample_length, num_pkts);
2182           
[4309]2183            if (numloops == 1)
[4416]2184                node.sendCmd_noresp(myCmd);                            % Normal send
[4309]2185            else 
2186                node.transport.send(myCmd.serialize(), false, false);  % Do not increment the header for subsequent loops
2187            end
[1915]2188           
[4309]2189            % Wait for the node to send all requested samples
[4815]2190            seq_num = read_samples(obj, node, myCmd, curr_samples, ifcIndex, start_sample, num_samples_this_req, start_sample_this_req, buffer_id, num_pkts, max_samples);
[4309]2191
[4416]2192            % Update loop variables
2193            start_sample_this_req = start_sample_this_req + num_samples_this_req;
2194            rcvd_samples          = rcvd_samples + num_samples_this_req;
2195           
2196            % Determine if we are done
2197            if (rcvd_samples >= num_samples)
2198                done = 1;
2199            end
2200           
2201            % Fail-safe timeout, in case indexing is broken (in m or C), to keep read_baseband_buffers from running forever
2202            if(toc(startTime) > TIMEOUT)
2203                  error('read_baseband_buffers took too long to retrieve samples; check for indexing errors in C and M code');
2204            end
2205        end %end while
[4815]2206
2207        % Check the sequence number
2208        check_seq_num(obj, node, cmdStr, buffer_id, seq_num);
[4416]2209       
[4815]2210        % Update the sequence number
2211        update_seq_num(obj, cmdStr, buffer_id, seq_num);
2212       
[4416]2213        if(numloops > total_loops)
2214            warning('%s: Dropped frames on fast read... took %d iterations', cmdStr, numloops); 
2215        end
2216    end% end for all interfaces
2217   
2218    rx_samples = curr_samples.samps;
2219end% end function read_baseband_buffer
[4309]2220
[4416]2221
2222
[4815]2223function out = update_seq_num(obj, cmd_str, buffer_id, seq_num)
2224% Update the RX counters for the given buffer ID
[4416]2225
[4815]2226    if (strcmp(cmd_str, 'read_iq'))
2227        if (buffer_id == obj.BB_SEL_RFA) obj.seq_num_tracker(1)  = seq_num; end
2228        if (buffer_id == obj.BB_SEL_RFB) obj.seq_num_tracker(3)  = seq_num; end
2229        if (buffer_id == obj.BB_SEL_RFC) obj.seq_num_tracker(5)  = seq_num; end
2230        if (buffer_id == obj.BB_SEL_RFD) obj.seq_num_tracker(7)  = seq_num; end
2231    end
2232   
2233    if (strcmp(cmd_str, 'read_rssi'))
2234        if (buffer_id == obj.BB_SEL_RFA) obj.seq_num_tracker(2)  = seq_num; end
2235        if (buffer_id == obj.BB_SEL_RFB) obj.seq_num_tracker(4)  = seq_num; end
2236        if (buffer_id == obj.BB_SEL_RFC) obj.seq_num_tracker(6)  = seq_num; end
2237        if (buffer_id == obj.BB_SEL_RFD) obj.seq_num_tracker(8)  = seq_num; end
2238    end
2239   
2240    % fprintf('Seq Num: %5d %5d %5d %5d %5d %5d %5d %5d\n', obj.seq_num_tracker(1), obj.seq_num_tracker(2), obj.seq_num_tracker(3), obj.seq_num_tracker(4), ...
2241    %                                                       obj.seq_num_tracker(5), obj.seq_num_tracker(6), obj.seq_num_tracker(7), obj.seq_num_tracker(8));
2242end
2243
2244
2245
2246function check_seq_num(obj, node, cmd_str, buffer_id, seq_num)
2247% Check the sequence number and issue the appropriate response based on the severity
2248
2249    seq_num_matches = false;
2250
2251    if (strcmp(cmd_str, 'read_iq'))
2252        if ((buffer_id == obj.BB_SEL_RFA) && (obj.seq_num_tracker(1) == seq_num)) seq_num_matches = true; end 
2253        if ((buffer_id == obj.BB_SEL_RFB) && (obj.seq_num_tracker(3) == seq_num)) seq_num_matches = true; end 
2254        if ((buffer_id == obj.BB_SEL_RFC) && (obj.seq_num_tracker(5) == seq_num)) seq_num_matches = true; end 
2255        if ((buffer_id == obj.BB_SEL_RFD) && (obj.seq_num_tracker(7) == seq_num)) seq_num_matches = true; end 
2256    end 
2257
2258    if (strcmp(cmd_str, 'read_rssi'))
2259        if ((buffer_id == obj.BB_SEL_RFA) && (obj.seq_num_tracker(2) == seq_num)) seq_num_matches = true; end 
2260        if ((buffer_id == obj.BB_SEL_RFB) && (obj.seq_num_tracker(4) == seq_num)) seq_num_matches = true; end 
2261        if ((buffer_id == obj.BB_SEL_RFC) && (obj.seq_num_tracker(6) == seq_num)) seq_num_matches = true; end 
2262        if ((buffer_id == obj.BB_SEL_RFD) && (obj.seq_num_tracker(8) == seq_num)) seq_num_matches = true; end 
2263    end
2264   
2265    % fprintf('%10s:  Buffer %d:  Seq Num = %d matches %d\n', cmd_str, buffer_id, seq_num, seq_num_matches);
2266   
2267    % If the current sequence number matches the recorded sequence number, this means
2268    % that the buffer has already been read and the appropriate message should be sent
2269    if (seq_num_matches)
2270        switch(obj.seq_num_match_severity)
2271            case obj.SEQ_NUM_MATCH_IGNORE
2272                % Do nothing
2273            case obj.SEQ_NUM_MATCH_WARNING
2274                % Issue a warning
2275                warning('%s Detected multiple reads of same %s waveform.  If this is unintentional, ensure Rx node triggers are configured correctly.', node.repr(), cmd_str);
2276            case obj.SEQ_NUM_MATCH_ERROR
2277                % Issue an error
2278                error('ERROR:  %s Detected multiple reads of same %s waveform.', node.repr(), cmd_str);
2279            otherwise
2280                error('ERROR:  %s Unknown sequence number error severity = %s', node.repr(), obj.seq_num_match_severity);
2281        end
2282    end
2283end
2284
2285
2286
2287function seq_num = read_samples(obj, node, command, samples, interface, initial_offset, num_samples, start_sample, buffer_id, num_pkts, max_samples)
[4416]2288% read_samples
[4815]2289%   Read the given number of samples from the node
[4416]2290%
2291
2292    sample_start_tracker = zeros(1, num_pkts);
2293    sample_num_tracker   = zeros(1, num_pkts);
2294   
2295    max_retries          = 2;                               % FIXME - Need to centralize
2296    max_iq_retries       = 10;
2297   
2298    iq_busy_warn         = 1;
2299    curr_time            = tic;
2300    num_retries          = 0;
2301    num_iq_retries       = 0;     
2302    rcvd_pkts            = 1;
2303    done                 = 0;
[4815]2304   
2305    seq_num              = 0;
[4416]2306
2307    while (done == 0)
2308
2309        if (toc(curr_time) > obj.readTimeout)
2310       
2311            if (num_retries >= max_retries)
2312                fprintf('ERROR:  Exceeded %d retrys for current Read IQ / Read RSSI request \n', max_retries);
2313                fprintf('    Requested %d samples from buffer %d starting from sample number %d \n', num_samples, buffer_id, start_sample);
2314                fprintf('    Received %d out of %d packets from node before timeout.\n', rcvd_pkts, num_pkts);
2315                fprintf('    Please check the node and look at the ethernet traffic to isolate the issue. \n');
[4309]2316           
[4416]2317                error('Error:  Reached maximum number of retrys without a response... aborting.');
2318            else
2319                warning('Read IQ / Read RSSI request timed out.  Re-requesting samples.\n');
[4309]2320               
[4416]2321                % Find the first packet error and request the remaining samples
2322                %   - TBD - For now just request all the packets again
2323                %         - See MEX C code for template on how to do this
[4309]2324               
[4416]2325                % Send command
2326                node.transport.send(command.serialize(), false, false);  % Do not increment the header for subsequent loops
2327               
2328                num_retries = num_retries + 1;
2329                curr_time   = tic;
2330            end
2331        end
[4309]2332
[4416]2333        % Receive packet       
2334        resp = node.receiveResp();
[4309]2335
[4416]2336        % Process the packet       
2337        if(~isempty(resp))
2338       
2339            % Get the packet data
2340            args = resp.getArgs;
2341           
2342            % Deserialize the sample header
2343            %     NOTE:  For performance reasons, we are only grabbing values we need directly out of the
2344            %            packet data.  Any adjustments to wl_samples will need to be reflected here.
2345            %
2346            sample_flags = bitshift(bitand(args(1), 65280), -8);
[1952]2347
[4416]2348            % Check the sample header
2349            if ((sample_flags & samples.FLAG_IQ_ERROR) == samples.FLAG_IQ_ERROR )
2350                error('ERROR:  Node returned ''SAMPLE_IQ_ERROR''.  Check that node is not currently transmitting in continuous TX mode.');
2351           
2352            elseif ((sample_flags & samples.FLAG_IQ_NOT_READY) == samples.FLAG_IQ_NOT_READY )
2353           
2354                if (iq_busy_warn == 1)
[4706]2355                    fprintf('WARNING:  Node was not ready to process Read IQ request.  Waiting to request again.\n');
2356                    fprintf('    This warning can be removed by waiting until the node is not busy with a TX or RX\n');
2357                    fprintf('    operation.  To do this, please add ''pause(1.5 * NUM_SAMPLES * 1/(40e6));'' after\n');
2358                    fprintf('    any triggers and before the Read IQ request.\n\n');
[4416]2359                    iq_busy_warn = 0;
2360                end
2361               
2362                % If the node is not ready, then we need to wait until the node is ready and try again from the
2363                % beginning of the Write IQ.
2364                %
2365                wait_time = compute_sample_wait_time(args(4:end));
2366           
2367                % Wait until the samples should be done
2368                if ( wait_time ~= 0 )
2369                    pause( wait_time + 0.001 );
2370                end
2371               
2372                num_iq_retries = num_iq_retries + 1;
[1952]2373
[4416]2374                % Start over at the beginning
2375                node.transport.send(command.serialize(), false, false);  % Do not increment the header for subsequent loops
2376                rcvd_pkts = 1;
2377               
2378                % Check that we have not spent a "long time" waiting for samples to be ready
2379                if (num_iq_retries > max_iq_retries)
2380                    error('ERROR:  Timeout waiting for node to return samples.  Please check the node operation.');
2381                end
2382               
2383            else
[4815]2384                % Normal IQ data               
[4701]2385                sample_num  = args(2) - initial_offset;
[4416]2386                sample_size = args(3);
[4815]2387               
[4416]2388                % If we are tracking packets, record which samples have been received
[4701]2389                sample_start_tracker(rcvd_pkts) = args(2);
[4416]2390                sample_num_tracker(rcvd_pkts)   = sample_size;
[4309]2391
[4416]2392                % Fill in the output arrays - output arrays are (num_requested_samples x num_interfaces)
2393                start_index = sample_num + 1;
2394                end_index   = start_index + sample_size - 1;
2395
2396                % fprintf('  Start index      = 0x%08x     End index             = 0x%08x\n', start_index, end_index);
2397               
2398                samples.samps((start_index:end_index), interface) = args(4:end);
2399               
2400                % Update loop variables
2401                rcvd_pkts     = rcvd_pkts + 1;
2402                num_iq_retrys = 0;
2403           
2404                % Exit the loop when we have enough packets
2405                if (rcvd_pkts > num_pkts) 
2406                   
2407                    % Check for errors
2408                    if (read_iq_sample_error(sample_num_tracker, sample_start_tracker, num_samples, start_sample, num_pkts, max_samples) == 1)
2409                   
2410                        if (num_retries >= max_retries)
2411                            fprintf('ERROR:  Exceeded %d retrys for current Read IQ / Read RSSI request \n', max_retries);
2412                            fprintf('    Requested %d samples from buffer %d starting from sample number %d \n', num_samples, buffer_id, start_sample);
2413                            fprintf('    Received %d out of %d packets from node before timeout.\n', rcvd_pkts, num_pkts);
2414                            fprintf('    Please check the node and look at the ethernet traffic to isolate the issue. \n');
2415                       
2416                            error('Error:  Reached maximum number of retrys without a response... aborting.');
2417                        else
2418                            warning('Read IQ / Read RSSI IQ Error.  Re-requesting samples.\n');
[4309]2419                           
[4416]2420                            % Find the first packet error and request the remaining samples
2421                            %   - TBD - For now just request all the packets again
2422                            %         - See MEX C code for template on how to do this
[4309]2423                           
[4416]2424                            % Start over at the beginning
2425                            node.transport.send(command.serialize(), false, false);  % Do not increment the header for subsequent loops
[4309]2426                           
[4416]2427                            sample_start_tracker = zeros(1, num_pkts);
2428                            sample_num_tracker   = zeros(1, num_pkts);
2429                            rcvd_pkts            = 1;
[4309]2430                           
[4416]2431                            num_retries = num_retries + 1;
2432                        end
2433                    else 
2434                        % No errors
[4815]2435                        %     Record sequence number and exit the function
2436                        seq_num = bitand(args(1), 255);
2437                        done    = 1;
[1915]2438                    end
2439                end
2440            end
[4416]2441           
2442            % Since we received a packet, reset the timeout
2443            curr_time = tic;
[1995]2444        end
[4416]2445    end
2446end
[1915]2447
[4416]2448
2449
2450function out = read_iq_sample_error(sample_num_tracker, sample_start_tracker, num_samples, start_sample, num_pkts, max_sample_size)
2451%  Function:  Read IQ sample check
2452%
2453%  Function to check if we received all the samples at the correct indexes
2454%
2455%  Returns:  0 if no errors
2456%            1 if if there is an error and prints debug information
2457%
[4974]2458    try
2459        num_samples_sum  = uint64(0);
2460        start_sample_sum = uint64(0);
[4416]2461
[4974]2462        % Compute the value of the start samples:
2463        %   We know that the start samples should follow the pattern:
2464        %       [ x, (x + y), (x + 2y), (x + 3y), ... , (x + (N - 1)y) ]
2465        %   where x = start_sample, y = max_sample_size, and N = num_pkts.  This is due
2466        %   to the fact that the node will fill all packets completely except the last packet.
2467        %   Therefore, the sum of all element in that array is:
2468        %       (N * x) + ((N * (N - 1) * Y) / 2
2469        %
2470        start_sample_total = uint64(uint64(num_pkts) * uint64(start_sample)) + uint64((uint64(num_pkts * (num_pkts - 1)) * uint64(max_sample_size)) / 2); 
2471       
2472        % Compute the totals using the sample tracker
2473        for idx = 1:num_pkts   
2474            num_samples_sum  = num_samples_sum + sample_num_tracker(idx);
2475            start_sample_sum = start_sample_sum + sample_start_tracker(idx);
2476        end
[4416]2477
[4974]2478        % Check the totals
2479        if ((num_samples_sum ~= num_samples) || (start_sample_sum ~= start_sample_total))
2480       
2481            % Debug prints
2482            %
2483            % fprintf('Num sample sum   = %16d    Num samples        = %16d\n', num_samples_sum, num_samples);
2484            % fprintf('Start sample sum = 0x%016x  Start sample total = 0x%016x\n', start_sample_sum, start_sample_total);
2485            % fprintf('num_pkts         = %16d    Max sample size    = %16d\n', num_pkts, max_sample_size);
2486            % fprintf('start sample     = 0x%016x  \n', start_sample);
2487       
2488            out = 1;
2489        else
2490            out = 0;
2491        end
2492    catch
2493        % Print warning that this syntax will be deprecated
2494        try
2495            temp = evalin('base', 'wl_uint64_did_warn');
2496        catch
2497            fprintf('WARNING:   Matlab version does not support uint64 arithmetic.  Please use Matlab R2011a or later.\n');
2498            fprintf('WARNING:   The transport will not detect sample transfer errors during readIQ operations.\n');
2499
[4975]2500            warning('Matlab version does not support uint64 arithmetic.  Please use Matlab R2011a or later.');
[4974]2501           
2502            assignin('base', 'wl_uint64_did_warn', 1)
2503        end
[4416]2504   
2505        out = 1;
2506    end
2507end
2508
2509
2510
Note: See TracBrowser for help on using the repository browser.