[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] | 12 | classdef wl_node < handle_light |
---|
[4330] | 13 | % The WARPLab node class represents one node in a WARPLab network |
---|
[1915] | 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) |
---|
[4330] | 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 |
---|
[1915] | 29 | wlVer_minor; |
---|
| 30 | wlVer_revision; |
---|
[4330] | 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 |
---|
[1915] | 40 | end |
---|
[4330] | 41 | |
---|
[1915] | 42 | properties (SetAccess = public) |
---|
[4332] | 43 | name; % User specified name for node; user scripts supply this |
---|
[1915] | 44 | end |
---|
[4330] | 45 | |
---|
[1948] | 46 | properties(Hidden = true,Constant = true) |
---|
[4330] | 47 | % Command Groups - Most Significant Byte of Command ID |
---|
[4332] | 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'); |
---|
[1948] | 54 | end |
---|
[4330] | 55 | |
---|
[1948] | 56 | properties(Hidden = true,Constant = true) |
---|
[4330] | 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 |
---|
[4784] | 64 | CMD_NODE_CONFIG_SETUP = 5; % 0x000005 |
---|
[4330] | 65 | CMD_NODE_CONFIG_RESET = 6; % 0x000006 |
---|
[4784] | 66 | |
---|
| 67 | CMD_MEM_RW = 16; % 0x000010 |
---|
[1948] | 68 | end |
---|
[4330] | 69 | |
---|
[1915] | 70 | methods |
---|
[2027] | 71 | function obj = wl_node() |
---|
[4332] | 72 | % The constructor is intentionally blank for wl_node objects. |
---|
| 73 | % Instead, the objects are configured via the separate applyConfiguration method. |
---|
[2027] | 74 | end |
---|
| 75 | |
---|
[4332] | 76 | |
---|
[2027] | 77 | function applyConfiguration(objVec, IDVec) |
---|
[4332] | 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)) |
---|
[2865] | 82 | error('applyConfiguration only operates on a single object with a single ID. Provided parameters with lengths: %d and %d', length(objVec), length(IDVec) ); |
---|
[2027] | 83 | end |
---|
[1915] | 84 | |
---|
[2027] | 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 |
---|
[4332] | 90 | switch (class(currID)) |
---|
[2027] | 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 |
---|
[2865] | 95 | error('Unknown argument. Serial Number provided is blank'); |
---|
[2027] | 96 | end |
---|
| 97 | |
---|
| 98 | if ( ~strcmp( currID.ID, '' ) ) |
---|
| 99 | obj.ID = sscanf( currID.ID, '%d'); |
---|
| 100 | else |
---|
[2865] | 101 | error('Unknown argument. Node ID provided is blank'); |
---|
[2027] | 102 | end |
---|
[1915] | 103 | |
---|
[2027] | 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 |
---|
[2865] | 112 | error('Unknown argument. IDVec is of type "%s", need "struct", or "double"', class(currID)); |
---|
[2027] | 113 | end |
---|
| 114 | |
---|
| 115 | |
---|
| 116 | % Get ini configuration file |
---|
| 117 | configFile = which('wl_config.ini'); |
---|
| 118 | if(isempty(configFile)) |
---|
[2865] | 119 | error('cannot find wl_config.ini. please run wl_setup.m'); |
---|
[2027] | 120 | end |
---|
[2001] | 121 | |
---|
[4332] | 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',''}; |
---|
[2027] | 125 | transportType = inifile(configFile,'read',readKeys); |
---|
| 126 | transportType = transportType{1}; |
---|
[2001] | 127 | |
---|
[2027] | 128 | switch(transportType) |
---|
| 129 | case 'java' |
---|
| 130 | obj.transport = wl_transport_eth_udp_java; |
---|
[2149] | 131 | case 'wl_mex_udp' |
---|
[2084] | 132 | obj.transport = wl_transport_eth_udp_mex; |
---|
[2027] | 133 | end |
---|
[2001] | 134 | |
---|
[2084] | 135 | if(isempty(obj.trigger_manager)) |
---|
| 136 | obj.wl_setTriggerManager('wl_trigger_manager_proc'); |
---|
| 137 | end |
---|
| 138 | |
---|
[2027] | 139 | readKeys = {'network','','host_address',''}; |
---|
| 140 | IP = inifile(configFile,'read',readKeys); |
---|
| 141 | IP = IP{1}; |
---|
| 142 | IP = sscanf(IP,'%d.%d.%d.%d'); |
---|
[1915] | 143 | |
---|
[2027] | 144 | readKeys = {'network','','host_ID',''}; |
---|
| 145 | hostID = inifile(configFile,'read',readKeys); |
---|
| 146 | hostID = hostID{1}; |
---|
| 147 | hostID = sscanf(hostID,'%d'); |
---|
[1915] | 148 | |
---|
[2027] | 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'); |
---|
[1915] | 153 | |
---|
[2027] | 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, '' ) ) |
---|
[4416] | 162 | obj.transport.setAddress(currID.ipAddress); |
---|
[2027] | 163 | else |
---|
[2865] | 164 | error('Unknown argument. IP Address provided is blank'); |
---|
[2027] | 165 | end |
---|
| 166 | case 'double' |
---|
[4416] | 167 | obj.transport.setAddress(sprintf('%d.%d.%d.%d', IP(1), IP(2), IP(3), (obj.ID + 1))); |
---|
[2027] | 168 | end |
---|
| 169 | |
---|
[4674] | 170 | obj.transport.setPort(unicast_starting_port); |
---|
[1915] | 171 | |
---|
[2027] | 172 | obj.transport.open(); |
---|
| 173 | obj.transport.hdr.srcID = hostID; % ????? Redundant ????? - TBD |
---|
| 174 | obj.transport.hdr.destID = obj.ID; % ????? Redundant ????? - TBD |
---|
[1964] | 175 | |
---|
[2027] | 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 = []; |
---|
[2009] | 181 | |
---|
[2027] | 182 | if(isempty(obj.baseband)) |
---|
[4332] | 183 | % Instantiate baseband object |
---|
[2027] | 184 | obj.wl_setBaseband('wl_baseband_buffers'); |
---|
| 185 | end |
---|
[2009] | 186 | |
---|
[4332] | 187 | % Read details from the hardware (serial number, etc) and save to local properties |
---|
[2027] | 188 | obj.wl_nodeCmd('get_hardware_info'); |
---|
[1959] | 189 | |
---|
[2027] | 190 | % Instantiate interfaces group. |
---|
| 191 | if(isempty(obj.interfaceGroups)) |
---|
| 192 | obj.interfaceGroups{1} = wl_interface_group_X245(1:obj.num_interfaces, 'w3'); |
---|
| 193 | end |
---|
[1959] | 194 | |
---|
[2027] | 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 |
---|
[2009] | 201 | |
---|
[2027] | 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); |
---|
[1915] | 205 | end |
---|
[2027] | 206 | |
---|
[4332] | 207 | |
---|
[2002] | 208 | function wl_setUserExtension(obj,module) |
---|
[4332] | 209 | % Sets the User Extension module to a user-provided object or |
---|
| 210 | % string of that object's class name. |
---|
| 211 | % |
---|
[2002] | 212 | makeComparison = 1; |
---|
[4332] | 213 | |
---|
| 214 | if(module == 0) % No module attached |
---|
[2002] | 215 | module = 'double(0)'; |
---|
| 216 | makeComparison = 0; |
---|
[4332] | 217 | elseif(~ischar(module)) % Input is an actual instance of an object |
---|
[2002] | 218 | module = class(module); |
---|
[1948] | 219 | end |
---|
| 220 | |
---|
[2002] | 221 | if(makeComparison && ~any(strcmp(superclasses(module),'wl_user_ext'))) |
---|
[2865] | 222 | error('Module is not a wl_user_ext type'); |
---|
[2002] | 223 | end |
---|
| 224 | |
---|
[1948] | 225 | for n = 1:length(obj) |
---|
[2002] | 226 | obj(n).user = eval(module); |
---|
[1948] | 227 | end |
---|
[2002] | 228 | end |
---|
| 229 | |
---|
[4332] | 230 | |
---|
[2002] | 231 | function wl_setBaseband(obj,module) |
---|
[4332] | 232 | % Sets the Baseband module to a user-provided object or |
---|
| 233 | % string of that object's class name. |
---|
| 234 | % |
---|
[2002] | 235 | makeComparison = 1; |
---|
[4332] | 236 | |
---|
| 237 | if(module == 0) % No module attached |
---|
[2002] | 238 | module = 'double(0)'; |
---|
| 239 | makeComparison = 0; |
---|
[4332] | 240 | elseif(~ischar(module)) % Input is an actual instance of an object |
---|
[2002] | 241 | module = class(module); |
---|
| 242 | end |
---|
[1948] | 243 | |
---|
[2002] | 244 | if(makeComparison && ~any(strcmp(superclasses(module),'wl_baseband'))) |
---|
[2865] | 245 | error('Module is not a wl_baseband type'); |
---|
[2002] | 246 | end |
---|
| 247 | |
---|
| 248 | for n = 1:length(obj) |
---|
| 249 | obj(n).baseband = eval(module); |
---|
| 250 | end |
---|
[1948] | 251 | end |
---|
| 252 | |
---|
[4332] | 253 | |
---|
[2002] | 254 | function wl_setTriggerManager(obj,module) |
---|
[4332] | 255 | % Sets the Trigger Manager module to a user-provided object or |
---|
| 256 | % string of that object's class name. |
---|
| 257 | % |
---|
[2002] | 258 | makeComparison = 1; |
---|
[4332] | 259 | |
---|
| 260 | if(module == 0) % No module attached |
---|
[2002] | 261 | module = 'double(0)'; |
---|
| 262 | makeComparison = 0; |
---|
[4332] | 263 | elseif(~ischar(module)) % Input is an actual instance of an object |
---|
[2002] | 264 | module = class(module); |
---|
| 265 | end |
---|
| 266 | |
---|
| 267 | if(makeComparison && ~any(strcmp(superclasses(module),'wl_trigger_manager'))) |
---|
[2865] | 268 | error('Module is not a wl_trigger_manager type'); |
---|
[2002] | 269 | end |
---|
| 270 | |
---|
| 271 | for n = 1:length(obj) |
---|
| 272 | obj(n).trigger_manager = eval(module); |
---|
| 273 | end |
---|
| 274 | end |
---|
| 275 | |
---|
[4332] | 276 | |
---|
[1951] | 277 | function out = wl_basebandCmd(obj, varargin) |
---|
[4332] | 278 | % Sends commands to the baseband object. |
---|
[1915] | 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 | % |
---|
[1951] | 283 | % wl_basebandCmd(node0, args ) |
---|
| 284 | % wl_basebandCmd([node0, node1], args ) |
---|
| 285 | % node0.wl_basebandCmd( args ) |
---|
[1915] | 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. |
---|
[4332] | 291 | % |
---|
| 292 | nodes = obj; |
---|
[1915] | 293 | numNodes = numel(nodes); |
---|
| 294 | |
---|
| 295 | for n = numNodes:-1:1 |
---|
| 296 | currNode = nodes(n); |
---|
[2001] | 297 | if(any(strcmp(superclasses(currNode.baseband),'wl_baseband'))) |
---|
| 298 | out(n) = currNode.baseband.procCmd(n, currNode, varargin{:}); |
---|
| 299 | else |
---|
[2865] | 300 | error('Node %d does not have an attached baseband module',currNode.ID); |
---|
[2001] | 301 | end |
---|
[1915] | 302 | end |
---|
[4332] | 303 | |
---|
| 304 | if((length(out) == 1) && iscell(out)) |
---|
| 305 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
[1915] | 306 | end |
---|
| 307 | end |
---|
| 308 | |
---|
[4332] | 309 | |
---|
[1915] | 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. |
---|
[4332] | 324 | % |
---|
| 325 | nodes = obj; |
---|
[1915] | 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 | |
---|
[4332] | 333 | if((length(out) == 1) && iscell(out)) |
---|
| 334 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
| 335 | end |
---|
[1915] | 336 | end |
---|
| 337 | |
---|
[4332] | 338 | |
---|
[1951] | 339 | function out = wl_transportCmd(obj, varargin) |
---|
[4332] | 340 | % Sends commands to the transport object. |
---|
[1915] | 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 | % |
---|
[1951] | 345 | % wl_transportCmd(node0, args ) |
---|
| 346 | % wl_transportCmd([node0, node1], args ) |
---|
| 347 | % node0.wl_transportCmd( args ) |
---|
[1915] | 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. |
---|
[4332] | 353 | % |
---|
| 354 | nodes = obj; |
---|
[1915] | 355 | numNodes = numel(nodes); |
---|
| 356 | |
---|
| 357 | for n = numNodes:-1:1 |
---|
| 358 | currNode = nodes(n); |
---|
[2001] | 359 | if(any(strcmp(superclasses(currNode.transport),'wl_transport'))) |
---|
| 360 | out(n) = currNode.transport.procCmd(n, currNode, varargin{:}); |
---|
| 361 | else |
---|
[2865] | 362 | error('Node %d does not have an attached transport module',currNode.ID); |
---|
[2001] | 363 | end |
---|
| 364 | |
---|
[1915] | 365 | end |
---|
[4332] | 366 | |
---|
| 367 | if((length(out) == 1) && iscell(out)) |
---|
| 368 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
[1948] | 369 | end |
---|
| 370 | end |
---|
| 371 | |
---|
[4332] | 372 | |
---|
[2919] | 373 | function out = wl_userExtCmd(obj, varargin) |
---|
[4332] | 374 | % Sends commands to the user extension object. |
---|
[1948] | 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. |
---|
[4332] | 387 | % |
---|
| 388 | nodes = obj; |
---|
[1948] | 389 | numNodes = numel(nodes); |
---|
[1915] | 390 | |
---|
[4332] | 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); |
---|
[1948] | 397 | end |
---|
[4332] | 398 | end |
---|
[1948] | 399 | |
---|
[4332] | 400 | if((length(out) == 1) && iscell(out)) |
---|
| 401 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
| 402 | end |
---|
[1915] | 403 | end |
---|
| 404 | |
---|
[4332] | 405 | |
---|
[1951] | 406 | function out = wl_triggerManagerCmd(obj, varargin) |
---|
[4332] | 407 | % Sends commands to the trigger manager object. |
---|
[1915] | 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 | % |
---|
[1972] | 412 | % wl_triggerManagerCmd(node0, args ) |
---|
| 413 | % wl_triggerManagerCmd([node0, node1], args ) |
---|
| 414 | % node0.wl_triggerManagerCmd( args ) |
---|
[1915] | 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. |
---|
[4332] | 420 | % |
---|
| 421 | nodes = obj; |
---|
[1915] | 422 | numNodes = numel(nodes); |
---|
| 423 | |
---|
| 424 | for n = numNodes:-1:1 |
---|
| 425 | currNode = nodes(n); |
---|
[2001] | 426 | if(any(strcmp(superclasses(currNode.trigger_manager),'wl_trigger_manager'))) |
---|
| 427 | out(n) = currNode.trigger_manager.procCmd(n, currNode, varargin{:}); |
---|
| 428 | else |
---|
[2865] | 429 | error('Node %d does not have an attached trigger manager module',currNode.ID); |
---|
[2001] | 430 | end |
---|
[1915] | 431 | end |
---|
[4332] | 432 | |
---|
| 433 | if((length(out) == 1) && iscell(out)) |
---|
| 434 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
[1915] | 435 | end |
---|
| 436 | end |
---|
| 437 | |
---|
| 438 | |
---|
[1951] | 439 | function out = wl_interfaceCmd(obj, rfSel, varargin) |
---|
[4332] | 440 | % Sends commands to the interface object. |
---|
[1915] | 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 | % |
---|
[1951] | 450 | % wl_interfaceCmd(node0, RFMASKS, args ) |
---|
| 451 | % wl_interfaceCmd([node0, node1], RFMASKS, args ) |
---|
[1915] | 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. |
---|
[4332] | 458 | % |
---|
| 459 | nodes = obj; |
---|
[1915] | 460 | numNodes = numel(nodes); |
---|
| 461 | |
---|
[1959] | 462 | ifcIndex = 1; |
---|
| 463 | for nodeIndex = numNodes:-1:1 |
---|
[2001] | 464 | currNode = nodes(nodeIndex); |
---|
[4687] | 465 | if(any(strcmp(superclasses(currNode.interfaceGroups{ifcIndex}), 'wl_interface_group'))) |
---|
[2001] | 466 | out(nodeIndex) = currNode.interfaceGroups{ifcIndex}.procCmd(nodeIndex, currNode, rfSel, varargin{:}); |
---|
| 467 | else |
---|
[2865] | 468 | error('Node %d does not have an attached interface group module',currNode.ID); |
---|
[2001] | 469 | end |
---|
[1959] | 470 | end |
---|
[4687] | 471 | |
---|
[4330] | 472 | if((length(out) == 1) && iscell(out)) |
---|
[4332] | 473 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
[1915] | 474 | end |
---|
| 475 | end |
---|
| 476 | |
---|
[4332] | 477 | |
---|
[1915] | 478 | function varargout = wl_getInterfaceIDs(obj) |
---|
[4681] | 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. |
---|
[1972] | 481 | % |
---|
[4681] | 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. |
---|
[4327] | 486 | % |
---|
[4681] | 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) |
---|
[4817] | 532 | error('Node %d has only %d interfaces. User has requested %d interface IDs', obj.ID, obj.num_interfaces, nargout); |
---|
[4681] | 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}; |
---|
[4332] | 604 | end |
---|
[1915] | 605 | end |
---|
[4332] | 606 | |
---|
| 607 | |
---|
[2007] | 608 | function varargout = wl_getTriggerInputIDs(obj) |
---|
[4681] | 609 | % Returns the trigger input IDs that can be used as inputs to trigger manager commands |
---|
[2007] | 610 | % |
---|
[4681] | 611 | % The trigger input IDs are returned in a structure that contains fields for individual |
---|
| 612 | % triggers. |
---|
[2007] | 613 | % |
---|
[4681] | 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}; |
---|
[2007] | 685 | end |
---|
| 686 | end |
---|
[1915] | 687 | |
---|
[4332] | 688 | |
---|
[2007] | 689 | function varargout = wl_getTriggerOutputIDs(obj) |
---|
[4681] | 690 | % Returns the trigger output IDs that can be used as inputs to trigger manager commands |
---|
[2007] | 691 | % |
---|
[4681] | 692 | % The trigger output IDs are returned in a structure that contains fields for individual |
---|
| 693 | % triggers. |
---|
[2007] | 694 | % |
---|
[4681] | 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: |
---|
[4704] | 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) |
---|
[4681] | 725 | % |
---|
| 726 | if(nargout > length(obj.trigger_manager.triggerOutputIDs)) |
---|
[4704] | 727 | error('Node %d has only %d trigger outputs. User has requested %d trigger output IDs',obj.ID,length(obj.trigger_manager.triggerOutputIDs),nargout); |
---|
[4681] | 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 |
---|
[4704] | 736 | fprintf('WARNING: This syntax for wl_getTriggerOutputIDs() is being deprecated.\n'); |
---|
[4681] | 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}; |
---|
[2007] | 763 | end |
---|
[4332] | 764 | end |
---|
| 765 | |
---|
| 766 | |
---|
[2919] | 767 | function verify_writeIQ_checksum(obj, checksum) |
---|
[4332] | 768 | % This is a callback to verify the WriteIQ checksum in the case that WriteIQ is called by a broadcast |
---|
| 769 | % transport. |
---|
[2919] | 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 |
---|
[2007] | 781 | |
---|
[4332] | 782 | |
---|
[1915] | 783 | function delete(obj) |
---|
[4332] | 784 | % Clears the transport object to close any open socket |
---|
| 785 | % connections in the event that the node object is deleted. |
---|
[2001] | 786 | if(~isempty(obj.transport)) |
---|
| 787 | obj.transport.delete(); |
---|
| 788 | obj.transport = []; |
---|
| 789 | end |
---|
[1915] | 790 | end |
---|
| 791 | end |
---|
| 792 | |
---|
[1972] | 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 |
---|
[4332] | 800 | |
---|
| 801 | |
---|
[1915] | 802 | methods(Hidden=true) |
---|
[4332] | 803 | % These methods are hidden because users are not intended to call |
---|
| 804 | % them directly from their WARPLab scripts. |
---|
[1948] | 805 | |
---|
| 806 | function out = calcCmd(obj, grp, cmdID) |
---|
[1972] | 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. |
---|
[1948] | 810 | switch(lower(grp)) |
---|
| 811 | case 'node' |
---|
[4332] | 812 | out = obj.calcCmd_helper(obj.GRPID_NODE, cmdID); |
---|
[1948] | 813 | case 'interface' |
---|
[4332] | 814 | out = obj.calcCmd_helper(obj.GRPID_IFC, cmdID); |
---|
[1948] | 815 | case 'baseband' |
---|
[4332] | 816 | out = obj.calcCmd_helper(obj.GRPID_BB, cmdID); |
---|
[1948] | 817 | case 'trigger_manager' |
---|
[4332] | 818 | out = obj.calcCmd_helper(obj.GRPID_TRIGMNGR, cmdID); |
---|
[1948] | 819 | case 'transport' |
---|
[4332] | 820 | out = obj.calcCmd_helper(obj.GRPID_TRANS, cmdID); |
---|
[1948] | 821 | case 'user_extension' |
---|
[4332] | 822 | out = obj.calcCmd_helper(obj.GRPID_USER, cmdID); |
---|
[1948] | 823 | end |
---|
[4332] | 824 | end |
---|
| 825 | |
---|
| 826 | |
---|
[1972] | 827 | function out = procCmd(obj, nodeInd, node, cmdStr, varargin) |
---|
[4332] | 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); |
---|
[1915] | 839 | switch(cmdStr) |
---|
[4332] | 840 | |
---|
| 841 | %--------------------------------------------------------- |
---|
[1915] | 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 | % |
---|
[1989] | 848 | % Hardware support: |
---|
[4332] | 849 | % All: |
---|
| 850 | % WARPLab design version |
---|
| 851 | % Hardware version |
---|
| 852 | % Ethernet MAC Address |
---|
| 853 | % Number of Interface Groups |
---|
| 854 | % Number of Interfaces |
---|
[1915] | 855 | % |
---|
[4332] | 856 | % WARP v3: |
---|
| 857 | % Serial number |
---|
| 858 | % Virtex-6 FPGA DNA |
---|
| 859 | % |
---|
| 860 | [MAJOR, MINOR, REVISION, XTRA] = wl_ver(); |
---|
[2036] | 861 | |
---|
[4332] | 862 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_INFO)); |
---|
| 863 | resp = node.sendCmd(myCmd); |
---|
| 864 | resp = resp.getArgs(); |
---|
[2036] | 865 | |
---|
[4332] | 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) |
---|
[2027] | 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) ) ) |
---|
[2865] | 884 | error('Serial Number provided in config, W3-a-%d, does not match HW serial number W3-a-%d ', obj.serialNumber, resp(1)) |
---|
[2027] | 885 | end |
---|
| 886 | else |
---|
| 887 | obj.serialNumber = resp(1); |
---|
| 888 | end |
---|
[1915] | 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 | |
---|
[4332] | 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)); |
---|
[1915] | 897 | obj.wlVer_revision = double(bitand(resp(6), 255)); |
---|
| 898 | |
---|
[4332] | 899 | if((obj.wlVer_major ~= MAJOR) || (obj.wlVer_minor ~= MINOR)) |
---|
[2036] | 900 | myErrorMsg = sprintf('Node %d reports WARPLab version %d.%d.%d while this PC is configured with %d.%d.%d', ... |
---|
[4332] | 901 | obj.ID, obj.wlVer_major, obj.wlVer_minor, obj.wlVer_revision, MAJOR, MINOR, REVISION); |
---|
[2865] | 902 | error(myErrorMsg); |
---|
[2036] | 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', ... |
---|
[4332] | 907 | obj.ID, obj.wlVer_major, obj.wlVer_minor, obj.wlVer_revision, MAJOR, MINOR, REVISION); |
---|
[2865] | 908 | warning(myWarningMsg); |
---|
[2036] | 909 | end |
---|
| 910 | |
---|
[4320] | 911 | % Get the maximum supported IQ lengths |
---|
[4334] | 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 |
---|
[4320] | 915 | |
---|
| 916 | % Get the current supported IQ lengths |
---|
| 917 | obj.baseband.txIQLen = double(resp(9)); |
---|
| 918 | obj.baseband.rxIQLen = double(resp(10)); |
---|
[4334] | 919 | obj.baseband.rxRSSILen = double(resp(10))/4; |
---|
[2036] | 920 | |
---|
[4197] | 921 | % Trigger Manager -- core runs at different speed depending on HW version. |
---|
[4320] | 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)); |
---|
[4817] | 925 | |
---|
[2011] | 926 | switch(obj.hwVer) |
---|
[4384] | 927 | %TODO: These parameters should be passed up from |
---|
| 928 | %the board |
---|
[2011] | 929 | case 1 |
---|
[4332] | 930 | error('WARP v1 Hardware is not supported by WARPLab 7'); |
---|
[4474] | 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 | % |
---|
[4817] | 935 | clock_freq = 160e6; |
---|
| 936 | trig_in_ids = obj.wl_getTriggerInputIDs(); |
---|
| 937 | trig_out_ids = obj.wl_getTriggerOutputIDs(); |
---|
[4474] | 938 | |
---|
| 939 | % Trigger output delays |
---|
[4384] | 940 | for k = length(obj.trigger_manager.triggerOutputIDs):-1:1 |
---|
[4928] | 941 | % With Trigger Processor v1.07.a, all delays were increased to 65535 |
---|
[4474] | 942 | obj.trigger_manager.output_delayStep_ns(k) = (1/(clock_freq))*1e9; |
---|
[4928] | 943 | obj.trigger_manager.output_delayMax_ns(k) = 65535 * obj.trigger_manager.output_delayStep_ns(k); |
---|
[4474] | 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 |
---|
[2011] | 951 | end |
---|
[2009] | 952 | |
---|
[4320] | 953 | obj.num_interfacesGroups = resp(12); |
---|
| 954 | obj.num_interfaces = resp(13); |
---|
[4332] | 955 | |
---|
| 956 | %% TODO - parse each interface descriptor and create interface group objects |
---|
| 957 | |
---|
| 958 | %--------------------------------------------------------- |
---|
[2003] | 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 |
---|
[4332] | 967 | % |
---|
| 968 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TEMPERATURE)); |
---|
| 969 | resp = node.sendCmd(myCmd); |
---|
| 970 | resp = resp.getArgs(); |
---|
[2003] | 971 | |
---|
[4332] | 972 | % Convert from raw temperature to Celsius |
---|
[2003] | 973 | out = ((double(resp)/65536.0)/0.00198421639) - 273.15; |
---|
[1915] | 974 | |
---|
[4332] | 975 | %--------------------------------------------------------- |
---|
[1915] | 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 |
---|
[4332] | 981 | % |
---|
| 982 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_INITIALIZE)); |
---|
[1915] | 983 | node.sendCmd(myCmd); |
---|
[4332] | 984 | |
---|
| 985 | %--------------------------------------------------------- |
---|
[1915] | 986 | case 'identify' |
---|
[1989] | 987 | % Blinks the user LEDs on the WARP node, to help identify MATLAB node-to-hardware node mapping |
---|
[1915] | 988 | % |
---|
| 989 | % Arguments: none |
---|
| 990 | % Returns: none |
---|
[4332] | 991 | % |
---|
| 992 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_IDENTIFY)); |
---|
[1915] | 993 | node.sendCmd(myCmd); |
---|
[4332] | 994 | |
---|
| 995 | %--------------------------------------------------------- |
---|
[2029] | 996 | case 'node_config_reset' |
---|
| 997 | % Resets the HW state of the node to accept a new node configuration |
---|
[2027] | 998 | % |
---|
| 999 | % Arguments: none |
---|
| 1000 | % Returns: none |
---|
[4332] | 1001 | % |
---|
| 1002 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODE_CONFIG_RESET)); |
---|
| 1003 | myCmd.addArgs(uint32(node.serialNumber)); |
---|
[2027] | 1004 | node.sendCmd(myCmd); |
---|
[4332] | 1005 | |
---|
| 1006 | %--------------------------------------------------------- |
---|
[4784] | 1007 | % 'node_config_setup' is only implemented in wl_initNodes.m |
---|
| 1008 | % |
---|
[4788] | 1009 | |
---|
[4784] | 1010 | %--------------------------------------------------------- |
---|
[4788] | 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 | % |
---|
[4819] | 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 | % |
---|
[4788] | 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 | % |
---|
[4819] | 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 | % |
---|
[4788] | 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 | %--------------------------------------------------------- |
---|
[1915] | 1141 | otherwise |
---|
[2865] | 1142 | error( 'unknown node command %s', cmdStr); |
---|
[1915] | 1143 | end |
---|
| 1144 | |
---|
[4332] | 1145 | if((iscell(out) == 0) && (numel(out) ~= 1)) |
---|
[1915] | 1146 | out = {out}; |
---|
| 1147 | end |
---|
| 1148 | end |
---|
| 1149 | |
---|
[4332] | 1150 | |
---|
[1915] | 1151 | function out = sendCmd(obj, cmd) |
---|
[4332] | 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 | % |
---|
[4309] | 1160 | resp = obj.transport.send(cmd.serialize(), true); |
---|
[1923] | 1161 | |
---|
[1915] | 1162 | out = wl_resp(resp); |
---|
| 1163 | end |
---|
| 1164 | |
---|
[4332] | 1165 | |
---|
[1915] | 1166 | function sendCmd_noresp(obj, cmd) |
---|
[4332] | 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 |
---|
[1915] | 1172 | % the command and then immediately return. |
---|
[4332] | 1173 | % |
---|
[4309] | 1174 | obj.transport.send(cmd.serialize(), false); |
---|
[1915] | 1175 | end |
---|
| 1176 | |
---|
[4332] | 1177 | |
---|
[1915] | 1178 | function out = receiveResp(obj) |
---|
[4332] | 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 | % |
---|
[1915] | 1183 | resp = obj.transport.receive(); |
---|
[4332] | 1184 | |
---|
[1915] | 1185 | if(~isempty(resp)) |
---|
[4309] | 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; |
---|
[1915] | 1190 | while ~done |
---|
[4309] | 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)) |
---|
[1915] | 1198 | done = true; |
---|
| 1199 | end |
---|
| 1200 | end |
---|
| 1201 | else |
---|
| 1202 | out = []; |
---|
| 1203 | end |
---|
| 1204 | end |
---|
| 1205 | |
---|
[4332] | 1206 | |
---|
[4815] | 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 | |
---|
[1923] | 1235 | function disp(obj) |
---|
[4332] | 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 | % |
---|
[1948] | 1239 | hasUserExt = 0; |
---|
[4332] | 1240 | strLen = 0; |
---|
| 1241 | |
---|
[1948] | 1242 | for n = 1:length(obj) |
---|
| 1243 | currObj = obj(n); |
---|
| 1244 | hasUserExt = hasUserExt || ~isempty(currObj.user); |
---|
[4332] | 1245 | |
---|
[1948] | 1246 | if(~isempty(currObj.user)) |
---|
[4332] | 1247 | strLen = max(strLen,length(class(currObj.user)) + 3); % +3 is for surrounding spaces and the | |
---|
[1948] | 1248 | end |
---|
| 1249 | end |
---|
| 1250 | |
---|
| 1251 | extraTitle = ''; |
---|
| 1252 | extraLine = ''; |
---|
| 1253 | extraArgs = ''; |
---|
| 1254 | |
---|
| 1255 | if(hasUserExt) |
---|
[4332] | 1256 | extraTitle = repmat(' ', 1, strLen-1); |
---|
| 1257 | extraLine = repmat('-', 1, strLen-1); |
---|
| 1258 | extraTitle = [extraTitle, '|']; |
---|
| 1259 | extraLine = [extraLine, '-']; |
---|
[1948] | 1260 | |
---|
| 1261 | newTitle = 'User Ext.'; |
---|
| 1262 | extraTitle((strLen - length(newTitle) - 1):(end - 2)) = newTitle; |
---|
| 1263 | end |
---|
[1923] | 1264 | fprintf('Displaying properties of %d wl_node objects:\n',length(obj)); |
---|
[4332] | 1265 | fprintf('| ID | WLVER | HWVER | Serial # | Ethernet MAC Addr | Address | %s\n', extraTitle) |
---|
| 1266 | fprintf('-------------------------------------------------------------------------------%s\n', extraLine) |
---|
[1923] | 1267 | for n = 1:length(obj) |
---|
| 1268 | currObj = obj(n); |
---|
[1948] | 1269 | |
---|
| 1270 | if(~isempty(currObj.user)) |
---|
| 1271 | myFormat = sprintf('%%%ds |',strLen-2); |
---|
[1950] | 1272 | extraArgs = sprintf(myFormat,class(currObj.user)); |
---|
[1948] | 1273 | elseif(hasUserExt) |
---|
| 1274 | extraArgs = extraLine; |
---|
| 1275 | extraArgs(end) = '|'; |
---|
| 1276 | end |
---|
| 1277 | |
---|
[1923] | 1278 | if(isempty(currObj.ID)) |
---|
[4332] | 1279 | fprintf('| N/A Node object has not been initialized |%s\n', extraArgs) |
---|
[1923] | 1280 | else |
---|
[4332] | 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); |
---|
[1923] | 1284 | |
---|
| 1285 | if(currObj.hwVer == 3) |
---|
| 1286 | SN = sprintf('W3-a-%05d',currObj.serialNumber); |
---|
| 1287 | else |
---|
| 1288 | SN = 'N/A'; |
---|
| 1289 | end |
---|
| 1290 | |
---|
[4332] | 1291 | temp = dec2hex(uint64(currObj.eth_MAC_addr),12); |
---|
[1923] | 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)); |
---|
[2027] | 1294 | |
---|
[4416] | 1295 | if ( ~isempty( currObj.transport ) & ~isempty( currObj.transport.getAddress() ) ) |
---|
| 1296 | ADDR = currObj.transport.getAddress(); |
---|
[2027] | 1297 | else |
---|
| 1298 | ADDR = ''; |
---|
| 1299 | end |
---|
| 1300 | |
---|
[4332] | 1301 | fprintf('|%4s |%7s |%7s |%12s |%19s |%17s |%s\n', ID, WLVER, HWVER, SN, MACADDR, ADDR, extraArgs); |
---|
[1923] | 1302 | |
---|
| 1303 | end |
---|
[4332] | 1304 | fprintf('-------------------------------------------------------------------------------%s\n', extraLine) |
---|
[1923] | 1305 | end |
---|
[1972] | 1306 | end |
---|
[4332] | 1307 | end % end methods(Hidden) |
---|
| 1308 | end % end classdef |
---|