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

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

Increased trigger output delay for all outputs to conform to trigger manager v1.07.g.

File size: 62.9 KB
Line 
1%-------------------------------------------------------------------------
2% WARPLab Framework
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
12classdef wl_node < handle_light
13    % The WARPLab node class represents one node in a WARPLab network
14    % wl_node is the primary interface for interacting with WARPLab nodes.
15    % This class provides methods for sending commands, exchanging samples
16    % and checking the status of WARPLab nodes.
17    % wl_node wraps the six underlying components that comprise a WARPLab
18    % node implementation: node, baseband, interface, transport, trigger
19    % setup and user extensions.
20   
21    properties (SetAccess = protected)
22        ID;                    % Unique identification for this node
23        description;           % String description of this node (auto-generated)
24        serialNumber;          % Node's serial number, read from EEPROM on hardware
25        eth_MAC_addr;          % Node's MAC address (for ETH_A on WARP v3)
26        fpgaDNA;               % Node's FPGA's unique identification (on select hardware)
27        hwVer;                 % WARP hardware version of this node
28        wlVer_major;           % WARPLab version running on this node
29        wlVer_minor;
30        wlVer_revision;
31        num_interfacesGroups;  % # of interface groups attached to node
32        num_interfaces;        % Vector of length num_interfaceGroups with
33                               %     # of interfaces per group
34        interfaceGroups;       % Node's interface group object(s)
35        interfaceIDs;          % Vector of interface identifiers
36        baseband;              % Node's baseband object
37        trigger_manager;       % Node's trigger configuration object
38        transport;             % Node's transport object
39        user;                  % Node's user extension object
40    end
41   
42    properties (SetAccess = public)
43        name;                  % User specified name for node; user scripts supply this
44    end
45   
46    properties(Hidden = true,Constant = true)
47        % Command Groups - Most Significant Byte of Command ID
48        GRPID_NODE                     = hex2dec('00');
49        GRPID_TRANS                    = hex2dec('10');
50        GRPID_IFC                      = hex2dec('20');
51        GRPID_BB                       = hex2dec('30');
52        GRPID_TRIGMNGR                 = hex2dec('40');
53        GRPID_USER                     = hex2dec('50');
54    end
55   
56    properties(Hidden = true,Constant = true)
57        % These constants define specific command IDs used by this object.
58        % Their C counterparts are found in wl_node.h
59        GRP                            = 'node';
60        CMD_INITIALIZE                 = 1;                % 0x000001
61        CMD_INFO                       = 2;                % 0x000002
62        CMD_IDENTIFY                   = 3;                % 0x000003
63        CMD_TEMPERATURE                = 4;                % 0x000004
64        CMD_NODE_CONFIG_SETUP          = 5;                % 0x000005
65        CMD_NODE_CONFIG_RESET          = 6;                % 0x000006
66       
67        CMD_MEM_RW                     = 16;               % 0x000010
68    end
69   
70    methods
71        function obj = wl_node()
72            % The constructor is intentionally blank for wl_node objects.
73            % Instead, the objects are configured via the separate applyConfiguration method.
74        end
75       
76       
77        function applyConfiguration(objVec, IDVec)
78            % Set all the node object parameters
79       
80            % Apply Configuration only operates on one object at a time
81            if ((length(objVec) > 1) || (length(IDVec) > 1)) 
82                error('applyConfiguration only operates on a single object with a single ID.  Provided parameters with lengths: %d and %d', length(objVec), length(IDVec) ); 
83            end
84
85            % Apply configuration will finish setting properties for a specific hardware node
86            obj    = objVec(1);
87            currID = IDVec(1);
88           
89            % currID can be either a structure containing node information or a number
90            switch (class(currID))
91                case 'struct'
92                    if ( ~strcmp( currID.serialNumber, '' ) )
93                        obj.serialNumber = sscanf( currID.serialNumber, 'W3-a-%d' );  % Only store the last 5 digits ( "W3-a-" is not stored )
94                    else
95                        error('Unknown argument.  Serial Number provided is blank');
96                    end
97
98                    if ( ~strcmp( currID.ID, '' ) )
99                        obj.ID           = sscanf( currID.ID, '%d');
100                    else
101                        error('Unknown argument.  Node ID provided is blank');
102                    end
103               
104                    if ( ~strcmp( currID.name, '' ) ) % Name is an optional parameter in the structure
105                        obj.name         = currID.name;
106                    end
107
108                case 'double'
109                    obj.ID = currID;                  % The node ID must match the DIP switch on the WARP board
110
111                otherwise
112                    error('Unknown argument.  IDVec is of type "%s", need "struct", or "double"', class(currID));
113            end       
114           
115
116            % Get ini configuration file
117            configFile = which('wl_config.ini');
118            if(isempty(configFile))
119                error('cannot find wl_config.ini. please run wl_setup.m'); 
120            end
121               
122            % Use Ethernet/UDP transport for all wl_nodes. The specific type of transport
123            % is specified in the user's wl_config.ini file that is created via wl_setup.m
124            readKeys      = {'network','','transport',''};
125            transportType = inifile(configFile,'read',readKeys);
126            transportType = transportType{1};
127
128            switch(transportType)
129                case 'java'
130                    obj.transport = wl_transport_eth_udp_java;
131                case 'wl_mex_udp'
132                    obj.transport = wl_transport_eth_udp_mex;
133            end
134               
135            if(isempty(obj.trigger_manager))
136                obj.wl_setTriggerManager('wl_trigger_manager_proc');
137            end
138
139            readKeys = {'network','','host_address',''};
140            IP = inifile(configFile,'read',readKeys);
141            IP = IP{1};
142            IP = sscanf(IP,'%d.%d.%d.%d');
143
144            readKeys = {'network','','host_ID',''};
145            hostID = inifile(configFile,'read',readKeys);
146            hostID = hostID{1};
147            hostID = sscanf(hostID,'%d');
148               
149            readKeys = {'network','','unicast_starting_port',''};
150            unicast_starting_port = inifile(configFile,'read',readKeys);
151            unicast_starting_port = unicast_starting_port{1};
152            unicast_starting_port = sscanf(unicast_starting_port,'%d');
153               
154            % Configure transport with settings associated with provided ID
155            obj.transport.hdr.srcID  = hostID;
156            obj.transport.hdr.destID = obj.ID;
157
158            % Determine IP address based on the input parameter
159            switch( class(currID) ) 
160                case 'struct'
161                    if ( ~strcmp( currID.ipAddress, '' ) )
162                        obj.transport.setAddress(currID.ipAddress);
163                    else
164                        error('Unknown argument.  IP Address provided is blank');
165                    end
166                case 'double'
167                    obj.transport.setAddress(sprintf('%d.%d.%d.%d', IP(1), IP(2), IP(3), (obj.ID + 1)));
168            end       
169                           
170            obj.transport.setPort(unicast_starting_port);
171               
172            obj.transport.open();
173            obj.transport.hdr.srcID  = hostID;   % ????? Redundant ????? - TBD
174            obj.transport.hdr.destID = obj.ID;   % ????? Redundant ????? - TBD
175
176            obj.wl_nodeCmd('initialize');
177            obj.wl_transportCmd('ping');              % Confirm the WARP node is online
178            obj.wl_transportCmd('payload_size_test'); % Perform a test to figure out max payload size
179           
180            obj.interfaceIDs = [];
181
182            if(isempty(obj.baseband))
183                % Instantiate baseband object
184                obj.wl_setBaseband('wl_baseband_buffers');
185            end
186               
187            % Read details from the hardware (serial number, etc) and save to local properties
188            obj.wl_nodeCmd('get_hardware_info');
189               
190            % Instantiate interfaces group.
191            if(isempty(obj.interfaceGroups))
192                obj.interfaceGroups{1} = wl_interface_group_X245(1:obj.num_interfaces, 'w3');
193            end
194
195            % Extract the interface IDs from the interface group. These IDs
196            % will be supplied to user scripts to identify individual
197            % interfaces for interface and baseband commands
198            for ifcGroupIndex = 1:length(obj.interfaceGroups)
199                obj.interfaceIDs = [obj.interfaceIDs, obj.interfaceGroups{1}.ID(:).'];
200            end
201               
202            % Populate the description property with a human-readable
203            % description of the node
204            obj.description = sprintf('WARP v%d Node - ID %d', obj.hwVer, obj.ID);
205       end
206       
207       
208       function wl_setUserExtension(obj,module)
209           % Sets the User Extension module to a user-provided object or
210           % string of that object's class name.
211           %
212           makeComparison = 1;
213           
214           if(module == 0)                                 % No module attached
215              module = 'double(0)';
216              makeComparison = 0;
217           elseif(~ischar(module))                         % Input is an actual instance of an object
218              module = class(module);               
219           end
220           
221           if(makeComparison && ~any(strcmp(superclasses(module),'wl_user_ext')))
222              error('Module is not a wl_user_ext type');
223           end
224           
225           for n = 1:length(obj)
226              obj(n).user = eval(module); 
227           end
228       end 
229       
230       
231       function wl_setBaseband(obj,module)
232           % Sets the Baseband module to a user-provided object or
233           % string of that object's class name.
234           %
235           makeComparison = 1;
236
237           if(module == 0)                                 % No module attached
238              module = 'double(0)';
239              makeComparison = 0;
240           elseif(~ischar(module))                         % Input is an actual instance of an object
241              module = class(module);               
242           end
243           
244           if(makeComparison && ~any(strcmp(superclasses(module),'wl_baseband')))
245              error('Module is not a wl_baseband type');
246           end
247           
248           for n = 1:length(obj)
249              obj(n).baseband = eval(module); 
250           end
251       end 
252       
253       
254       function wl_setTriggerManager(obj,module)
255           % Sets the Trigger Manager module to a user-provided object or
256           % string of that object's class name.
257           %
258           makeComparison = 1;
259           
260           if(module == 0)                                 % No module attached
261              module = 'double(0)';
262              makeComparison = 0;
263           elseif(~ischar(module))                         % Input is an actual instance of an object
264              module = class(module);               
265           end
266           
267           if(makeComparison && ~any(strcmp(superclasses(module),'wl_trigger_manager')))
268              error('Module is not a wl_trigger_manager type');
269           end
270           
271           for n = 1:length(obj)
272              obj(n).trigger_manager = eval(module); 
273           end
274       end 
275       
276       
277       function out = wl_basebandCmd(obj, varargin)
278            % Sends commands to the baseband object.
279            % This method is safe to call with multiple wl_node objects as
280            % its first argument. For example, let node0 and node1 be
281            % wl_node objects:
282            %
283            %   wl_basebandCmd(node0, args )
284            %   wl_basebandCmd([node0, node1], args )
285            %   node0.wl_basebandCmd( args )
286            %
287            % are all valid ways to call this method. Note, when calling
288            % this method for multiple node objects, the interpretation of
289            % other vectored arguments is left up to the underlying
290            % components.
291            %
292            nodes    = obj;
293            numNodes = numel(nodes);
294           
295            for n = numNodes:-1:1
296                currNode = nodes(n);
297                if(any(strcmp(superclasses(currNode.baseband),'wl_baseband')))
298                    out(n) = currNode.baseband.procCmd(n, currNode, varargin{:});
299                else
300                    error('Node %d does not have an attached baseband module',currNode.ID);
301                end
302            end
303
304            if((length(out) == 1) && iscell(out))
305               out = out{1};                               % Strip away the cell if it's just a single element.
306            end
307        end
308       
309       
310        function out = wl_nodeCmd(obj, varargin)
311            %Sends commands to the node object.
312            % This method is safe to call with multiple wl_node objects as
313            % its first argument. For example, let node0 and node1 be
314            % wl_node objects:
315            %
316            %   wl_nodeCmd(node0, args )
317            %   wl_nodeCmd([node0, node1], args )
318            %   node0.wl_nodeCmd( args )
319            %
320            % are all valid ways to call this method. Note, when calling
321            % this method for multiple node objects, the interpretation of
322            % other vectored arguments is left up to the underlying
323            % components.
324            %
325            nodes    = obj;
326            numNodes = numel(nodes);
327           
328            for n = numNodes:-1:1
329                currNode = nodes(n);
330                out(n) = currNode.procCmd(n, currNode, varargin{:});
331            end
332           
333            if((length(out) == 1) && iscell(out))
334               out = out{1};                               % Strip away the cell if it's just a single element.
335            end           
336        end
337       
338       
339        function out = wl_transportCmd(obj, varargin)
340            % Sends commands to the transport object.
341            % This method is safe to call with multiple wl_node objects as
342            % its first argument. For example, let node0 and node1 be
343            % wl_node objects:
344            %
345            %   wl_transportCmd(node0, args )
346            %   wl_transportCmd([node0, node1], args )
347            %   node0.wl_transportCmd( args )
348            %
349            % are all valid ways to call this method. Note, when calling
350            % this method for multiple node objects, the interpretation of
351            % other vectored arguments is left up to the underlying
352            % components.
353            %
354            nodes    = obj;
355            numNodes = numel(nodes);
356           
357            for n = numNodes:-1:1
358                currNode = nodes(n);
359                if(any(strcmp(superclasses(currNode.transport),'wl_transport')))
360                    out(n) = currNode.transport.procCmd(n, currNode, varargin{:});
361                else
362                    error('Node %d does not have an attached transport module',currNode.ID);
363                end
364               
365            end
366           
367            if((length(out) == 1) && iscell(out))
368               out = out{1};                               % Strip away the cell if it's just a single element.
369            end           
370        end
371       
372       
373        function out = wl_userExtCmd(obj, varargin)
374            % Sends commands to the user extension object.
375            % This method is safe to call with multiple wl_node objects as
376            % its first argument. For example, let node0 and node1 be
377            % wl_node objects:
378            %
379            %   wl_userExtCmd(node0, args )
380            %   wl_userExtCmd([node0, node1], args )
381            %   node0.wl_userExtCmd( args )
382            %
383            % are all valid ways to call this method. Note, when calling
384            % this method for multiple node objects, the interpretation of
385            % other vectored arguments is left up to the underlying
386            % components.
387            %
388            nodes    = obj;
389            numNodes = numel(nodes);
390           
391            for n = numNodes:-1:1
392                currNode = nodes(n);
393                if(any(strcmp(superclasses(currNode.user),'wl_user_ext')))
394                     out(n) = {currNode.user.procCmd(n, currNode, varargin{:})};
395                else
396                    error('Node %d does not have an attached user extension module',currNode.ID);
397                end
398            end
399           
400            if((length(out) == 1) && iscell(out))
401               out = out{1};                               % Strip away the cell if it's just a single element.
402            end
403        end
404       
405       
406        function out = wl_triggerManagerCmd(obj, varargin)
407            % Sends commands to the trigger manager object.
408            % This method is safe to call with multiple wl_node objects as
409            % its first argument. For example, let node0 and node1 be
410            % wl_node objects:
411            %
412            %   wl_triggerManagerCmd(node0, args )
413            %   wl_triggerManagerCmd([node0, node1], args )
414            %   node0.wl_triggerManagerCmd( args )
415            %
416            % are all valid ways to call this method. Note, when calling
417            % this method for multiple node objects, the interpretation of
418            % other vectored arguments is left up to the underlying
419            % components.
420            %
421            nodes    = obj;
422            numNodes = numel(nodes);
423           
424            for n = numNodes:-1:1
425                currNode = nodes(n);
426                if(any(strcmp(superclasses(currNode.trigger_manager),'wl_trigger_manager')))
427                    out(n) = currNode.trigger_manager.procCmd(n, currNode, varargin{:});
428                else
429                    error('Node %d does not have an attached trigger manager module',currNode.ID);
430                end
431            end
432           
433            if((length(out) == 1) && iscell(out))
434               out = out{1};                               % Strip away the cell if it's just a single element.
435            end           
436        end
437       
438       
439        function out = wl_interfaceCmd(obj, rfSel, varargin)
440            % Sends commands to the interface object.
441            % This method must be called with RF selection masks as an
442            % argument. These masks are returned by the wl_getInterfaceIDs
443            % method. The RFMASKS argument must be a scaler or vector of
444            % bit-OR'd interface IDs from a single interface group
445            %
446            % This method is safe to call with multiple wl_node objects as
447            % its first argument. For example, let node0 and node1 be
448            % wl_node objects:
449            %
450            %   wl_interfaceCmd(node0, RFMASKS, args )
451            %   wl_interfaceCmd([node0, node1], RFMASKS, args )
452            %   node0.wl_trigConfCmd(RFMASKS, args )
453            %
454            % are all valid ways to call this method. Note, when calling
455            % this method for multiple node objects, the interpretation of
456            % other vectored arguments is left up to the underlying
457            % components.
458            %
459            nodes    = obj;
460            numNodes = numel(nodes);
461           
462            ifcIndex = 1;
463            for nodeIndex = numNodes:-1:1
464                currNode = nodes(nodeIndex); 
465                if(any(strcmp(superclasses(currNode.interfaceGroups{ifcIndex}), 'wl_interface_group')))
466                    out(nodeIndex) = currNode.interfaceGroups{ifcIndex}.procCmd(nodeIndex, currNode, rfSel, varargin{:});
467                else
468                    error('Node %d does not have an attached interface group module',currNode.ID);
469                end
470            end   
471
472            if((length(out) == 1) && iscell(out))
473               out = out{1};                               % Strip away the cell if it's just a single element.
474            end
475        end
476       
477       
478        function varargout = wl_getInterfaceIDs(obj)
479            % Returns the interfaces IDs that can be used as inputs to all interface commands, some
480            % baseband commands and possibly some user extension commands.
481            %
482            % The interface IDs are returned in a structure that contains fields for individual
483            % interfaces and combinations of interfaces.  When a node only supports 2 interfaces,
484            % the fields for RFC and RFD (ie the fields specific to a 4 interface node) are not
485            % present in the structure.
486            %
487            % The fields in the structure are:
488            %     - Scalar fields:
489            %       - RF_A
490            %       - RF_B
491            %       - RF_C
492            %       - RF_D
493            %       - RF_ON_BOARD      NOTE: RF_ON_BOARD      = RF_A + RF_B
494            %       - RF_FMC           NOTE: RF_FMC           = RF_C + RF_D
495            %       - RF_ALL           NOTE: 2RF:  RF_ALL     = RF_A + RF_B
496            %                                4RF:  RF_ALL     = RF_A + RF_B + RF_C + RF_D
497            %     - Vector fields:
498            %       - RF_ON_BOARD_VEC  NOTE: RF_ON_BOARD_VEC  = [RF_A, RF_B]
499            %       - RF_FMC_VEC       NOTE: RF_FMC_VEC       = [RF_C, RF_D]
500            %       - RF_ALL_VEC       NOTE: 2RF:  RF_ALL_VEC = [RF_A, RF_B]
501            %                                4RF:  RF_ALL_VEC = [RF_A, RF_B, RF_C, RF_D]
502            %
503            % NOTE:  Due to Matlab behavior, the scalar fields for RF_A, RF_B, RF_C, and RF_D can be
504            %     used as vectors and therefore do not need separate vector fields in the structure
505            %
506            % Examples:
507            %     Get the interface ID structure (let node be a wl_node object):
508            %         ifc_ids = wl_getInterfaceIDs(node);
509            %         ifc_ids = node.wl_getInterfaceIDs();
510            %
511            %     Use the interface ID structure:
512            %         1) Enable RF_A for transmit: 
513            %                wl_interfaceCmd(node, ifc_ids.RF_A, 'tx_en');
514            %
515            %         2) Get 1000 samples of Read IQ data from all interfaces:
516            %                rx_IQ = wl_basebandCmd(node, ifc_ids.RF_ALL_VEC, 'read_IQ', 0, 1000);
517            %
518           
519            if (nargout > 1) 
520                % Legacy code for compatibility
521                %
522                % Returns a vector of interface IDs that can be used as inputs to all interface
523                % commands and some baseband or user extension commands.
524                %
525                % Let node0 be a wl_node object:
526                %   [RFA, RFB] = wl_getInterfaceIDs(node0)
527                %   [RFA, RFB] = node0.wl_getInterfaceIDs(node0)
528                %
529                % Issues a warning that this syntax will be deprecated in future releases.
530                %
531                if(nargout > obj.num_interfaces)
532                   error('Node %d has only %d interfaces. User has requested %d interface IDs', obj.ID, obj.num_interfaces, nargout); 
533                end
534               
535                varargout = num2cell(obj.interfaceIDs(1:nargout)); 
536               
537                % Print warning that this syntax will be deprecated
538                try
539                    temp = evalin('base', 'wl_get_interface_ids_did_warn');
540                catch
541                    fprintf('WARNING:   This syntax for wl_getInterfaceIDs() is being deprecated.\n');
542                    fprintf('WARNING:   Please use:  ifc_ids = wl_getInterfaceIDs(node);\n');
543                    fprintf('WARNING:       where ifc_ids is a structure that contains the interface IDs\n');
544                    fprintf('WARNING:   See WARPLab documentation for more information\n\n');
545                   
546                    assignin('base', 'wl_get_interface_ids_did_warn', 1)
547                end
548               
549            else
550                ifc_ids = struct();
551               
552                switch(obj.num_interfaces)
553               
554                    %---------------------------------------------------------
555                    case 2
556                        % Structure contains:
557                        %     Scalar variables:  RF_A, RF_B, RF_ON_BOARD, RF_ALL
558                        %     Vector variables:  RF_ON_BOARD_VEC, RF_ALL_VEC
559                        %
560                       
561                        % Scalar variables
562                        ifc_ids(1).RF_A               = obj.interfaceIDs(1);
563                        ifc_ids(1).RF_B               = obj.interfaceIDs(2);
564                       
565                        ifc_ids(1).RF_ON_BOARD        = obj.interfaceIDs(1) + obj.interfaceIDs(2);
566                       
567                        ifc_ids(1).RF_ALL             = obj.interfaceIDs(1) + obj.interfaceIDs(2);
568                       
569                        % Vector variables
570                        ifc_ids(1).RF_ON_BOARD_VEC    = [obj.interfaceIDs(1), obj.interfaceIDs(2)];
571                       
572                        ifc_ids(1).RF_ALL_VEC         = [obj.interfaceIDs(1), obj.interfaceIDs(2)];
573                       
574                    %---------------------------------------------------------
575                    case 4
576                        % Structure contains:
577                        %     Scalar variables:  RF_A, RF_B, RF_C, RF_D, RF_ON_BOARD, RF_FMC, RF_ALL
578                        %     Vector variables:  RF_ON_BOARD_VEC, RF_FMC_VEC, RF_ALL_VEC
579                        %
580               
581                        % Scalar variables
582                        ifc_ids(1).RF_A               = obj.interfaceIDs(1);
583                        ifc_ids(1).RF_B               = obj.interfaceIDs(2);
584                        ifc_ids(1).RF_C               = obj.interfaceIDs(3);
585                        ifc_ids(1).RF_D               = obj.interfaceIDs(4);
586                       
587                        ifc_ids(1).RF_ON_BOARD        = obj.interfaceIDs(1) + obj.interfaceIDs(2);
588                        ifc_ids(1).RF_FMC             = obj.interfaceIDs(3) + obj.interfaceIDs(4);
589                       
590                        ifc_ids(1).RF_ALL             = obj.interfaceIDs(1) + obj.interfaceIDs(2) + obj.interfaceIDs(3) + obj.interfaceIDs(4);
591                       
592                        % Vector variables
593                        ifc_ids(1).RF_ON_BOARD_VEC    = [obj.interfaceIDs(1), obj.interfaceIDs(2)];
594                        ifc_ids(1).RF_FMC_VEC         = [obj.interfaceIDs(3), obj.interfaceIDs(4)];
595                       
596                        ifc_ids(1).RF_ALL_VEC         = [obj.interfaceIDs(1), obj.interfaceIDs(2), obj.interfaceIDs(3), obj.interfaceIDs(4)];
597                       
598                    %---------------------------------------------------------
599                    otherwise
600                        error( 'Number of interfaces not supported.  Node reported %d interfaces, should be in [2, 4].', obj.num_interfaces);
601                end
602               
603                varargout = {ifc_ids};
604            end
605        end
606       
607       
608        function varargout = wl_getTriggerInputIDs(obj)
609            % Returns the trigger input IDs that can be used as inputs to trigger manager commands
610            %
611            % The trigger input IDs are returned in a structure that contains fields for individual
612            % triggers. 
613            %
614            % The fields in the structure are:
615            %     - Scalar fields:
616            %       - ETH_A                 - Ethernet Port A
617            %       - ETH_B                 - Ethernet Port B
618            %       - ENERGY_DET            - Energy detection (See 'energy_config_*' commands in the Trigger Manager documentation)
619            %       - AGC_DONE              - Automatic Gain Controller complete
620            %       - SW_REG                - Software register (ie Memory mapped registers that can be triggered by a CPU write)
621            %       - EXT_IN_P0             - External Input Pin 0
622            %       - EXT_IN_P1             - External Input Pin 1
623            %       - EXT_IN_P2             - External Input Pin 2
624            %       - EXT_IN_P3             - External Input Pin 3
625            %
626            % Examples:
627            %     Get the trigger input ID structure (let node be a wl_node object):
628            %         trig_in_ids = wl_getTriggerInputIDs(node);
629            %         trig_in_ids = node.wl_getTriggerInputIDs();
630            %
631            %     Use the trigger input ID structure:
632            %         1) Enable baseband and agc output triggers to be triggered on the Ethernet A input trigger: 
633            %                wl_triggerManagerCmd(nodes, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]);
634            %
635           
636            if (nargout > 1) 
637                % Legacy code for compatibility
638                %
639                % Returns a vector of trigger input IDs that can be used as inputs to trigger manager commands
640                %
641                % Let node0 be a wl_node object
642                %   [T_IN_ETH_A, T_IN_ENERGY, T_IN_AGCDONE, T_IN_REG, T_IN_D0, T_IN_D1, T_IN_D2, T_IN_D3, T_IN_ETH_B] = wl_getTriggerInputIDs(node0)
643                %   [T_IN_ETH_A, T_IN_ENERGY, T_IN_AGCDONE, T_IN_REG, T_IN_D0, T_IN_D1, T_IN_D2, T_IN_D3, T_IN_ETH_B] = node0.wl_getTriggerInputIDs(node0)
644                %
645                if(nargout > length(obj.trigger_manager.triggerInputIDs))
646                   error('Node %d has only %d trigger inputs. User has requested %d trigger input IDs',obj.ID,length(obj.trigger_manager.triggerInputIDs),nargout); 
647                end
648               
649                varargout = num2cell(obj.trigger_manager.triggerInputIDs(1:nargout));
650           
651                % Print warning that this syntax will be deprecated
652                try
653                    temp = evalin('base', 'wl_get_trigger_input_ids_did_warn');
654                catch
655                    fprintf('WARNING:   This syntax for wl_getTriggerInputIDs() is being deprecated.\n');
656                    fprintf('WARNING:   Please use:  trig_in_ids = wl_getTriggerInputIDs(node);\n');
657                    fprintf('WARNING:       where trig_in_ids is a structure that contains the trigger input IDs\n');
658                    fprintf('WARNING:   See WARPLab documentation for more information\n\n');
659                   
660                    assignin('base', 'wl_get_trigger_input_ids_did_warn', 1)
661                end
662               
663            else
664                % Structure contains:
665                %     Scalar variables:  ETH_A, ETH_B, ENERGY, AGC_DONE, SW_REG, DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3
666                %
667                trig_in_ids = struct();
668               
669                if(length(obj.trigger_manager.triggerInputIDs) ~= 9)
670                   error('Node %d has %d trigger inputs. Expected 9.', obj.ID, length(obj.trigger_manager.triggerInputIDs)); 
671                end
672               
673                % Scalar variables
674                trig_in_ids(1).ETH_A        = obj.trigger_manager.triggerInputIDs(1);
675                trig_in_ids(1).ENERGY_DET   = obj.trigger_manager.triggerInputIDs(2);
676                trig_in_ids(1).AGC_DONE     = obj.trigger_manager.triggerInputIDs(3);
677                trig_in_ids(1).SW_REG       = obj.trigger_manager.triggerInputIDs(4);
678                trig_in_ids(1).EXT_IN_P0    = obj.trigger_manager.triggerInputIDs(5);
679                trig_in_ids(1).EXT_IN_P1    = obj.trigger_manager.triggerInputIDs(6);
680                trig_in_ids(1).EXT_IN_P2    = obj.trigger_manager.triggerInputIDs(7);
681                trig_in_ids(1).EXT_IN_P3    = obj.trigger_manager.triggerInputIDs(8);
682                trig_in_ids(1).ETH_B        = obj.trigger_manager.triggerInputIDs(9);
683       
684                varargout = {trig_in_ids};
685            end
686        end
687       
688       
689        function varargout = wl_getTriggerOutputIDs(obj)
690            % Returns the trigger output IDs that can be used as inputs to trigger manager commands
691            %
692            % The trigger output IDs are returned in a structure that contains fields for individual
693            % triggers. 
694            %
695            % The fields in the structure are:
696            %     - Scalar fields:
697            %       - BASEBAND              - Baseband buffers module
698            %       - AGC                   - Automatic Gain Controller module
699            %       - EXT_OUT_P0            - External Output Pin 0
700            %       - EXT_OUT_P1            - External Output Pin 1
701            %       - EXT_OUT_P2            - External Output Pin 2
702            %       - EXT_OUT_P3            - External Output Pin 3
703            %
704            % Examples:
705            %     Get the trigger output ID structure (let node be a wl_node object):
706            %         trig_out_ids = wl_getTriggerInputIDs(node);
707            %         trig_out_ids = node.wl_getTriggerInputIDs();
708            %
709            %     Use the trigger input ID structure:
710            %         1) Enable baseband and agc output triggers to be triggered on the Ethernet A input trigger: 
711            %                wl_triggerManagerCmd(node, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]);
712            %
713            %         2) Configure output delay for the baseband:
714            %                node.wl_triggerManagerCmd('output_config_delay', [trig_out_ids.BASEBAND], 0);
715            %
716           
717            if (nargout > 1) 
718                % Legacy code for compatibility
719                %
720                % Returns a vector of trigger output IDs that can be used as inputs to trigger manager commands
721                %
722                % Let node0 be a wl_node object:
723                %   [T_OUT_BASEBAND, T_OUT_AGC, T_OUT_D0, T_OUT_D1, T_OUT_D2, T_OUT_D3] = wl_getTriggerOutputIDs(node0)
724                %   [T_OUT_BASEBAND, T_OUT_AGC, T_OUT_D0, T_OUT_D1, T_OUT_D2, T_OUT_D3] = node0.wl_getTriggerOutputIDs(node0)
725                %
726                if(nargout > length(obj.trigger_manager.triggerOutputIDs))
727                   error('Node %d has only %d trigger outputs. User has requested %d trigger output IDs',obj.ID,length(obj.trigger_manager.triggerOutputIDs),nargout); 
728                end
729               
730                varargout = num2cell(obj.trigger_manager.triggerOutputIDs(1:nargout)); 
731           
732                % Print warning that this syntax will be deprecated
733                try
734                    temp = evalin('base', 'wl_get_trigger_output_ids_did_warn');
735                catch
736                    fprintf('WARNING:   This syntax for wl_getTriggerOutputIDs() is being deprecated.\n');
737                    fprintf('WARNING:   Please use:  trig_out_ids = wl_getTriggerOutputIDs(node);\n');
738                    fprintf('WARNING:       where trig_out_ids is a structure that contains the trigger output IDs\n');
739                    fprintf('WARNING:   See WARPLab documentation for more information\n\n');
740                   
741                    assignin('base', 'wl_get_trigger_output_ids_did_warn', 1)
742                end
743               
744            else
745                % Structure contains:
746                %     Scalar variables:  BASEBAND, AGC, DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3
747                %
748                trig_out_ids = struct();
749               
750                if(length(obj.trigger_manager.triggerOutputIDs) ~= 6)
751                   error('Node %d has %d trigger outputs. Expected 6.', obj.ID, length(obj.trigger_manager.triggerOutputIDs)); 
752                end
753               
754                % Scalar variables
755                trig_out_ids(1).BASEBAND    = obj.trigger_manager.triggerOutputIDs(1);
756                trig_out_ids(1).AGC         = obj.trigger_manager.triggerOutputIDs(2);
757                trig_out_ids(1).EXT_OUT_P0  = obj.trigger_manager.triggerOutputIDs(3);
758                trig_out_ids(1).EXT_OUT_P1  = obj.trigger_manager.triggerOutputIDs(4);
759                trig_out_ids(1).EXT_OUT_P2  = obj.trigger_manager.triggerOutputIDs(5);
760                trig_out_ids(1).EXT_OUT_P3  = obj.trigger_manager.triggerOutputIDs(6);
761       
762                varargout = {trig_out_ids};
763            end
764        end
765       
766       
767        function verify_writeIQ_checksum(obj, checksum)
768            % This is a callback to verify the WriteIQ checksum in the case that WriteIQ is called by a broadcast
769            % transport.
770            if ( numel(checksum) > 1 )
771                error('Cannot verify more than one checksum at a time.')
772            end
773
774            % Get the current checksum on the node
775            node_checksum = obj.wl_basebandCmd('write_iq_checksum');
776
777            if ( node_checksum ~= checksum )
778                warning('Checksums do not match on node %d:  %d != %d', obj.ID, node_checksum, checksum)
779            end
780        end
781       
782       
783        function delete(obj)
784            % Clears the transport object to close any open socket
785            % connections in the event that the node object is deleted.
786            if(~isempty(obj.transport))
787                obj.transport.delete();
788                obj.transport = [];
789            end
790        end
791    end
792   
793   
794    methods(Static=true,Hidden=true)   
795        function command_out = calcCmd_helper(group,command)
796            % Performs the actual command calculation for calcCmd
797            command_out = uint32(bitor(bitshift(group,24),command));
798        end
799    end
800   
801   
802    methods(Hidden=true)
803        % These methods are hidden because users are not intended to call
804        % them directly from their WARPLab scripts.     
805       
806        function out = calcCmd(obj, grp, cmdID)
807            % Takes a group ID and a cmd ID to form a single
808            % uint32 command. Every WARPLab module calls this method
809            % to construct their outgoing commands.
810            switch(lower(grp))
811                case 'node'
812                    out = obj.calcCmd_helper(obj.GRPID_NODE, cmdID);
813                case 'interface'
814                    out = obj.calcCmd_helper(obj.GRPID_IFC, cmdID);
815                case 'baseband'
816                    out = obj.calcCmd_helper(obj.GRPID_BB, cmdID);
817                case 'trigger_manager'
818                    out = obj.calcCmd_helper(obj.GRPID_TRIGMNGR, cmdID);
819                case 'transport'
820                    out = obj.calcCmd_helper(obj.GRPID_TRANS, cmdID);
821                case 'user_extension'
822                    out = obj.calcCmd_helper(obj.GRPID_USER, cmdID);
823            end
824        end
825       
826       
827        function out = procCmd(obj, nodeInd, node, cmdStr, varargin)
828            % wl_node procCmd(obj, nodeInd, node, varargin)
829            %     obj:       Node object (when called using dot notation)
830            %     nodeInd:   Index of the current node, when wl_node is iterating over nodes
831            %     node:      Current node object
832            %     cmdStr:    Command string of the interface command
833            %     varargin:
834            %         [1:N}  Command arguments
835            %
836            out    = [];
837
838            cmdStr = lower(cmdStr);           
839            switch(cmdStr)
840           
841                %---------------------------------------------------------
842                case 'get_hardware_info'
843                    % Reads details from the WARP hardware and updates node object parameters
844                    %
845                    % Arguments: none
846                    % Returns: none (access updated node parameters if needed)
847                    %
848                    % Hardware support:
849                    %     All:
850                    %         WARPLab design version
851                    %         Hardware version
852                    %         Ethernet MAC Address
853                    %         Number of Interface Groups
854                    %         Number of Interfaces
855                    %
856                    %     WARP v3:
857                    %         Serial number
858                    %         Virtex-6 FPGA DNA
859                    %
860                    [MAJOR, MINOR, REVISION, XTRA] = wl_ver();
861                   
862                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_INFO));
863                    resp  = node.sendCmd(myCmd);
864                    resp  = resp.getArgs();
865                   
866                    % Response payload (all u32):
867                    %      1:   Serial number
868                    %      2:   FPGA DNA MSB
869                    %      3:   FPGA DNA LSB
870                    %      4:   MAC address bytes 5:4
871                    %      5:   MAC address bytes 3:0
872                    %      6:   [hw_version wl_ver_major wl_ver_minor wl_ver_rev]
873                    %      7:   Current txIQ Buffer Length
874                    %      8:   Current rxIQ Buffer Length
875                    %      9:   Maximum txIQ Buffer Length
876                    %     10:   Maximum rxIQ Buffer Length
877                    %     11:   [trigger manager coreID, trigger manager numOutputs, trigger manager numInputs]
878                    %     12:   number of interface groups
879                    %     13:N: interface group descriptions (one u32 per group)
880
881                    % If the serial number was provided via the network setup, then check the serial number against the HW
882                    if ( ~isempty( obj.serialNumber ) ) 
883                        if ( ~eq( obj.serialNumber, resp(1) ) )
884                            error('Serial Number provided in config, W3-a-%d, does not match HW serial number W3-a-%d ', obj.serialNumber, resp(1))         
885                        end
886                    else 
887                        obj.serialNumber = resp(1);
888                    end
889                   
890                    obj.fpgaDNA = bitshift(resp(2), 32) + resp(3);
891                   
892                    obj.eth_MAC_addr = 2^32*double(bitand(resp(4),2^16-1)) + double(resp(5));
893                   
894                    obj.hwVer          = double(bitand(bitshift(resp(6), -24), 255));
895                    obj.wlVer_major    = double(bitand(bitshift(resp(6), -16), 255));
896                    obj.wlVer_minor    = double(bitand(bitshift(resp(6), -8), 255));
897                    obj.wlVer_revision = double(bitand(resp(6), 255));
898                   
899                    if((obj.wlVer_major ~= MAJOR) || (obj.wlVer_minor ~= MINOR))
900                        myErrorMsg = sprintf('Node %d reports WARPLab version %d.%d.%d while this PC is configured with %d.%d.%d', ...
901                            obj.ID, obj.wlVer_major, obj.wlVer_minor, obj.wlVer_revision, MAJOR, MINOR, REVISION);
902                        error(myErrorMsg);
903                    end
904
905                    if(obj.wlVer_revision ~= REVISION)
906                        myWarningMsg = sprintf('Node %d reports WARPLab version %d.%d.%d while this PC is configured with %d.%d.%d', ...
907                            obj.ID, obj.wlVer_major, obj.wlVer_minor, obj.wlVer_revision, MAJOR, MINOR, REVISION);
908                        warning(myWarningMsg);
909                    end
910                   
911                    % Get the maximum supported IQ lengths                   
912                    % obj.baseband.max_txIQLen   = double(resp(7));
913                    % obj.baseband.max_rxIQLen   = double(resp(8));
914                    % obj.baseband.max_rxRSSILen = (obj.baseband.max_rxIQLen) / 4;    % RSSI is sampled at 1/4 the speed of IQ
915
916                    % Get the current supported IQ lengths                   
917                    obj.baseband.txIQLen       = double(resp(9));
918                    obj.baseband.rxIQLen       = double(resp(10));
919                    obj.baseband.rxRSSILen     = double(resp(10))/4;
920                   
921                    % Trigger Manager -- core runs at different speed depending on HW version.
922                    obj.trigger_manager.setNumInputs(double(bitand(resp(11),255)));
923                    obj.trigger_manager.setNumOutputs(double(bitand(bitshift(resp(11),-8),255)));
924                    obj.trigger_manager.coreVersion = double(bitand(bitshift(resp(11),-16),255));
925
926                    switch(obj.hwVer)
927                        %TODO: These parameters should be passed up from
928                        %the board
929                        case 1
930                            error('WARP v1 Hardware is not supported by WARPLab 7');
931                        case {2, 3}
932                            % Clock frequency of the sysgen core in the design
933                            %   NOTE:  In WARPLab 7.5.1, the WARP v2 sysgen clock was increased to 160 MHz
934                            %
935                            clock_freq   = 160e6;
936                            trig_in_ids  = obj.wl_getTriggerInputIDs();
937                            trig_out_ids = obj.wl_getTriggerOutputIDs();
938                           
939                            % Trigger output delays
940                            for k = length(obj.trigger_manager.triggerOutputIDs):-1:1
941                                % With Trigger Processor v1.07.a, all delays were increased to 65535                               
942                                obj.trigger_manager.output_delayStep_ns(k) = (1/(clock_freq))*1e9;
943                                obj.trigger_manager.output_delayMax_ns(k)  = 65535 * obj.trigger_manager.output_delayStep_ns(k);
944                            end
945                           
946                            % Trigger input delays
947                            for k = length(obj.trigger_manager.triggerInputIDs):-1:1
948                                obj.trigger_manager.input_delayStep_ns(k) = (1/(clock_freq))*1e9;
949                                obj.trigger_manager.input_delayMax_ns(k)  = 31 * obj.trigger_manager.input_delayStep_ns(k);
950                            end
951                    end
952                   
953                    obj.num_interfacesGroups = resp(12);
954                    obj.num_interfaces = resp(13);
955                   
956                    %% TODO - parse each interface descriptor and create interface group objects
957                   
958                %---------------------------------------------------------
959                case 'get_fpga_temperature'
960                    % Reads the temperature (in Celsius) from the FPGA
961                    %
962                    % Arguments: none
963                    % Returns: (double currTemp), (double minTemp), (double maxTemp)
964                    %   currTemp - current temperature of FPGA in degrees Celsius
965                    %   minTemp - minimum recorded temperature of FPGA in degrees Celsius
966                    %   maxTemp - maximum recorded temperature temperature of FPGA in degrees Celsius
967                    %                   
968                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TEMPERATURE));
969                    resp  = node.sendCmd(myCmd);
970                    resp  = resp.getArgs();
971                   
972                    % Convert from raw temperature to Celsius
973                    out = ((double(resp)/65536.0)/0.00198421639) - 273.15;
974
975                %---------------------------------------------------------
976                case 'initialize'
977                    % Initializes the node; this must be called at least once per power cycle of the node
978                    %
979                    % Arguments: none
980                    % Returns: none
981                    %
982                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_INITIALIZE));
983                    node.sendCmd(myCmd);
984                   
985                %---------------------------------------------------------
986                case 'identify'
987                    % Blinks the user LEDs on the WARP node, to help identify MATLAB node-to-hardware node mapping
988                    %
989                    % Arguments: none
990                    % Returns: none
991                    %
992                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_IDENTIFY));
993                    node.sendCmd(myCmd);
994                   
995                %---------------------------------------------------------
996                case 'node_config_reset'
997                    % Resets the HW state of the node to accept a new node configuration
998                    %
999                    % Arguments: none
1000                    % Returns: none
1001                    %
1002                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODE_CONFIG_RESET));                   
1003                    myCmd.addArgs(uint32(node.serialNumber)); 
1004                    node.sendCmd(myCmd);
1005                   
1006                %---------------------------------------------------------
1007                % 'node_config_setup' is only implemented in wl_initNodes.m
1008                %
1009
1010                %---------------------------------------------------------
1011                case 'node_mem_read'
1012                    % Read memory addresses in the node
1013                    %
1014                    % Arguments:   (uint32 ADDRESS), (uint32 LENGTH)
1015                    % Returns:     Vector of uint32 values read from the node
1016                    %
1017                    % ADDRESS:     Address to start the read
1018                    %
1019                    % LENGTH:      Number of uint32 words to read from the node
1020                    %
1021                    % NOTE:  The node enforces a maximum number of words that can be transferred for a
1022                    %     given read.  This is typically on the order of 350 words.
1023                    %
1024                    % NOTE:  Please use the C code files, such as xparameters.h and other header files,
1025                    %     to understand the addresses of various registers in the system and the meaning
1026                    %     of the bits within those registers.
1027                    %
1028                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_MEM_RW));
1029                   
1030                    % Check arguments
1031                    if(length(varargin) ~= 2)
1032                        error('%s: Requires two arguments:  Address, Length (in uint32 words of the read)', cmdStr);
1033                    end
1034                   
1035                    address     = varargin{1};
1036                    read_length = varargin{2};
1037
1038                    myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL);
1039                   
1040                    % Check arguments
1041                    if ((address < 0) || (address > hex2dec('FFFFFFFF')))
1042                        error('%s: Address must be in [0, 0xFFFFFFFF].\n', cmdStr);
1043                    end
1044
1045                    if ((read_length < 0) || (read_length > 350))
1046                        error('%s: Length must be in [0, 350] words.\n', cmdStr);
1047                    end
1048                   
1049                    % Send command to the node
1050                    myCmd.addArgs(address);
1051                    myCmd.addArgs(read_length);
1052                   
1053                    resp = node.sendCmd(myCmd);
1054                   
1055                    % Process response from the node.  Return arguments:
1056                    %         [1] - Status
1057                    %         [2] - Length
1058                    %     [ 3: N] - Values
1059                    %
1060                    for i = 1:numel(resp)                   % Needed for unicast node_group support
1061                        ret   = resp(i).getArgs();
1062
1063                        if (ret(1) == myCmd.CMD_PARAM_SUCCESS)
1064                            if ((ret(2) + 2) == length(ret))
1065                                if (i == 1) 
1066                                    out = ret(3:end);
1067                                else
1068                                    out(:,i) = ret(3:end);
1069                                end
1070                            else
1071                                msg = sprintf('%s: Memory read length mismatch error in node %d:  %d != %d\n', cmdStr, nodeInd, (ret(2) + 2), length(ret));
1072                                error(msg);
1073                            end
1074                        else 
1075                            msg = sprintf('%s: Memory read error in node %d.\n', cmdStr, nodeInd);
1076                            error(msg);
1077                        end
1078                    end
1079                   
1080                %---------------------------------------------------------
1081                case 'node_mem_write'
1082                    % Write memory addresses in the node
1083                    %
1084                    % Arguments: (uint32 ADDRESS), (uint32 VALUES)
1085                    % Returns: none
1086                    %
1087                    % ADDRESS:     Address to start the write
1088                    %
1089                    % VALUES:      Vector of uint32 words to write to the node
1090                    %
1091                    % NOTE:  The node enforces a maximum number of words that can be transferred for a
1092                    %     given write.  This is typically on the order of 350 words.
1093                    %
1094                    % NOTE:  Please use the C code files, such as xparameters.h and other header files,
1095                    %     to understand the addresses of various registers in the system and the meaning
1096                    %     of the bits within those registers.
1097                    %
1098                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_MEM_RW));
1099                   
1100                    % Check arguments
1101                    if(length(varargin) ~= 2)
1102                        error('%s: Requires two arguments:  Address, Vector of uint32 words to write', cmdStr);
1103                    end
1104                   
1105                    address     = varargin{1};
1106                    values      = varargin{2};
1107
1108                    myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL);
1109                   
1110                    % Check arguments
1111                    if ((address < 0) || (address > hex2dec('FFFFFFFF')))
1112                        error('%s: Address must be in [0, 0xFFFFFFFF].\n', cmdStr);
1113                    end
1114
1115                    if ((length(values) < 0) || (length(values) > 350))
1116                        error('%s: Length of VALUES vector must be in [0, 350] words.\n', cmdStr);
1117                    end
1118                   
1119                    % Send command to the node
1120                    if(~isempty(values))
1121                        myCmd.addArgs(address);
1122                        myCmd.addArgs(length(values));
1123                        myCmd.addArgs(values);
1124                       
1125                        resp = node.sendCmd(myCmd);
1126
1127                        % Process response from the node.  Return arguments:
1128                        %         [1] - Status
1129                        %
1130                        for i = 1:numel(resp)                   % Needed for unicast node_group support
1131                            ret   = resp(i).getArgs();
1132
1133                            if (ret(1) ~= myCmd.CMD_PARAM_SUCCESS)
1134                                msg = sprintf('%s: Memory write error in node %d.\n', cmdStr, nodeInd);
1135                                error(msg);
1136                            end
1137                        end
1138                    end
1139                   
1140                %---------------------------------------------------------
1141                otherwise
1142                    error( 'unknown node command %s', cmdStr);
1143            end
1144           
1145            if((iscell(out) == 0) && (numel(out) ~= 1))
1146                out = {out}; 
1147            end     
1148        end
1149       
1150       
1151        function out = sendCmd(obj, cmd)
1152            % This method is responsible for serializing the command
1153            % objects provided by each of the components of WARPLab into a
1154            % vector of values that the transport object can send. This
1155            % method is used when the board must at least provide a
1156            % transport-level acknowledgement. If the response has actual
1157            % payload that needs to be provided back to the calling method,
1158            % that response is passed as an output of this method.
1159            %
1160            resp = obj.transport.send(cmd.serialize(), true);
1161           
1162            out = wl_resp(resp);
1163        end
1164       
1165       
1166        function sendCmd_noresp(obj, cmd)
1167            % This method is responsible for serializing the command
1168            % objects provided by each of the components of WARPLab into a
1169            % vector of values that the transport object can send. This
1170            % method is used when a board should not send an immediate
1171            % response. The transport object is non-blocking -- it will send
1172            % the command and then immediately return.
1173            %
1174            obj.transport.send(cmd.serialize(), false);
1175        end 
1176       
1177       
1178        function out = receiveResp(obj)
1179            % This method will return a vector of responses that are
1180            % sitting in the host's receive queue. It will empty the queue
1181            % and return them all to the calling method.
1182            %
1183            resp = obj.transport.receive();
1184           
1185            if(~isempty(resp))
1186                % Create vector of response objects if received string of bytes is a concatenation of many responses
1187                done       = false;
1188                index      = 1;
1189                resp_index = 1;
1190                while ~done
1191                    out(index)  = wl_resp(resp(resp_index:end));
1192                    currRespLen = out(index).len();
1193                   
1194                    resp_index  = resp_index + currRespLen;
1195                    index       = index + 1;
1196                   
1197                    if(resp_index >= length(resp))
1198                        done = true;
1199                    end
1200                end
1201            else
1202                out = [];
1203            end
1204        end
1205       
1206       
1207        function out = repr(obj)
1208            % Return a string representation of the wl_node
1209            out = cell(1,length(obj));
1210           
1211            for n = 1:length(obj)
1212                currObj = obj(n); 
1213               
1214                if(isempty(currObj.ID))
1215                    out(n) =  {'Node has not been initialized'};
1216                else
1217                    ID     = sprintf('%d', currObj.ID);
1218                   
1219                    if(currObj.hwVer == 3)
1220                        SN = sprintf('W3-a-%05d',currObj.serialNumber);
1221                    else
1222                        SN = 'N/A';
1223                    end
1224                   
1225                    out(n) = {sprintf('%12s (ID = %3s)', SN, ID)};
1226                end
1227            end
1228           
1229            if (length(obj) == 1) 
1230                out = out{1};
1231            end
1232        end
1233       
1234       
1235        function disp(obj)
1236            % This is a "pretty print" method to summarize details about wl_node
1237            % objects and print them in a table on Matlab's command line.
1238            %
1239            hasUserExt = 0;
1240            strLen     = 0;
1241           
1242            for n = 1:length(obj)
1243               currObj = obj(n);
1244               hasUserExt = hasUserExt || ~isempty(currObj.user); 
1245               
1246               if(~isempty(currObj.user))
1247                  strLen = max(strLen,length(class(currObj.user)) + 3);        % +3 is for surrounding spaces and the |
1248               end
1249            end
1250           
1251            extraTitle = '';
1252            extraLine = '';
1253            extraArgs = '';
1254           
1255            if(hasUserExt)
1256                extraTitle = repmat(' ', 1, strLen-1);
1257                extraLine  = repmat('-', 1, strLen-1);
1258                extraTitle = [extraTitle, '|'];
1259                extraLine  = [extraLine, '-'];
1260               
1261                newTitle = 'User Ext.';
1262                extraTitle((strLen - length(newTitle) - 1):(end - 2)) = newTitle;
1263            end
1264                    fprintf('Displaying properties of %d wl_node objects:\n',length(obj));
1265                    fprintf('|  ID |  WLVER |  HWVER |    Serial # |  Ethernet MAC Addr |          Address | %s\n', extraTitle)
1266                    fprintf('-------------------------------------------------------------------------------%s\n', extraLine)
1267            for n = 1:length(obj)
1268                currObj = obj(n); 
1269               
1270                if(~isempty(currObj.user))
1271                    myFormat = sprintf('%%%ds |',strLen-2);
1272                    extraArgs = sprintf(myFormat,class(currObj.user));
1273                elseif(hasUserExt)
1274                    extraArgs = extraLine;
1275                    extraArgs(end) = '|';
1276                end
1277               
1278                if(isempty(currObj.ID))
1279                    fprintf('|     N/A     Node object has not been initialized                            |%s\n', extraArgs)
1280                else
1281                    ID    = sprintf('%d', currObj.ID);
1282                    WLVER = sprintf('%d.%d.%d', currObj.wlVer_major, currObj.wlVer_minor, currObj.wlVer_revision);
1283                    HWVER = sprintf('%d', currObj.hwVer);
1284                   
1285                    if(currObj.hwVer == 3)
1286                        SN = sprintf('W3-a-%05d',currObj.serialNumber);
1287                    else
1288                        SN = 'N/A';
1289                    end
1290                   
1291                    temp    = dec2hex(uint64(currObj.eth_MAC_addr),12);                   
1292                    MACADDR = sprintf('%2s-%2s-%2s-%2s-%2s-%2s',...
1293                        temp(1:2),temp(3:4),temp(5:6),temp(7:8),temp(9:10),temp(11:12));
1294
1295                    if ( ~isempty( currObj.transport ) & ~isempty( currObj.transport.getAddress() ) )
1296                        ADDR = currObj.transport.getAddress();
1297                    else
1298                        ADDR = '';
1299                    end                     
1300                       
1301                    fprintf('|%4s |%7s |%7s |%12s |%19s |%17s |%s\n', ID, WLVER, HWVER, SN, MACADDR, ADDR, extraArgs);
1302                   
1303                end
1304                    fprintf('-------------------------------------------------------------------------------%s\n', extraLine)
1305            end
1306        end       
1307    end % end methods(Hidden)
1308end % end classdef
Note: See TracBrowser for help on using the repository browser.