[4332] | 1 | %------------------------------------------------------------------------- |
---|
| 2 | % WARPLab Framework |
---|
[2084] | 3 | % |
---|
[4332] | 4 | % Copyright 2013, Mango Communications. All rights reserved. |
---|
| 5 | % Distributed under the WARP license (http://warpproject.org/license) |
---|
[2084] | 6 | % |
---|
[4332] | 7 | % Chris Hunter (chunter [at] mangocomm.com) |
---|
| 8 | % Patrick Murphy (murphpo [at] mangocomm.com) |
---|
| 9 | % Erik Welsh (welsh [at] mangocomm.com) |
---|
| 10 | %------------------------------------------------------------------------- |
---|
[2149] | 11 | |
---|
[2084] | 12 | classdef wl_transport_eth_udp_mex < wl_transport & handle_light |
---|
| 13 | % Mex physical layer Ethernet UDP Transport for unicast traffic |
---|
| 14 | % User code should not use this object directly-- the parent wl_node will |
---|
| 15 | % instantiate the appropriate transport object for the hardware in use |
---|
| 16 | |
---|
| 17 | %******************************** Properties ********************************** |
---|
| 18 | |
---|
| 19 | properties (SetAccess = public) |
---|
[4416] | 20 | timeout; % Maximum time spent waiting before retransmission |
---|
[2084] | 21 | end |
---|
[2149] | 22 | |
---|
[2084] | 23 | properties (SetAccess = protected, Hidden = true) |
---|
[4416] | 24 | sock; % UDP socket |
---|
| 25 | status; % Status of UDP socket |
---|
| 26 | maxSamples; % Maximum number of samples able to be transmitted (based on maxPayload) |
---|
| 27 | maxPayload; % Maximum payload size (e.g. MTU - ETH/IP/UDP headers) |
---|
[2084] | 28 | end |
---|
[2149] | 29 | |
---|
[4416] | 30 | properties (SetAccess = protected) |
---|
| 31 | address; % IP address of destination |
---|
| 32 | port; % Port of destination |
---|
| 33 | end |
---|
| 34 | |
---|
[2084] | 35 | properties (SetAccess = public) |
---|
[4416] | 36 | hdr; % Transport header object |
---|
| 37 | rxBufferSize; % OS's receive buffer size in bytes |
---|
[2084] | 38 | end |
---|
[2149] | 39 | |
---|
[4332] | 40 | properties(Hidden = true, Constant = true) |
---|
| 41 | % These constants define specific command IDs used by this object. |
---|
| 42 | % Their C counterparts are found in wl_transport.h |
---|
| 43 | GRP = 'transport'; |
---|
| 44 | CMD_PING = 1; % 0x000001 |
---|
[4784] | 45 | CMD_PAYLOADSIZETEST = 2; % 0x000002 |
---|
| 46 | |
---|
| 47 | CMD_NODEGRPID_ADD = 16; % 0x000010 |
---|
| 48 | CMD_NODEGRPID_CLEAR = 17; % 0x000011 |
---|
| 49 | |
---|
| 50 | TRANSPORT_NOT_READY_MAX_RETRY = 50; |
---|
| 51 | TRANSPORT_NOT_READY_WAIT_TIME = 0.1; |
---|
[4818] | 52 | |
---|
| 53 | REQUIRED_MEX_VERSION = '1.0.4a'; % Must match version in MEX transport |
---|
[2084] | 54 | end |
---|
| 55 | |
---|
[4784] | 56 | |
---|
[2084] | 57 | %********************************* Methods ************************************ |
---|
[2149] | 58 | |
---|
[2084] | 59 | methods |
---|
| 60 | |
---|
| 61 | function obj = wl_transport_eth_udp_mex() |
---|
[4332] | 62 | obj.hdr = wl_transport_header; |
---|
[2084] | 63 | obj.hdr.pktType = obj.hdr.PKTTYPE_HTON_MSG; |
---|
[4332] | 64 | obj.status = 0; |
---|
| 65 | obj.timeout = 1; |
---|
[4416] | 66 | obj.maxPayload = 1000; % Sane default. This gets overwritten by CMD_PAYLOADSIZETEST command. |
---|
| 67 | obj.port = 0; |
---|
| 68 | obj.address = '10.0.0.0'; |
---|
[4332] | 69 | |
---|
[2084] | 70 | obj.checkSetup(); |
---|
| 71 | end |
---|
| 72 | |
---|
| 73 | function checkSetup(obj) |
---|
[4332] | 74 | % Check to make sure wl_mex_udp_transport exists and is is configured |
---|
| 75 | % |
---|
[2149] | 76 | temp = which('wl_mex_udp_transport'); |
---|
[4332] | 77 | |
---|
[4818] | 78 | if(isempty(temp)) |
---|
| 79 | error('wl_transport_eth_udp_mex:constructor', 'WARPLab Mex UDP transport not found in Matlab''s path'); |
---|
| 80 | elseif(strcmp(temp((end-length(mexext)+1):end ), mexext) == 0) |
---|
| 81 | error('wl_transport_eth_udp_mex:constructor', 'WARPLab Mex UDP transport found, but it is not a compiled mex file'); |
---|
| 82 | end |
---|
| 83 | |
---|
| 84 | version = wl_mex_udp_transport('version'); |
---|
| 85 | version = sscanf(version, '%d.%d.%d%c'); |
---|
| 86 | version_req = sscanf(obj.REQUIRED_MEX_VERSION, '%d.%d.%d%c'); |
---|
| 87 | |
---|
| 88 | % Version must match required version |
---|
| 89 | if(~(version(1) == version_req(1) && version(2) == version_req(2) && version(3) == version_req(3) && version(4) >= version_req(4))) |
---|
| 90 | version = wl_mex_udp_transport('version'); |
---|
| 91 | version_req = obj.REQUIRED_MEX_VERSION; |
---|
| 92 | |
---|
| 93 | error('wl_transport_eth_udp_mex:constructor', 'MEX transport version mismatch.\nRequires version %s, found version %s', version_req, version); |
---|
| 94 | end |
---|
[2084] | 95 | end |
---|
| 96 | |
---|
[4818] | 97 | function setMaxPayload(obj, value) |
---|
[2149] | 98 | |
---|
[4818] | 99 | if (isempty(value)) |
---|
| 100 | error('wl_transport_eth_udp_mex:setMaxPayload', 'setMaxPayload requires a non-empty argument.'); |
---|
| 101 | else |
---|
| 102 | obj.maxPayload = value; |
---|
[4332] | 103 | |
---|
[4818] | 104 | % Compute the maximum number of samples in each Ethernet packet |
---|
| 105 | % - Start with maxPayload is the max number of bytes per packet (nominally the Ethernet MTU) |
---|
| 106 | % - Subtract sizes of the transport header, command header and samples header |
---|
| 107 | % - Due to DMA alignment issues in the node, the max samples must be 4 sample aligned. |
---|
| 108 | obj.maxSamples = double(bitand(((floor(double(obj.maxPayload)/4) - sizeof(obj.hdr)/4 - sizeof(wl_cmd)/4) - (sizeof(wl_samples)/4)), 4294967292)); |
---|
| 109 | |
---|
| 110 | % fprintf('Max samples: %d\n', obj.maxSamples); |
---|
| 111 | end |
---|
[2149] | 112 | end |
---|
| 113 | |
---|
| 114 | function out = getMaxPayload(obj) |
---|
[2856] | 115 | out = double(obj.maxPayload); |
---|
[2149] | 116 | end |
---|
| 117 | |
---|
[4416] | 118 | function setAddress(obj, value) |
---|
| 119 | if(ischar(value)) |
---|
| 120 | obj.address = value; |
---|
| 121 | else |
---|
| 122 | obj.address = obj.int2IP(value); |
---|
| 123 | end |
---|
| 124 | end |
---|
| 125 | |
---|
| 126 | function out = getAddress(obj) |
---|
| 127 | out = obj.address; |
---|
| 128 | end |
---|
| 129 | |
---|
| 130 | function setPort(obj, value) |
---|
| 131 | obj.port = value; |
---|
| 132 | end |
---|
| 133 | |
---|
| 134 | function out = getPort(obj) |
---|
| 135 | out = obj.port; |
---|
| 136 | end |
---|
| 137 | |
---|
[2084] | 138 | function open(obj,varargin) |
---|
[4332] | 139 | % varargin{1}: (optional) IP address |
---|
| 140 | % varargin{2}: (optional) port |
---|
| 141 | % |
---|
[2084] | 142 | if(nargin==3) |
---|
[4416] | 143 | obj.setAddress(varargin{1}); |
---|
[2084] | 144 | obj.port = varargin{2}; |
---|
| 145 | end |
---|
| 146 | |
---|
| 147 | REQUESTED_BUF_SIZE = 2^22; |
---|
[2124] | 148 | |
---|
[4818] | 149 | % Call to 'init_sockets' will internally call setReuseAddress and setBroadcast |
---|
[2149] | 150 | obj.sock = wl_mex_udp_transport('init_socket'); |
---|
| 151 | wl_mex_udp_transport('set_so_timeout', obj.sock, 1); |
---|
| 152 | wl_mex_udp_transport('set_send_buf_size', obj.sock, REQUESTED_BUF_SIZE); |
---|
| 153 | wl_mex_udp_transport('set_rcvd_buf_size', obj.sock, REQUESTED_BUF_SIZE); |
---|
[2084] | 154 | |
---|
[2149] | 155 | x = wl_mex_udp_transport('get_rcvd_buf_size', obj.sock); |
---|
[2084] | 156 | obj.rxBufferSize = x; |
---|
| 157 | |
---|
| 158 | if(x < REQUESTED_BUF_SIZE) |
---|
| 159 | fprintf('OS reduced recv buffer size to %d\n', x); |
---|
| 160 | end |
---|
| 161 | |
---|
| 162 | obj.status = 1; |
---|
| 163 | end |
---|
| 164 | |
---|
| 165 | function out = procCmd(obj,nodeInd,node,cmdStr,varargin) |
---|
[4332] | 166 | % wl_node procCmd(obj, nodeInd, node, varargin) |
---|
| 167 | % obj: Node object (when called using dot notation) |
---|
| 168 | % nodeInd: Index of the current node, when wl_node is iterating over nodes |
---|
| 169 | % node: Current node object |
---|
| 170 | % cmdStr: Command string of the interface command |
---|
| 171 | % varargin: |
---|
| 172 | % [1:N} Command arguments |
---|
| 173 | % |
---|
| 174 | out = []; |
---|
| 175 | |
---|
[2084] | 176 | cmdStr = lower(cmdStr); |
---|
| 177 | switch(cmdStr) |
---|
[4332] | 178 | |
---|
| 179 | %--------------------------------------------------------- |
---|
[2084] | 180 | case 'ping' |
---|
| 181 | % Test to make sure node can be accessed via this |
---|
| 182 | % transport |
---|
| 183 | % |
---|
| 184 | % Arguments: none |
---|
| 185 | % Returns: true if board responds; raises error otherwise |
---|
[4332] | 186 | % |
---|
[2084] | 187 | myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_PING)); |
---|
| 188 | node.sendCmd(myCmd); |
---|
[4332] | 189 | out = true; % sendCmd will timeout and raise error if board doesn't respond |
---|
[2084] | 190 | |
---|
[4332] | 191 | %--------------------------------------------------------- |
---|
[2084] | 192 | case 'payload_size_test' |
---|
[4332] | 193 | % Determine objects maxPayload parameter |
---|
[2084] | 194 | % |
---|
| 195 | % Arguments: none |
---|
| 196 | % Returns: none |
---|
[4332] | 197 | % |
---|
[2084] | 198 | configFile = which('wl_config.ini'); |
---|
[4332] | 199 | |
---|
[2084] | 200 | if(isempty(configFile)) |
---|
[2865] | 201 | error('cannot find wl_config.ini. please run wl_setup.m'); |
---|
[2084] | 202 | end |
---|
[4332] | 203 | |
---|
[4375] | 204 | readKeys = {'network', '', 'max_transport_payload_size', ''}; |
---|
| 205 | max_transport_payload_size = inifile(configFile, 'read', readKeys); |
---|
| 206 | max_transport_payload_size = str2num(max_transport_payload_size{1}); |
---|
[2084] | 207 | |
---|
[4375] | 208 | % Determine the payloads to test |
---|
| 209 | payloadTestSizes = []; |
---|
| 210 | |
---|
| 211 | for i = [1000 1470 5000 8966] |
---|
| 212 | if (i < max_transport_payload_size) |
---|
| 213 | payloadTestSizes = [payloadTestSizes, i]; |
---|
| 214 | end |
---|
[2084] | 215 | end |
---|
| 216 | |
---|
[4375] | 217 | payloadTestSizes = [payloadTestSizes, max_transport_payload_size]; |
---|
| 218 | |
---|
[4386] | 219 | % WARPLab Header is (see http://warpproject.org/trac/wiki/WARPLab/Reference/Architecture/WireFormat ): |
---|
| 220 | % - 2 byte pad |
---|
| 221 | % - Transport header |
---|
| 222 | % - Command header |
---|
| 223 | payloadTestSizes = floor((payloadTestSizes - (sizeof(node.transport.hdr) + sizeof(wl_cmd) + 2)) / 4); |
---|
[4332] | 224 | |
---|
[2084] | 225 | for index = 1:length(payloadTestSizes) |
---|
| 226 | myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_PAYLOADSIZETEST)); |
---|
| 227 | myCmd.addArgs(1:payloadTestSizes(index)); |
---|
| 228 | try |
---|
| 229 | resp = node.sendCmd(myCmd); |
---|
[4818] | 230 | obj.setMaxPayload(resp.getArgs); |
---|
[4375] | 231 | if(obj.getMaxPayload() < (payloadTestSizes(index) * 4)) |
---|
[2084] | 232 | break; |
---|
| 233 | end |
---|
| 234 | catch ME |
---|
| 235 | break |
---|
| 236 | end |
---|
| 237 | end |
---|
| 238 | |
---|
[4332] | 239 | %--------------------------------------------------------- |
---|
[2084] | 240 | case 'add_node_group_id' |
---|
| 241 | % Adds a Node Group ID to the node so that it can |
---|
| 242 | % process broadcast commands that are received from |
---|
| 243 | % that node group. |
---|
| 244 | % |
---|
| 245 | % Arguments: (uint32 NODE_GRP_ID) |
---|
| 246 | % Returns: none |
---|
| 247 | % |
---|
| 248 | % NODE_GRP_ID: ID provided by wl_node_grp |
---|
[4332] | 249 | % |
---|
| 250 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODEGRPID_ADD)); |
---|
| 251 | myCmd.addArgs(varargin{1}); |
---|
[2084] | 252 | node.sendCmd(myCmd); |
---|
| 253 | |
---|
[4332] | 254 | %--------------------------------------------------------- |
---|
[2084] | 255 | case 'clear_node_group_id' |
---|
| 256 | % Clears a Node Group ID from the node so it can ignore |
---|
| 257 | % broadcast commands that are received from that node |
---|
| 258 | % group. |
---|
| 259 | % |
---|
| 260 | % Arguments: (uint32 NODE_GRP_ID) |
---|
| 261 | % Returns: none |
---|
| 262 | % |
---|
| 263 | % NODE_GRP_ID: ID provided by wl_node_grp |
---|
[4332] | 264 | % |
---|
| 265 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODEGRPID_CLEAR)); |
---|
| 266 | myCmd.addArgs(varargin{1}); |
---|
[2084] | 267 | node.sendCmd(myCmd); |
---|
| 268 | |
---|
[4332] | 269 | %--------------------------------------------------------- |
---|
[2084] | 270 | otherwise |
---|
[2865] | 271 | error('unknown command ''%s''',cmdStr); |
---|
[2084] | 272 | end |
---|
| 273 | |
---|
[4332] | 274 | if((iscell(out) == 0) && (numel(out) ~= 1)) |
---|
[2084] | 275 | out = {out}; |
---|
| 276 | end |
---|
| 277 | end |
---|
| 278 | |
---|
[2124] | 279 | function close(obj) |
---|
[2084] | 280 | if(~isempty(obj.sock)) |
---|
| 281 | try |
---|
[2149] | 282 | wl_mex_udp_transport('close', obj.sock); |
---|
[2084] | 283 | catch closeError |
---|
[2865] | 284 | warning( 'Error closing socket; mex error was %s', closeError.message) |
---|
[2084] | 285 | end |
---|
| 286 | end |
---|
| 287 | obj.status=0; |
---|
| 288 | end |
---|
| 289 | |
---|
| 290 | function delete(obj) |
---|
| 291 | obj.close(); |
---|
| 292 | end |
---|
| 293 | |
---|
| 294 | function flush(obj) |
---|
[4332] | 295 | % Currently not implemented |
---|
[2084] | 296 | end |
---|
[4332] | 297 | end % methods |
---|
[2084] | 298 | |
---|
| 299 | |
---|
| 300 | methods (Hidden = true) |
---|
| 301 | |
---|
[4309] | 302 | function reply = send(obj, send_data, response, varargin) |
---|
| 303 | % send_data : Data to be sent to the node |
---|
| 304 | % response : Does the transmission require a response from the node |
---|
| 305 | % varargin{1}: (optional) |
---|
| 306 | % - increment the transport header; defaults to true if not specified |
---|
| 307 | % |
---|
[2084] | 308 | |
---|
[4309] | 309 | % Initialize variables |
---|
| 310 | maxAttempts = 2; % Maximum times the transport will re-try a packet |
---|
| 311 | payload = uint32(send_data); % Change data to 32 bit unsigned integers for transmit |
---|
| 312 | obj.hdr.msgLength = ((length(payload)) * 4); % Length in bytes (4 bytes / sample) |
---|
| 313 | reply = []; % Initialize array for response from the node |
---|
| 314 | robust = response; |
---|
| 315 | |
---|
| 316 | % Process command line arguments |
---|
| 317 | increment_hdr = true; |
---|
| 318 | |
---|
| 319 | if (nargin == 4) |
---|
| 320 | increment_hdr = varargin{1}; |
---|
[2084] | 321 | end |
---|
| 322 | |
---|
[4309] | 323 | % Set the appropriate flags in the header |
---|
[2084] | 324 | if(robust) |
---|
[4309] | 325 | obj.hdr.flags = bitset(obj.hdr.flags, 1, 1); |
---|
[2084] | 326 | else |
---|
[4309] | 327 | obj.hdr.flags = bitset(obj.hdr.flags, 1, 0); |
---|
[2084] | 328 | end |
---|
| 329 | |
---|
[4309] | 330 | % Increment the header and serialize all the data into a uint32 array |
---|
| 331 | if (increment_hdr) |
---|
| 332 | obj.hdr.increment; |
---|
| 333 | end |
---|
| 334 | |
---|
| 335 | % Format the data / address arguments for transmission |
---|
| 336 | data = [obj.hdr.serialize, payload]; % Get a unified array with the header and data |
---|
| 337 | data8 = [zeros(1,2,'uint8') typecast(swapbytes(uint32(data)), 'uint8')]; % Change to uint8 and pad by 2 bytes so payload is 32 bit aligned w/ Eth header |
---|
| 338 | |
---|
| 339 | % Send the packet |
---|
[2149] | 340 | size = wl_mex_udp_transport('send', obj.sock, data8, length(data8), obj.address, obj.port); |
---|
[2084] | 341 | |
---|
[4309] | 342 | % Wait to receive reply from the board |
---|
| 343 | if(robust == 1) |
---|
| 344 | MAX_PKT_LEN = obj.getMaxPayload() + 100; |
---|
| 345 | hdr_length = obj.hdr.length; |
---|
| 346 | currTx = 1; |
---|
| 347 | numWaitRetries = 0; |
---|
[2084] | 348 | receivedResponse = 0; |
---|
[4309] | 349 | currTime = tic; |
---|
[2084] | 350 | |
---|
| 351 | while (receivedResponse == 0) |
---|
| 352 | try |
---|
[2149] | 353 | [recv_len, recv_data8] = wl_mex_udp_transport('receive', obj.sock, MAX_PKT_LEN); |
---|
[2084] | 354 | |
---|
| 355 | catch receiveError |
---|
[4818] | 356 | error('%s.m -- Failed to receive UDP packet.\nMEX transport error message follows:\n %s\n', mfilename, receiveError.message); |
---|
[2084] | 357 | end |
---|
| 358 | |
---|
[4309] | 359 | % If we have a packet, then process the contents |
---|
[2084] | 360 | if(recv_len > 0) |
---|
[4818] | 361 | reply8 = [recv_data8(3:recv_len) zeros(mod(-(recv_len - 2), 4), 1, 'uint8')]; |
---|
[2084] | 362 | reply = swapbytes(typecast(reply8, 'uint32')); |
---|
[4309] | 363 | |
---|
| 364 | % Check the header to see if this was a valid reply |
---|
| 365 | if(obj.hdr.isReply(reply(1:hdr_length))) |
---|
[2084] | 366 | |
---|
[4309] | 367 | % Check the header to see if we need to wait for the node to be ready |
---|
[4818] | 368 | if (obj.hdr.isNodeReady(reply(1:hdr_length))) |
---|
[4309] | 369 | |
---|
| 370 | % Strip off transport header to give response to caller |
---|
| 371 | reply = reply((hdr_length + 1):end); |
---|
| 372 | |
---|
| 373 | if(isempty(reply)) |
---|
| 374 | reply = []; |
---|
| 375 | end |
---|
| 376 | |
---|
| 377 | receivedResponse = 1; |
---|
| 378 | |
---|
| 379 | else |
---|
| 380 | % Node is not ready; Wait and try again |
---|
[4818] | 381 | pause(obj.TRANSPORT_NOT_READY_WAIT_TIME); |
---|
[4309] | 382 | numWaitRetries = numWaitRetries + 1; |
---|
| 383 | |
---|
| 384 | % Send packet packet again |
---|
| 385 | obj.sock.send(pkt_send); |
---|
| 386 | |
---|
| 387 | % Check that we have not spent a "long time" waiting for samples to be ready |
---|
[4818] | 388 | if (numWaitRetries > obj.TRANSPORT_NOT_READY_MAX_RETRY) |
---|
[4418] | 389 | error('wl_transport_eth_mex:send:isReady', 'Error: Timeout waiting for node to be ready. Please check the node operation.'); |
---|
[4309] | 390 | end |
---|
| 391 | |
---|
| 392 | reply = []; |
---|
[2084] | 393 | end |
---|
[4309] | 394 | end |
---|
[2084] | 395 | end |
---|
| 396 | |
---|
[4309] | 397 | % Look for timeout |
---|
| 398 | if ((toc(currTime) > obj.timeout) && (receivedResponse == 0)) |
---|
[2084] | 399 | if(currTx == maxAttempts) |
---|
[4818] | 400 | error('wl_transport_eth_mex:send:noReply', 'maximum number of retransmissions met without reply from node'); |
---|
[2084] | 401 | end |
---|
[4309] | 402 | |
---|
| 403 | % Retry the packet |
---|
[2149] | 404 | size = wl_mex_udp_transport('send', obj.sock, data8, length(data8), obj.address, obj.port); |
---|
[4309] | 405 | currTx = currTx + 1; |
---|
[2084] | 406 | currTime = tic; |
---|
[4309] | 407 | end |
---|
[2084] | 408 | end |
---|
| 409 | end |
---|
| 410 | end |
---|
| 411 | |
---|
| 412 | function resp = receive(obj) |
---|
[4309] | 413 | % Receive all packets from the Ethernet interface and pass array |
---|
| 414 | % of valid responses to the caller. |
---|
| 415 | % |
---|
| 416 | % NOTE: This function will strip off the transport header from the responses |
---|
| 417 | % NOTE: This function is non-blocking and will return an empty response if |
---|
| 418 | % there are no packets available. |
---|
| 419 | % |
---|
| 420 | |
---|
| 421 | % Initialize variables |
---|
[2149] | 422 | MAX_PKT_LEN = obj.getMaxPayload() + 100; |
---|
[4309] | 423 | hdr_length = obj.hdr.length; |
---|
[2124] | 424 | done = false; |
---|
| 425 | resp = []; |
---|
[2084] | 426 | |
---|
| 427 | while ~done |
---|
| 428 | try |
---|
[2149] | 429 | [recv_len, recv_data8] = wl_mex_udp_transport('receive', obj.sock, MAX_PKT_LEN); |
---|
[2084] | 430 | |
---|
| 431 | catch receiveError |
---|
[4818] | 432 | error('%s.m -- Failed to receive UDP packet.\nMEX transport error message follows:\n %s\n', mfilename, receiveError.message); |
---|
[2084] | 433 | end |
---|
| 434 | |
---|
| 435 | if(recv_len > 0) |
---|
[4818] | 436 | reply8 = [recv_data8(3:recv_len) zeros(mod(-(recv_len - 2), 4), 1, 'uint8')].'; |
---|
[2124] | 437 | reply = swapbytes(typecast(reply8, 'uint32')); |
---|
[2084] | 438 | |
---|
[4309] | 439 | if(obj.hdr.isReply(reply(1:hdr_length))) |
---|
| 440 | % Strip off transport header to give response to caller |
---|
| 441 | reply = reply((hdr_length + 1):end); |
---|
| 442 | |
---|
[2084] | 443 | if(isempty(reply)) |
---|
| 444 | reply = []; |
---|
| 445 | end |
---|
[4309] | 446 | |
---|
| 447 | resp = [resp, reply]; |
---|
| 448 | |
---|
| 449 | done = true; |
---|
[2084] | 450 | end |
---|
| 451 | else |
---|
| 452 | done = true; |
---|
| 453 | end |
---|
| 454 | end |
---|
| 455 | end |
---|
| 456 | |
---|
| 457 | function dottedIPout = int2IP(obj,intIn) |
---|
| 458 | addrChars(4) = mod(intIn, 2^8); |
---|
| 459 | addrChars(3) = mod(bitshift(intIn, -8), 2^8); |
---|
| 460 | addrChars(2) = mod(bitshift(intIn, -16), 2^8); |
---|
| 461 | addrChars(1) = mod(bitshift(intIn, -24), 2^8); |
---|
| 462 | dottedIPout = sprintf('%d.%d.%d.%d', addrChars); |
---|
| 463 | end |
---|
| 464 | |
---|
| 465 | function intOut = IP2int(obj,dottedIP) |
---|
| 466 | addrChars = sscanf(dottedIP, '%d.%d.%d.%d')'; |
---|
| 467 | intOut = 2^0 * addrChars(4) + 2^8 * addrChars(3) + 2^16 * addrChars(2) + 2^24 * addrChars(1); |
---|
| 468 | end |
---|
[2124] | 469 | |
---|
| 470 | function reply = print_cmd(obj, type, num_samples, start_sample, buffer_ids, command) |
---|
| 471 | fprintf('Command: %s \n', type); |
---|
| 472 | fprintf(' # samples = %d start sample = %d \n', num_samples, start_sample); |
---|
| 473 | fprintf(' buffer IDs = '); |
---|
| 474 | for index = 1:length(buffer_ids) |
---|
| 475 | fprintf('%d ', buffer_ids(index)); |
---|
| 476 | end |
---|
| 477 | fprintf('\n Command (%d bytes): ', length(command) ); |
---|
| 478 | for index = 1:length(command) |
---|
| 479 | switch(index) |
---|
| 480 | case 1 |
---|
| 481 | fprintf('\n Padding : '); |
---|
| 482 | case 3 |
---|
| 483 | fprintf('\n Dest ID : '); |
---|
| 484 | case 5 |
---|
| 485 | fprintf('\n Src ID : '); |
---|
| 486 | case 7 |
---|
| 487 | fprintf('\n Rsvd : '); |
---|
| 488 | case 8 |
---|
| 489 | fprintf('\n Pkt Type : '); |
---|
| 490 | case 9 |
---|
| 491 | fprintf('\n Length : '); |
---|
| 492 | case 11 |
---|
| 493 | fprintf('\n Seq Num : '); |
---|
| 494 | case 13 |
---|
| 495 | fprintf('\n Flags : '); |
---|
| 496 | case 15 |
---|
| 497 | fprintf('\n Command : '); |
---|
| 498 | case 19 |
---|
| 499 | fprintf('\n Length : '); |
---|
| 500 | case 21 |
---|
| 501 | fprintf('\n # Args : '); |
---|
| 502 | case 23 |
---|
| 503 | fprintf('\n Args : '); |
---|
| 504 | otherwise |
---|
| 505 | if ( index > 23 ) |
---|
| 506 | if ( ( mod( index + 1, 4 ) == 0 ) && not( index == length(command) ) ) |
---|
| 507 | fprintf('\n '); |
---|
| 508 | end |
---|
| 509 | end |
---|
| 510 | end |
---|
| 511 | fprintf('%2x ', command(index) ); |
---|
| 512 | end |
---|
| 513 | fprintf('\n\n'); |
---|
| 514 | |
---|
| 515 | reply = 0; |
---|
| 516 | end |
---|
| 517 | |
---|
| 518 | %----------------------------------------------------------------- |
---|
| 519 | % read_buffers |
---|
[2149] | 520 | % Command to utilize additional functionality in the wl_mex_udp_transport C code in order to |
---|
[2124] | 521 | % speed up processing of 'readIQ' and 'readRSSI' commands |
---|
| 522 | % |
---|
| 523 | % Supports the following calling conventions: |
---|
| 524 | % - start_sample -> must be a single value |
---|
| 525 | % - num_samples -> must be a single value |
---|
| 526 | % - buffer_ids -> Can be a vector of single RF interfaces |
---|
| 527 | % |
---|
[4815] | 528 | function reply = read_buffers(obj, func, num_samples, buffer_ids, start_sample, seq_num_tracker, seq_num_match_severity, node_id_str, wl_command, input_type) |
---|
| 529 | % func : Function within read_buffers to call |
---|
| 530 | % number_samples : Number of samples requested |
---|
| 531 | % buffer_ids : Array of Buffer IDs |
---|
| 532 | % start_sample : Start sample |
---|
| 533 | % seq_num_tracker : Sequence number tracker |
---|
| 534 | % seq_num_match_severity : Severity of message when sequence numbers match on reads |
---|
| 535 | % node_id_str : String representation of Node ID |
---|
| 536 | % wl_command : Ethernet WARPLab command |
---|
| 537 | % input_type : Type of sample array: |
---|
| 538 | % 0 ==> 'double' |
---|
| 539 | % 1 ==> 'single' |
---|
| 540 | % 2 ==> 'int16' |
---|
| 541 | % 3 ==> 'raw' |
---|
[2124] | 542 | |
---|
| 543 | % Get the lowercase version of the function |
---|
| 544 | func = lower(func); |
---|
| 545 | |
---|
| 546 | % Calculate how many transport packets are required |
---|
| 547 | numPktsRequired = ceil(double(num_samples)/double(obj.maxSamples)); |
---|
[4311] | 548 | |
---|
| 549 | % Arguments for the command will be set in the MEX function since it is faster |
---|
| 550 | % wl_command.setArgs(buffer_id, start_sample, num_samples, obj.maxSamples * 4, numPktsRequired); |
---|
[2124] | 551 | |
---|
[4311] | 552 | % Construct the minimal WARPLab command that will be used used to get the samples |
---|
| 553 | % NOTE: Since we did not add the arguments of the command thru setArgs, we need to pad the structure so that |
---|
| 554 | % the proper amount of memory is allocated to be available to the MEX |
---|
| 555 | payload = uint32( wl_command.serialize() ); % Convert command to uint32 |
---|
| 556 | cmd_args_pad = uint32( zeros(1, 5) ); % Padding for command args |
---|
| 557 | obj.hdr.flags = bitset(obj.hdr.flags,1,0); % We do not need a response for the sent command |
---|
| 558 | obj.hdr.msgLength = ( ( length( payload ) ) + 5) * 4; % Length in bytes |
---|
| 559 | |
---|
| 560 | data = [obj.hdr.serialize, payload, cmd_args_pad]; |
---|
[4393] | 561 | data8 = [zeros(1,2,'uint8') typecast(swapbytes(uint32(data)), 'uint8')]; |
---|
[4311] | 562 | |
---|
[4393] | 563 | % Pass all of the command arguments down to MEX |
---|
| 564 | switch(func) |
---|
| 565 | case 'iq' |
---|
| 566 | % Calls the MEX read_iq command |
---|
| 567 | % |
---|
| 568 | % obj.print_cmd('READ_IQ', num_samples, start_sample, buffer_ids, data8); |
---|
[4311] | 569 | |
---|
[4815] | 570 | [num_rcvd_samples, cmds_used, rx_samples] = wl_mex_udp_transport('read_iq', obj.sock, data8, length(data8), obj.address, obj.port, num_samples, buffer_ids, start_sample, obj.maxSamples * 4, numPktsRequired, input_type, seq_num_tracker, seq_num_match_severity, node_id_str); |
---|
[4311] | 571 | |
---|
[4393] | 572 | % Code to test higher level matlab code without invoking the MEX transport |
---|
| 573 | % |
---|
| 574 | % rx_samples = zeros( num_samples, 1 ); |
---|
| 575 | % cmds_used = 0; |
---|
[4311] | 576 | |
---|
[4393] | 577 | case 'rssi' |
---|
| 578 | % Calls the MEX read_rssi command |
---|
| 579 | % |
---|
| 580 | % obj.print_cmd('READ_RSSI', num_samples, start_sample, buffer_ids, data8); |
---|
[4311] | 581 | |
---|
[4815] | 582 | [num_rcvd_samples, cmds_used, rx_samples] = wl_mex_udp_transport('read_rssi', obj.sock, data8, length(data8), obj.address, obj.port, num_samples, buffer_ids, start_sample, obj.maxSamples * 4, numPktsRequired, input_type, seq_num_tracker, seq_num_match_severity, node_id_str); |
---|
[4311] | 583 | |
---|
[4393] | 584 | % Code to test higher level matlab code without invoking the MEX transport |
---|
| 585 | % |
---|
| 586 | % rx_samples = zeros( num_samples * 2, 1 ); |
---|
| 587 | % cmds_used = 0; |
---|
[4311] | 588 | |
---|
[4393] | 589 | otherwise |
---|
[4815] | 590 | error('unknown command ''%s''', cmdStr); |
---|
[2124] | 591 | end |
---|
| 592 | |
---|
[4393] | 593 | obj.hdr.increment(cmds_used); |
---|
| 594 | |
---|
[2124] | 595 | reply = rx_samples; |
---|
| 596 | end |
---|
| 597 | |
---|
| 598 | |
---|
| 599 | %----------------------------------------------------------------- |
---|
| 600 | % write_buffers |
---|
[2149] | 601 | % Command to utilize additional functionality in the wl_mex_udp_transport C code in order to |
---|
[2124] | 602 | % speed up processing of 'writeIQ' commands |
---|
| 603 | % |
---|
[4393] | 604 | function reply = write_buffers(obj, func, num_samples, samples, buffer_ids, start_sample, hw_ver, wl_command, check_chksum, input_type) |
---|
[2124] | 605 | % func : Function within read_buffers to call |
---|
| 606 | % number_samples : Number of samples requested |
---|
[4393] | 607 | % samples : Array of IQ samples |
---|
[2124] | 608 | % buffer_ids : Array of Buffer IDs |
---|
| 609 | % start_sample : Start sample |
---|
[2166] | 610 | % hw_ver : Hardware version of the Node |
---|
[2124] | 611 | % wl_command : Ethernet WARPLab command |
---|
[2919] | 612 | % check_chksum : Perform the WriteIQ checksum check inside the function |
---|
[4393] | 613 | % input_type : Type of sample array: |
---|
| 614 | % 0 ==> 'double' |
---|
| 615 | % 1 ==> 'single' |
---|
| 616 | % 2 ==> 'int16' |
---|
| 617 | % 3 ==> 'raw' |
---|
[2124] | 618 | |
---|
[4393] | 619 | % Calculate how many transport packets are required |
---|
| 620 | num_pkts_required = ceil(double(num_samples)/double(obj.maxSamples)); |
---|
[2124] | 621 | |
---|
| 622 | % Construct the WARPLab command that will be used used to write the samples |
---|
| 623 | payload = uint32( wl_command.serialize() ); % Convert command to uint32 |
---|
[4309] | 624 | obj.hdr.flags = bitset(obj.hdr.flags,1,0); % We do not need a response for the sent command |
---|
| 625 | obj.hdr.msgLength = ( length( payload ) ) * 4; % Length in bytes |
---|
[4393] | 626 | |
---|
[2124] | 627 | data = [obj.hdr.serialize, payload]; |
---|
[4393] | 628 | data8 = [zeros(1,2,'uint8') typecast(swapbytes(uint32(data)), 'uint8')]; |
---|
[2124] | 629 | |
---|
[4393] | 630 | socket = obj.sock; |
---|
| 631 | address = obj.address; |
---|
| 632 | port = obj.port; |
---|
| 633 | max_payload = obj.getMaxPayload(); |
---|
| 634 | max_samples = obj.maxSamples; |
---|
| 635 | |
---|
| 636 | |
---|
[2124] | 637 | func = lower(func); |
---|
| 638 | switch(func) |
---|
| 639 | case 'iq' |
---|
| 640 | % Calls the MEX read_iq command |
---|
| 641 | % |
---|
[4393] | 642 | [cmds_used, checksum] = wl_mex_udp_transport('write_iq', socket, data8, max_payload, address, port, num_samples, samples, buffer_ids, start_sample, num_pkts_required, max_samples, hw_ver, check_chksum, input_type); |
---|
[2124] | 643 | |
---|
[4393] | 644 | % Increment the transport header by cmds_used (ie number of commands used |
---|
| 645 | obj.hdr.increment(cmds_used); |
---|
| 646 | |
---|
| 647 | % Record the checksum for that Write IQ |
---|
| 648 | reply = checksum; |
---|
| 649 | |
---|
[2124] | 650 | otherwise |
---|
[2865] | 651 | error('unknown command ''%s''',cmdStr); |
---|
[2124] | 652 | end |
---|
| 653 | |
---|
[2919] | 654 | reply = checksum; |
---|
[2124] | 655 | end |
---|
[2084] | 656 | end |
---|
[4332] | 657 | end % classdef |
---|
[2124] | 658 | |
---|
| 659 | |
---|
| 660 | |
---|
| 661 | |
---|